View Javadoc

1   // 
2   // Copyright (c) 2003, Caltha - Gajda, Krzewski, Mach, Potempski Sp.J. 
3   // All rights reserved. 
4   // 
5   // Redistribution and use in source and binary forms, with or without modification,  
6   // are permitted provided that the following conditions are met: 
7   // 
8   // * Redistributions of source code must retain the above copyright notice,  
9   //       this list of conditions and the following disclaimer. 
10  // * Redistributions in binary form must reproduce the above copyright notice,  
11  //       this list of conditions and the following disclaimer in the documentation  
12  //       and/or other materials provided with the distribution. 
13  // * Neither the name of the Caltha - Gajda, Krzewski, Mach, Potempski Sp.J.  
14  //       nor the names of its contributors may be used to endorse or promote products  
15  //       derived from this software without specific prior written permission. 
16  // 
17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  
18  // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED  
19  // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
20  // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,  
21  // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,  
22  // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
23  // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
24  // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)  
25  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE  
26  // POSSIBILITY OF SUCH DAMAGE. 
27  // 
28  
29  package org.objectledge.configuration;
30  
31  import java.io.IOException;
32  import java.net.URL;
33  
34  import javax.xml.parsers.ParserConfigurationException;
35  import javax.xml.parsers.SAXParserFactory;
36  
37  import org.jcontainer.dna.Configuration;
38  import org.jcontainer.dna.impl.SAXConfigurationHandler;
39  import org.jcontainer.dna.impl.SAXConfigurationSerializer;
40  import org.objectledge.ComponentInitializationError;
41  import org.objectledge.filesystem.FileSystem;
42  import org.objectledge.xml.XMLValidator;
43  import org.xml.sax.InputSource;
44  import org.xml.sax.SAXException;
45  import org.xml.sax.SAXParseException;
46  import org.xml.sax.XMLReader;
47  
48  import com.sun.msv.verifier.Verifier;
49  
50  /***
51   * Returns a configuration for the specific component.
52   * 
53   * <p>
54   * Configuration factory plugs into the container using 
55   * {@link org.objectledge.pico.customization component customization} mechanism,
56   * and provides components that declare a dependency on a DNA Configuration (using a
57   * <code>org.jcontainer.dna.Configuration</code> component parameter) with an initialized
58   * configuration object.
59   * </p>
60   *  
61   * <p>
62   * The contents of the configuration object are read from an XML file,
63   * and verified against a RelaxNG schema.
64   * </p>
65   *
66   * <h3>Functionality anticipated in the future</h3>
67   * <ul>
68   * <li>Detecting of components that support runtime reconfiguration (possibly by extending
69   *  <code>org.jcontainer.dna.Configurable</code>interface.</li>
70   * <li>Triggering of re-reading configuration from files on reconfigurable components</li>
71   * <li>Programmatic setting of configuration of reconfigurable components.</li>
72   * </ul>
73   *
74   * @author <a href="Rafal.Krzewski">rafal@caltha.pl</a>
75   * @version $Id: ConfigurationFactory.java,v 1.12 2005/07/29 12:06:21 pablo Exp $
76   */
77  public class ConfigurationFactory
78  {
79      private FileSystem fileSystem;
80      
81      private String directory;
82      
83      private XMLValidator xmlValidator;
84      
85      private URL relaxngUrl;
86      
87      /***
88       * Creates a new instance of ConfigurationFactory.
89       * 
90       * @param fileSystem the file system to read configurations from.
91       * @param xmlValidator the validator for configuration files.
92       * @param directory the name of the directory where configurations reside.
93       * @throws IOException if the RelaxNG schema cannot be found in classpath.
94       */
95      public ConfigurationFactory(FileSystem fileSystem, XMLValidator xmlValidator, String directory)
96          throws IOException
97      {
98          this.fileSystem = fileSystem;
99          this.xmlValidator = xmlValidator;
100         this.directory = directory;
101         this.relaxngUrl = fileSystem.getResource(XMLValidator.RELAXNG_SCHEMA);
102     }
103 
104     /***
105      * Returns the configuration of the specific compoenent in the system.
106      * 
107      * @param componentName the name of the component
108      * @param componentClass the implementation class of the component.
109      * @return the configuration.
110      */
111     public Configuration getConfig(String componentName, Class componentClass)
112     {
113         String path = getComponentConfigurationPath(componentName);
114         InputSource source = getConfigurationSource(componentName, componentClass);
115         Configuration configuration;
116         try
117         {
118             SAXParserFactory parserFactory = SAXParserFactory.newInstance();
119             XMLReader reader = parserFactory.newSAXParser().getXMLReader();
120             SAXConfigurationHandler handler = new SAXConfigurationHandler();
121             reader.setContentHandler(handler);
122             reader.setErrorHandler(handler);
123             reader.parse(source);
124             configuration = handler.getConfiguration();
125         }
126         catch(SAXParseException e)
127         {
128             throw new ComponentInitializationError("parse error in configuration of component "+
129                 componentName+": "+e.getMessage()+" in "+e.getSystemId()+" at line "+
130                 e.getLineNumber(), e);
131         }
132         catch(Exception e)
133         {
134             throw new ComponentInitializationError("configuration file "+
135                 path+" for component "+componentName+" is malformed", e);
136         }
137         return configuration;
138     }
139     
140     /***
141      * Returns the configuration of the specific compoenent in the system.
142      * 
143      * @param componentRole the role of the component.
144      * @param componentImplementation the implementation class of the component.
145      * @return the configuration.
146      */
147     public Configuration getConfig(Class componentRole, Class componentImplementation)
148     {
149         return getConfig(componentRole.getName(), componentImplementation);
150     }
151 
152     /***
153      * Returns an input source for reading in the configuration file.
154      * 
155      * @param componentName the name of the component
156      * @param componentClass the implementation class of the component.
157      * @return the input source.
158      */
159     public InputSource getConfigurationSource(String componentName, Class componentClass)
160     {
161         String path = getComponentConfigurationPath(componentName);
162         InputSource source = getRawConfigurationSource(componentName);
163         String schema = getComponentConfigurationSchemaPath(componentClass);
164         if(!fileSystem.exists(schema))
165         {
166             throw new ComponentInitializationError("schema file "+schema+" for component "+
167                 componentName+" not found");
168         }
169         try
170         {
171             checkSchema(path, schema);
172         }
173         catch(SAXParseException e)
174         {
175             throw new ComponentInitializationError("parse error in configuration of component "+
176                 componentName+": "+e.getMessage()+" in "+e.getSystemId()+" at line "+
177                 e.getLineNumber(), e);
178         }
179         catch(Exception e)
180         {
181             throw new ComponentInitializationError("configuration file "+
182                 path+" for component "+componentName+" is malformed: '" + e.getMessage() + "'", e);
183         }
184         return source;
185     }
186 
187     /***
188      * Return a raw (unchecked) configuration source for a component.
189      * 
190      * @param componentName the name of the component.
191      * @return an InputSource for reading the configuration.
192      */
193     public InputSource getRawConfigurationSource(String componentName)
194     {
195         String path = getComponentConfigurationPath(componentName);
196         if(!fileSystem.exists(path))
197         {
198             throw new ComponentInitializationError("configuration file "+path+" for component "+
199                 componentName+" not found");
200         }
201         return new InputSource(fileSystem.getInputStream(path));
202     }
203 
204     /***
205      * Checks if a given component has a configration file available.
206      * 
207      * @param componentName the name of the component.
208      * @return <code>true</code> if the component has a configration file available.
209      */
210     public boolean hasConfig(String componentName)
211     {
212         String path = getComponentConfigurationPath(componentName);
213         return fileSystem.exists(path);
214     }
215     
216     /***
217      * Returns an input source for reading in the configuration file.
218      * 
219      * @param componentRole the role of the component
220      * @param componentClass the implementation class of the component.
221      * @return the input source.
222      */
223     public InputSource getConfigurationSource(Class componentRole, Class componentClass)
224     {
225         return getConfigurationSource(componentRole.getName(), componentClass);
226     }
227     
228     /***
229      * Returns an InputSource for reading the container composition file.
230      * 
231      * @return an InputSource for reading the container composition file. 
232      */
233     public InputSource getCompositionSource()
234     {
235         String path = directory + "/container.xml";
236         if(!fileSystem.exists(path))
237         {
238             throw new ComponentInitializationError("composition file " + path + " not found");
239         }
240         return new InputSource(fileSystem.getInputStream(path));
241     }
242     
243     // implemnetation /////////////////////////////////////////////////////////////////////////////
244 
245     /***
246      * Returns the path of the configuration file for the specified key.
247      * 
248      * @param componentName the name of the component.
249      * @return path the configuration file path.
250      */
251     protected String getComponentConfigurationPath(String componentName)
252     {
253         return directory+"/"+componentName+".xml";
254     }
255 
256     /***
257      * Returns the path of the configuration schema file for the specified key.
258      * 
259      * @param componentImplementation the implementation class of the component.
260      * @return path the configuration file path.
261      */
262     protected String getComponentConfigurationSchemaPath(Class componentImplementation)
263     {
264         return ((Class)componentImplementation).getName().replace('.','/')+".rng";
265     }
266 
267     /***
268      * Checks if an xml file fulfills it's associated schema.
269      * 
270      * @param configuration the configuration.
271      * @param schemaPath the the schema file path.
272      * @throws IOException if the configuration, or schema cannot be read.
273      * @throws SAXException if the configuration, or schema cannot be parsed.
274      * @throws ParserConfigurationException if the parser is badly configured.
275      */
276     protected void checkSchema(Configuration configuration, String schemaPath)
277         throws SAXException, IOException, ParserConfigurationException
278     {
279         URL schemaUrl = fileSystem.getResource(schemaPath);
280         xmlValidator.validate(schemaUrl, relaxngUrl);
281         Verifier verifier= xmlValidator.getVerifier(schemaUrl);
282         SAXConfigurationSerializer serializer = new SAXConfigurationSerializer();
283         serializer.serialize(configuration, verifier);
284     }
285 
286     /***
287      * Checks if an xml file fulfills it's associated schema.
288      * 
289      * @param configuration the configuration file path.
290      * @param schemaPath the the schema file path.
291      * @throws IOException if the configuration, or schema cannot be read.
292      * @throws SAXException if the configuration, or schema cannot be parsed.
293      * @throws ParserConfigurationException if the parser is badly configured.
294      */
295     protected void checkSchema(String configuration, String schemaPath)
296         throws SAXException, IOException, ParserConfigurationException
297     {
298         URL schemaUrl = fileSystem.getResource(schemaPath);
299         xmlValidator.validate(schemaUrl, relaxngUrl);
300         xmlValidator.validate(fileSystem.getResource(configuration), schemaUrl);
301     }
302 }