db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kahat...@apache.org
Subject svn commit: r1073874 - in /db/derby/code/trunk: java/engine/org/apache/derby/iapi/services/loader/ java/engine/org/apache/derby/iapi/types/ java/testing/org/apache/derbyTesting/functionTests/tests/lang/ java/testing/org/apache/derbyTesting/junit/ tools...
Date Wed, 23 Feb 2011 18:28:11 GMT
Author: kahatlen
Date: Wed Feb 23 18:28:10 2011
New Revision: 1073874

URL: http://svn.apache.org/viewvc?rev=1073874&view=rev
Log:
DERBY-2739: Use DOM interfaces to implement XML operators

Make SqlXmlUtil use JAXP and DOM interfaces and factory classes to
provide support for XML serialization and XPath queries.

Added:
    db/derby/code/trunk/tools/java/xml-apis.jar
      - copied unchanged from r588374, db/derby/code/trunk/tools/java/xml-apis.jar
Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/build.xml
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XMLMissingClassesTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/EnvTest.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/XML.java
    db/derby/code/trunk/tools/ant/properties/extrapath.properties

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java?rev=1073874&r1=1073873&r2=1073874&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java
(original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/services/loader/ClassInspector.java
Wed Feb 23 18:28:10 2011
@@ -28,11 +28,6 @@ import org.apache.derby.iapi.error.Stand
 import org.apache.derby.iapi.reference.SQLState;
 
 import java.lang.reflect.*;
-import java.util.StringTokenizer;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
-import java.util.Collections;
 
 /**
 	Methods to find out relationships between classes and methods within a class.
@@ -1062,27 +1057,6 @@ nextMethod:	for (int i = 0; i < methods.
 	}
 
 	/**
-	 * Determine whether or not the received class can be
-	 * loaded.
-	 *
-	 * @param className The name of the class in question
-	 * @return True if className can be loaded, false otherwise
-	 */
-	public static boolean classIsLoadable(String className)
-	{
-		try {
-
-			Class.forName(className);
-			return true;
-
-		} catch (ClassNotFoundException ce) {
-			return false;
-		} catch (LinkageError ce) {
-			return false;
-		}
-	}
-
-	/**
 	 * Get the declaring class for a method.
 	 *
 	 * @param method	A Member describing a method

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java?rev=1073874&r1=1073873&r2=1073874&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/SqlXmlUtil.java Wed Feb 23
18:28:10 2011
@@ -29,21 +29,29 @@ import org.apache.derby.iapi.services.sa
 
 import java.util.Properties;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 import java.io.IOException;
 import java.io.ObjectOutput;
 import java.io.ObjectInput;
 import java.io.StringReader;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+
 // -- JDBC 3.0 JAXP API classes.
 
 import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
-import org.w3c.dom.Element;
 import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
 import org.w3c.dom.Text;
 
+import org.w3c.dom.xpath.XPathEvaluator;
+import org.w3c.dom.xpath.XPathExpression;
+import org.w3c.dom.xpath.XPathResult;
+
 import org.xml.sax.ErrorHandler;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
@@ -53,21 +61,12 @@ import javax.xml.parsers.DocumentBuilder
 import javax.xml.parsers.DocumentBuilderFactory;
 
 import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
 import javax.xml.transform.TransformerException;
-
-// -- Xalan-specific classes.
-
-import org.apache.xpath.XPath;
-import org.apache.xpath.XPathContext;
-import org.apache.xpath.objects.XObject;
-import org.apache.xpath.objects.XNodeSet;
-
-import org.apache.xml.utils.PrefixResolverDefault;
-
-import org.apache.xalan.serialize.DOMSerializer;
-import org.apache.xalan.serialize.Serializer;
-import org.apache.xalan.serialize.SerializerFactory;
-import org.apache.xalan.templates.OutputProperties;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
 
 /**
  * This class contains "utility" methods that work with XML-specific
@@ -122,19 +121,52 @@ public class SqlXmlUtil implements Forma
 
     // Used to serialize an XML value according the standard
     // XML serialization rules.
-    private Serializer serializer;
+    private Transformer serializer;
 
     // Classes used to compile and execute an XPath expression
     // against Xalan.
-    private XPath query;
-    private XPathContext xpContext;
+    private XPathExpression query;
 
     // Used to recompile the XPath expression when this formatable
     // object is reconstructed.  e.g.:  SPS 
     private String queryExpr;
     private String opName;
     private boolean recompileQuery;
-    
+
+    /**
+     * <p>
+     * An object representing the {@code BigDecimal.toPlainString()} method
+     * if it's available on the platform. If it's not available, this field
+     * will be initialized to {@code null}, and in that case the
+     * {@code BigDecimal.toString()} method should be used instead without
+     * reflection.
+     * </p>
+     *
+     * <p>
+     * The behaviour of the {@code toString()} method changed when
+     * {@code toPlainString()} was introduced in Java SE 5. On older
+     * platforms, it behaves just like {@code toPlainString()} does on
+     * newer platforms. So when {@code toPlainString()} is not
+     * available, it is safe to fall back to {@code toString()}. It
+     * behaves differently on newer platforms, so we need to use
+     * {@code toPlainString()} when it is available in order to get
+     * consistent behaviour across all platforms.
+     * </p>
+     *
+     * @see #numberToString(double)
+     */
+    private static final Method TO_PLAIN_STRING;
+    static {
+        Method m = null;
+        try {
+            m = BigDecimal.class.getMethod("toPlainString", new Class[0]);
+        } catch (NoSuchMethodException nsme) {
+            // Couldn't find the method, so we'll just fall back to toString()
+            // on this platform.
+        }
+        TO_PLAIN_STRING = m;
+    }
+
     /**
      * Constructor: Initializes objects required for parsing
      * and serializing XML values.  Since most XML operations
@@ -256,18 +288,17 @@ public class SqlXmlUtil implements Forma
         try {
 
             /* The following XPath constructor compiles the expression
-             * as part of the construction process.  We have to pass
-             * in a PrefixResolver object in order to avoid NPEs when
-             * invalid/unknown functions are used, so we just create
-             * a dummy one, which means prefixes will not be resolved
+             * as part of the construction process.  We pass a null
+             * namespace resolver object so that the implementation will
+             * provide one for us, which means prefixes will not be resolved
              * in the query (Xalan will just throw an error if a prefix
              * is used).  In the future we may want to revisit this
              * to make it easier for users to query based on namespaces.
              */
-            query = new XPath(queryExpr, null,
-                new PrefixResolverDefault(dBuilder.newDocument()),
-                XPath.SELECT);
-            
+            XPathEvaluator eval = (XPathEvaluator)
+                dBuilder.getDOMImplementation().getFeature("+XPath", "3.0");
+            query = eval.createExpression(queryExpr, null);
+
             this.queryExpr = queryExpr;
             this.opName = opName;
             this.recompileQuery = false;
@@ -381,12 +412,13 @@ public class SqlXmlUtil implements Forma
      *  normalized sequence created from the items in the received
      *  list.
      */
-    protected String serializeToString(ArrayList items,
-        XMLDataValue xmlVal) throws java.io.IOException
+    protected String serializeToString(List items,
+        XMLDataValue xmlVal) throws TransformerException
     {
-        if ((items == null) || (items.size() == 0))
+        if ((items == null) || items.isEmpty()) {
         // nothing to do; return empty sequence.
             return "";
+        }
 
         java.io.StringWriter sWriter = new java.io.StringWriter();
 
@@ -397,9 +429,6 @@ public class SqlXmlUtil implements Forma
                 "Tried to serialize with uninitialized XML serializer.");
         }
 
