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      /** Whether the XML configuration elements that configured object providers should be retained. */
59      private boolean retainXMLConfiguration;
60  
61      /** Pool of parsers used to read and validate configurations. */
62      private BasicParserPool parserPool;
63  
64      /** Schema used to validate configruation files. */
65      private Schema configurationSchema;
66  
67      /**
68       * Constructor.
69       * 
70       * @throws ConfigurationException thrown if the validation schema for configuration files can not be created
71       */
72      public XMLConfigurator() throws ConfigurationException {
73          this(false);
74      }
75      
76      /**
77       * Constructor.
78       * 
79       * @param retainXML whether to retain the XML configuration elements within the {@link Configuration}.
80       * 
81       * @throws ConfigurationException thrown if the validation schema for configuration files can not be created
82       * 
83       * @deprecated this method will be removed once {@link Configuration} no longer has the option to store the XML configuration fragements
84       */
85      public XMLConfigurator(boolean retainXML) throws ConfigurationException {
86          retainXMLConfiguration = retainXML;
87          parserPool = new BasicParserPool();
88          SchemaFactory factory = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
89          Source schemaSource = new StreamSource(XMLConfigurator.class
90                  .getResourceAsStream(XMLConstants.XMLTOOLING_SCHEMA_LOCATION));
91          try {
92              configurationSchema = factory.newSchema(schemaSource);
93  
94              parserPool.setIgnoreComments(true);
95              parserPool.setIgnoreElementContentWhitespace(true);
96              parserPool.setSchema(configurationSchema);
97          } catch (SAXException e) {
98              throw new ConfigurationException("Unable to read XMLTooling configuration schema", e);
99          }
100     }
101 
102     /**
103      * Loads the configuration file(s) from the given file. If the file is a directory each file within the directory is
104      * loaded.
105      * 
106      * @param configurationFile the configuration file(s) to be loaded
107      * 
108      * @throws ConfigurationException thrown if the configuration file(s) can not be be read or invalid
109      */
110     public void load(File configurationFile) throws ConfigurationException {
111         if (configurationFile == null || !configurationFile.canRead()) {
112             log.error("Unable to read configuration file {}", configurationFile);
113         }
114 
115         try {
116             if (configurationFile.isDirectory()) {
117                 File[] configurations = configurationFile.listFiles();
118                 for (int i = 0; i < configurations.length; i++) {
119                     log.debug("Parsing configuration file {}", configurations[i].getAbsolutePath());
120                     load(new FileInputStream(configurations[i]));
121                 }
122             } else {
123                 // Given file is not a directory so try to load it directly
124                 log.debug("Parsing configuration file {}", configurationFile.getAbsolutePath());
125                 load(new FileInputStream(configurationFile));
126             }
127         } catch (FileNotFoundException e) {
128             // ignore, we already have the files
129         }
130     }
131 
132     /**
133      * Loads a configuration file from an input stream.
134      * 
135      * @param configurationStream configuration stream
136      * 
137      * @throws ConfigurationException thrown if the given configuration is invalid or can not be read
138      */
139     public void load(InputStream configurationStream) throws ConfigurationException {
140         try {
141             Document configuration = parserPool.parse(configurationStream);
142             load(configuration);
143         } catch (XMLParserException e) {
144             log.error("Invalid configuration file", e);
145             throw new ConfigurationException("Unable to create DocumentBuilder", e);
146         }
147 
148     }
149 
150     /**
151      * Loads the configuration docuement.
152      * 
153      * @param configuration the configurationd document
154      * @throws ConfigurationException thrown if the configuration file(s) can not be be read or invalid
155      */
156     public void load(Document configuration) throws ConfigurationException {
157         log.debug("Loading configuration from XML Document");
158         log.trace("{}", XMLHelper.nodeToString(configuration.getDocumentElement()));
159 
160         // Schema validation
161         log.debug("Schema validating configuration Document");
162         validateConfiguration(configuration);
163         log.debug("Configuration document validated");
164 
165         load(configuration.getDocumentElement());
166     }
167 
168     /**
169      * Loads a configuration after it's been schema validated.
170      * 
171      * @param configurationRoot root of the configuration
172      * 
173      * @throws ConfigurationException thrown if there is a problem processing the configuration
174      */
175     protected void load(Element configurationRoot) throws ConfigurationException {
176         // Initialize object providers
177         NodeList objectProviders = configurationRoot.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
178                 "ObjectProviders");
179         if (objectProviders.getLength() > 0) {
180             log.info("Preparing to load ObjectProviders");
181             initializeObjectProviders((Element) objectProviders.item(0));
182             log.info("ObjectProviders load complete");
183         }
184 
185         // Initialize validator suites
186         NodeList validatorSuitesNodes = configurationRoot.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
187                 "ValidatorSuites");
188         if (validatorSuitesNodes.getLength() > 0) {
189             log.info("Preparing to load ValidatorSuites");
190             initializeValidatorSuites((Element) validatorSuitesNodes.item(0));
191             log.info("ValidatorSuites load complete");
192         }
193 
194         // Initialize ID attributes
195         NodeList idAttributesNodes = configurationRoot.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
196                 "IDAttributes");
197         if (idAttributesNodes.getLength() > 0) {
198             log.info("Preparing to load IDAttributes");
199             initializeIDAttributes((Element) idAttributesNodes.item(0));
200             log.info("IDAttributes load complete");
201         }
202     }
203 
204     /**
205      * Intializes the object providers defined in the configuration file.
206      * 
207      * @param objectProviders the configuration for the various object providers
208      * 
209      * @throws ConfigurationException thrown if the configuration elements are invalid
210      */
211     protected void initializeObjectProviders(Element objectProviders) throws ConfigurationException {
212         // Process ObjectProvider child elements
213         Element objectProvider;
214         Attr qNameAttrib;
215         QName objectProviderName;
216         Element configuration;
217         XMLObjectBuilder builder;
218         Marshaller marshaller;
219         Unmarshaller unmarshaller;
220 
221         NodeList providerList = objectProviders.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
222                 "ObjectProvider");
223         for (int i = 0; i < providerList.getLength(); i++) {
224             objectProvider = (Element) providerList.item(i);
225 
226             // Get the element name of type this object provider is for
227             qNameAttrib = objectProvider.getAttributeNodeNS(null, "qualifiedName");
228             objectProviderName = XMLHelper.getAttributeValueAsQName(qNameAttrib);
229 
230             log.debug("Initializing object provider {}", objectProviderName);
231 
232             try {
233                 configuration = (Element) objectProvider.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
234                         "BuilderClass").item(0);
235                 builder = (XMLObjectBuilder) createClassInstance(configuration);
236 
237                 configuration = (Element) objectProvider.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
238                         "MarshallingClass").item(0);
239                 marshaller = (Marshaller) createClassInstance(configuration);
240 
241                 configuration = (Element) objectProvider.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
242                         "UnmarshallingClass").item(0);
243                 unmarshaller = (Unmarshaller) createClassInstance(configuration);
244 
245                 if(retainXMLConfiguration){
246                 Configuration.registerObjectProvider(objectProviderName, builder, marshaller, unmarshaller,
247                         objectProvider);
248                 }else{
249                     Configuration.registerObjectProvider(objectProviderName, builder, marshaller, unmarshaller);
250                 }
251 
252                 log.debug("{} intialized and configuration cached", objectProviderName);
253             } catch (ConfigurationException e) {
254                 log.error("Error initializing object provier " + objectProvider, e);
255                 // clean up any parts of the object provider that might have been registered before the failure
256                 Configuration.deregisterObjectProvider(objectProviderName);
257                 throw e;
258             }
259         }
260     }
261 
262     /**
263      * Initializes the validator suites specified in the configuration file.
264      * 
265      * @param validatorSuitesElement the ValidatorSuites element from the configuration file
266      * 
267      * @throws ConfigurationException thrown if there is a problem initializing the validator suites, usually because of
268      *             malformed elements
269      */
270     protected void initializeValidatorSuites(Element validatorSuitesElement) throws ConfigurationException {
271         ValidatorSuite validatorSuite;
272         Validator validator;
273         Element validatorSuiteElement;
274         String validatorSuiteId;
275         Element validatorElement;
276         QName validatorQName;
277 
278         NodeList validatorSuiteList = validatorSuitesElement.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
279                 "ValidatorSuite");
280         for (int i = 0; i < validatorSuiteList.getLength(); i++) {
281             validatorSuiteElement = (Element) validatorSuiteList.item(i);
282             validatorSuiteId = validatorSuiteElement.getAttributeNS(null, "id");
283             validatorSuite = new ValidatorSuite(validatorSuiteId);
284 
285             log.debug("Initializing ValidatorSuite {}", validatorSuiteId);
286             log.trace(XMLHelper.nodeToString(validatorSuiteElement));
287 
288             NodeList validatorList = validatorSuiteElement.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
289                     "Validator");
290             for (int j = 0; j < validatorList.getLength(); j++) {
291                 validatorElement = (Element) validatorList.item(j);
292                 validatorQName = XMLHelper.getAttributeValueAsQName(validatorElement.getAttributeNodeNS(null,
293                         "qualifiedName"));
294 
295                 validator = (Validator) createClassInstance(validatorElement);
296                 validatorSuite.registerValidator(validatorQName, validator);
297             }
298 
299             log.debug("ValidtorSuite {} has been initialized", validatorSuiteId);
300             if(retainXMLConfiguration){
301                 Configuration.registerValidatorSuite(validatorSuiteId, validatorSuite, validatorSuiteElement);
302             }else{
303                 Configuration.registerValidatorSuite(validatorSuiteId, validatorSuite);
304             }
305         }
306     }
307 
308     /**
309      * Registers the global ID attributes specified in the configuration file.
310      * 
311      * @param idAttributesElement the IDAttributes element from the configuration file
312      * 
313      * @throws ConfigurationException thrown if there is a problem with a parsing or registering the the ID attribute
314      */
315     protected void initializeIDAttributes(Element idAttributesElement) throws ConfigurationException {
316         Element idAttributeElement;
317         QName attributeQName;
318 
319         NodeList idAttributeList = idAttributesElement.getElementsByTagNameNS(XMLConstants.XMLTOOLING_CONFIG_NS,
320                 "IDAttribute");
321 
322         for (int i = 0; i < idAttributeList.getLength(); i++) {
323             idAttributeElement = (Element) idAttributeList.item(i);
324             attributeQName = XMLHelper.getElementContentAsQName(idAttributeElement);
325             if (attributeQName == null) {
326                 log.info("IDAttribute element was empty, no registration performed");
327             } else {
328                 Configuration.registerIDAttribute(attributeQName);
329                 log.debug("IDAttribute {} has been registered", attributeQName);
330             }
331         }
332     }
333 
334     /**
335      * Constructs an instance of the given class.
336      * 
337      * @param configuration the current configuration element
338      * 
339      * @return an instance of the given class
340      * 
341      * @throws ConfigurationException thrown if the class can not be instaniated
342      */
343     protected Object createClassInstance(Element configuration) throws ConfigurationException {
344         String className = configuration.getAttributeNS(null, "className");
345         className = DatatypeHelper.safeTrimOrNullString(className);
346 
347         if (className == null) {
348             return null;
349         }
350 
351         try {
352             log.trace("Creating instance of {}", className);
353             ClassLoader classLoader = this.getClass().getClassLoader();
354             Class clazz = classLoader.loadClass(className);
355             Constructor constructor = clazz.getConstructor();
356             return constructor.newInstance();
357         } catch (Exception e) {
358             log.error("Can not create instance of " + className, e);
359             throw new ConfigurationException("Can not create instance of " + className, e);
360         }
361     }
362 
363     /**
364      * Schema validates the given configuration.
365      * 
366      * @param configuration the configuration to validate
367      * 
368      * @throws ConfigurationException thrown if the configuration is not schema-valid
369      */
370     protected void validateConfiguration(Document configuration) throws ConfigurationException {
371         try {
372             javax.xml.validation.Validator schemaValidator = configurationSchema.newValidator();
373             schemaValidator.validate(new DOMSource(configuration));
374         } catch (IOException e) {
375             // Should never get here as the DOM is already in memory
376             String errorMsg = "Unable to read configuration file DOM";
377             log.error(errorMsg, e);
378             throw new ConfigurationException(errorMsg, e);
379         } catch (SAXException e) {
380             String errorMsg = "Configuration file does not validate against schema";
381             log.error(errorMsg, e);
382             throw new ConfigurationException(errorMsg, e);
383         }
384     }
385 }