dev@javaserverfaces.java.net

[REVIEW] Fine tuning of parsing/validation code for _05

From: Ryan Lubke <Ryan.Lubke_at_Sun.COM>
Date: Tue, 15 May 2007 14:20:44 -0700


Fine tuning of new parsing/validation code.


SECTION: Modified Files
----------------------------
M conf/xslt/jsf1_0-1_1to1_2.xsl
  - removed the xslt for handing JSF 1.2 to 1.2 - it's not needed
M src/com/sun/faces/config/ConfigManager.java
  - If validating:
      * document represents a JSF 1.2 faces-config.xml, do not
        transform, instead use the cached Schema from DbfFactory
        to validate and return.
      * document represents a JSF 1.0 or 1.1 faces-config.xml,
        transform to 1.2, validate using the cached Schema from
        DbfFactory and return.

M src/com/sun/faces/config/DbfFactory.java
  - Leverage the JAXP 1.3 Schema caching feature. This elimatings
    the double parse that we were doing before (one to parse and transform,
    and another validating parse *after* the transform


SECTION: Diffs
----------------------------
Index: conf/xslt/jsf1_0-1_1to1_2.xsl
===================================================================
RCS file: /cvs/javaserverfaces-sources/jsf-ri/conf/xslt/jsf1_0-1_1to1_2.xsl,v
retrieving revision 1.1
diff -u -r1.1 jsf1_0-1_1to1_2.xsl
--- conf/xslt/jsf1_0-1_1to1_2.xsl 24 Apr 2007 19:04:22 -0000 1.1
+++ conf/xslt/jsf1_0-1_1to1_2.xsl 15 May 2007 21:16:38 -0000
@@ -41,8 +41,7 @@
 -->
 
 <xsl:stylesheet version="1.0"
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
- xmlns:new="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                 xmlns:old="http://java.sun.com/JSF/Configuration">
     <xsl:output method="xml"/>
     <xsl:template match="/old:faces-config">
@@ -66,15 +65,4 @@
         </xsl:element>
     </xsl:template>
 
- <!--
- We may be processing a 1.2 document, just copy the elements.
- -->
- <xsl:template match="new:*">
- <xsl:element name="{local-name()}"
- namespace="http://java.sun.com/xml/ns/javaee">
- <xsl:copy-of select="@*"/>
- <xsl:apply-templates/>
- </xsl:element>
- </xsl:template>
-
 </xsl:stylesheet>
\ No newline at end of file
Index: src/com/sun/faces/config/ConfigManager.java
===================================================================
RCS file: /cvs/javaserverfaces-sources/jsf-ri/src/com/sun/faces/config/ConfigManager.java,v
retrieving revision 1.7
diff -u -r1.7 ConfigManager.java
--- src/com/sun/faces/config/ConfigManager.java 9 May 2007 05:04:14 -0000 1.7
+++ src/com/sun/faces/config/ConfigManager.java 15 May 2007 21:16:38 -0000
@@ -63,15 +63,10 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.transform.Transformer;
 import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.URIResolver;
-import javax.xml.transform.Source;
-import javax.xml.transform.TransformerException;
 import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.dom.DOMResult;
 import javax.xml.transform.stream.StreamSource;
 import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
@@ -102,7 +97,7 @@
      * </p>
      */
     private static final List<ConfigurationResourceProvider> RESOURCE_PROVIDERS
- = new ArrayList(3);
+ = new ArrayList<ConfigurationResourceProvider>(3);
 
     /**
      * <p>
@@ -130,7 +125,7 @@
      */
     @SuppressWarnings({"CollectionWithoutInitialCapacity"})
     private List<ServletContext> initializedContexts =
- new CopyOnWriteArrayList();
+ new CopyOnWriteArrayList<ServletContext>();
 
     /**
      * <p>
@@ -270,22 +265,19 @@
                     docTasks.add(d);
                     executor.execute(d);
                 }
- } catch (InterruptedException e) {
- ;
+ } catch (InterruptedException ignored) {
             } catch (Exception e) {
                 throw new ConfigurationException(e);
             }
         }
 
- List<Document> docs = new ArrayList(docTasks.size());
+ List<Document> docs = new ArrayList<Document>(docTasks.size());
         for (FutureTask<Document> t : docTasks) {
             try {
                 docs.add(t.get());
             } catch (ExecutionException e) {
                 throw new ConfigurationException(e);
- } catch (InterruptedException e) {
- ;
- }
+ } catch (InterruptedException ignored) { }
         }
 
         executor.shutdown();
@@ -306,7 +298,8 @@
      * </p>
      */
     private static class ParseTask implements Callable<Document> {
-
+ private static final String FACES_SCHEMA_DEFAULT_NS =
+ "http://java.sun.com/xml/ns/javaee";
         private URL documentURL;
         private DocumentBuilder builder;
 
@@ -320,6 +313,7 @@
          * @param factory a DocumentBuilderFactory configured with the desired
          * parse settings
          * @param documentURL a URL to the configuration resource to be parsed
+ * @throws Exception general error
          */
         public ParseTask(DocumentBuilderFactory factory,
                          URL documentURL)
@@ -342,14 +336,13 @@
          */
         public Document call() throws Exception {
 
- InputStream stream = getInputStream(documentURL);
             try {
                 Timer timer = Timer.getInstance();
                 if (timer != null) {
                     timer.startTiming();
                 }
 
- Document d = builder.parse(getParseSource());
+ Document d = getDocument();
 
                 if (timer != null) {
                     timer.stopTiming();
@@ -362,14 +355,6 @@
                      "Unable to parse document ''{0}'': {1}",
                      documentURL.toExternalForm(),
                      e.getMessage()));
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException ioe) {
- ;
- }
- }
             }
         }
 
