You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by bp...@apache.org on 2006/08/11 07:06:29 UTC

svn commit: r430670 - in /db/derby/code/trunk/java/engine/org/apache/derby: iapi/services/loader/ClassInspector.java iapi/types/SqlXmlUtil.java iapi/types/XML.java impl/sql/compile/sqlgrammar.jj

Author: bpendleton
Date: Thu Aug 10 22:06:29 2006
New Revision: 430670

URL: http://svn.apache.org/viewvc?rev=430670&view=rev
Log:
DERBY-688: Enhancements to XML functionality toward XPath and XQuery support

This revision contains d688_phase5_v1.patch.

This patch was contributed by Army Brown (qozinx@gmail.com).

The phase 5 patch, d688_phase5_v1.patch, adds code to determine whether or
not the user's classpath has the required XML classes and, if not, to throw
a user-friendly(-ier) error message whenver the user attempts to use any of
the XML operators.

I inquired as to the best way to do this in the following thread:

http://thread.gmane.org/gmane.comp.apache.db.derby.devel/25307/focus=25315

Dan suggested a) looking at the Derby code that loads modules, and
b) adding a new utility method to the ClassInspector class.

I looked at the module-loading code and it ultimately just makes a call to
Class.forName() and ignores a module if that call throws a LinkageError or
a ClassNotFoundException; see the getImplementations() method in
BaseMonitor.java. So based on that I added a utility method to ClassInspector
that does the same thing, except that it just returns "true" if the call
to Class.forName() succeeds and "false" otherwise. I made the new method
static because it doesn't rely on any state specific to ClassInspector and
because it would have taken a good deal of searching for me to figure out
how to instantiate an instance of ClassInspector correctly from within
the XML datatype class.


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/impl/sql/compile/sqlgrammar.jj

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=430670&r1=430669&r2=430670&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 Thu Aug 10 22:06:29 2006
@@ -1083,6 +1083,27 @@
 	}
 
 	/**
+	 * 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=430670&r1=430669&r2=430670&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 Thu Aug 10 22:06:29 2006
@@ -89,7 +89,8 @@
  *       this class's constructor, we can detect early on whether
  *       some classes (ex. Xalan) are missing, and can throw a friendly
  *       error up front, instead of a ClassNotFoundException somewhere
- *       deeper in the execution codepath.
+ *       deeper in the execution codepath.  The initial check for the
+ *       required XML classes can be found in XML.checkXMLRequirements().
  *
  *       Note that we don't want to put references to XML-specific
  *       objects directly into XML.java because that class (XML.java) is
@@ -154,7 +155,38 @@
              * etc--but that's it; no validation errors will be thrown.
              */
 
-            DocumentBuilderFactory dBF = DocumentBuilderFactory.newInstance();
+            DocumentBuilderFactory dBF = null;
+            try {
+
+                dBF = DocumentBuilderFactory.newInstance();
+
+            } catch (Throwable e) {
+
+                /* We assume that if we get an error creating the
+                 * DocumentBuilderFactory, it's because there's no
+                 * JAXP implementation.  This can happen in the
+                 * (admittedly unlikely) case where the classpath
+                 * contains the JAXP _interfaces_ (ex. via xml-apis.jar)
+                 * and the Xalan classes but does not actually
+                 * contain a JAXP _implementation_.  In that case the
+                 * check in XML.checkXMLRequirements() will pass
+                 * and this class (SqlXmlUtil) will be instantiated
+                 * successfully--which is how we get to this constructor.
+                 * But then attempts to create a DocumentBuilderFactory
+                 * will fail, bringing us here.  Note that we can't
+                 * check for a valid JAXP implementation in the
+                 * XML.checkXMLRequirements() method because we
+                 * always want to allow the XML.java class to be
+                 * instantiated, even if the required XML classes
+                 * are not present--and that means that it (the
+                 * XML class) cannot reference DocumentBuilder nor
+                 * any of the JAXP classes directly.
+                 */
+                 throw StandardException.newException(
+                     SQLState.LANG_MISSING_XML_CLASSES, "JAXP");
+
+            }
+
             dBF.setValidating(false);
             dBF.setNamespaceAware(true);
 
@@ -165,6 +197,11 @@
             // Load serializer for serializing XML into string according
             // XML serialization rules.
             loadSerializer();
