Bug 60226 - ClassLoader workaround for OSGI when processing OOXML files

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1763922 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2016-10-08 17:07:15 +00:00
parent 9b908a1994
commit cb03495d36
3 changed files with 103 additions and 34 deletions

View File

@ -23,6 +23,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
@ -32,6 +33,7 @@ import javax.xml.stream.XMLStreamReader;
import org.apache.poi.util.DocumentHelper;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaTypeLoader;
import org.apache.xmlbeans.XmlBeans;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
@ -46,6 +48,8 @@ import org.xml.sax.SAXException;
@SuppressWarnings("deprecation")
public class POIXMLTypeLoader {
private static ThreadLocal<ClassLoader> classLoader = new ThreadLocal<ClassLoader>();
public static final XmlOptions DEFAULT_XML_OPTIONS;
static {
DEFAULT_XML_OPTIONS = new XmlOptions();
@ -80,8 +84,32 @@ public class POIXMLTypeLoader {
return options == null ? DEFAULT_XML_OPTIONS : options;
}
/**
* Sets the {@link ClassLoader} which is used, when XmlBeans are dynamically instantiated -
* opposed to being loaded by the factory class which is accompanied by each generated XmlBeans interface.
* <p>
* This is especially necessary in a context which doesn't guarantee that the current (thread) context
* cassloader has access to all XmlBeans schema definitions (*.xsb) - which is typically in OSGI the case.
* <p>
* The classloader will be only set for the current thread in a {@link ThreadLocal}. Although the
* ThreadLocal is implemented via a {@link WeakReference}, it's good style to {@code null} the classloader
* when the user code is finalized.
*
* @param cl the classloader to be used when XmlBeans classes and definitions are looked up
*/
public static void setClassLoader(ClassLoader cl) {
classLoader.set(cl);
}
private static SchemaTypeLoader getTypeLoader() {
ClassLoader cl = classLoader.get();
return (cl == null)
? XmlBeans.getContextTypeLoader()
: XmlBeans.typeLoaderForClassLoader(cl);
}
public static XmlObject newInstance(SchemaType type, XmlOptions options) {
return XmlBeans.getContextTypeLoader().newInstance(type, getXmlOptions(options));
return getTypeLoader().newInstance(type, getXmlOptions(options));
}
public static XmlObject parse(String xmlText, SchemaType type, XmlOptions options) throws XmlException {
@ -113,34 +141,34 @@ public class POIXMLTypeLoader {
public static XmlObject parse(InputStream jiois, SchemaType type, XmlOptions options) throws XmlException, IOException {
try {
Document doc = DocumentHelper.readDocument(jiois);
return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));
return getTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));
} catch (SAXException e) {
throw new IOException("Unable to parse xml bean", e);
}
}
public static XmlObject parse(XMLStreamReader xsr, SchemaType type, XmlOptions options) throws XmlException {
return XmlBeans.getContextTypeLoader().parse(xsr, type, getXmlOptions(options));
return getTypeLoader().parse(xsr, type, getXmlOptions(options));
}
public static XmlObject parse(Reader jior, SchemaType type, XmlOptions options) throws XmlException, IOException {
try {
Document doc = DocumentHelper.readDocument(new InputSource(jior));
return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));
return getTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));
} catch (SAXException e) {
throw new XmlException("Unable to parse xml bean", e);
}
}
public static XmlObject parse(Node node, SchemaType type, XmlOptions options) throws XmlException {
return XmlBeans.getContextTypeLoader().parse(node, type, getXmlOptions(options));
return getTypeLoader().parse(node, type, getXmlOptions(options));
}
public static XmlObject parse(XMLInputStream xis, SchemaType type, XmlOptions options) throws XmlException, XMLStreamException {
return XmlBeans.getContextTypeLoader().parse(xis, type, getXmlOptions(options));
return getTypeLoader().parse(xis, type, getXmlOptions(options));
}
public static XMLInputStream newValidatingXMLInputStream ( XMLInputStream xis, SchemaType type, XmlOptions options ) throws XmlException, XMLStreamException {
return XmlBeans.getContextTypeLoader().newValidatingXMLInputStream(xis, type, getXmlOptions(options));
return getTypeLoader().newValidatingXMLInputStream(xis, type, getXmlOptions(options));
}
}

View File

