1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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
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 }