KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > osgi > util > xml > XMLParserActivator


1 /*
2  * $Header: /cvshome/build/org.osgi.util.xml/src/org/osgi/util/xml/XMLParserActivator.java,v 1.10 2006/06/21 17:41:20 hargrave Exp $
3  *
4  * Copyright (c) OSGi Alliance (2002, 2006). All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18
19 package org.osgi.util.xml;
20
21 import java.io.*;
22 import java.net.URL JavaDoc;
23 import java.util.*;
24
25 import javax.xml.parsers.*;
26
27 import org.osgi.framework.*;
28
29 /**
30  * A BundleActivator class that allows any JAXP compliant XML Parser to register
31  * itself as an OSGi parser service.
32  *
33  * Multiple JAXP compliant parsers can concurrently register by using this
34  * BundleActivator class. Bundles who wish to use an XML parser can then use the
35  * framework's service registry to locate available XML Parsers with the desired
36  * characteristics such as validating and namespace-aware.
37  *
38  * <p>
39  * The services that this bundle activator enables a bundle to provide are:
40  * <ul>
41  * <li><code>javax.xml.parsers.SAXParserFactory</code>({@link #SAXFACTORYNAME})
42  * <li><code>javax.xml.parsers.DocumentBuilderFactory</code>(
43  * {@link #DOMFACTORYNAME})
44  * </ul>
45  *
46  * <p>
47  * The algorithm to find the implementations of the abstract parsers is derived
48  * from the JAR file specifications, specifically the Services API.
49  * <p>
50  * An XMLParserActivator assumes that it can find the class file names of the
51  * factory classes in the following files:
52  * <ul>
53  * <li><code>/META-INF/services/javax.xml.parsers.SAXParserFactory</code> is
54  * a file contained in a jar available to the runtime which contains the
55  * implementation class name(s) of the SAXParserFactory.
56  * <li><code>/META-INF/services/javax.xml.parsers.DocumentBuilderFactory</code>
57  * is a file contained in a jar available to the runtime which contains the
58  * implementation class name(s) of the <code>DocumentBuilderFactory</code>
59  * </ul>
60  * <p>
61  * If either of the files does not exist, <code>XMLParserActivator</code>
62  * assumes that the parser does not support that parser type.
63  *
64  * <p>
65  * <code>XMLParserActivator</code> attempts to instantiate both the
66  * <code>SAXParserFactory</code> and the <code>DocumentBuilderFactory</code>.
67  * It registers each factory with the framework along with service properties:
68  * <ul>
69  * <li>{@link #PARSER_VALIDATING}- indicates if this factory supports
70  * validating parsers. It's value is a <code>Boolean</code>.
71  * <li>{@link #PARSER_NAMESPACEAWARE}- indicates if this factory supports
72  * namespace aware parsers It's value is a <code>Boolean</code>.
73  * </ul>
74  * <p>
75  * Individual parser implementations may have additional features, properties,
76  * or attributes which could be used to select a parser with a filter. These can
77  * be added by extending this class and overriding the
78  * <code>setSAXProperties</code> and <code>setDOMProperties</code> methods.
79  */