@@ -377,6 +362,38 @@
         // ----------------------------------------------------- Private Methods
 
 
+ private Document getDocument() throws Exception {
+ if (builder.getSchema() != null) { // the Schema won't be null if validation is enabled.
+ DocumentBuilder db = getNonValidatingBuilder();
+ DOMSource domSource
+ = new DOMSource(db.parse(getInputStream(documentURL),
+ documentURL.toExternalForm()));
+
+ /*
+ * If the Document in question is 1.2 (i.e. it has a namespace matching
+ * FACES_SCHEMA_DEFAULT_NS, then perform validation using the cached schema
+ * and return. Otherwise we assume a 1.0 or 1.1 faces-config in which case
+ * we need to transform it to 1.2 before validating using the cached schema.
+ */
+ if (FACES_SCHEMA_DEFAULT_NS.equals(((Document) domSource.getNode()).getDocumentElement().getNamespaceURI())) {
+ builder.getSchema().newValidator().validate(domSource);
+ return ((Document) domSource.getNode());
+ } else {
+ DOMResult domResult = new DOMResult();
+ Transformer transformer = getTransformer();
+ transformer.transform(domSource, domResult);
+ builder.getSchema().newValidator().validate(new DOMSource(domResult.getNode()));
+ return (Document) domResult.getNode();
+ }
+ } else {
+ // validation isn't required, parse and return
+ InputSource is = new InputSource(getInputStream(documentURL));
+ is.setSystemId(documentURL.toExternalForm());
+ return builder.parse(is);
+ }
+ }
+
+
         /**
          * Obtain a <code>Transformer</code> using the style sheet
          * referenced by the <code>XSL</code> constant.
@@ -387,15 +404,6 @@
         private static Transformer getTransformer() throws Exception {
 
             TransformerFactory factory = TransformerFactory.newInstance();
- factory.setURIResolver(new URIResolver() {
-
- public Source resolve(String href, String base)
- throws TransformerException {
- System.out.println("URI Resolver href: " + href);
- System.out.println("URI Resolver base: " + base);
- return null; //To change body of implemented methods use File | Settings | File Templates.
- }
- });
             return factory
                  .newTransformer(new StreamSource(getInputStream(ConfigManager
                       .class.getResource(XSL))));
@@ -406,6 +414,7 @@
         /**
          * @return an <code>InputStream</code> to the resource referred to by
          * <code>url</code>
+ * @param url source <code>URL</code>
          * @throws IOException if an error occurs
          */
         private static InputStream getInputStream(URL url) throws IOException {
@@ -414,33 +423,10 @@
             conn.setUseCaches(false);
             return new BufferedInputStream(conn.getInputStream());
 
- }
-
- private InputSource getParseSource() throws Exception {
-
- if (builder.isValidating()) {
- // if we're validating, we need to apply xslt transformations
- // to convert all documents to 1.2
- ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
- StreamResult sResult = new StreamResult(baos);
- DocumentBuilder tBuilder = getNonValidatingBuilder();
- DOMSource domSource
- = new DOMSource(tBuilder.parse(getInputStream(documentURL),
- documentURL.toExternalForm()));
- Transformer transformer = getTransformer();
- transformer.transform(domSource, sResult);
- InputSource is = new InputSource(new ByteArrayInputStream(baos.toByteArray()));
- is.setSystemId(documentURL.toExternalForm());
- return is;
- } else {
- InputSource is = new InputSource(getInputStream(documentURL));
- is.setSystemId(documentURL.toExternalForm());
- return is;
- }
-
         }
 
 
