You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2016/10/08 17:07:15 UTC

svn commit: r1763922 - in /poi: site/src/documentation/content/xdocs/ trunk/src/ooxml/java/org/apache/poi/ trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/ trunk/src/ooxml/testcases/org/apache/poi/

Author: kiwiwings
Date: Sat Oct  8 17:07:15 2016
New Revision: 1763922

URL: http://svn.apache.org/viewvc?rev=1763922&view=rev
Log:
Bug 60226 - ClassLoader workaround for OSGI when processing OOXML files

Modified:
    poi/site/src/documentation/content/xdocs/faq.xml
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/ooxml/java/org/apache/poi/POIXMLTypeLoader.java
    poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java

Modified: poi/site/src/documentation/content/xdocs/faq.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/faq.xml?rev=1763922&r1=1763921&r2=1763922&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/faq.xml (original)
+++ poi/site/src/documentation/content/xdocs/faq.xml Sat Oct  8 17:07:15 2016
@@ -577,4 +577,21 @@ and
       </p>
     </answer>
   </faq>
+  <faq>
+    <question>
+      Can POI be used with OSGI?
+    </question>
+    <answer>
+    	<p>Starting with POI 3.16 there's a workaround for OSGIs context classloader handling,
+    	 i.e. it replaces the threads current context classloader with an implementation of
+    	 limited class view. This will lead to IllegalStateExceptions, as xmlbeans can't find
+    	 the xml schema definitions in this reduced view. The workaround is to initialize
+    	 the classloader delegate of <em>POIXMLTypeLoader</em> , which defaults to the current
+    	 thread context classloader. The initialization should take place before any other
+    	 OOXML related calls. The class in the example could be any class, which is
+    	 part of the poi-ooxml-schema or ooxml-schema:<br/>
+    	 <em> POIXMLTypeLoader.setClassLoader(CTTable.class.getClassLoader());</em>
+    	</p>
+	</answer>
+  </faq>
 </faqs>

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1763922&r1=1763921&r2=1763922&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Sat Oct  8 17:07:15 2016
@@ -40,6 +40,7 @@
     </devs>
 
     <release version="3.16-beta1" date="2016-11-??">
+        <action dev="PD" type="add" fixes-bug="60226">ClassLoader workaround for OSGI when processing OOXML files</action>
         <action dev="PD" type="add" fixes-bug="60187">SS Common: support BorderStyle enums in RegionUtil</action>
         <action dev="PD" type="add" fixes-bug="59857">Password protected files with "Microsoft Enhanced Cryptographic Provider v1.0"</action>
         <action dev="PD" type="fix" fixes-bug="59687">XSSF: Comments removed from wrong row when removing a row from a sheet with empty rows</action>

Modified: poi/trunk/src/ooxml/java/org/apache/poi/POIXMLTypeLoader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/POIXMLTypeLoader.java?rev=1763922&r1=1763921&r2=1763922&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/POIXMLTypeLoader.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/POIXMLTypeLoader.java Sat Oct  8 17:07:15 2016
@@ -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));
     }
 }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java?rev=1763922&r1=1763921&r2=1763922&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java Sat Oct  8 17:07:15 2016
@@ -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.Table
 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.presen
 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 XSLFGraph
     /*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);
-            }
-        }
-
-        _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);
+        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();
+
+        _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 XSLFGraph
 
         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;
     }

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java?rev=1763922&r1=1763921&r2=1763922&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/TestPOIXMLDocument.java Sat Oct  8 17:07:15 2016
@@ -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.Pack
 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();
+        }
+    }
+
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org