Use SchemasResourceListImpl to build config file Schemas
authorgilbert <gilbert@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 9 May 2005 20:27:26 +0000 (20:27 +0000)
committergilbert <gilbert@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 9 May 2005 20:27:26 +0000 (20:27 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1457 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/xml/Parser.java
src/edu/internet2/middleware/shibboleth/xml/SchemaStore.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/xml/Schemas.java [deleted file]
src/edu/internet2/middleware/shibboleth/xml/SchemasDirectoryImpl.java
src/edu/internet2/middleware/shibboleth/xml/SchemasResourceListImpl.java [new file with mode: 0644]

index 3009e7a..5c679b0 100644 (file)
  * 
  * Currently, this class exposes static methods. Should a real 
  * framework be installed, it would become a singleton object.
+ * --------------------
+ * Copyright 2002, 2004 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * [Thats all we have to say to protect ourselves]
+ * Your permission to use this code is governed by "The Shibboleth License".
+ * A copy may be found at http://shibboleth.internet2.edu/license.html
+ * [Nothing in copyright law requires license text in every file.]
  */
 package edu.internet2.middleware.shibboleth.xml;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
+import java.util.Iterator;
+import java.util.Map;
 
 import javax.xml.transform.OutputKeys;
 import javax.xml.transform.Transformer;
@@ -79,11 +89,66 @@ public class Parser {
             "http://schemas.xmlsoap.org/soap/envelope/"
       };
     
+    private static String[] resources = new String[]{
+            "credentials.xsd",
+            "cs-sstc-schema-assertion-1.1.xsd",
+            "cs-sstc-schema-protocol-1.1.xsd",
+            "namemapper.xsd",
+            "saml-schema-assertion-2.0.xsd",
+            "saml-schema-metadata-2.0.xsd",
+            "shibboleth-arp-1.0.xsd",
+            "shibboleth-idpconfig-1.0.xsd",
+            "shibboleth-metadata-1.0.xsd",
+            "shibboleth-resolver-1.0.xsd",
+            "shibboleth-targetconfig-1.0.xsd",
+            "shibboleth-trust-1.0.xsd",
+            "shibboleth.xsd",
+            "soap-envelope.xsd",
+            "wayfconfig.xsd",
+            "xenc-schema.xsd",
+            "xml.xsd",
+            "xmldsig-core-schema.xsd"
+       };
+    private static String[] oldResources = new String[]{
+            "cs-sstc-schema-assertion-01.xsd",
+            "cs-sstc-schema-protocol-01.xsd"
+       };
+    
     // If there were a real Framework here (like Spring) then
     // the schemaBuilder would be inserted 
-    private static SchemasDirectoryImpl schemaBuilder = new SchemasDirectoryImpl();
+    private static String defaultDirectory = "/schemas/";
+    private static String oldSchemasDir = "/schemas/saml-1.0/";
+    private static final boolean useResourceBuilder=true;
+    
+    private static SchemaStore schemaBuilder = 
+        (useResourceBuilder?
+            (SchemaStore)
+                new SchemasResourceListImpl(defaultDirectory,resources):
+            (SchemaStore)
+                new SchemasDirectoryImpl(defaultDirectory));
+    private static SchemaStore oldSchemasBuilder = 
+        (useResourceBuilder?
+                (SchemaStore)
+                    new SchemasResourceListImpl(oldSchemasDir,oldResources):
+                (SchemaStore)
+                       new SchemasDirectoryImpl(oldSchemasDir));
+    
     private static Schema schema = schemaBuilder.compileSchema(namespaces);
-       private static Schema schemaOldSAML= schemaBuilder.compileSchema(namespaces,"/schemas/saml-1.0/");
+    static {
+        // Merge in the XSDs defining non-conflicting namespaces
+        // A non-replacing putAll()
+        Map/*<String,Document>*/ source = schemaBuilder.getSchemaMap();
+        Map/*<String,Document>*/ sink   = oldSchemasBuilder.getSchemaMap();
+        Iterator/*<String>*/ nsi = source.keySet().iterator();
+        while (nsi.hasNext()) {
+            String namespace = (String) nsi.next();
+            if (!sink.containsKey(namespace)) {
+                sink.put(namespace,source.get(namespace));
+            }
+        }
+    }
+       private static Schema schemaOldSAML= 
+           oldSchemasBuilder.compileSchema(namespaces);
     
     /**
      * Load a DOM from a wrapped byte stream.
diff --git a/src/edu/internet2/middleware/shibboleth/xml/SchemaStore.java b/src/edu/internet2/middleware/shibboleth/xml/SchemaStore.java
new file mode 100644 (file)
index 0000000..d6454f9
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Load and compile a bunch of XSD files from some repository.
+ * 
+ * Schemas may be stored in a directory on disk, as resources
+ * in the Java classpath, in columns of an XML aware database,
+ * or in an XML "catalog" respository. This interface describes
+ * the functions that a SchemaStore must provide. Implementations
+ * will have additional properties or constructor arguments that
+ * define file paths, URL's (database, jdbc, jar, ...).
+ * --------------------
+ * Copyright 2002, 2004 
+ * University Corporation for Advanced Internet Development, Inc. 
+ * All rights reserved
+ * [Thats all we have to say to protect ourselves]
+ * Your permission to use this code is governed by "The Shibboleth License".
+ * A copy may be found at http://shibboleth.internet2.edu/license.html
+ * [Nothing in copyright law requires license text in every file.]
+ * 
+ */
+package edu.internet2.middleware.shibboleth.xml;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.XMLConstants;
+import javax.xml.transform.Source;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+/**
+ * Compile a Schema object from a Map of DOM objects representing
+ * XSD files keyed by the Namespace that the XSD defines.
+ * 
+ * <p>This class is typically subclassed by implementations that
+ * obtain the Map of XSD DOMs from some external source.</p>
+ * 
+ * @author Howard Gilbert
+ */
+public class SchemaStore {
+    
+    private static Logger log = Logger.getLogger(SchemaStore.class);
+    
+    protected Map/*<String,Document>*/ bucket = new HashMap/*<String,Document>*/();
+
+    /**
+     * You can create the Map through a subclass and then 
+     * just get a copy of the Map without compiling. This
+     * is useful when merging sources.
+     * @return
+     */
+    public Map/*<String,Document>*/ getSchemaMap() {
+        return bucket;
+    }
+    
+
+    /**
+     * Can only construct this class from a Map.
+     * @param bucket
+     */
+    public SchemaStore(Map/*<String,Document>*/ bucket) {
+        super();
+        this.bucket = bucket;
+    }
+    
+    protected SchemaStore() {}
+    
+    /**
+     * Create JAXP 1.3 Schema object from list of namespaces and resource dir
+     * 
+     * <p>This is an alternate approach to the Schema building logic used in
+     * org.opensaml.XML. That module is driven off a list of file names. 
+     * This code reads in all the *.xsd files in a directory, indexes them 
+     * by the namespace the schema defines, and then is driven off a list
+     * of namespaces. This is more more indirect and requires a bit more
+     * code, but it is more in line with the actual XSD standard where files
+     * and filenames are incidental. It can also be quickly ported to some
+     * other schema storage medium (LDAP, Database, ...).</p>
+     * 
+     * @param namespaces Array of required XML namespaces for validation
+     * @param resourcedir Resource directory with schema files ("/schemas/")
+     * @return Schema object combining all namespaces.
+     */
+    public Schema compileSchema(String[] namespaces) {
+       Schema schema = null;
+       ArrayList sources = new ArrayList();
+       for (int i=0;i<namespaces.length;i++) {
+            Document doc = (Document) bucket.get(namespaces[i]);
+            if (doc==null) {
+                log.error("Schema missing for namespace (" +namespaces[i] + ").");
+            } else {
+            sources.add(new DOMSource(doc));
+            }
+        }
+       // Now compile all the XSD files into a single composite Schema object
+        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+       try {
+            schema = factory.newSchema((Source[]) sources.toArray(new Source[0]));
+        } catch (SAXException e) {
+            log.error("Schemas failed to compile, dependencies may be corrupt: " +e);
+        }
+       return schema;
+    }
+
+}
diff --git a/src/edu/internet2/middleware/shibboleth/xml/Schemas.java b/src/edu/internet2/middleware/shibboleth/xml/Schemas.java
deleted file mode 100644 (file)
index 9f1e8ec..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Schemas.java
- * 
- * For now there is only one implementation of Schema store -
- * a resource directory in the WAR file of the application.
- * So there is only one current implementation of this interface,
- * the SchemasDirectoryImpl class. But we define the interface
- * and later on there may be other schema-store options.
- */
-package edu.internet2.middleware.shibboleth.xml;
-
-import javax.xml.validation.Schema;
-
-/**
- * @author Howard Gilbert
- */
-public interface Schemas {
-    
-    Schema compileSchema(String[] namespaces);
-
-}
index 5cdc45b..4076a62 100644 (file)
@@ -10,113 +10,42 @@ package edu.internet2.middleware.shibboleth.xml;
 import java.io.File;
 import java.io.InputStream;
 import java.net.URL;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.xml.XMLConstants;
-import javax.xml.transform.Source;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.validation.Schema;
-import javax.xml.validation.SchemaFactory;
 
 import org.apache.log4j.Logger;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
 
 /**
  * @author Howard Gilbert
  */
-public class SchemasDirectoryImpl implements Schemas {
+public class SchemasDirectoryImpl extends SchemaStore {
     
     private static Logger log = Logger.getLogger(SchemasDirectoryImpl.class);
     
-    // The default is the /schemas/ resource directory in the WAR file.
     private String resourcedir = "/schemas/";
 
-    private Map bucket = new HashMap();
-       
        
-    public String getResourcedir() {
-        return resourcedir;
-    }
-    
     /**
-     * Allow alternate schema directories to be injected if xsd files
-     * cannot coexist within a single schema. For example, SAML 1.0 and
-     * 1.1 schemas use the same targetNamespace incompatibly. If you 
-     * upgrade from "list of file names" to "list of namespaces", then
-     * two incompatible uses of the same namespace have to be stored in
-     * two separate schema directories.
+     * Load the bucket initially from a supplied directory.
      * 
-     * @param resourcedir Resource directory from which to load xsd files.
+     * @param resourcedir
      */
-    public void setResourcedir(String resourcedir) {
+    public SchemasDirectoryImpl(String resourcedir) {
+        super();
         this.resourcedir = resourcedir;
+        this.loadBucket();
     }
-    /**
-     * Create JAXP 1.3 Schema object from list of namespaces and resource dir
-     * 
-     * <p>This is an alternate approach to the Schema building logic used in
-     * org.opensaml.XML. That module is driven off a list of file names. 
-     * This code reads in all the *.xsd files in a directory, indexes them 
-     * by the namespace the schema defines, and then is driven off a list
-     * of namespaces. This is more more indirect and requires a bit more
-     * code, but it is more in line with the actual XSD standard where files
-     * and filenames are incidental. It can also be quickly ported to some
-     * other schema storage medium (LDAP, Database, ...).</p>
-     * 
-     * @param namespaces Array of required XML namespaces for validation
-     * @param resourcedir Resource directory with schema files ("/schemas/")
-     * @return Schema object combining all namespaces.
-     */
-    public Schema compileSchema(String[] namespaces, String resourcedir) {
-        // Find a directory of schemas
-        // It is a resource in WEB-INF/classes or the same jar file
-        // from which this class was loaded.
+    
+       private boolean loadBucket() {
+               // for each .xsd file in the directory
         URL resource = Parser.class.getResource(resourcedir);
         String path = resource.getPath();
         File dir = new File(path);
         if (!dir.isDirectory()) {
             log.error("Cannot find the schemas resource directory");
-            return null;
+            return false;
         }
-        
-        loadBucketFromDirectory(bucket, dir);
-        
-        return generateSchema(namespaces, bucket);
-        
-    }
-       
-       public Schema compileSchema(String[] namespaces) {
-               return compileSchema(namespaces,this.resourcedir);
-       }
-
-       private Schema generateSchema(String[] namespaces, Map bucket) {
-               Schema schema = null;
-               ArrayList sources = new ArrayList();
-               for (int i=0;i<namespaces.length;i++) {
-            Document doc = (Document) bucket.get(namespaces[i]);
-            if (doc==null) {
-                log.error("Schema missing for namespace (" +namespaces[i] + ").");
-            } else {
-            sources.add(new DOMSource(doc));
-            }
-        }
-               // Now compile all the XSD files into a single composite Schema object
-        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-               try {
-            schema = factory.newSchema((Source[]) sources.toArray(new Source[0]));
-        } catch (SAXException e) {
-            log.error("Schemas failed to compile, dependencies may be corrupt: " +e);
-        }
-               return schema;
-       }
-
-       private void loadBucketFromDirectory(Map bucket, File dir) {
-               // for each .xsd file in the directory
         String[] filenames = dir.list();
                int nextsource=0;
                for (int i=0;i<filenames.length;i++) {
@@ -157,6 +86,7 @@ public class SchemasDirectoryImpl implements Schemas {
             }
             bucket.put(targetNamespace,xsddom);
         }
+               return true;
        }
 
 }
diff --git a/src/edu/internet2/middleware/shibboleth/xml/SchemasResourceListImpl.java b/src/edu/internet2/middleware/shibboleth/xml/SchemasResourceListImpl.java
new file mode 100644 (file)
index 0000000..d2efe60
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * SchemasDirectoryImpl.java
+ * 
+ * Find Schemas as a list of resource files.
+ * 
+ * <p>Java resources are files found in the Classpath of the current
+ * ClassLoader. They may be in directories on disk, in jar files, or
+ * elsewhere. This class must be passed a list of resource names, but
+ * it uses the Java runtime to actually locate the xsd data.
+ */
+package edu.internet2.middleware.shibboleth.xml;
+
+import java.io.InputStream;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+
+/**
+ * @author Howard Gilbert
+ */
+public class SchemasResourceListImpl extends SchemaStore {
+    
+    private static Logger log = Logger.getLogger(SchemasDirectoryImpl.class);
+    
+    private String resourceprefix = "/schemas/";
+    private String[] resourceNames = null;
+
+    /**
+     * @param resourcedir
+     */
+    public SchemasResourceListImpl(String resourcedir, String[] resources) {
+        this.resourceprefix = resourcedir;
+        this.resourceNames = resources;
+        this.loadBucket();
+    }
+    
+   
+    private void loadBucket() {
+               // for each .xsd file in the directory
+               int nextsource=0;
+               for (int i=0;i<resourceNames.length;i++) {
+            String filename = resourceNames[i];
+            if (!filename.endsWith(".xsd")) {
+                log.error(filename + " doesn't end in .xsd, ignoring it.");
+                continue;
+            }
+            String resourceName = resourceprefix+filename;
+            InputStream inputStream =
+                    Parser.class.getResourceAsStream(
+                            resourceName);
+            if (inputStream == null) {
+                log.error("Resource "+resourceName+" not found, ignoring it.");
+                continue;
+            }
+            InputSource insrc = new InputSource(inputStream);
+           
+            // Non-validating parse to DOM
+            Document xsddom;
+                       try {
+                               xsddom = Parser.loadDom(insrc,false);
+                       } catch (Exception e) {
+                               log.error("Error parsing XML schema (" + resourceName + "): " + e);
+                               continue;
+                       }
+            
+            // Get the target namespace from the root element
+            Element ele = xsddom.getDocumentElement();
+            if (!ele.getLocalName().equals("schema")) {
+                log.error("Schema file wrong root element:"+resourceName);
+                continue;
+            }
+            String targetNamespace = ele.getAttribute("targetNamespace");
+            if (targetNamespace==null) {
+                log.error("Schema has no targetNamespace: "+resourceName);
+                continue;
+            }
+            
+            // Put the DOM in the Bucket keyed by namespace
+            if (bucket.containsKey(targetNamespace)) {
+                log.debug("Replacing XSD for namespace: "+targetNamespace+" "+filename);
+            } else {
+                log.debug("Defining XSD for namespace:  "+targetNamespace+" "+filename);
+            }
+            bucket.put(targetNamespace,xsddom);
+        }
+       }
+
+}