Use SchemasResourceListImpl to build config file Schemas
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / xml / Parser.java
1 /*
2  * Parser.java
3  * 
4  * Validating and non-validating XML parsing using JAXP 1.3.
5  * 
6  * Previous versions of the code directly used the Xerces DOMParser
7  * class. This class has been hidden in the Sun XML stack, and the
8  * public interface is to use DocumentBuilderFactory. This module 
9  * requires the DOM 3 and JAXP 1.3 support built into J2SE 5.0 and
10  * distributed separately for earlier releases of Java from
11  * https://jaxp.dev.java.net/. It should also work with Xerces 2.7.0
12  * when that release becomes available.
13  * 
14  * The org.opensaml.XML class already has most of the parsing code,
15  * but it uses a subset of the required Schemas. Here we build a
16  * wider Schema object, set it as the default SAML schema (because
17  * some Shibboleth namespace fields appear in SAML statements), and
18  * demand that Schema for every parser (DocumentBuilder) we request.
19  * 
20  * Currently, this class exposes static methods. Should a real 
21  * framework be installed, it would become a singleton object.
22  * --------------------
23  * Copyright 2002, 2004 
24  * University Corporation for Advanced Internet Development, Inc. 
25  * All rights reserved
26  * [Thats all we have to say to protect ourselves]
27  * Your permission to use this code is governed by "The Shibboleth License".
28  * A copy may be found at http://shibboleth.internet2.edu/license.html
29  * [Nothing in copyright law requires license text in every file.]
30  */
31 package edu.internet2.middleware.shibboleth.xml;
32
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.StringWriter;
36 import java.util.Iterator;
37 import java.util.Map;
38
39 import javax.xml.transform.OutputKeys;
40 import javax.xml.transform.Transformer;
41 import javax.xml.transform.TransformerConfigurationException;
42 import javax.xml.transform.TransformerException;
43 import javax.xml.transform.TransformerFactory;
44 import javax.xml.transform.dom.DOMSource;
45 import javax.xml.transform.stream.StreamResult;
46 import javax.xml.validation.Schema;
47
48 import org.apache.log4j.Logger;
49 import org.opensaml.SAMLException;
50 import org.w3c.dom.Document;
51 import org.w3c.dom.Node;
52 import org.xml.sax.InputSource;
53 import org.xml.sax.SAXException;
54
55 /**
56  * Obtain schema validating and non-validating XML parsers.
57  * 
58  * @author Howard Gilbert
59  */
60 public class Parser {
61     private static Logger log = Logger.getLogger(Parser.class);
62     
63     
64     /**
65      * All the namespaces used by any part of Shibboleth
66      * 
67      * Note: The current Schema compiler requires that dependencies
68      * (imports) be listed before the namespace of the schema
69      * that imports them.
70      */
71     private static String[] namespaces = new String[]{
72             "http://www.w3.org/2000/09/xmldsig#",  
73             "http://www.w3.org/2001/04/xmlenc#",
74             "urn:oasis:names:tc:SAML:1.0:assertion",
75             "urn:oasis:names:tc:SAML:2.0:assertion",
76             "http://www.w3.org/XML/1998/namespace",
77             "http://schemas.xmlsoap.org/soap/envelope/",
78             "urn:mace:shibboleth:credentials:1.0",
79             "urn:oasis:names:tc:SAML:1.0:protocol",
80             "urn:mace:shibboleth:namemapper:1.0",
81             "urn:mace:shibboleth:idp:config:1.0",
82             "urn:mace:shibboleth:arp:1.0",
83             "urn:mace:shibboleth:resolver:1.0",
84             "urn:oasis:names:tc:SAML:2.0:metadata",
85             "urn:mace:shibboleth:target:config:1.0",
86             "urn:mace:shibboleth:trust:1.0",
87             "urn:mace:shibboleth:metadata:1.0",
88             "urn:mace:shibboleth:1.0",
89             "http://schemas.xmlsoap.org/soap/envelope/"
90       };
91     
92     private static String[] resources = new String[]{
93             "credentials.xsd",
94             "cs-sstc-schema-assertion-1.1.xsd",
95             "cs-sstc-schema-protocol-1.1.xsd",
96             "namemapper.xsd",
97             "saml-schema-assertion-2.0.xsd",
98             "saml-schema-metadata-2.0.xsd",
99             "shibboleth-arp-1.0.xsd",
100             "shibboleth-idpconfig-1.0.xsd",
101             "shibboleth-metadata-1.0.xsd",
102             "shibboleth-resolver-1.0.xsd",
103             "shibboleth-targetconfig-1.0.xsd",
104             "shibboleth-trust-1.0.xsd",
105             "shibboleth.xsd",
106             "soap-envelope.xsd",
107             "wayfconfig.xsd",
108             "xenc-schema.xsd",
109             "xml.xsd",
110             "xmldsig-core-schema.xsd"
111        };
112     private static String[] oldResources = new String[]{
113             "cs-sstc-schema-assertion-01.xsd",
114             "cs-sstc-schema-protocol-01.xsd"
115        };
116     
117     // If there were a real Framework here (like Spring) then
118     // the schemaBuilder would be inserted 
119     private static String defaultDirectory = "/schemas/";
120     private static String oldSchemasDir = "/schemas/saml-1.0/";
121     private static final boolean useResourceBuilder=true;
122     
123     private static SchemaStore schemaBuilder = 
124         (useResourceBuilder?
125             (SchemaStore)
126                 new SchemasResourceListImpl(defaultDirectory,resources):
127             (SchemaStore)
128                 new SchemasDirectoryImpl(defaultDirectory));
129     private static SchemaStore oldSchemasBuilder = 
130         (useResourceBuilder?
131                 (SchemaStore)
132                     new SchemasResourceListImpl(oldSchemasDir,oldResources):
133                 (SchemaStore)
134                         new SchemasDirectoryImpl(oldSchemasDir));
135     
136     private static Schema schema = schemaBuilder.compileSchema(namespaces);
137     static {
138         // Merge in the XSDs defining non-conflicting namespaces
139         // A non-replacing putAll()
140         Map/*<String,Document>*/ source = schemaBuilder.getSchemaMap();
141         Map/*<String,Document>*/ sink   = oldSchemasBuilder.getSchemaMap();
142         Iterator/*<String>*/ nsi = source.keySet().iterator();
143         while (nsi.hasNext()) {
144             String namespace = (String) nsi.next();
145             if (!sink.containsKey(namespace)) {
146                 sink.put(namespace,source.get(namespace));
147             }
148         }
149     }
150         private static Schema schemaOldSAML= 
151             oldSchemasBuilder.compileSchema(namespaces);
152     
153     /**
154      * Load a DOM from a wrapped byte stream.
155      * 
156      * @param ins InputSource The XML document
157      * @param validate If true, use Schema. Otherwise, its raw XML.
158      * @return A DOM 3 tree
159      */
160     public static Document loadDom(InputSource ins, boolean validate) throws SAMLException, SAXException, IOException {
161
162                 Document doc = null;
163                 log.debug("Loading XML from (" + ins.getSystemId() + ")" + (validate ? " with Schema validation" : ""));
164                 if (validate) {
165                         doc = org.opensaml.XML.parserPool.parse(ins, schema);
166                 } else {
167                         doc = org.opensaml.XML.parserPool.parse(ins, null);
168                 }
169                 return doc;
170         }
171     
172     
173     /**
174          * A dummy class that pretends to be an old Xerces DOMParser to simplify conversion of existing code.
175          */
176     public static class DOMParser {
177         Document doc = null;
178         boolean validate = false;
179         
180         public DOMParser(boolean validate) {
181             this.validate=validate;
182         }
183         
184         public Document parse(InputSource ins) throws SAXException, IOException, SAMLException {
185             doc = loadDom(ins,true);
186             return doc;
187         }
188         
189         public Document getDocument() {
190             return doc;
191         }
192     }
193     
194     /**
195      * Write a DOM out to a character stream (for debugging and logging)
196      * 
197      * @param dom The DOM tree to write
198      * @return A string containing the XML in character form.
199      */
200     public static String serialize(Node dom) {
201         String ret = null;
202         
203         TransformerFactory factory = TransformerFactory.newInstance();
204         Transformer transformer = null;
205         DOMSource source = new DOMSource(dom);
206         try {
207             transformer = factory.newTransformer();
208             transformer.setOutputProperty(OutputKeys.INDENT, "yes");
209         } catch (TransformerConfigurationException e) {
210             return null;
211         }
212         StringWriter stringWriter = new StringWriter();
213         StreamResult result = new StreamResult(stringWriter);
214         try {
215             transformer.transform(source, result);
216         } catch (TransformerException e1) {
217             return null;
218         }
219         return stringWriter.toString();
220     }
221     
222     /**
223      * Version of loadDom where the file is specified as a resource name
224      * 
225      * @param configFilePath input resource
226      * @param validate if true, use Schema
227      * @return DOM tree
228      */
229     public static Document loadDom(String configFilePath,boolean validate) throws SAMLException, SAXException, IOException 
230     {
231         InputSource insrc;
232         String schemaCannonicalFilePath;
233        try {
234             InputStream resourceAsStream = Parser.class.getResourceAsStream(configFilePath);
235             insrc = new InputSource(resourceAsStream);
236             insrc.setSystemId(configFilePath);
237         } catch (Exception e1) {
238             log.error("Configuration file "+configFilePath+" could not be located.");
239             return null;
240         }
241         
242         return loadDom(insrc,validate); // Now pass on to the main routine
243         
244     }
245     
246     /**
247      * Override the OpenSAML default schema from SAML 1.1 to 
248      * SAML 1.1 plus Shibboleth (and some SAML 2.0).
249      */
250     static {
251         //org.opensaml.XML.parserPool.setDefaultSchema(schema);
252                 org.opensaml.XML.parserPool.setDefaultSchemas(schemaOldSAML,schema);
253     }
254     
255 }