80 public class XMLParserActivator implements BundleActivator, ServiceFactory {
81     /** Context of this bundle */
82     private BundleContext context;
83     /**
84      * Filename containing the SAX Parser Factory Class name. Also used as the
85      * basis for the <code>SERVICE_PID<code> registration property.
86      */

87     public static final String JavaDoc SAXFACTORYNAME = "javax.xml.parsers.SAXParserFactory";
88     /**
89      * Filename containing the DOM Parser Factory Class name. Also used as the
90      * basis for the <code>SERVICE_PID</code> registration property.
91      */

92     public static final String JavaDoc DOMFACTORYNAME = "javax.xml.parsers.DocumentBuilderFactory";
93     /** Path to the factory class name files */
94     private static final String JavaDoc PARSERCLASSFILEPATH = "/META-INF/services/";
95     /** Fully qualified path name of SAX Parser Factory Class Name file */
96     public static final String JavaDoc SAXCLASSFILE = PARSERCLASSFILEPATH
97                                                                 + SAXFACTORYNAME;
98     /** Fully qualified path name of DOM Parser Factory Class Name file */
99     public static final String JavaDoc DOMCLASSFILE = PARSERCLASSFILEPATH
100                                                                 + DOMFACTORYNAME;
101     /** SAX Factory Service Description */
102     private static final String JavaDoc SAXFACTORYDESCRIPTION = "A JAXP Compliant SAX Parser";
103     /** DOM Factory Service Description */
104     private static final String JavaDoc DOMFACTORYDESCRIPTION = "A JAXP Compliant DOM Parser";
105     /**
106      * Service property specifying if factory is configured to support
107      * validating parsers. The value is of type <code>Boolean</code>.
108      */

109     public static final String JavaDoc PARSER_VALIDATING = "parser.validating";
110     /**
111      * Service property specifying if factory is configured to support namespace
112      * aware parsers. The value is of type <code>Boolean</code>.
113      */

114     public static final String JavaDoc PARSER_NAMESPACEAWARE = "parser.namespaceAware";
115     /**
116      * Key for parser factory name property - this must be saved in the parsers
117      * properties hashtable so that the parser factory can be instantiated from
118      * a ServiceReference
119      */

120     private static final String JavaDoc FACTORYNAMEKEY = "parser.factoryname";
121
122     /**
123      * Called when this bundle is started so the Framework can perform the
124      * bundle-specific activities necessary to start this bundle. This method
125      * can be used to register services or to allocate any resources that this
126      * bundle needs.
127      *
128      * <p>
129      * This method must complete and return to its caller in a timely manner.
130      *
131      * <p>
132      * This method attempts to register a SAX and DOM parser with the
133      * Framework's service registry.
134      *
135      * @param context The execution context of the bundle being started.
136      * @throws java.lang.Exception If this method throws an exception, this
137      * bundle is marked as stopped and the Framework will remove this
138      * bundle's listeners, unregister all services registered by this
139      * bundle, and release all services used by this bundle.
140      * @see Bundle#start
141      */

142     public void start(BundleContext context) throws Exception JavaDoc {
143         this.context = context;
144         Bundle parserBundle = context.getBundle();
145         try {
146             // check for sax parsers
147
registerSAXParsers(getParserFactoryClassNames(parserBundle
148                     .getResource(SAXCLASSFILE)));
149             // check for dom parsers
150
registerDOMParsers(getParserFactoryClassNames(parserBundle
151                     .getResource(DOMCLASSFILE)));
152         }
153         catch (IOException ioe) {
154             // if there were any IO errors accessing the resource files
155
// containing the class names
156
ioe.printStackTrace();
157             throw new FactoryConfigurationError(ioe);
158         }
159     }
160
161     /**
162      * <p>
163      * This method has nothing to do as all active service registrations will
164      * automatically get unregistered when the bundle stops.
165      *
166      * @param context The execution context of the bundle being stopped.
167      * @throws java.lang.Exception If this method throws an exception, the
168      * bundle is still marked as stopped, and the Framework will remove
169      * the bundle's listeners, unregister all services registered by the
170      * bundle, and release all services used by the bundle.
171      * @see Bundle#stop
172      */

173     public void stop(BundleContext context) throws Exception JavaDoc {
174     }
175
176     /**
177      * Given the URL for a file, reads and returns the parser class names. There
178      * may be multiple classes specified in this file, one per line. There may
179      * also be comment lines in the file, which begin with "#".
180      *
181      * @param parserUrl The URL of the service file containing the parser class
182      * names
183      * @return A vector of strings containing the parser class names or null if
184      * parserUrl is null
185      * @throws IOException if there is a problem reading the URL input stream
186      */

187     private Vector getParserFactoryClassNames(URL JavaDoc parserUrl) throws IOException {
188         Vector v = new Vector(1);
189         if (parserUrl != null) {
190             String JavaDoc parserFactoryClassName = null;
191             InputStream is = parserUrl.openStream();
192             BufferedReader br = new BufferedReader(new InputStreamReader(is));
193             while (true) {
194                 parserFactoryClassName = br.readLine();
195                 if (parserFactoryClassName == null) {
196                     break; // end of file reached
197
}
198                 String JavaDoc pfcName = parserFactoryClassName.trim();
199                 if (pfcName.length() == 0) {
200                     continue; // blank line
201
}
202                 int commentIdx = pfcName.indexOf("#");
203                 if (commentIdx == 0) { // comment line
204
continue;
205                 }
206                 else
207                     if (commentIdx < 0) { // no comment on this line
208
v.addElement(pfcName);
209                     }
210                     else {
211                         v.addElement(pfcName.substring(0, commentIdx).trim());
212                     }
213             }
214             return v;
215         }
216         else {
217             return null;
218         }
219     }
220
221     /**
222      * Register SAX Parser Factory Services with the framework.
223      *
224      * @param parserFactoryClassNames - a <code>Vector</code> of
225      * <code>String</code> objects containing the names of the parser
226      * Factory Classes
227      * @throws FactoryConfigurationError if thrown from <code>getFactory</code>
228      */

229     private void registerSAXParsers(Vector parserFactoryClassNames)
230             throws FactoryConfigurationError {
231         if (parserFactoryClassNames != null) {
232             Enumeration e = parserFactoryClassNames.elements();
233             int index = 0;
234             while (e.hasMoreElements()) {
235                 String JavaDoc parserFactoryClassName = (String JavaDoc) e.nextElement();
236                 // create a sax parser factory just to get it's default
237
// properties. It will never be used since
238
// this class will operate as a service factory and give each
239
// service requestor it's own SaxParserFactory
240
SAXParserFactory factory = (SAXParserFactory) getFactory(parserFactoryClassName);
241                 Hashtable properties = new Hashtable(7);
242                 // figure out the default properties of the parser
243
setDefaultSAXProperties(factory, properties, index);
244                 // store the parser factory class name in the properties so that
245
// it can be retrieved when getService is called
246
// to return a parser factory
247
properties.put(FACTORYNAMEKEY, parserFactoryClassName);
248                 // release the factory
249
factory = null;
250                 // register the factory as a service
251
context.registerService(SAXFACTORYNAME, this, properties);
252                 index++;
253             }
254         }
255     }
256
257     /**
258      * <p>
259      * Set the SAX Parser Service Properties. By default, the following
260      * properties are set:
261      * <ul>
262      * <li><code>SERVICE_DESCRIPTION</code>
263      * <li><code>SERVICE_PID</code>
264      * <li><code>PARSER_VALIDATING</code>- instantiates a parser and queries
265      * it to find out whether it is validating or not
266      * <li><code>PARSER_NAMESPACEAWARE</code>- instantiates a parser and
267      * queries it to find out whether it is namespace aware or not
268      * <ul>
269      *
270      * @param factory The <code>SAXParserFactory</code> object
271      * @param props <code>Hashtable</code> of service properties.
272      */

273     private void setDefaultSAXProperties(SAXParserFactory factory,
274             Hashtable props, int index) {
275         props.put(Constants.SERVICE_DESCRIPTION, SAXFACTORYDESCRIPTION);
276         props.put(Constants.SERVICE_PID, SAXFACTORYNAME + "."
277                 + context.getBundle().getBundleId() + "." + index);
278         setSAXProperties(factory, props);
279     }
280
281     /**
282      * <p>
283      * Set the customizable SAX Parser Service Properties.
284      *
285      * <p>
286      * This method attempts to instantiate a validating parser and a
287      * namespaceaware parser to determine if the parser can support those
288      * features. The appropriate properties are then set in the specified
289      * properties object.
290      *
291      * <p>
292      * This method can be overridden to add additional SAX2 features and
293      * properties. If you want to be able to filter searches of the OSGi service
294      * registry, this method must put a key, value pair into the properties
295      * object for each feature or property. For example,
296      *
297      * properties.put("http://www.acme.com/features/foo", Boolean.TRUE);
298      *
299      * @param factory - the SAXParserFactory object
300      * @param properties - the properties object for the service
301      */

302     public void setSAXProperties(SAXParserFactory factory, Hashtable properties) {
303         // check if this parser can be configured to validate
304
boolean validating = true;
305         factory.setValidating(true);
306         factory.setNamespaceAware(false);
307         try {
308             factory.newSAXParser();
309         }
310         catch (Exception JavaDoc pce_val) {
311             validating = false;
312         }
313         // check if this parser can be configured to be namespaceaware
314
boolean namespaceaware = true;
315         factory.setValidating(false);
316         factory.setNamespaceAware(true);
317         try {
318             factory.newSAXParser();
319         }
320         catch (Exception JavaDoc pce_nsa) {
321             namespaceaware = false;
322         }
323         // set the factory values
324
factory.setValidating(validating);
325         factory.setNamespaceAware(namespaceaware);
326         // set the OSGi service properties
327
properties.put(PARSER_NAMESPACEAWARE, new Boolean JavaDoc(namespaceaware));
328         properties.put(PARSER_VALIDATING, new Boolean JavaDoc(validating));
329     }
330
331     /**
332      * Register DOM Parser Factory Services with the framework.
333      *
334      * @param parserFactoryClassNames - a <code>Vector</code> of
335      * <code>String</code> objects containing the names of the parser
336      * Factory Classes
337      * @throws FactoryConfigurationError if thrown from <code>getFactory</code>
338      */

339     private void registerDOMParsers(Vector parserFactoryClassNames)
340             throws FactoryConfigurationError {
341         if (parserFactoryClassNames != null) {
342             Enumeration e = parserFactoryClassNames.elements();
343             int index = 0;
344             while (e.hasMoreElements()) {
345                 String JavaDoc parserFactoryClassName = (String JavaDoc) e.nextElement();
346                 // create a dom parser factory just to get it's default
347
// properties. It will never be used since
348
// this class will operate as a service factory and give each
349
// service requestor it's own DocumentBuilderFactory
350
DocumentBuilderFactory factory = (DocumentBuilderFactory) getFactory(parserFactoryClassName);
351                 Hashtable properties = new Hashtable(7);
352                 // figure out the default properties of the parser
353
setDefaultDOMProperties(factory, properties, index);
354                 // store the parser factory class name in the properties so that
355
// it can be retrieved when getService is called
356
// to return a parser factory
357
properties.put(FACTORYNAMEKEY, parserFactoryClassName);
358                 // release the factory
359
factory = null;
360                 // register the factory as a service
361
context.registerService(DOMFACTORYNAME, this, properties);
362                 index++;
363             }
364         }
365     }
366
367     /**
368      * Set the DOM parser service properties.
369      *
370      * By default, the following properties are set:
371      * <ul>
372      * <li><code>SERVICE_DESCRIPTION</code>
373      * <li><code>SERVICE_PID</code>
374      * <li><code>PARSER_VALIDATING</code>
375      * <li><code>PARSER_NAMESPACEAWARE</code>
376      * <ul>
377      *
378      * @param factory The <code>DocumentBuilderFactory</code> object
379      * @param props <code>Hashtable</code> of service properties.
380      */

381     private void setDefaultDOMProperties(DocumentBuilderFactory factory,
382             Hashtable props, int index) {
383         props.put(Constants.SERVICE_DESCRIPTION, DOMFACTORYDESCRIPTION);
384         props.put(Constants.SERVICE_PID, DOMFACTORYNAME + "."
385                 + context.getBundle().getBundleId() + "." + index);
386         setDOMProperties(factory, props);
387     }
388
389     /**
390      * <p>
391      * Set the customizable DOM Parser Service Properties.
392      *
393      * <p>
394      * This method attempts to instantiate a validating parser and a
395      * namespaceaware parser to determine if the parser can support those
396      * features. The appropriate properties are then set in the specified props
397      * object.
398      *
399      * <p>
400      * This method can be overridden to add additional DOM2 features and
401      * properties. If you want to be able to filter searches of the OSGi service
402      * registry, this method must put a key, value pair into the properties
403      * object for each feature or property. For example,
404      *
405      * properties.put("http://www.acme.com/features/foo", Boolean.TRUE);
406      *
407      * @param factory - the DocumentBuilderFactory object
408      * @param props - Hashtable of service properties.
409      */

410     public void setDOMProperties(DocumentBuilderFactory factory, Hashtable props) {
411         // check if this parser can be configured to validate
412
boolean validating = true;
413         factory.setValidating(true);
414         factory.setNamespaceAware(false);
415         try {
416             factory.newDocumentBuilder();
417         }
418         catch (Exception JavaDoc pce_val) {
419             validating = false;
420         }
421         // check if this parser can be configured to be namespaceaware
422
boolean namespaceaware = true;
423         factory.setValidating(false);
424         factory.setNamespaceAware(true);
425         try {
426             factory.newDocumentBuilder();
427         }
428         catch (Exception JavaDoc pce_nsa) {
429             namespaceaware = false;
430         }
431         // set the factory values
432
factory.setValidating(validating);
433         factory.setNamespaceAware(namespaceaware);
434         // set the OSGi service properties
435
props.put(PARSER_VALIDATING, new Boolean JavaDoc(validating));
436         props.put(PARSER_NAMESPACEAWARE, new Boolean JavaDoc(namespaceaware));
437     }
438
439     /**
440      * Given a parser factory class name, instantiate that class.
441      *
442      * @param parserFactoryClassName A <code>String</code> object containing
443      * the name of the parser factory class
444      * @return a parserFactoryClass Object
445      * @pre parserFactoryClassName!=null
446      */

447     private Object JavaDoc getFactory(String JavaDoc parserFactoryClassName)
448             throws FactoryConfigurationError {
449         Exception JavaDoc e = null;
450         try {
451             return Class.forName(parserFactoryClassName).newInstance();
452         }
453         catch (ClassNotFoundException JavaDoc cnfe) {
454             e = cnfe;
455         }
456         catch (InstantiationException JavaDoc ie) {
457             e = ie;
458         }
459         catch (IllegalAccessException JavaDoc iae) {
460             e = iae;
461         }
462         throw new FactoryConfigurationError(e);
463     }
464
465     /**
466      * Creates a new XML Parser Factory object.
467      *
468      * <p>
469      * A unique XML Parser Factory object is returned for each call to this
470      * method.
471      *
472      * <p>
473      * The returned XML Parser Factory object will be configured for validating
474      * and namespace aware support as specified in the service properties of the
475      * specified ServiceRegistration object.
476      *
477      * This method can be overridden to configure additional features in the
478      * returned XML Parser Factory object.
479      *
480      * @param bundle The bundle using the service.
481      * @param registration The <code>ServiceRegistration</code> object for the
482      * service.
483      * @return A new, configured XML Parser Factory object or null if a
484      * configuration error was encountered
485      */

486     public Object JavaDoc getService(Bundle bundle, ServiceRegistration registration) {
487         ServiceReference sref = registration.getReference();
488         String JavaDoc parserFactoryClassName = (String JavaDoc) sref
489                 .getProperty(FACTORYNAMEKEY);
490         try {
491             // need to set factory properties
492
Object JavaDoc factory = getFactory(parserFactoryClassName);
493             if (factory instanceof SAXParserFactory) {
494                 ((SAXParserFactory) factory).setValidating(((Boolean JavaDoc) sref
495                         .getProperty(PARSER_VALIDATING)).booleanValue());
496                 ((SAXParserFactory) factory).setNamespaceAware(((Boolean JavaDoc) sref
497                         .getProperty(PARSER_NAMESPACEAWARE)).booleanValue());
498             }
499             else
500                 if (factory instanceof DocumentBuilderFactory) {
501                     ((DocumentBuilderFactory) factory)
502                             .setValidating(((Boolean JavaDoc) sref
503                                     .getProperty(PARSER_VALIDATING))
504                                     .booleanValue());
505                     ((DocumentBuilderFactory) factory)
506                             .setNamespaceAware(((Boolean JavaDoc) sref
507                                     .getProperty(PARSER_NAMESPACEAWARE))
508                                     .booleanValue());
509                 }
510             return factory;
511         }
512         catch (FactoryConfigurationError fce) {
513             fce.printStackTrace();
514             return null;
515         }
516     }
517
518     /**
519      * Releases a XML Parser Factory object.
520      *
521      * @param bundle The bundle releasing the service.
522      * @param registration The <code>ServiceRegistration</code> object for the
523      * service.
524      * @param service The XML Parser Factory object returned by a previous call
525      * to the <code>getService</code> method.
526      */

527     public void ungetService(Bundle bundle, ServiceRegistration registration,
528             Object JavaDoc service) {
529     }
530 }
531
Popular Tags