You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by gn...@apache.org on 2009/07/10 13:28:10 UTC

svn commit: r792887 - in /geronimo/sandbox/blueprint: blueprint-cm/src/main/resources/org/apache/geronimo/blueprint/compendium/cm/ blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/ blueprint-core/src/main/java/org/apache/geronimo/bl...

Author: gnodet
Date: Fri Jul 10 11:28:10 2009
New Revision: 792887

URL: http://svn.apache.org/viewvc?rev=792887&view=rev
Log:
Boost performance of schema validation by maintaining a cache of schemas

Modified:
    geronimo/sandbox/blueprint/blueprint-cm/src/main/resources/org/apache/geronimo/blueprint/compendium/cm/blueprint-cm.xsd
    geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/NamespaceHandlerRegistry.java
    geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/Parser.java
    geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/namespace/NamespaceHandlerRegistryImpl.java

Modified: geronimo/sandbox/blueprint/blueprint-cm/src/main/resources/org/apache/geronimo/blueprint/compendium/cm/blueprint-cm.xsd
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-cm/src/main/resources/org/apache/geronimo/blueprint/compendium/cm/blueprint-cm.xsd?rev=792887&r1=792886&r2=792887&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-cm/src/main/resources/org/apache/geronimo/blueprint/compendium/cm/blueprint-cm.xsd (original)
+++ geronimo/sandbox/blueprint/blueprint-cm/src/main/resources/org/apache/geronimo/blueprint/compendium/cm/blueprint-cm.xsd Fri Jul 10 11:28:10 2009
@@ -26,7 +26,7 @@
             attributeFormDefault="unqualified"
             version="1.0.0">
 
-    <xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0" schemaLocation="../../blueprint/v1.0.0/blueprint.xsd"/>
+    <xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0" />
 
     <!-- property placeholder -->
 

Modified: geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/NamespaceHandlerRegistry.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/NamespaceHandlerRegistry.java?rev=792887&r1=792886&r2=792887&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/NamespaceHandlerRegistry.java (original)
+++ geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/NamespaceHandlerRegistry.java Fri Jul 10 11:28:10 2009
@@ -19,8 +19,13 @@
 package org.apache.geronimo.blueprint.container;
 
 import java.net.URI;
+import java.util.Set;
+import java.io.IOException;
+
+import javax.xml.validation.Schema;
 
 import org.apache.geronimo.blueprint.NamespaceHandler;
+import org.xml.sax.SAXException;
 
 /**
  * Registry of NamespaceHandler.
@@ -53,6 +58,14 @@
     void removeListener(Listener listener);
 
     /**
+     * Obtain a schema to validate the XML for the given list of namespaces
+     *
+     * @param namespaces
+     * @return
+     */
+    Schema getSchema(Set<URI> namespaces) throws SAXException, IOException;
+
+    /**
      * Destroy this registry
      */
     void destroy();

Modified: geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/Parser.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/Parser.java?rev=792887&r1=792886&r2=792887&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/Parser.java (original)
+++ geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/container/Parser.java Fri Jul 10 11:28:10 2009
@@ -18,7 +18,6 @@
  */
 package org.apache.geronimo.blueprint.container;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.net.URI;
 import java.net.URL;
@@ -26,17 +25,15 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
 import javax.xml.XMLConstants;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.transform.Source;
 import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamSource;
 import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
 import javax.xml.validation.Validator;
 
 import org.w3c.dom.Attr;
@@ -60,15 +57,14 @@
 import org.apache.geronimo.blueprint.reflect.MapMetadataImpl;
 import org.apache.geronimo.blueprint.reflect.MetadataUtil;
 import org.apache.geronimo.blueprint.reflect.PropsMetadataImpl;
-import org.apache.geronimo.blueprint.reflect.ReferenceListMetadataImpl;
 import org.apache.geronimo.blueprint.reflect.RefMetadataImpl;
+import org.apache.geronimo.blueprint.reflect.ReferenceListMetadataImpl;
 import org.apache.geronimo.blueprint.reflect.ReferenceListenerImpl;
 import org.apache.geronimo.blueprint.reflect.ReferenceMetadataImpl;
 import org.apache.geronimo.blueprint.reflect.RegistrationListenerImpl;
 import org.apache.geronimo.blueprint.reflect.ServiceMetadataImpl;
 import org.apache.geronimo.blueprint.reflect.ServiceReferenceMetadataImpl;
 import org.apache.geronimo.blueprint.reflect.ValueMetadataImpl;
-import org.osgi.service.blueprint.container.BlueprintContainer;
 import org.osgi.service.blueprint.container.ComponentDefinitionException;
 import org.osgi.service.blueprint.reflect.BeanArgument;
 import org.osgi.service.blueprint.reflect.BeanMetadata;
@@ -82,8 +78,8 @@
 import org.osgi.service.blueprint.reflect.NonNullMetadata;
 import org.osgi.service.blueprint.reflect.NullMetadata;
 import org.osgi.service.blueprint.reflect.PropsMetadata;
