*
* 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;
"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.
--- /dev/null
+/*
+ * 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;
+ }
+
+}
+++ /dev/null
-/*
- * 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);
-
-}
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++) {
}
bucket.put(targetNamespace,xsddom);
}
+ return true;
}
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+
+}