-        serializer.setWriter(sWriter);
-        DOMSerializer dSer = serializer.asDOMSerializer();
-
         int sz = items.size();
         Object obj = null;
 
@@ -473,7 +502,8 @@ public class SqlXmlUtil implements Forma
                  */
                 if (xmlVal != null)
                     xmlVal.markAsHavingTopLevelAttr();
-                dSer.serialize((Node)obj);
+                serializer.transform(
+                        new DOMSource((Node) obj), new StreamResult(sWriter));
                 lastItemWasString = false;
             }
             else
@@ -507,7 +537,8 @@ public class SqlXmlUtil implements Forma
                      * "serialized" as an atomic value, attribute, or
                      * text node.
                      */
-                    dSer.serialize(n);
+                    serializer.transform(
+                            new DOMSource(n), new StreamResult(sWriter));
                 }
 
                 lastItemWasString = false;
@@ -555,7 +586,7 @@ public class SqlXmlUtil implements Forma
      * @exception Exception thrown on error (and turned into a
      *  StandardException by the caller).
      */
-    protected ArrayList evalXQExpression(XMLDataValue xmlContext,
+    protected List evalXQExpression(XMLDataValue xmlContext,
         boolean returnResults, int [] resultXType) throws Exception
     {
         // if this object is in an SPS, we need to recompile the query
@@ -567,7 +598,7 @@ public class SqlXmlUtil implements Forma
         // Make sure we have a compiled query.
         if (SanityManager.DEBUG) {
             SanityManager.ASSERT(
-                (query != null) && (query.getExpression() != null),
+                (query != null),
                 "Failed to locate compiled XML query expression.");
         }
 
@@ -592,64 +623,74 @@ public class SqlXmlUtil implements Forma
                 new StringReader(xmlContext.getString())));
 
         // Evaluate the expresion using Xalan.