@ -19,8 +19,6 @@
package org.apache.poi.xslf.usermodel;
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
@ -29,7 +27,6 @@ import java.util.List;
import javax.xml.namespace.QName;
import org.apache.poi.POIXMLException;
import org.apache.poi.sl.draw.DrawFactory;
import org.apache.poi.sl.draw.DrawTableShape;
import org.apache.poi.sl.draw.DrawTextShape;
@ -37,7 +34,6 @@ import org.apache.poi.sl.usermodel.TableShape;
import org.apache.poi.util.Internal;
import org.apache.poi.util.Units;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;
import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;
@ -53,6 +49,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFra
public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow>,
TableShape<XSLFShape,XSLFTextParagraph> {
/* package */ static final String TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
/* package */ static final String DRAWINGML_URI = "http://schemas.openxmlformats.org/drawingml/2006/main";
private CTTable _table;
private List<XSLFTableRow> _rows;
@ -60,28 +57,30 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow
/*package*/ XSLFTable(CTGraphicalObjectFrame shape, XSLFSheet sheet){
super(shape, sheet);
XmlObject[] rs = shape.getGraphic().getGraphicData()
.selectPath("declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ./a:tbl");
if (rs.length == 0) {
throw new IllegalStateException("a:tbl element was not found in\n " + shape.getGraphic().getGraphicData());
CTGraphicalObjectData god = shape.getGraphic().getGraphicData();
XmlCursor xc = god.newCursor();
if (!xc.toChild(DRAWINGML_URI, "tbl")) {
throw new IllegalStateException("a:tbl element was not found in\n " + god);
}
XmlObject xo = xc.getObject();
// Pesky XmlBeans bug - see Bugzilla #49934
// it never happens when using the full ooxml-schemas jar but may happen with the abridged poi-ooxml-schemas
if(rs[0] instanceof XmlAnyTypeImpl){
try {
rs[0] = CTTable.Factory.parse(rs[0].toString(), DEFAULT_XML_OPTIONS);
}catch (XmlException e){
throw new POIXMLException(e);
}
if (xo instanceof XmlAnyTypeImpl){
String errStr =
"Schemas (*.xsb) for CTTable can't be loaded - usually this happens when OSGI " +
"loading is used and the thread context classloader has no reference to " +
"the xmlbeans classes - use POIXMLTypeLoader.setClassLoader() to set the loader, " +
"e.g. with CTTable.class.getClassLoader()"
;
throw new IllegalStateException(errStr);
}
_table = (CTTable)xo;
xc.dispose();
_table = (CTTable) rs[0];
CTTableRow[] trArray = _table.getTrArray();
_rows = new ArrayList<XSLFTableRow>(trArray.length);
for(CTTableRow row : trArray) {
XSLFTableRow xr = new XSLFTableRow(row, this);
_rows.add(xr);
_rows = new ArrayList<XSLFTableRow>(_table.sizeOfTrArray());
for(CTTableRow row : _table.getTrArray()) {
_rows.add(new XSLFTableRow(row, this));
}
updateRowColIndexes();
}
@ -171,13 +170,18 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow
frame.addNewXfrm();
CTGraphicalObjectData gr = frame.addNewGraphic().addNewGraphicData();
XmlCursor cursor = gr.newCursor();
cursor.toNextToken();
cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tbl"));
cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tblPr"));
cursor.toNextToken();
cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tblGrid"));
cursor.dispose();
XmlCursor grCur = gr.newCursor();
grCur.toNextToken();
grCur.beginElement(new QName(DRAWINGML_URI, "tbl"));
CTTable tbl = CTTable.Factory.newInstance();
tbl.addNewTblPr();
tbl.addNewTblGrid();
XmlCursor tblCur = tbl.newCursor();
tblCur.moveXmlContents(grCur);
tblCur.dispose();
grCur.dispose();
gr.setUri(TABLE_URI);
return frame;
}

View File

@ -27,6 +27,7 @@ import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
@ -41,6 +42,8 @@ import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.apache.poi.util.NullOutputStream;
import org.apache.poi.util.PackageHelper;
import org.apache.poi.util.TempFile;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xslf.usermodel.XSLFShape;
import org.junit.Test;
/**
@ -277,4 +280,38 @@ public final class TestPOIXMLDocument {
open.close();
}
}
@Test(expected=IllegalStateException.class)
public void testOSGIClassLoadingAsIs() throws IOException {
Thread thread = Thread.currentThread();
ClassLoader cl = thread.getContextClassLoader();
InputStream is = POIDataSamples.getSlideShowInstance().openResourceAsStream("table_test.pptx");
try {
thread.setContextClassLoader(cl.getParent());
XMLSlideShow ppt = new XMLSlideShow(is);
ppt.getSlides().get(0).getShapes();
} finally {
thread.setContextClassLoader(cl);
is.close();
}
}
@Test
public void testOSGIClassLoadingFixed() throws IOException {
Thread thread = Thread.currentThread();
ClassLoader cl = thread.getContextClassLoader();
InputStream is = POIDataSamples.getSlideShowInstance().openResourceAsStream("table_test.pptx");
try {
thread.setContextClassLoader(cl.getParent());
POIXMLTypeLoader.setClassLoader(cl);
XMLSlideShow ppt = new XMLSlideShow(is);
ppt.getSlides().get(0).getShapes();
} finally {
thread.setContextClassLoader(cl);
POIXMLTypeLoader.setClassLoader(null);
is.close();
}
}
}