+
+        } catch (StandardException se) {
+
+            // Just rethrow it.
+            throw se;
 
         } catch (Exception e) {
 

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=430670&r1=430669&r2=430670&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 Thu Aug 10 22:06:29 2006
@@ -29,6 +29,7 @@
 import org.apache.derby.iapi.services.io.StreamStorable;
 import org.apache.derby.iapi.services.io.Storable;
 import org.apache.derby.iapi.services.io.TypedFormat;
+import org.apache.derby.iapi.services.loader.ClassInspector;
 import org.apache.derby.iapi.services.sanity.SanityManager;
 
 import org.apache.derby.iapi.types.DataValueDescriptor;
@@ -123,17 +124,11 @@
     // Derby string types.
     private SQLChar xmlStringValue;
 
-    /**
-      Loaded at execution time, this holds XML-related objects
-      that were created once during compilation but can be re-used
-      for each row in the target result set for the current
-      SQL statement.  In other words, we create the objects
-      once per SQL statement, instead of once per row.  In the
-      case of XMLEXISTS, one of the "objects" is the compiled
-      query expression, which means we don't have to compile
-      the expression for each row and thus we save some time.
+    /*
+     * Status variable used to verify that user's classpath contains
+     * required classes for accessing/operating on XML data values.
      */
-    private SqlXmlUtil sqlxUtil;
+    private static String xmlReqCheck = null;
 
     /**
      * Default constructor.
@@ -292,7 +287,7 @@
          * brings us to this method).
          */
         if (theValue instanceof XMLDataValue)
-        	setXType(((XMLDataValue)theValue).getXType());
+            setXType(((XMLDataValue)theValue).getXType());
     }
 
     /** 
@@ -779,6 +774,71 @@
     public int getXType()
     {
         return xType;
+    }
+
+    /**
+     * See if the required JAXP and Xalan classes are in the
+     * user's classpath.  Assumption is that we will always
+     * call this method before instantiating an instance of
+     * SqlXmlUtil, and thus we will never get a ClassNotFound
+     * exception caused by missing JAXP/Xalan classes.  Instead,
+     * if either is missing we should throw an informative
+     * error indicating what the problem is.
+     *
+     * NOTE: This method only does the checks necessary to
+     * allow successful instantiation of the SqlXmlUtil
+     * class.  Further checks (esp. the presence of a JAXP
+     * _implementation_ in addition to the JAXP _interfaces_)
+     * are performed in the SqlXmlUtil constructor.
+     *
+     * @exception StandardException thrown if the required
+     *  classes cannot be located in the classpath.
+     */
+    public static void checkXMLRequirements()
+        throws StandardException
+    {
+        // Only check once; after that, just re-use the result.
+        if (xmlReqCheck == null)
+        {
+            xmlReqCheck = "";
+
+            /* If the w3c Document class exists, then we
+             * assume a JAXP implementation is present in
+             * the classpath.  If this assumption is incorrect
+             * then we at least know that the JAXP *interface*
+             * exists and thus we'll be able to instantiate
+             * the SqlXmlUtil class.  We can then do a check
+             * for an actual JAXP *implementation* from within
+             * the SqlXmlUtil class (see the constructor of
+             * that class).
+             *
+             * Note: The JAXP API and implementation are
+             * provided as part the JVM if it is jdk 1.4 or
+             * greater.
+             */
+            if (!ClassInspector.classIsLoadable("org.w3c.dom.Document"))
+                xmlReqCheck = "JAXP";
+
+            /* If the XPath class exists, then we assume that our XML
+             * query processor (in this case, Xalan), is present in the
+             * classpath.  Note: if JAXP API classes aren't present
+             * then the following check will return false even if the
+             * Xalan classes *are* present; this is because the Xalan
+             * XPath class relies on JAXP, as well.  Thus there's no
+             * 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";
+        }
+
+        if (xmlReqCheck.length() != 0)
+        {
+            throw StandardException.newException(
+                SQLState.LANG_MISSING_XML_CLASSES, xmlReqCheck);
+        }
+
+        return;
     }
 
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj?rev=430670&r1=430669&r2=430670&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/sqlgrammar.jj Thu Aug 10 22:06:29 2006
@@ -6662,6 +6662,10 @@
 {
 	ValueNode value;
 	checkVersion(DataDictionary.DD_VERSION_DERBY_10_1, "XML");
+
+	// We only allow XML operations if the classpath has all
+	// of the required external classes (namley, JAXP and Xalan).
+	org.apache.derby.iapi.types.XML.checkXMLRequirements();
 }
 {
 	<XMLPARSE> <LEFT_PAREN>