-import org.osgi.service.blueprint.reflect.ReferenceListMetadata;
 import org.osgi.service.blueprint.reflect.RefMetadata;
+import org.osgi.service.blueprint.reflect.ReferenceListMetadata;
 import org.osgi.service.blueprint.reflect.ReferenceListener;
 import org.osgi.service.blueprint.reflect.ReferenceMetadata;
 import org.osgi.service.blueprint.reflect.RegistrationListener;
@@ -185,7 +181,6 @@
     private static final Logger LOGGER = LoggerFactory.getLogger(Parser.class);
 
     private static DocumentBuilderFactory documentBuilderFactory;
-    private static SchemaFactory schemaFactory;
 
     private List<Document> documents;
     private ComponentDefinitionRegistry registry;
@@ -234,7 +229,7 @@
             if (documents == null) {
                 throw new IllegalStateException("Documents should be parsed before retrieving required namespaces");
             }
-            Set<URI> namespaces = new HashSet<URI>();
+            Set<URI> namespaces = new LinkedHashSet<URI>();
             for (Document doc : documents) {
                 findNamespaces(namespaces, doc);
             }
@@ -246,7 +241,7 @@
     private void findNamespaces(Set<URI> namespaces, Node node) {
         if (node instanceof Element || node instanceof Attr) {
             String ns = node.getNamespaceURI();
-            if (ns != null && !isBlueprintNamespace(ns)) {
+            if (ns != null) {
                 namespaces.add(URI.create(ns));
             }
         }
@@ -275,36 +270,15 @@
     }
 
     private void validate() {
-        List<StreamSource> schemaSources = new ArrayList<StreamSource>();
+        // Use a LinkedHashSet to ensure that the blueprint schema is loaded first
         try {
-            schemaSources.add(new StreamSource(getClass().getResourceAsStream("/org/apache/geronimo/blueprint/blueprint.xsd")));
-            for (URI uri : getNamespaces()) {
-                NamespaceHandler handler = this.namespaceHandlerRegistry.getNamespaceHandler(uri);
-                if (handler == null) {
-                    throw new ComponentDefinitionException("Unsupported node namespace: " + uri);
-                }
-                URL url = handler.getSchemaLocation(uri.toString());
-                if (url != null) {
-                    schemaSources.add(new StreamSource(url.openStream()));
-                } else {
-                    LOGGER.warn("No URL is defined for schema " + uri + ". This schema will not be validated");
-                }
-            }
-            Schema schema = getSchemaFactory().newSchema(schemaSources.toArray(new Source[schemaSources.size()]));
+            Schema schema = this.namespaceHandlerRegistry.getSchema(getNamespaces());
             Validator validator = schema.newValidator();
             for (Document doc : this.documents) {
                 validator.validate(new DOMSource(doc));
             }
         } catch (Exception e) {
             throw new ComponentDefinitionException("Unable to validate xml", e);
-        } finally {
-            for (StreamSource s : schemaSources) {
-                try {
-                    s.getInputStream().close();
-                } catch (IOException e) {
-                    // Ignore
-                }
-            }
         }
     }
 
@@ -1225,11 +1199,4 @@
         return documentBuilderFactory;
     }
 
-    private static SchemaFactory getSchemaFactory() {
-        if (schemaFactory == null) {
-            schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-        }
-        return schemaFactory;
-    }
-
 }

Modified: geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/namespace/NamespaceHandlerRegistryImpl.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/namespace/NamespaceHandlerRegistryImpl.java?rev=792887&r1=792886&r2=792887&view=diff
==============================================================================
--- geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/namespace/NamespaceHandlerRegistryImpl.java (original)
+++ geronimo/sandbox/blueprint/blueprint-core/src/main/java/org/apache/geronimo/blueprint/namespace/NamespaceHandlerRegistryImpl.java Fri Jul 10 11:28:10 2009
@@ -19,6 +19,7 @@
 package org.apache.geronimo.blueprint.namespace;
 
 import java.net.URI;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -26,6 +27,19 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.AbstractMap;
+import java.util.LinkedList;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.HashSet;
+import java.io.IOException;
+
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.transform.Source;
+import javax.xml.XMLConstants;
 
 import org.apache.geronimo.blueprint.NamespaceHandler;
 import org.apache.geronimo.blueprint.container.NamespaceHandlerRegistry;
