| /* |
| * $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 $ |
| * |
| * Copyright (c) OSGi Alliance (2002, 2006). All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.osgi.util.xml; |
| |
| import java.io.*; |
| import java.net.URL; |
| import java.util.*; |
| |
| import javax.xml.parsers.*; |
| |
| import org.osgi.framework.*; |
| |
| /** |
| * A BundleActivator class that allows any JAXP compliant XML Parser to register |
| * itself as an OSGi parser service. |
| * |
| * Multiple JAXP compliant parsers can concurrently register by using this |
| * BundleActivator class. Bundles who wish to use an XML parser can then use the |
| * framework's service registry to locate available XML Parsers with the desired |
| * characteristics such as validating and namespace-aware. |
| * |
| * <p> |
| * The services that this bundle activator enables a bundle to provide are: |
| * <ul> |
| * <li><code>javax.xml.parsers.SAXParserFactory</code>({@link #SAXFACTORYNAME}) |
| * <li><code>javax.xml.parsers.DocumentBuilderFactory</code>( |
| * {@link #DOMFACTORYNAME}) |
| * </ul> |
| * |
| * <p> |
| * The algorithm to find the implementations of the abstract parsers is derived |
| * from the JAR file specifications, specifically the Services API. |
| * <p> |
| * An XMLParserActivator assumes that it can find the class file names of the |
| * factory classes in the following files: |
| * <ul> |
| * <li><code>/META-INF/services/javax.xml.parsers.SAXParserFactory</code> is |
| * a file contained in a jar available to the runtime which contains the |
| * implementation class name(s) of the SAXParserFactory. |
| * <li><code>/META-INF/services/javax.xml.parsers.DocumentBuilderFactory</code> |
| * is a file contained in a jar available to the runtime which contains the |
| * implementation class name(s) of the <code>DocumentBuilderFactory</code> |
| * </ul> |
| * <p> |
| * If either of the files does not exist, <code>XMLParserActivator</code> |
| * assumes that the parser does not support that parser type. |
| * |
| * <p> |
| * <code>XMLParserActivator</code> attempts to instantiate both the |
| * <code>SAXParserFactory</code> and the <code>DocumentBuilderFactory</code>. |
| * It registers each factory with the framework along with service properties: |
| * <ul> |
| * <li>{@link #PARSER_VALIDATING}- indicates if this factory supports |
| * validating parsers. It's value is a <code>Boolean</code>. |
| * <li>{@link #PARSER_NAMESPACEAWARE}- indicates if this factory supports |
| * namespace aware parsers It's value is a <code>Boolean</code>. |
| * </ul> |
| * <p> |
| * Individual parser implementations may have additional features, properties, |
| * or attributes which could be used to select a parser with a filter. These can |
| * be added by extending this class and overriding the |
| * <code>setSAXProperties</code> and <code>setDOMProperties</code> methods. |
| */ |
| public class XMLParserActivator implements BundleActivator, ServiceFactory { |
| /** Context of this bundle */ |
| private BundleContext context; |
| /** |
| * Filename containing the SAX Parser Factory Class name. Also used as the |
| * basis for the <code>SERVICE_PID<code> registration property. |
| */ |
| public static final String SAXFACTORYNAME = "javax.xml.parsers.SAXParserFactory"; |
| /** |
| * Filename containing the DOM Parser Factory Class name. Also used as the |
| * basis for the <code>SERVICE_PID</code> registration property. |
| */ |
| public static final String DOMFACTORYNAME = "javax.xml.parsers.DocumentBuilderFactory"; |
| /** Path to the factory class name files */ |
| private static final String PARSERCLASSFILEPATH = "/META-INF/services/"; |
| /** Fully qualified path name of SAX Parser Factory Class Name file */ |
| public static final String SAXCLASSFILE = PARSERCLASSFILEPATH |
| + SAXFACTORYNAME; |
| /** Fully qualified path name of DOM Parser Factory Class Name file */ |
| public static final String DOMCLASSFILE = PARSERCLASSFILEPATH |
| + DOMFACTORYNAME; |
| /** SAX Factory Service Description */ |
| private static final String SAXFACTORYDESCRIPTION = "A JAXP Compliant SAX Parser"; |
| /** DOM Factory Service Description */ |
| private static final String DOMFACTORYDESCRIPTION = "A JAXP Compliant DOM Parser"; |
| /** |
| * Service property specifying if factory is configured to support |
| * validating parsers. The value is of type <code>Boolean</code>. |
| */ |
| public static final String PARSER_VALIDATING = "parser.validating"; |
| /** |
| * Service property specifying if factory is configured to support namespace |
| * aware parsers. The value is of type <code>Boolean</code>. |
| */ |
| public static final String PARSER_NAMESPACEAWARE = "parser.namespaceAware"; |
| /** |
| * Key for parser factory name property - this must be saved in the parsers |
| * properties hashtable so that the parser factory can be instantiated from |
| * a ServiceReference |
| */ |
| private static final String FACTORYNAMEKEY = "parser.factoryname"; |
| |
| /** |
| * Called when this bundle is started so the Framework can perform the |
| * bundle-specific activities necessary to start this bundle. This method |
| * can be used to register services or to allocate any resources that this |
| * bundle needs. |
| * |
| * <p> |
| * This method must complete and return to its caller in a timely manner. |
| * |
| * <p> |
| * This method attempts to register a SAX and DOM parser with the |
| * Framework's service registry. |
| * |
| * @param context The execution context of the bundle being started. |
| * @throws java.lang.Exception If this method throws an exception, this |
| * bundle is marked as stopped and the Framework will remove this |
| * bundle's listeners, unregister all services registered by this |
| * bundle, and release all services used by this bundle. |
| * @see Bundle#start |
| */ |
| public void start(BundleContext context) throws Exception { |
| this.context = context; |
| Bundle parserBundle = context.getBundle(); |
| try { |
| // check for sax parsers |
| registerSAXParsers(getParserFactoryClassNames(parserBundle |
| .getResource(SAXCLASSFILE))); |
| // check for dom parsers |
| registerDOMParsers(getParserFactoryClassNames(parserBundle |
| .getResource(DOMCLASSFILE))); |
| } |
| catch (IOException ioe) { |
| // if there were any IO errors accessing the resource files |
| // containing the class names |
| ioe.printStackTrace(); |
| throw new FactoryConfigurationError(ioe); |
| } |
| } |
| |
| /** |
| * <p> |
| * This method has nothing to do as all active service registrations will |
| * automatically get unregistered when the bundle stops. |
| * |
| * @param context The execution context of the bundle being stopped. |
| * @throws java.lang.Exception If this method throws an exception, the |
| * bundle is still marked as stopped, and the Framework will remove |
| * the bundle's listeners, unregister all services registered by the |
| * bundle, and release all services used by the bundle. |
| * @see Bundle#stop |
| */ |
| public void stop(BundleContext context) throws Exception { |
| } |
| |
| /** |
| * Given the URL for a file, reads and returns the parser class names. There |
| * may be multiple classes specified in this file, one per line. There may |
| * also be comment lines in the file, which begin with "#". |
| * |
| * @param parserUrl The URL of the service file containing the parser class |
| * names |
| * @return A vector of strings containing the parser class names or null if |
| * parserUrl is null |
| * @throws IOException if there is a problem reading the URL input stream |
| */ |
| private Vector getParserFactoryClassNames(URL parserUrl) throws IOException { |
| Vector v = new Vector(1); |
| if (parserUrl != null) { |
| String parserFactoryClassName = null; |
| InputStream is = parserUrl.openStream(); |
| BufferedReader br = new BufferedReader(new InputStreamReader(is)); |
| while (true) { |
| parserFactoryClassName = br.readLine(); |
| if (parserFactoryClassName == null) { |
| break; // end of file reached |
| } |
| String pfcName = parserFactoryClassName.trim(); |
| if (pfcName.length() == 0) { |
| continue; // blank line |
| } |
| int commentIdx = pfcName.indexOf("#"); |
| if (commentIdx == 0) { // comment line |
| continue; |
| } |
| else |
| if (commentIdx < 0) { // no comment on this line |
| v.addElement(pfcName); |
| } |
| else { |
| v.addElement(pfcName.substring(0, commentIdx).trim()); |
| } |
| } |
| return v; |
| } |
| else { |
| return null; |
| } |
| } |
| |
| /** |
| * Register SAX Parser Factory Services with the framework. |
| * |
| * @param parserFactoryClassNames - a <code>Vector</code> of |
| * <code>String</code> objects containing the names of the parser |
| * Factory Classes |
| * @throws FactoryConfigurationError if thrown from <code>getFactory</code> |
| */ |
| private void registerSAXParsers(Vector parserFactoryClassNames) |
| throws FactoryConfigurationError { |
| if (parserFactoryClassNames != null) { |
| Enumeration e = parserFactoryClassNames.elements(); |
| int index = 0; |
| while (e.hasMoreElements()) { |
| String parserFactoryClassName = (String) e.nextElement(); |
| // create a sax parser factory just to get it's default |
| // properties. It will never be used since |
| // this class will operate as a service factory and give each |
| // service requestor it's own SaxParserFactory |
| SAXParserFactory factory = (SAXParserFactory) getFactory(parserFactoryClassName); |
| Hashtable properties = new Hashtable(7); |
| // figure out the default properties of the parser |
| setDefaultSAXProperties(factory, properties, index); |
| // store the parser factory class name in the properties so that |
| // it can be retrieved when getService is called |
| // to return a parser factory |
| properties.put(FACTORYNAMEKEY, parserFactoryClassName); |
| // release the factory |
| factory = null; |
| // register the factory as a service |
| context.registerService(SAXFACTORYNAME, this, properties); |
| index++; |
| } |
| } |
| } |
| |
| /** |
| * <p> |
| * Set the SAX Parser Service Properties. By default, the following |
| * properties are set: |
| * <ul> |
| * <li><code>SERVICE_DESCRIPTION</code> |
| * <li><code>SERVICE_PID</code> |
| * <li><code>PARSER_VALIDATING</code>- instantiates a parser and queries |
| * it to find out whether it is validating or not |
| * <li><code>PARSER_NAMESPACEAWARE</code>- instantiates a parser and |
| * queries it to find out whether it is namespace aware or not |
| * <ul> |
| * |
| * @param factory The <code>SAXParserFactory</code> object |
| * @param props <code>Hashtable</code> of service properties. |
| */ |
| private void setDefaultSAXProperties(SAXParserFactory factory, |
| Hashtable props, int index) { |
| props.put(Constants.SERVICE_DESCRIPTION, SAXFACTORYDESCRIPTION); |
| props.put(Constants.SERVICE_PID, SAXFACTORYNAME + "." |
| + context.getBundle().getBundleId() + "." + index); |
| setSAXProperties(factory, props); |
| } |
| |
| /** |
| * <p> |
| * Set the customizable SAX Parser Service Properties. |
| * |
| * <p> |
| * This method attempts to instantiate a validating parser and a |
| * namespaceaware parser to determine if the parser can support those |
| * features. The appropriate properties are then set in the specified |
| * properties object. |
| * |
| * <p> |
| * This method can be overridden to add additional SAX2 features and |
| * properties. If you want to be able to filter searches of the OSGi service |
| * registry, this method must put a key, value pair into the properties |
| * object for each feature or property. For example, |
| * |
| * properties.put("http://www.acme.com/features/foo", Boolean.TRUE); |
| * |
| * @param factory - the SAXParserFactory object |
| * @param properties - the properties object for the service |
| */ |
| public void setSAXProperties(SAXParserFactory factory, Hashtable properties) { |
| // check if this parser can be configured to validate |
| boolean validating = true; |
| factory.setValidating(true); |
| factory.setNamespaceAware(false); |
| try { |
| factory.newSAXParser(); |
| } |
| catch (Exception pce_val) { |
| validating = false; |
| } |
| // check if this parser can be configured to be namespaceaware |
| boolean namespaceaware = true; |
| factory.setValidating(false); |
| factory.setNamespaceAware(true); |
| try { |
| factory.newSAXParser(); |
| } |
| catch (Exception pce_nsa) { |
| namespaceaware = false; |
| } |
| // set the factory values |
| factory.setValidating(validating); |
| factory.setNamespaceAware(namespaceaware); |
| // set the OSGi service properties |
| properties.put(PARSER_NAMESPACEAWARE, new Boolean(namespaceaware)); |
| properties.put(PARSER_VALIDATING, new Boolean(validating)); |
| } |
| |
| /** |
| * Register DOM Parser Factory Services with the framework. |
| * |
| * @param parserFactoryClassNames - a <code>Vector</code> of |
| * <code>String</code> objects containing the names of the parser |
| * Factory Classes |
| * @throws FactoryConfigurationError if thrown from <code>getFactory</code> |
| */ |
| private void registerDOMParsers(Vector parserFactoryClassNames) |
| throws FactoryConfigurationError { |
| if (parserFactoryClassNames != null) { |
| Enumeration e = parserFactoryClassNames.elements(); |
| int index = 0; |
| while (e.hasMoreElements()) { |
| String parserFactoryClassName = (String) e.nextElement(); |
| // create a dom parser factory just to get it's default |
| // properties. It will never be used since |
| // this class will operate as a service factory and give each |
| // service requestor it's own DocumentBuilderFactory |
| DocumentBuilderFactory factory = (DocumentBuilderFactory) getFactory(parserFactoryClassName); |
| Hashtable properties = new Hashtable(7); |
| // figure out the default properties of the parser |
| setDefaultDOMProperties(factory, properties, index); |
| // store the parser factory class name in the properties so that |
| // it can be retrieved when getService is called |
| // to return a parser factory |
| properties.put(FACTORYNAMEKEY, parserFactoryClassName); |
| // release the factory |
| factory = null; |
| // register the factory as a service |
| context.registerService(DOMFACTORYNAME, this, properties); |
| index++; |
| } |
| } |
| } |
| |
| /** |
| * Set the DOM parser service properties. |
| * |
| * By default, the following properties are set: |
| * <ul> |
| * <li><code>SERVICE_DESCRIPTION</code> |
| * <li><code>SERVICE_PID</code> |
| * <li><code>PARSER_VALIDATING</code> |
| * <li><code>PARSER_NAMESPACEAWARE</code> |
| * <ul> |
| * |
| * @param factory The <code>DocumentBuilderFactory</code> object |
| * @param props <code>Hashtable</code> of service properties. |
| */ |
| private void setDefaultDOMProperties(DocumentBuilderFactory factory, |
| Hashtable props, int index) { |
| props.put(Constants.SERVICE_DESCRIPTION, DOMFACTORYDESCRIPTION); |
| props.put(Constants.SERVICE_PID, DOMFACTORYNAME + "." |
| + context.getBundle().getBundleId() + "." + index); |
| setDOMProperties(factory, props); |
| } |
| |
| /** |
| * <p> |
| * Set the customizable DOM Parser Service Properties. |
| * |
| * <p> |
| * This method attempts to instantiate a validating parser and a |
| * namespaceaware parser to determine if the parser can support those |
| * features. The appropriate properties are then set in the specified props |
| * object. |
| * |
| * <p> |
| * This method can be overridden to add additional DOM2 features and |
| * properties. If you want to be able to filter searches of the OSGi service |
| * registry, this method must put a key, value pair into the properties |
| * object for each feature or property. For example, |
| * |
| * properties.put("http://www.acme.com/features/foo", Boolean.TRUE); |
| * |
| * @param factory - the DocumentBuilderFactory object |
| * @param props - Hashtable of service properties. |
| */ |
| public void setDOMProperties(DocumentBuilderFactory factory, Hashtable props) { |
| // check if this parser can be configured to validate |
| boolean validating = true; |
| factory.setValidating(true); |
| factory.setNamespaceAware(false); |
| try { |
| factory.newDocumentBuilder(); |
| } |
| catch (Exception pce_val) { |
| validating = false; |
| } |
| // check if this parser can be configured to be namespaceaware |
| boolean namespaceaware = true; |
| factory.setValidating(false); |
| factory.setNamespaceAware(true); |
| try { |
| factory.newDocumentBuilder(); |
| } |
| catch (Exception pce_nsa) { |
| namespaceaware = false; |
| } |
| // set the factory values |
| factory.setValidating(validating); |
| factory.setNamespaceAware(namespaceaware); |
| // set the OSGi service properties |
| props.put(PARSER_VALIDATING, new Boolean(validating)); |
| props.put(PARSER_NAMESPACEAWARE, new Boolean(namespaceaware)); |
| } |
| |
| /** |
| * Given a parser factory class name, instantiate that class. |
| * |
| * @param parserFactoryClassName A <code>String</code> object containing |
| * the name of the parser factory class |
| * @return a parserFactoryClass Object |
| * @pre parserFactoryClassName!=null |
| */ |
| private Object getFactory(String parserFactoryClassName) |
| throws FactoryConfigurationError { |
| Exception e = null; |
| try { |
| return Class.forName(parserFactoryClassName).newInstance(); |
| } |
| catch (ClassNotFoundException cnfe) { |
| e = cnfe; |
| } |
| catch (InstantiationException ie) { |
| e = ie; |
| } |
| catch (IllegalAccessException iae) { |
| e = iae; |
| } |
| throw new FactoryConfigurationError(e); |
| } |
| |
| /** |
| * Creates a new XML Parser Factory object. |
| * |
| * <p> |
| * A unique XML Parser Factory object is returned for each call to this |
| * method. |
| * |
| * <p> |
| * The returned XML Parser Factory object will be configured for validating |
| * and namespace aware support as specified in the service properties of the |
| * specified ServiceRegistration object. |
| * |
| * This method can be overridden to configure additional features in the |
| * returned XML Parser Factory object. |
| * |
| * @param bundle The bundle using the service. |
| * @param registration The <code>ServiceRegistration</code> object for the |
| * service. |
| * @return A new, configured XML Parser Factory object or null if a |
| * configuration error was encountered |
| */ |
| public Object getService(Bundle bundle, ServiceRegistration registration) { |
| ServiceReference sref = registration.getReference(); |
| String parserFactoryClassName = (String) sref |
| .getProperty(FACTORYNAMEKEY); |
| try { |
| // need to set factory properties |
| Object factory = getFactory(parserFactoryClassName); |
| if (factory instanceof SAXParserFactory) { |
| ((SAXParserFactory) factory).setValidating(((Boolean) sref |
| .getProperty(PARSER_VALIDATING)).booleanValue()); |
| ((SAXParserFactory) factory).setNamespaceAware(((Boolean) sref |
| .getProperty(PARSER_NAMESPACEAWARE)).booleanValue()); |
| } |
| else |
| if (factory instanceof DocumentBuilderFactory) { |
| ((DocumentBuilderFactory) factory) |
| .setValidating(((Boolean) sref |
| .getProperty(PARSER_VALIDATING)) |
| .booleanValue()); |
| ((DocumentBuilderFactory) factory) |
| .setNamespaceAware(((Boolean) sref |
| .getProperty(PARSER_NAMESPACEAWARE)) |
| .booleanValue()); |
| } |
| return factory; |
| } |
| catch (FactoryConfigurationError fce) { |
| fce.printStackTrace(); |
| return null; |
| } |
| } |
| |
| /** |
| * Releases a XML Parser Factory object. |
| * |
| * @param bundle The bundle releasing the service. |
| * @param registration The <code>ServiceRegistration</code> object for the |
| * service. |
| * @param service The XML Parser Factory object returned by a previous call |
| * to the <code>getService</code> method. |
| */ |
| public void ungetService(Bundle bundle, ServiceRegistration registration, |
| Object service) { |
| } |
| } |