]> source.dussan.org Git - poi.git/commitdiff
Bug 60226 - ClassLoader workaround for OSGI when processing OOXML files
authorAndreas Beeker <kiwiwings@apache.org>
Sat, 8 Oct 2016 17:07:15 +0000 (17:07 +0000)
committerAndreas Beeker <kiwiwings@apache.org>
Sat, 8 Oct 2016 17:07:15 +0000 (17:07 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1763922 13f79535-47bb-0310-9956-ffa450edef68

src/ooxml/java/org/apache/poi/POIXMLTypeLoader.java
src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java
src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java

index b703d73d87a65ee76b126447e5da72dc3e747ab1..eeed5f7c8d6a55c40ffb50dc12fab3bb92ab43ae 100644 (file)
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.io.InputStream;\r
 import java.io.Reader;\r
 import java.io.StringReader;\r
+import java.lang.ref.WeakReference;\r
 import java.net.URL;\r
 import java.util.Collections;\r
 import java.util.HashMap;\r
@@ -32,6 +33,7 @@ import javax.xml.stream.XMLStreamReader;
 \r
 import org.apache.poi.util.DocumentHelper;\r
 import org.apache.xmlbeans.SchemaType;\r
+import org.apache.xmlbeans.SchemaTypeLoader;\r
 import org.apache.xmlbeans.XmlBeans;\r
 import org.apache.xmlbeans.XmlException;\r
 import org.apache.xmlbeans.XmlObject;\r
@@ -46,6 +48,8 @@ import org.xml.sax.SAXException;
 @SuppressWarnings("deprecation")\r
 public class POIXMLTypeLoader {\r
 \r
+    private static ThreadLocal<ClassLoader> classLoader = new ThreadLocal<ClassLoader>();\r
+    \r
     public static final XmlOptions DEFAULT_XML_OPTIONS;\r
     static {\r
         DEFAULT_XML_OPTIONS = new XmlOptions();\r
@@ -80,8 +84,32 @@ public class POIXMLTypeLoader {
         return options == null ? DEFAULT_XML_OPTIONS : options;\r
     }\r
 \r
+    /**\r
+     * Sets the {@link ClassLoader} which is used, when XmlBeans are dynamically instantiated -\r
+     * opposed to being loaded by the factory class which is accompanied by each generated XmlBeans interface.\r
+     * <p>\r
+     * This is especially necessary in a context which doesn't guarantee that the current (thread) context\r
+     * cassloader has access to all XmlBeans schema definitions (*.xsb) - which is typically in OSGI the case.\r
+     * <p>\r
+     * The classloader will be only set for the current thread in a {@link ThreadLocal}. Although the\r
+     * ThreadLocal is implemented via a {@link WeakReference}, it's good style to {@code null} the classloader\r
+     * when the user code is finalized.\r
+     * \r
+     * @param cl the classloader to be used when XmlBeans classes and definitions are looked up\r
+     */\r
+    public static void setClassLoader(ClassLoader cl) {\r
+        classLoader.set(cl);\r
+    }\r
+    \r
+    private static SchemaTypeLoader getTypeLoader() {\r
+        ClassLoader cl = classLoader.get();\r
+        return (cl == null)\r
+            ? XmlBeans.getContextTypeLoader()\r
+            : XmlBeans.typeLoaderForClassLoader(cl);\r
+    }\r
+    \r
     public static XmlObject newInstance(SchemaType type, XmlOptions options) {\r
-        return XmlBeans.getContextTypeLoader().newInstance(type, getXmlOptions(options));\r
+        return getTypeLoader().newInstance(type, getXmlOptions(options));\r
     }\r
 \r
     public static XmlObject parse(String xmlText, SchemaType type, XmlOptions options) throws XmlException {\r
@@ -113,34 +141,34 @@ public class POIXMLTypeLoader {
     public static XmlObject parse(InputStream jiois, SchemaType type, XmlOptions options) throws XmlException, IOException {\r
         try {\r
             Document doc = DocumentHelper.readDocument(jiois);\r
-            return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));\r
+            return getTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));\r
         } catch (SAXException e) {\r
             throw new IOException("Unable to parse xml bean", e);\r
         }\r
     }\r
 \r
     public static XmlObject parse(XMLStreamReader xsr, SchemaType type, XmlOptions options) throws XmlException {\r
-        return XmlBeans.getContextTypeLoader().parse(xsr, type, getXmlOptions(options));\r
+        return getTypeLoader().parse(xsr, type, getXmlOptions(options));\r
     }\r
 \r
     public static XmlObject parse(Reader jior, SchemaType type, XmlOptions options) throws XmlException, IOException {\r
         try {\r
             Document doc = DocumentHelper.readDocument(new InputSource(jior));\r
-            return XmlBeans.getContextTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));\r
+            return getTypeLoader().parse(doc.getDocumentElement(), type, getXmlOptions(options));\r
         } catch (SAXException e) {\r
             throw new XmlException("Unable to parse xml bean", e);\r
         }\r
     }\r
 \r
     public static XmlObject parse(Node node, SchemaType type, XmlOptions options) throws XmlException {\r
-        return XmlBeans.getContextTypeLoader().parse(node, type, getXmlOptions(options));\r
+        return getTypeLoader().parse(node, type, getXmlOptions(options));\r
     }\r
 \r
     public static XmlObject parse(XMLInputStream xis, SchemaType type, XmlOptions options) throws XmlException, XMLStreamException {\r
-        return XmlBeans.getContextTypeLoader().parse(xis, type, getXmlOptions(options));\r
+        return getTypeLoader().parse(xis, type, getXmlOptions(options));\r
     }\r
     \r
     public static XMLInputStream newValidatingXMLInputStream ( XMLInputStream xis, SchemaType type, XmlOptions options ) throws XmlException, XMLStreamException {\r
-        return XmlBeans.getContextTypeLoader().newValidatingXMLInputStream(xis, type, getXmlOptions(options));\r
+        return getTypeLoader().newValidatingXMLInputStream(xis, type, getXmlOptions(options));\r
     }\r
 }\r
