/* * $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. * *

* The services that this bundle activator enables a bundle to provide are: *

* *

* The algorithm to find the implementations of the abstract parsers is derived * from the JAR file specifications, specifically the Services API. *

* An XMLParserActivator assumes that it can find the class file names of the * factory classes in the following files: *

*

* If either of the files does not exist, XMLParserActivator * assumes that the parser does not support that parser type. * *

* XMLParserActivator attempts to instantiate both the * SAXParserFactory and the DocumentBuilderFactory. * It registers each factory with the framework along with service properties: *

*

* 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 * setSAXProperties and setDOMProperties 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 SERVICE_PID 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 SERVICE_PID 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 Boolean. */ 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 Boolean. */ 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. * *

* This method must complete and return to its caller in a timely manner. * *

* 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); } } /** *

* 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 Vector of * String objects containing the names of the parser * Factory Classes * @throws FactoryConfigurationError if thrown from getFactory */ 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++; } } } /** *

* Set the SAX Parser Service Properties. By default, the following * properties are set: *