-        getXPathContext();
-        xpContext.reset();
-        XObject xOb = query.execute(xpContext, docNode, null);
+        XPathResult result = (XPathResult)
+                query.evaluate(docNode, XPathResult.ANY_TYPE, null);
 
         if (!returnResults)
         {
             // We don't want to return the actual results, we just
             // want to know if there was at least one item in the
             // result sequence.
-            if ((xOb instanceof XNodeSet) &&
-                (((XNodeSet)xOb).nodelist().getLength() > 0))
-            { // If we have a sequence (XNodeSet) of length greater
-              // than zero, then we know that at least one item
-              // "exists" in the result so return a non-null list.
-                return new ArrayList(0);
-            }
-            else if (!(xOb instanceof XNodeSet))
-            // we have a single atomic value, which means the result is
-            // non-empty.  So return a non-null list.
-                return new ArrayList(0);
-            else {
-            // return null; caller will take this to mean we have an
-            // an empty sequence.
-                return null;
+            switch (result.getResultType()) {
+                case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
+                case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+                    if (result.iterateNext() == null) {
+                        // We have an empty sequence, so return null.
+                        return null;
+                    } else {
+                        // We have a non-empty sequence, so return a non-null
+                        // list to indicate that we found at least one item.
+                        return Collections.EMPTY_LIST;
+                    }
+                default:
+                    // We have a single atomic value, which means the result is
+                    // non-empty. So return a non-null list.
+                    return Collections.EMPTY_LIST;
             }
         }
 
         // Else process the results.
-        NodeList nodeList = null;
-        int numItems = 0;
-        if (!(xOb instanceof XNodeSet))
-        // then we only have a single (probably atomic) item.
-            numItems = 1;
-        else {
-            nodeList = xOb.nodelist();
-            numItems = nodeList.getLength();
-        }
-
-        // Return a list of the items contained in the query results.
-        ArrayList itemRefs = new ArrayList();
-        if (nodeList == null)
-        // result is a single, non-node value (ex. it's an atomic number);
-        // in this case, just take the string value.
-            itemRefs.add(xOb.str());
-        else {
-            for (int i = 0; i < numItems; i++)
-                itemRefs.add(nodeList.item(i));
+        List itemRefs;
+        switch (result.getResultType()) {
+            case XPathResult.NUMBER_TYPE:
+                // Single atomic number. Get its string value.
+                String val = numberToString(result.getNumberValue());
+                itemRefs = Collections.singletonList(val);
+                break;
+            case XPathResult.STRING_TYPE:
+                // Single atomic string value.
+                itemRefs = Collections.singletonList(result.getStringValue());
+                break;
+            case XPathResult.BOOLEAN_TYPE:
+                // Single atomic boolean. Get its string value.
+                itemRefs = Collections.singletonList(
+                        String.valueOf(result.getBooleanValue()));
+                break;
+            case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
+            case XPathResult.ORDERED_NODE_ITERATOR_TYPE:
+                // We have a sequence. Get all nodes.
+                itemRefs = new ArrayList();
+                Node node;
+                while ((node = result.iterateNext()) != null) {
+                    itemRefs.add(node);
+                }
+                break;
+            default:
+                if (SanityManager.DEBUG) {
+                    SanityManager.THROWASSERT(
+                            "Don't know how to handle XPath result type " +
+                            result.getResultType());
+                }
+                itemRefs = null;
         }
 
-        nodeList = null;
-
         /* Indicate what kind of XML result value we have.  If
          * we have a sequence of exactly one Document then it
          * is XMLPARSE-able and so we consider it to be of type
          * XML_DOC_ANY (which means we can store it in a Derby
          * XML column).
          */
-        if ((numItems == 1) && (itemRefs.get(0) instanceof Document))
+        if ((itemRefs.size() == 1) && (itemRefs.get(0) instanceof Document))
             resultXType[0] = XML.XML_DOC_ANY;
         else
             resultXType[0] = XML.XML_SEQUENCE;
@@ -662,26 +703,14 @@ public class SqlXmlUtil implements Forma
      * */
 
     /**
-     * Create and return an instance of Xalan's XPathContext
-     * that can be used to compile an XPath expression.
-     */
-    private XPathContext getXPathContext()
-    {
-        if (xpContext == null)
-            xpContext = new XPathContext();
-
-        return xpContext;
-    }
-
-    /**
      * Create an instance of Xalan serializer for the sake of
      * serializing an XML value according the SQL/XML specification
      * for serialization.
      */
-    private void loadSerializer() throws java.io.IOException
+    private void loadSerializer() throws TransformerConfigurationException
     {
         // Set serialization properties.
-        Properties props = OutputProperties.getDefaultMethodProperties("xml");
+        Properties props = new Properties();
 
         // SQL/XML[2006] 10.15:General Rules:6 says method is "xml".
         props.setProperty(OutputKeys.METHOD, "xml");
@@ -718,10 +747,62 @@ public class SqlXmlUtil implements Forma
         props.setProperty(OutputKeys.ENCODING, "UTF-8");
 
         // Load the serializer with the correct properties.
-        serializer = SerializerFactory.getSerializer(props);
+        serializer = TransformerFactory.newInstance().newTransformer();
+        serializer.setOutputProperties(props);
         return;
     }
 
+    /**
+     * Convert a number returned by an XPath query to a string, following the
+     * rules for the <a href="http://www.w3.org/TR/xpath/#function-string">
+     * XPath string function</a>.
+     *
+     * @param d {@code double} representation of the number
+     * @return {@code String} representation of the number
+     */
+    private static String numberToString(double d)
+            throws IllegalAccessException, InvocationTargetException {
+        if (Double.isNaN(d) || Double.isInfinite(d)) {
+            // BigDecimal doesn't know how to handle NaN or +/- infinity, so
+            // use Double to handle those cases.
+            return Double.toString(d);
+        } else {
+            // Otherwise, use BigDecimal to format the number the way we want.
+            // Ideally, we'd just return
+            // BigDecimal.valueOf(d).stripTrailingZeros().toPlainString(),
+            // but valueOf(double), stripTrailingZeros() and toPlainString()
+            // were all introduced in Java 5, and we still need to support
+            // older platforms.
+            BigDecimal dec = new BigDecimal(Double.toString(d));
+
+            // See how many trailing zeros we have after the decimal point.
+            long unscaledValue = dec.unscaledValue().longValue();
+            int scale = dec.scale();
+            while (scale > 0 && unscaledValue % 10 == 0) {
+                scale--;
+                unscaledValue /= 10;
+            }
+
+            // If we have trailing zeros after the decimal point, remove them.
+            if (scale != dec.scale()) {
+                dec = BigDecimal.valueOf(unscaledValue, scale);
+            }
+
+            // Finally, convert the value to a string. The method
+            // BigDecimal.toPlainString() formats the number the way we want
+            // it, but it's only available on Java 5 and later. Luckily, on
+            // older platforms, BigDecimal.toString() is defined the same way
+            // as toPlainString(), so we can fall back to that method if
+            // toPlainString() isn't available. toString() was redefined in
+            // Java 5, so we cannot use toString() unconditionally, however.
+            if (TO_PLAIN_STRING == null) {
+                return dec.toString();
+            } else {
+                return (String) TO_PLAIN_STRING.invoke(dec, (Object[]) null);
+            }
+        }
+    }
+
     /* ****
      * Formatable interface implementation
      * */

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java?rev=1073874&r1=1073873&r2=1073874&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/XML.java Wed Feb 23 18:28:10
2011
@@ -33,10 +33,6 @@ import org.apache.derby.iapi.services.lo
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import org.apache.derby.iapi.sql.conn.ConnectionUtil;
 
-import org.apache.derby.iapi.types.DataValueDescriptor;
-import org.apache.derby.iapi.types.StringDataValue;
-import org.apache.derby.iapi.types.BooleanDataValue;
-
 import org.apache.derby.iapi.reference.SQLState;
 
 import java.sql.ResultSet;
@@ -48,9 +44,9 @@ import java.io.InputStream;
 import java.io.IOException;
 import java.io.ObjectOutput;
 import java.io.ObjectInput;
-import java.io.StringReader;
+import java.lang.reflect.Method;
 
-import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This type implements the XMLDataValue interface and thus is
@@ -867,7 +863,7 @@ public class XML
             // Return an XML data value whose contents are the
             // serialized version of the query results.
             int [] xType = new int[1];
-            ArrayList itemRefs = sqlxUtil.evalXQExpression(
+            List itemRefs = sqlxUtil.evalXQExpression(
                 this, true, xType);
 
             if (result == null)
@@ -995,7 +991,8 @@ public class XML
              * provided as part the JVM if it is jdk 1.4 or
              * greater.
              */
-            if (!ClassInspector.classIsLoadable("org.w3c.dom.Document"))
+            Object docImpl = checkJAXPRequirement();
+            if (docImpl == null)
                 xmlReqCheck = "JAXP";
 
             /* If the XPath class exists, then we assume that our XML
@@ -1007,8 +1004,8 @@ public class XML
              * point in checking for Xalan unless we've already confirmed
              * that we have the JAXP interfaces.
              */
-            else if (!ClassInspector.classIsLoadable("org.apache.xpath.XPath"))
-                xmlReqCheck = "Xalan";
+            else if (!checkXPathRequirement(docImpl))
+                xmlReqCheck = "XPath 3.0";
         }
 
         if (xmlReqCheck.length() != 0)
@@ -1020,4 +1017,60 @@ public class XML
         return;
     }
 
+    /**
+     * Check if we have a JAXP implementation installed.
+     *
+     * @return a {@code DOMImplementation} object retrieved from the
+     * JAXP implementation, if one is installed, or {@code null} if an
+     * implementation couldn't be found
+     */
+    private static Object checkJAXPRequirement() {
+        try {
+            Class factoryClass =
+                    Class.forName("javax.xml.parsers.DocumentBuilderFactory");
+            Method newFactory = factoryClass.getMethod(
+                    "newInstance", new Class[0]);
+            Method newBuilder = factoryClass.getMethod(
+                    "newDocumentBuilder", new Class[0]);
+
+            Class builderClass =
+                    Class.forName("javax.xml.parsers.DocumentBuilder");
+            Method getImpl = builderClass.getMethod(
+                    "getDOMImplementation", new Class[0]);
+
+            Object factory = newFactory.invoke(null, new Object[0]);
+            Object builder = newBuilder.invoke(factory, new Object[0]);
+            Object impl = getImpl.invoke(builder, new Object[0]);
+
+            return impl;
+
+        } catch (Throwable t) {
+            // Oops... Couldn't get a DOMImplementation object for
+            // some reason. Assume we don't have JAXP.
+            return null;
+        }
+    }
+
+    /**
+     * Check if the supplied {@code DOMImplementation} object has
+     * support for DOM Level 3 XPath.
+     *
+     * @param domImpl the {@code DOMImplementation} instance to check
+     * @return {@code true} if the required XPath level is supported,
+     * {@code false} otherwise
+     */
+    private static boolean checkXPathRequirement(Object domImpl) {
+        try {
+            Class domImplClass = Class.forName("org.w3c.dom.DOMImplementation");
+            Method getFeature = domImplClass.getMethod(
+                    "getFeature", new Class[] {String.class, String.class});
+            Object impl =
+                    getFeature.invoke(domImpl, new Object[] {"+XPath", "3.0"});
+            return impl != null;
+        } catch (Throwable t) {
+            // Oops... Something went wrong when checking for XPath
+            // 3.0 support. Assume we don't have it.
+            return false;
+        }
+    }
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/build.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/build.xml?rev=1073874&r1=1073873&r2=1073874&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/build.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/types/build.xml Wed Feb 23 18:28:10
2011
@@ -52,7 +52,7 @@
       srcdir="${derby.engine.src.dir}"
       destdir="${out.dir}">
       <classpath>
-        <pathelement path="${java14compile.classpath};${xercesImpl}"/>
+        <pathelement path="${xmlApis};${java14compile.classpath}"/>
       </classpath>
       <include name="${derby.dir}/iapi/types/SqlXmlUtil.java"/>
     </javac>

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XMLMissingClassesTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XMLMissingClassesTest.java?rev=1073874&r1=1073873&r2=1073874&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XMLMissingClassesTest.java
(original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/XMLMissingClassesTest.java
Wed Feb 23 18:28:10 2011
@@ -22,7 +22,6 @@ package org.apache.derbyTesting.function
 import junit.framework.Test;
 import junit.framework.TestSuite;
 
-import org.apache.derbyTesting.junit.JDBC;
 import org.apache.derbyTesting.junit.XML;
 import org.apache.derbyTesting.junit.BaseJDBCTestCase;
 import org.apache.derbyTesting.junit.TestConfiguration;
@@ -73,17 +72,11 @@ public final class XMLMissingClassesTest
      * for use of Derby SQL/XML operators, then just return
      * an empty suite (the operators are tested in a different
      * JUnit test--namely XMLTypeAndOpTests.java).
-     *
-     * NOTE: We do not want to run this test if it has Xalan
-     * but the version of Xalan is not the minimum required.
-     * Attempts to do so can lead to unexpected errors (which
-     * is why we have a "minimum Xalan version" to begin with;
-     * see JDBC.checkXalanVersion()).
      */
     public static Test suite()
     {
         TestSuite suite = new TestSuite("XML Missing Classes Suite");
-        if (!XML.classpathHasXalanAndJAXP())
+        if (!XML.classpathMeetsXMLReqs())
         {
             // Run this test in embedded and client modes.
             suite.addTest(TestConfiguration.defaultSuite(

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/EnvTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/EnvTest.java?rev=1073874&r1=1073873&r2=1073874&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/EnvTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/EnvTest.java Wed Feb 23
18:28:10 2011
@@ -68,7 +68,7 @@ public class EnvTest extends TestCase {
     ** XML related tests
     */
     public void testClasspathHasXalanAndJAXP() {
-        setName(XML.classpathHasXalanAndJAXP() + "_classpathHasXalanAndJAXP");
+        setName(XML.classpathHasJAXP() + "_classpathHasJAXP");
     }
     public void testClasspathMeetsXMLReqs() {
         setName(XML.classpathMeetsXMLReqs() + "_classpathMeetsXMLReqs");

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/XML.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/XML.java?rev=1073874&r1=1073873&r2=1073874&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/XML.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/junit/XML.java Wed Feb 23 18:28:10
2011
@@ -20,8 +20,6 @@
 package org.apache.derbyTesting.junit;
 
 import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.ByteArrayOutputStream;
 import java.io.InputStreamReader;
 
 import java.lang.reflect.Method;
@@ -31,8 +29,6 @@ import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 
-import java.util.StringTokenizer;
-
 import junit.framework.Assert;
 
 /**
@@ -72,15 +68,6 @@ import junit.framework.Assert;
  * </ul>
  */
 public class XML {
-    
-    /**
-     * Minimum version of Xalan required to run XML tests under
-     * Security Manager. In this case, we're saying that the
-     * minimum version is Xalan 2.5.0 (because there's a bug
-     * in earlier versions that causes problems with security
-     * manager).
-     */
-    private static int [] MIN_XALAN_VERSION = new int [] { 2, 5, 0 };
 
     /**
      * Determine whether or not the classpath with which we're
@@ -92,26 +79,17 @@ public class XML {
 
     /**
      * Determine whether or not the classpath with which we're
-     * running has a version of Xalan in it.  Xalan is required
-     * for use of the Derby XML operators.  In particular we
-     * check for:
-     *
-     *  1. Xalan classes (version doesn't matter here)
-     *  2. The Xalan "EnvironmentCheck" class, which is included
-     *     as part of Xalan.  This allows us to check the specific
-     *     version of Xalan in use so that we can determine if
-     *     if we satisfy the minimum requirement.
+     * running has a JAXP implementation.
      */
-    private static final boolean HAVE_XALAN =
-            JDBC.haveClass("org.apache.xpath.XPath") &&
-            JDBC.haveClass("org.apache.xalan.xslt.EnvironmentCheck");
+    private static final boolean HAVE_JAXP_IMPL =
+            HAVE_JAXP && checkJAXPImplementation();
 
     /**
-     * Determine if we have the minimum required version of Xalan
+     * Determine if we have support for DOM level 3 XPath, which is required
      * for successful use of the XML operators.
      */
-    private static final boolean HAVE_MIN_XALAN
-            = HAVE_XALAN && checkXalanVersion();
+    private static final boolean HAVE_XPATH_LEVEL_3
+            = HAVE_JAXP_IMPL && checkXPathSupport();
 
     /**
      * The filepath for the directory that holds the XML "helper" files
@@ -122,23 +100,24 @@ public class XML {
 
     /**
      * Return true if the classpath contains JAXP and
+     * an implementation of the JAXP interfaces, for example the
      * Xalan classes (this method doesn't care about
-     * the particular version of Xalan).
+     * support for DOM level 3 XPath).
      */
-    public static boolean classpathHasXalanAndJAXP()
+    public static boolean classpathHasJAXP()
     {
-        return HAVE_JAXP && HAVE_XALAN;
+        return HAVE_JAXP_IMPL;
     }
 
     /**
      * Return true if the classpath meets all of the requirements
      * for use of the SQL/XML operators.  This means that all
-     * required classes exist in the classpath AND the version
-     * of Xalan that we found is at least MIN_XALAN_VERSION.
+     * required classes exist in the classpath AND there is support
+     * for DOM level 3 XPath.
      */
     public static boolean classpathMeetsXMLReqs()
     {
-        return HAVE_JAXP && HAVE_MIN_XALAN;
+        return HAVE_XPATH_LEVEL_3;
     }
 
     /**
@@ -265,157 +244,79 @@ public class XML {
     }
 
     /**
+     * <p>
      * Determine whether or not the classpath with which we're
-     * running has a version of Xalan that meets the minimum
-     * Xalan version requirement.  We do that by using a Java
-     * utility that ships with Xalan--namely, "EnvironmentCheck"--
-     * and by parsing the info gathered by that method to find
-     * the Xalan version.  We use reflection when doing this
-     * so that this file will compile/execute even if XML classes
-     * are missing.
+     * running contains a JAXP implementation that supports
+     * DOM level 3 XPath.
+     * </p>
      *
+     * <p>
      * Assumption is that we only get to this method if we already
-     * know that there *is* a version of Xalan in the classpath
-     * and that version includes the "EnvironmentCheck" class.
-     *
-     * Note that this method returns false if the call to Xalan's
-     * EnvironmentCheck.checkEnvironment() returns false for any
-     * reason.  As a specific example, that method will always
-     * return false when running with ibm131 because it cannot
-     * find the required methods on the SAX 2 classes (apparently
-     * the classes in ibm131 jdk don't have all of the methods
-     * required by Xalan).  Thus this method will always return
-     * "false" for ibm131.
+     * know that there *is* an implementation of JAXP in the classpath.
+     * </p>
      */
-    private static boolean checkXalanVersion()
+    private static boolean checkXPathSupport()
     {
-        boolean haveMinXalanVersion = false;
+        boolean supportsXPath;
+
+        // Invoke the following using reflection to see if we have support
+        // for DOM level 3 XPath:
+        //
+        //     DocumentBuilderFactory.newInstance().newDocumentBuilder()
+        //             .getDOMImplementation().getFeature("+XPath", "3.0");
+        //
         try {
+            Class factoryClass =
+                    Class.forName("javax.xml.parsers.DocumentBuilderFactory");
 
-            // These io objects allow us to retrieve information generated
-            // by the call to EnvironmenCheck.checkEnvironment()
-            ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            PrintWriter pW = new PrintWriter(bos);
-
-            // Call the method using reflection.
-
-            Class cl = Class.forName("org.apache.xalan.xslt.EnvironmentCheck");
-            Method meth = cl.getMethod("checkEnvironment",
-                new Class[] { PrintWriter.class });
-
-            Boolean boolObj = (Boolean)meth.invoke(
-                cl.newInstance(), new Object [] { pW });
-
-            pW.flush();
-            bos.flush();
-
-            cl = null;
-            meth = null;
-            pW = null;
-
-            /* At this point 'bos' holds a list of properties with
-             * a bunch of environment information.  The specific
-             * property we're looking for is "version.xalan2_2",
-             * so get that property, parse the value, and see
-             * if the version is at least the minimum required.
-             */
-            if (boolObj.booleanValue())
-            {
-                /* We wrote the byte array using the platform's default
-                 * encodinging (that's what we get with the call to
-                 * "new PrintWriter(bos)" above), so read it in using
-                 * the default encoding, as well (i.e. don't pass an
-                 * encoding into toString()).
-                 */
-                String checkEnvOutput = bos.toString();
-                bos.close();
-
-                /* The property we're looking for is on a single line
-                 * of the output, and that line starts with the name
-                 * of the property.  So extract that line out now. If
-                 * we can't find it, just return "false" to say that
-                 * we could not find the minimum version. Note: it's
-                 * possible (though admittedly unlikely) that the
-                 * string "version.xalan2_2" appears in the user's
-                 * classpath.  Adding an equals sign ("=") at the end
-                 * of our search pattern reduces the chance of the
-                 * search string appearing in the classpath, but does
-                 * not eliminate it...
-                 */
-                int pos = checkEnvOutput.indexOf("version.xalan2_2=");
-                if (pos < 0)
-                    return false;
-
-                String ver = checkEnvOutput.substring(
-                    pos, checkEnvOutput.indexOf("\n", pos));
-
-                // Now pull out the one we need.
-                haveMinXalanVersion = (ver != null);
-                if (haveMinXalanVersion)
-                {
-                    /* We found the property, so parse out the necessary
-                     * piece.  The value is of the form:
-                     *
-                     *   <productName> Major.minor.x
-                     *
-                     * Ex:
-                     *
-                     *   version.xalan2_2=Xalan Java 2.5.1 
-                     *   version.xalan2_2=XSLT4J Java 2.6.6
-                     */
-                    int i = 0;
-                    StringTokenizer tok = new StringTokenizer(ver, ". ");
-                    while (tok.hasMoreTokens())
-                    {
-                        String str = tok.nextToken().trim();
-                        if (Character.isDigit(str.charAt(0)))
-                        {
-                            int val = Integer.valueOf(str).intValue();
-                            if (val < MIN_XALAN_VERSION[i])
-                            {
-                                haveMinXalanVersion = false;
-                                break;
-                            }
-                            i++;
-                        }
-
-                        /* If we've checked all parts of the min version,
-                         * then we assume we're okay. Ex. "2.5.0.2"
-                         * is considered greater than "2.5.0".
-                         */
-                        if (i >= MIN_XALAN_VERSION.length)
-                            break;
-                    }
-
-                    /* If the value had fewer parts than the
-                     * mininum version, then it doesn't meet
-                     * the requirement.  Ex. "2.5" is considered
-                     * to be a lower version than "2.5.0".
-                     */
-                    if (i < MIN_XALAN_VERSION.length)
-                        haveMinXalanVersion = false;
-                }
-            }
-
-            /* Else the call to checkEnvironment() returned "false",
-             * which means it couldn't find all of the classes/methods
-             * required for Xalan to function.  So in that case we'll
-             * fall through and just return false, as well.
-             */
+            Method newFactory =
+                    factoryClass.getMethod("newInstance", new Class[0]);
 
-        } catch (Throwable t) {
+            Object factory = newFactory.invoke(null, new Object[0]);
+
+            Method newBuilder = factoryClass.getMethod(
+                    "newDocumentBuilder", new Class[0]);
+
+            Object builder = newBuilder.invoke(factory, new Object[0]);
+
+            Class builderClass =
+                    Class.forName("javax.xml.parsers.DocumentBuilder");
+
+            Method getImpl = builderClass.getMethod(
+                    "getDOMImplementation", new Class[0]);
 
-            System.out.println("Unexpected exception while " +
-                "trying to find Xalan version:");
-            t.printStackTrace(System.err);
+            Object impl = getImpl.invoke(builder, new Object[0]);
 
+            Class domImplClass = Class.forName("org.w3c.dom.DOMImplementation");
+
+            Method getFeature = domImplClass.getMethod(
+                    "getFeature", new Class[] {String.class, String.class});
+
+            Object ret =
+                    getFeature.invoke(impl, new Object[] {"+XPath", "3.0"});
+
+            supportsXPath = (ret != null);
+
+        } catch (Throwable t) {
             // If something went wrong, assume we don't have the
             // necessary classes.
-            haveMinXalanVersion = false;
-
+            supportsXPath = false;
         }
 
-        return haveMinXalanVersion;
+        return supportsXPath;
+    }
+
+    private static boolean checkJAXPImplementation() {
+        try {
+            Class factoryClass =
+                    Class.forName("javax.xml.parsers.DocumentBuilderFactory");
+            Method newFactory =
+                    factoryClass.getMethod("newInstance", new Class[0]);
+            Object factory = newFactory.invoke(null, new Object[0]);
+            return factory != null;
+        } catch (Throwable t) {
+            return false;
+        }
     }
 
     /**
@@ -436,7 +337,7 @@ public class XML {
          * parser in the classpath, the result for J2ME would be
          * be a NoClassDefFound error (DERBY-2153).
          */
-        if (!classpathHasXalanAndJAXP())
+        if (!classpathHasJAXP())
             return null;
 
         return JAXPFinder.getJAXPParserLocation();

Modified: db/derby/code/trunk/tools/ant/properties/extrapath.properties
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/tools/ant/properties/extrapath.properties?rev=1073874&r1=1073873&r2=1073874&view=diff
==============================================================================
--- db/derby/code/trunk/tools/ant/properties/extrapath.properties (original)
+++ db/derby/code/trunk/tools/ant/properties/extrapath.properties Wed Feb 23 18:28:10 2011
@@ -23,6 +23,7 @@ jcc_l=${javatools.dir}/db2jcc_license_c.
 osgi=${out.felix.dir}
 xercesImpl=${javatools.dir}/xercesImpl.jar
 xalan=${javatools.dir}/xalan.jar
+xmlApis=${javatools.dir}/xml-apis.jar
 serializer=${javatools.dir}/serializer.jar
 javacc=${javatools.dir}/javacc.jar
 junit=${javatools.dir}/junit.jar



Mime
View raw message