index 023c8f7b4e6c40f3dd24870720570af9bf035d97..61ab6b44d7cd1d6a67a84665ed63f53e1bfdaafa 100644 (file)
@@ -19,8 +19,6 @@
 \r
 package org.apache.poi.xslf.usermodel;\r
 \r
-import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;\r
-\r
 import java.awt.geom.Rectangle2D;\r
 import java.util.ArrayList;\r
 import java.util.Collections;\r
@@ -29,7 +27,6 @@ import java.util.List;
 \r
 import javax.xml.namespace.QName;\r
 \r
-import org.apache.poi.POIXMLException;\r
 import org.apache.poi.sl.draw.DrawFactory;\r
 import org.apache.poi.sl.draw.DrawTableShape;\r
 import org.apache.poi.sl.draw.DrawTextShape;\r
@@ -37,7 +34,6 @@ import org.apache.poi.sl.usermodel.TableShape;
 import org.apache.poi.util.Internal;\r
 import org.apache.poi.util.Units;\r
 import org.apache.xmlbeans.XmlCursor;\r
-import org.apache.xmlbeans.XmlException;\r
 import org.apache.xmlbeans.XmlObject;\r
 import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;\r
 import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData;\r
@@ -53,6 +49,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFra
 public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow>,\r
     TableShape<XSLFShape,XSLFTextParagraph> {\r
     /* package */ static final String TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";\r
+    /* package */ static final String DRAWINGML_URI = "http://schemas.openxmlformats.org/drawingml/2006/main";\r
 \r
     private CTTable _table;\r
     private List<XSLFTableRow> _rows;\r
@@ -60,28 +57,30 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow
     /*package*/ XSLFTable(CTGraphicalObjectFrame shape, XSLFSheet sheet){\r
         super(shape, sheet);\r
 \r
-        XmlObject[] rs = shape.getGraphic().getGraphicData()\r
-                .selectPath("declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ./a:tbl");\r
-        if (rs.length == 0) {\r
-            throw new IllegalStateException("a:tbl element was not found in\n " + shape.getGraphic().getGraphicData());\r
+        CTGraphicalObjectData god = shape.getGraphic().getGraphicData();\r
+        XmlCursor xc = god.newCursor();\r
+        if (!xc.toChild(DRAWINGML_URI, "tbl")) {\r
+            throw new IllegalStateException("a:tbl element was not found in\n " + god);\r
         }\r
 \r
+        XmlObject xo = xc.getObject();\r
         // Pesky XmlBeans bug - see Bugzilla #49934\r
         // it never happens when using the full ooxml-schemas jar but may happen with the abridged poi-ooxml-schemas\r
-        if(rs[0] instanceof XmlAnyTypeImpl){\r
-            try {\r
-                rs[0] = CTTable.Factory.parse(rs[0].toString(), DEFAULT_XML_OPTIONS);\r
-            }catch (XmlException e){\r
-                throw new POIXMLException(e);\r
-            }\r
+        if (xo instanceof XmlAnyTypeImpl){\r
+            String errStr =\r
+                "Schemas (*.xsb) for CTTable can't be loaded - usually this happens when OSGI " +\r
+                "loading is used and the thread context classloader has no reference to " +\r
+                "the xmlbeans classes - use POIXMLTypeLoader.setClassLoader() to set the loader, " +\r
+                "e.g. with CTTable.class.getClassLoader()"\r
+            ;\r
+            throw new IllegalStateException(errStr);\r
         }\r
+        _table = (CTTable)xo;\r
+        xc.dispose();\r
 \r
-        _table = (CTTable) rs[0];\r
-        CTTableRow[] trArray = _table.getTrArray();\r
-        _rows = new ArrayList<XSLFTableRow>(trArray.length);\r
-        for(CTTableRow row : trArray) {\r
-            XSLFTableRow xr = new XSLFTableRow(row, this);\r
-            _rows.add(xr);\r
+        _rows = new ArrayList<XSLFTableRow>(_table.sizeOfTrArray());\r
+        for(CTTableRow row : _table.getTrArray()) {\r
+            _rows.add(new XSLFTableRow(row, this));\r
         }\r
         updateRowColIndexes();\r
     }\r
@@ -171,13 +170,18 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow
 \r
         frame.addNewXfrm();\r
         CTGraphicalObjectData gr = frame.addNewGraphic().addNewGraphicData();\r
-        XmlCursor cursor = gr.newCursor();\r
-        cursor.toNextToken();\r
-        cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tbl"));\r
-        cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tblPr"));\r
-        cursor.toNextToken();\r
-        cursor.beginElement(new QName("http://schemas.openxmlformats.org/drawingml/2006/main", "tblGrid"));\r
-        cursor.dispose();\r
+        XmlCursor grCur = gr.newCursor();\r
+        grCur.toNextToken();\r
+        grCur.beginElement(new QName(DRAWINGML_URI, "tbl"));\r
+        \r
+        CTTable tbl = CTTable.Factory.newInstance();\r
+        tbl.addNewTblPr();\r
+        tbl.addNewTblGrid();\r
+        XmlCursor tblCur = tbl.newCursor();\r
+        \r
+        tblCur.moveXmlContents(grCur);\r
+        tblCur.dispose();\r
+        grCur.dispose();\r
         gr.setUri(TABLE_URI);\r
         return frame;\r
     }\r
index 16b28a4507da72ac26b8e2b4ea3e274ee59e679b..77f59372d6978b64240c340a344a5297f96793f1 100644 (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();
+        }
+    }
+
 }