View Javadoc

1   /*
2    * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.opensaml.xml;
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.lang.reflect.Constructor;
25  
26  import javax.xml.namespace.QName;
27  import javax.xml.transform.Source;
28  import javax.xml.transform.dom.DOMSource;
29  import javax.xml.transform.stream.StreamSource;
30  import javax.xml.validation.Schema;
31  import javax.xml.validation.SchemaFactory;
32  
33  import org.opensaml.xml.io.Marshaller;
34  import org.opensaml.xml.io.Unmarshaller;
35  import org.opensaml.xml.parse.BasicParserPool;
36  import org.opensaml.xml.parse.XMLParserException;
37  import org.opensaml.xml.util.DatatypeHelper;
38  import org.opensaml.xml.util.XMLConstants;
39  import org.opensaml.xml.util.XMLHelper;
40  import org.opensaml.xml.validation.Validator;
41  import org.opensaml.xml.validation.ValidatorSuite;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  import org.w3c.dom.Attr;
45  import org.w3c.dom.Document;
46  import org.w3c.dom.Element;
47  import org.w3c.dom.NodeList;
48  import org.xml.sax.SAXException;
49  
50  /**
51   * Reads in an XML configuration and configures the XMLTooling library accordingly.
52   */
53  public class XMLConfigurator {
54  
55      /** Class logger. */
56      private final Logger log = LoggerFactory.getLogger(XMLConfigurator.class);
57  
58      /** Pool of parsers used to read and validate configurations. */
59      private BasicParserPool parserPool;
60  
61      /** Schema used to validate configruation files. */
62      private Schema configurationSchema;
63  
64      /**
65       * Constructor.
66       * 
67       * @throws ConfigurationException thrown if the validation schema for configuration files can not be created
68       */
69      public XMLConfigurator() throws ConfigurationException {
70          parserPool = new BasicParserPool();
71          SchemaFactory factory = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
72          Source schemaSource = new StreamSource(XMLConfigurator.class
73                  .getResourceAsStream(XMLConstants.XMLTOOLING_SCHEMA_LOCATION));
74          try {
75              configurationSchema = factory.newSchema(schemaSource);
76  
77              parserPool.setIgnoreComments(true);
78              parserPool.setIgnoreElementContentWhitespace(true);
79              parserPool.setSchema(configurationSchema);
80          } catch (SAXException e) {
81              throw new ConfigurationException("Unable to read XMLTooling configuration schema", e);
82          }
83      }
84  
85      /**
86       * Loads the configurtion file(s) from the given file. If the file is a directory each file within the directory is
87       * loaded.
88       * 
89       * @param configurationFile the configuration file(s) to be loaded
90       * 
91       * @throws ConfigurationException thrown if the configuration file(s) can not be be read or invalid
92       */
93      public void load(File configurationFile) throws ConfigurationException {
94          if (configurationFile == null || !configurationFile.canRead()) {
95              log.error("Unable to read configuration file {}", configurationFile);
96          }
97  
98          try {
99              if (configurationFile.isDirectory()) {
100                 File[] configurations = configurationFile.listFiles();
101                 for (int i = 0; i < configurations.length; i++) {
102                     log.debug("Parsing configuration file {}", configurations[i].getAbsolutePath());
103                     load(new FileInputStream(configurations[i]));
104                 }
105             } else {
106                 // Given file is not a directory so try to load it directly
107                 log.debug("Parsing configuration file {}", configurationFile.getAbsolutePath());
108                 load(new FileInputStream(configurationFile));
109             }
110         } catch (FileNotFoundException e) {
111             // ignore, we already have the files
112         }
113     }
114 
115     /**
116      * Loads a configuration file from an input stream.
117      * 
118      * @param configurationStream configuration stream
119      * 
120      * @throws ConfigurationException thrown if the given configuration is invalid or can not be read
121      */
122     public void load(InputStream configurationStream) throws ConfigurationException {
123         try {
124             Document configuration = parserPool.parse(configurationStream);
125             load(configuration);
126         } catch (XMLParserException e) {
127             log.error("Invalid configuration file", e);
128             throw new ConfigurationException("Unable to create DocumentBuilder", e);
129         }
130 
131     }
132 
133     /**
134      * Loads the configuration docuement.
135      * 
136      * @param configuration the configurationd document
137      * @throws ConfigurationException thrown if the configuration file(s) can not be be read or invalid
138      */
139     public void load(Document configuration) throws ConfigurationException {
140         log.debug("Loading configuration from XML Document");
141         log.trace("{}", XMLHelper.nodeToString(configuration.getDocumentElement()));
142 
143         // Schema validation
144         log.debug("Schema validating configuration Document");
145         validateConfiguration(configuration);
146         log.debug("Configuration document validated");
147 
148         load(configuration.getDocumentElement());
149     }
150 
151     /**
152      * Loads a configuration after it's been schema validated.
153      * 
154      * @param configurationRoot root of the configuration
155      * 
156      * @throws ConfigurationException thrown if there is a problem processing the configuration
157      */
158     protected void load(Element configurationRoot) throws ConfigurationException {
159         // Initialize object providers
160         NodeList objectProviders = configurationRoot.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
161                 "ObjectProviders");
162         if (objectProviders.getLength() > 0) {
163             log.info("Preparing to load ObjectProviders");
164             initializeObjectProviders((Element) objectProviders.item(0));
165             log.info("ObjectProviders load complete");
166         }
167 
168         // Initialize validator suites
169         NodeList validatorSuitesNodes = configurationRoot.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
170                 "ValidatorSuites");
171         if (validatorSuitesNodes.getLength() > 0) {
172             log.info("Preparing to load ValidatorSuites");
173             initializeValidatorSuites((Element) validatorSuitesNodes.item(0));
174             log.info("ValidatorSuites load complete");
175         }
176 
177         // Initialize ID attributes
178         NodeList idAttributesNodes = configurationRoot.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
179                 "IDAttributes");
180         if (idAttributesNodes.getLength() > 0) {
181             log.info("Preparing to load IDAttributes");
182             initializeIDAttributes((Element) idAttributesNodes.item(0));
183             log.info("IDAttributes load complete");
184         }
185     }
186 
187     /**
188      * Intializes the object providers defined in the configuration file.
189      * 
190      * @param objectProviders the configuration for the various object providers
191      * 
192      * @throws ConfigurationException thrown if the configuration elements are invalid
193      */
194     protected void initializeObjectProviders(Element objectProviders) throws ConfigurationException {
195         // Process ObjectProvider child elements
196         Element objectProvider;
197         Attr qNameAttrib;
198         QName objectProviderName;
199         Element configuration;
200         XMLObjectBuilder builder;
201         Marshaller marshaller;
202         Unmarshaller unmarshaller;
203 
204         NodeList providerList = objectProviders.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
205                 "ObjectProvider");
206         for (int i = 0; i < providerList.getLength(); i++) {
207             objectProvider = (Element) providerList.item(i);
208 
209             // Get the element name of type this object provider is for
210             qNameAttrib = objectProvider.getAttributeNodeNS(null, "qualifiedName");
211             objectProviderName = XMLHelper.getAttributeValueAsQName(qNameAttrib);
212 
213             log.debug("Initializing object provider {}", objectProviderName);
214 
215             try {
216                 configuration = (Element) objectProvider.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
217                         "BuilderClass").item(0);
218                 builder = (XMLObjectBuilder) createClassInstance(configuration);
219 
220                 configuration = (Element) objectProvider.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
221                         "MarshallingClass").item(0);
222                 marshaller = (Marshaller) createClassInstance(configuration);
223 
224                 configuration = (Element) objectProvider.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
225                         "UnmarshallingClass").item(0);
226                 unmarshaller = (Unmarshaller) createClassInstance(configuration);
227 
228                 Configuration.registerObjectProvider(objectProviderName, builder, marshaller, unmarshaller,
229                         objectProvider);
230 
231                 log.debug("{} intialized and configuration cached", objectProviderName);
232             } catch (ConfigurationException e) {
233                 log.error("Error initializing object provier " + objectProvider, e);
234                 // clean up any parts of the object provider that might have been registered before the failure
235                 Configuration.deregisterObjectProvider(objectProviderName);
236                 throw e;
237             }
238         }
239     }
240 
241     /**
242      * Initializes the validator suites specified in the configuration file.
243      * 
244      * @param validatorSuitesElement the ValidatorSuites element from the configuration file
245      * 
246      * @throws ConfigurationException thrown if there is a problem initializing the validator suites, usually because of
247      *             malformed elements
248      */
249     protected void initializeValidatorSuites(Element validatorSuitesElement) throws ConfigurationException {
250         ValidatorSuite validatorSuite;
251         Validator validator;
252         Element validatorSuiteElement;
253         String validatorSuiteId;
254         Element validatorElement;
255         QName validatorQName;
256 
257         NodeList validatorSuiteList = validatorSuitesElement.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
258                 "ValidatorSuite");
259         for (int i = 0; i < validatorSuiteList.getLength(); i++) {
260             validatorSuiteElement = (Element) validatorSuiteList.item(i);
261             validatorSuiteId = validatorSuiteElement.getAttributeNS(null, "id");
262             validatorSuite = new ValidatorSuite(validatorSuiteId);
263 
264             log.debug("Initializing ValidatorSuite {}", validatorSuiteId);
265             log.trace(XMLHelper.nodeToString(validatorSuiteElement));
266 
267             NodeList validatorList = validatorSuiteElement.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
268                     "Validator");
269             for (int j = 0; j < validatorList.getLength(); j++) {
270                 validatorElement = (Element) validatorList.item(j);
271                 validatorQName = XMLHelper.getAttributeValueAsQName(validatorElement.getAttributeNodeNS(null,
272                         "qualifiedName"));
273 
274                 validator = (Validator) createClassInstance(validatorElement);
275                 validatorSuite.registerValidator(validatorQName, validator);
276             }
277 
278             log.debug("ValidtorSuite {} has been initialized", validatorSuiteId);
279             Configuration.registerValidatorSuite(validatorSuiteId, validatorSuite, validatorSuiteElement);
280         }
281     }
282 
283     /**
284      * Registers the global ID attributes specified in the configuration file.
285      * 
286      * @param idAttributesElement the IDAttributes element from the configuration file
287      * 
288      * @throws ConfigurationException thrown if there is a problem with a parsing or registering the the ID attribute
289      */
290     protected void initializeIDAttributes(Element idAttributesElement) throws ConfigurationException {
291         Element idAttributeElement;
292         QName attributeQName;
293 
294         NodeList idAttributeList = idAttributesElement.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
295                 "IDAttribute");
296 
297         for (int i = 0; i < idAttributeList.getLength(); i++) {
298             idAttributeElement = (Element) idAttributeList.item(i);
299             attributeQName = XMLHelper.getElementContentAsQName(idAttributeElement);
300             if (attributeQName == null) {
301                 log.info("IDAttribute element was empty, no registration performed");
302             } else {
303                 Configuration.registerIDAttribute(attributeQName);
304                 log.debug("IDAttribute {} has been registered", attributeQName);
305             }
306         }
307     }
308 
309     /**
310      * Constructs an instance of the given class.
311      * 
312      * @param configuration the current configuration element
313      * 
314      * @return an instance of the given class
315      * 
316      * @throws ConfigurationException thrown if the class can not be instaniated
317      */
318     protected Object createClassInstance(Element configuration) throws ConfigurationException {
319         String className = configuration.getAttributeNS(null, "className");
320         className = DatatypeHelper.safeTrimOrNullString(className);
321 
322         if (className == null) {
323             return null;
324         }
325 
326         try {
327             log.trace("Creating instance of {}", className);
328             ClassLoader classLoader = this.getClass().getClassLoader();
329             Class clazz = classLoader.loadClass(className);
330             Constructor constructor = clazz.getConstructor();
331             return constructor.newInstance();
332         } catch (Exception e) {
333             log.error("Can not create instance of " + className, e);
334             throw new ConfigurationException("Can not create instance of " + className, e);
335         }
336     }
337 
338     /**
339      * Schema validates the given configuration.
340      * 
341      * @param configuration the configuration to validate
342      * 
343      * @throws ConfigurationException thrown if the configuration is not schema-valid
344      */
345     protected void validateConfiguration(Document configuration) throws ConfigurationException {
346         try {
347             javax.xml.validation.Validator schemaValidator = configurationSchema.newValidator();
348             schemaValidator.validate(new DOMSource(configuration));
349         } catch (IOException e) {
350             // Should never get here as the DOM is already in memory
351             String errorMsg = "Unable to read configuration file DOM";
352             log.error(errorMsg, e);
353             throw new ConfigurationException(errorMsg, e);
354         } catch (SAXException e) {
355             String errorMsg = "Configuration file does not validate against schema";
356             log.error(errorMsg, e);
357             throw new ConfigurationException(errorMsg, e);
358         }
359     }
360 }