+
         private DocumentBuilder getNonValidatingBuilder() throws Exception {
 
             DocumentBuilderFactory tFactory = DbfFactory.getFactory(false);
Index: src/com/sun/faces/config/DbfFactory.java
===================================================================
RCS file: /cvs/javaserverfaces-sources/jsf-ri/src/com/sun/faces/config/DbfFactory.java,v
retrieving revision 1.5
diff -u -r1.5 DbfFactory.java
--- src/com/sun/faces/config/DbfFactory.java 27 Apr 2007 22:00:55 -0000 1.5
+++ src/com/sun/faces/config/DbfFactory.java 15 May 2007 21:16:38 -0000
@@ -36,38 +36,46 @@
 
 package com.sun.faces.config;
 
-import com.sun.faces.util.FacesLogger;
-import org.xml.sax.EntityResolver;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.SAXParseException;
+import org.xml.sax.*;
 import org.xml.sax.helpers.DefaultHandler;
+import org.w3c.dom.ls.LSResourceResolver;
+import org.w3c.dom.ls.LSInput;
 
+import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import java.io.InputStream;
+import java.io.Reader;
 import java.net.URL;
-import java.util.Collections;
+import java.net.URLConnection;
 import java.util.HashMap;
+import java.util.Collections;
 import java.util.Map;
-import java.util.logging.Level;
 import java.util.logging.Logger;
+import java.util.logging.Level;
+
+import com.sun.faces.util.FacesLogger;
 
 /**
  * <p>Create and configure DocumentBuilderFactory instances.</p>
  */
 public class DbfFactory {
 
- private static final Logger LOGGER =FacesLogger.CONFIG.getLogger();
+ private static final Logger LOGGER = FacesLogger.CONFIG.getLogger();
 
- private static final String JAXP_SCHEMA_LANGUAGE =
- "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
- private static final String W3C_XML_SCHEMA =
- "http://www.w3.org/2001/XMLSchema";
- private static final String JAXP_SCHEMA_SOURCE =
- "http://java.sun.com/xml/jaxp/properties/schemaSource";
- private static final String FACES_1_2_SCHEMA =
+ /**
+ * Location of the Faces 1.2 Schema
+ */
+ private static final String FACES_1_2_XSD =
          "/com/sun/faces/web-facesconfig_1_2.xsd";
 
+ /**
+ * Our cached Schema object for validation
+ */
+ private static Schema FACES_SCHEMA;
 
     /**
      * EntityResolver
@@ -75,10 +83,19 @@
     public static final EntityResolver FACES_ENTITY_RESOLVER =
          new FacesEntityResolver();
 
+
+ /**
+ * ErrorHandler
+ */
     public static final FacesErrorHandler FACES_ERROR_HANDLER =
          new FacesErrorHandler();
 
 
+ static {
+ initSchema();
+ }
+
+
 
     // ---------------------------------------------------------- Public Methods
 
@@ -86,23 +103,42 @@
     public static DocumentBuilderFactory getFactory(boolean validating) {
 
         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setValidating(validating);
+ if (validating) {
+ factory.setSchema(FACES_SCHEMA);
+ }
         factory.setNamespaceAware(true);
         factory.setIgnoringComments(true);
         factory.setIgnoringElementContentWhitespace(true);
- factory.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
- factory.setAttribute(JAXP_SCHEMA_SOURCE,
- FacesEntityResolver.class.getResource(
- FACES_1_2_SCHEMA).toExternalForm());
         return factory;
 
- }
+ }
 
 
- // ----------------------------------------------------------- Inner Classes
+ /**
+ * Init our cache objects.
+ */
+ private static void initSchema() {
+
+ // First, cache the various files
+ try {
+ URL url = DbfFactory.class.getResource(FACES_1_2_XSD);
+ URLConnection conn = url.openConnection();
+ conn.setUseCaches(false);
+ InputStream in = conn.getInputStream();
+
+ // next, cache the schema
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ factory.setResourceResolver((LSResourceResolver) DbfFactory.FACES_ENTITY_RESOLVER);
+ FACES_SCHEMA = factory.newSchema(new StreamSource(in));
+ } catch (Exception e) {
+ throw new ConfigurationException(e);
+ }
+
+ }
 
+ // ----------------------------------------------------------- Inner Classes
 
- private static class FacesEntityResolver extends DefaultHandler {
+ private static class FacesEntityResolver extends DefaultHandler implements LSResourceResolver {
 
         /**
          * <p>Contains associations between grammar name and the physical
@@ -119,7 +155,7 @@
             },
             {
                 "web-facesconfig_1_2.xsd",
- FACES_1_2_SCHEMA
+ FACES_1_2_XSD
             },
             {
                 "javaee_5.xsd",
@@ -132,6 +168,14 @@
             {
                 "xml.xsd",
                 "/com/sun/faces/xml.xsd"
+ },
+ {
+ "datatypes.dtd",
+ "/com/sun/faces/datatypes.dtd"
+ },
+ {
+ "XMLSchema.dtd",
+ "/com/sun/faces/XMLSchema.dtd"
             }
         };
 
@@ -171,7 +215,7 @@
 
         } // END JsfEntityResolver
 
-
+
         // ----------------------------------------- Methods from DefaultHandler
 
 
@@ -246,8 +290,19 @@
 
         } // END resolveEntity
 
+ public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
+ try {
+ InputSource source = resolveEntity(publicId, systemId);
+ if (source != null) {
+ return new Input(source.getByteStream());
+ }
+ } catch (Exception e) {
+ throw new ConfigurationException(e);
+ }
+ return null;
+ }
 
- // ------------------------------------------------------ Public Methods
+ // ------------------------------------------------------ Public Methods
 
         public Map<String,String> getKnownEntities() {
 
@@ -270,4 +325,59 @@
             throw exception;
         }
     } // END FacesErrorHandler
+
+
+ private static final class Input implements LSInput {
+ InputStream in;
+ public Input(InputStream in) {
+ this.in = in;
+ }
+ public Reader getCharacterStream() {
+ return null;
+ }
+
+ public void setCharacterStream(Reader characterStream) { }
+
+ public InputStream getByteStream() {
+ return in;
+ }
+
+ public void setByteStream(InputStream byteStream) { }
+
+ public String getStringData() {
+ return null;
+ }
+
+ public void setStringData(String stringData) { }
+
+ public String getSystemId() {
+ return null;
+ }
+
+ public void setSystemId(String systemId) { }
+
+ public String getPublicId() {
+ return null;
+ }
+
+ public void setPublicId(String publicId) { }
+
+ public String getBaseURI() {
+ return null;
+ }
+
+ public void setBaseURI(String baseURI) { }
+
+ public String getEncoding() {
+ return null;
+ }
+
+ public void setEncoding(String encoding) { }
+
+ public boolean getCertifiedText() {
+ return false;
+ }
+
+ public void setCertifiedText(boolean certifiedText) { }
+ }
 }