@@ -35,6 +49,7 @@
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.xml.sax.SAXException;
 
 /**
  * Default implementation of the NamespaceHandlerRegistry.
@@ -47,6 +62,8 @@
  */
 public class NamespaceHandlerRegistryImpl implements NamespaceHandlerRegistry, ServiceTrackerCustomizer {
     
+    public static final URI BLUEPRINT_NAMESPACE = URI.create("http://www.osgi.org/xmlns/blueprint/v1.0.0");
+
     public static final String NAMESPACE = "osgi.service.blueprint.namespace";
 
     private static final Logger LOGGER = LoggerFactory.getLogger(NamespaceHandlerRegistryImpl.class);
@@ -55,6 +72,8 @@
     private final Map<URI, NamespaceHandler> handlers;
     private final ServiceTracker tracker;
     private final Map<Listener, Boolean> listeners;
+    private final Map<Set<URI>, Schema> schemas = new LRUMap<Set<URI>, Schema>(10);
+    private SchemaFactory schemaFactory;
 
     public NamespaceHandlerRegistryImpl(BundleContext bundleContext) {
         this.bundleContext = bundleContext;
@@ -117,6 +136,7 @@
             handlers.remove(uri);
             callListeners(uri, false);
         }
+        removeSchemasFor(namespaces);
     }
 
     private void callListeners(URI uri, boolean registered) {
@@ -194,4 +214,149 @@
     public synchronized void removeListener(Listener listener) {
         listeners.remove(listener);
     }
+
+    public synchronized Schema getSchema(Set<URI> namespaces) throws IOException, SAXException {
+        Schema schema = null;
+        // Find a schema that can handle all the requested namespaces
+        // If it contains additional namespaces, it should not be a problem since
+        // they won't be used at all
+        for (Set<URI> key : schemas.keySet()) {
+            if (key.containsAll(namespaces)) {
+                schema = schemas.get(key);
+                break;
+            }
+        }
+        if (schema == null) {
+            List<StreamSource> schemaSources = new ArrayList<StreamSource>();
+            try {
+                schemaSources.add(new StreamSource(getClass().getResourceAsStream("/org/apache/geronimo/blueprint/blueprint.xsd")));
+                // Create a schema for all namespaces known at this point
+                // It will speed things as it can be reused for all other blueprint containers
+                namespaces = new HashSet<URI>(handlers.keySet());
+                namespaces.add(BLUEPRINT_NAMESPACE);
+                for (URI ns : namespaces) {
+                    if (!BLUEPRINT_NAMESPACE.equals(ns)) {
+                        NamespaceHandler handler = getNamespaceHandler(ns);
+                        if (handler == null) {
+                            throw new IllegalArgumentException("No namespace handler has been registered for " + ns);
+                        }
+                        URL url = handler.getSchemaLocation(ns.toString());
+                        if (url == null) {
+                            LOGGER.warn("No URL is defined for schema " + ns + ". This schema will not be validated");
+                        } else {
+                            schemaSources.add(new StreamSource(url.openStream()));
+                        }
+                    }
+                }
+                schema = getSchemaFactory().newSchema(schemaSources.toArray(new Source[schemaSources.size()]));
+                schemas.put(namespaces, schema);
+            } finally {
+                for (StreamSource s : schemaSources) {
+                    try {
+                        s.getInputStream().close();
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+        return schema;
+    }
+
+    protected synchronized void removeSchemasFor(List<URI> namespaces) {
+        for (URI ns : namespaces) {
+            for (Set<URI> key : schemas.keySet()) {
+                if (key.contains(ns)) {
+                    schemas.remove(key);
+                }
+            }
+        }
+    }
+
+    private SchemaFactory getSchemaFactory() {
+        if (schemaFactory == null) {
+            schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        }
+        return schemaFactory;
+    }
+
+    private static class LRUMap<K,V> extends AbstractMap<K,V> {
+
+        private final int bound;
+        private final LinkedList<Entry<K,V>> entries = new LinkedList<Entry<K,V>>();
+
+        private static class LRUEntry<K,V> implements Entry<K,V> {
+            private final K key;
+            private final V value;
+
+            private LRUEntry(K key, V value) {
+                this.key = key;
+                this.value = value;
+            }
+
+            public K getKey() {
+                return key;
+            }
+
+            public V getValue() {
+                return value;
+            }
+
+            public V setValue(V value) {
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        private LRUMap(int bound) {
+            this.bound = bound;
+        }
+
+        public V get(Object key) {
+            if (key == null) {
+                throw new NullPointerException();
+            }
+            for (Entry<K,V> e : entries) {
+                if (e.getKey().equals(key)) {
+                    entries.remove(e);
+                    entries.addFirst(e);
+                    return e.getValue();
+                }
+            }
+            return null;
+        }
+
+        public V put(K key, V value) {
+            if (key == null) {
+                throw new NullPointerException();
+            }
+            V old = null;
+            for (Entry<K,V> e : entries) {
+                if (e.getKey().equals(key)) {
+                    entries.remove(e);
+                    old = e.getValue();
+                    break;
+                }
+            }
+            if (value != null) {
+                entries.addFirst(new LRUEntry<K,V>(key, value));
+                while (entries.size() > bound) {
+                    entries.removeLast();
+                }
+            }
+            return old;
+        }
+
+        public Set<Entry<K, V>> entrySet() {
+            return new AbstractSet<Entry<K,V>>() {
+                public Iterator<Entry<K, V>> iterator() {
+                    return entries.iterator();
+                }
+
+                public int size() {
+                    return entries.size();
+                }
+            };
+        }
+    }
+
 }