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:
parent
9b908a1994
commit
cb03495d36
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user