/*
* $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.
*/
/*
* Derived from XMLParserActivator
*/
package org.openanzo.osgi;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
/*
* 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 XMLStreamActivator implements BundleActivator, ServiceFactory {
/** Context of this bundle */
private BundleContext context;
/**
* Filename containing the Transformer Factory Class name. Also used as the basis for the <code>SERVICE_PID<code> registration property.
*/
private static final String EVENTFACTORYNAME = "javax.xml.stream.XMLEventFactory";
/** Path to the factory class name files */
private static final String EVENTCLASSFILEPATH = "/META-INF/services/";
/** Fully qualified path name of EVENT Factory Class Name file */
private static final String EVENTCLASSFILE = EVENTCLASSFILEPATH + EVENTFACTORYNAME;
/** DOM Factory Service Description */
private static final String EVENTFACTORYDESCRIPTION = "A Stax API Event Factory";
/**
* 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 = "stream.factoryName";
/**
* Filename containing the Transformer Factory Class name. Also used as the basis for the <code>SERVICE_PID<code> registration property.
*/
private static final String INPUTFACTORYNAME = "javax.xml.stream.XMLInputFactory";
/** Path to the factory class name files */
private static final String INPUTCLASSFILEPATH = "/META-INF/services/";
/** Fully qualified path name of INPUT Factory Class Name file */
private static final String INPUTCLASSFILE = INPUTCLASSFILEPATH + INPUTFACTORYNAME;
/** DOM Factory Service Description */
private static final String INPUTFACTORYDESCRIPTION = "A Stax input factory";
/**
* Filename containing the Output Factory Class name. Also used as the basis for the <code>SERVICE_PID<code> registration property.
*/
private static final String OUTPUTFACTORYNAME = "javax.xml.stream.XMLOutputFactory";
/** Path to the factory class name files */
private static final String OUTPUTCLASSFILEPATH = "/META-INF/services/";
/** Fully qualified path name of Output Factory Class Name file */
private static final String OUTPUTCLASSFILE = OUTPUTCLASSFILEPATH + OUTPUTFACTORYNAME;
/** DOM Factory Service Description */
private static final String OUTPUTFACTORYDESCRIPTION = "A Stax output factory";
/**
* 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 event factories
registerEvent(getFactoryClassNames(parserBundle.getResource(EVENTCLASSFILE)));
// check for input factories
registerInput(getFactoryClassNames(parserBundle.getResource(INPUTCLASSFILE)));
// check for output factories
registerOutput(getFactoryClassNames(parserBundle.getResource(OUTPUTCLASSFILE)));
} 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
*/
@SuppressWarnings("unchecked")
private Vector getFactoryClassNames(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));
try {
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());
}
}
} finally {
br.close();
}
return v;
} else {
return null;
}
}
/**
* Register Event 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>
*/
@SuppressWarnings("unchecked")
private void registerEvent(Vector eventFactoryClassNames) throws FactoryConfigurationError {
if (eventFactoryClassNames != null) {
Enumeration e = eventFactoryClassNames.elements();
int index = 0;
while (e.hasMoreElements()) {
String eventFactoryClassName = (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
XMLEventFactory factory = (XMLEventFactory) getFactory(eventFactoryClassName);
Hashtable properties = new Hashtable(7);
// figure out the default properties of the parser
setDefaultEventFactoryProperties(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, eventFactoryClassName);
// release the factory
factory = null;
// register the factory as a service
context.registerService(EVENTFACTORYNAME, this, properties);
index++;
}
}
}
/**
* Register Input 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>
*/
@SuppressWarnings("unchecked")
private void registerInput(Vector inputFactoryClassNames) throws FactoryConfigurationError {
if (inputFactoryClassNames != null) {
Enumeration e = inputFactoryClassNames.elements();
int index = 0;
while (e.hasMoreElements()) {
String inputFactoryClassName = (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
XMLInputFactory factory = (XMLInputFactory) getFactory(inputFactoryClassName);
Hashtable properties = new Hashtable(7);
// figure out the default properties of the parser
setDefaultInputFactoryProperties(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, inputFactoryClassName);
// release the factory
factory = null;
// register the factory as a service
context.registerService(INPUTFACTORYNAME, this, properties);
index++;
}
}
}
/**
* Register Output 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>
*/
@SuppressWarnings("unchecked")
private void registerOutput(Vector outputFactoryClassNames) throws FactoryConfigurationError {
if (outputFactoryClassNames != null) {
Enumeration e = outputFactoryClassNames.elements();
int index = 0;
while (e.hasMoreElements()) {
String outputFactoryClassName = (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
XMLOutputFactory factory = (XMLOutputFactory) getFactory(outputFactoryClassName);
Hashtable properties = new Hashtable(7);
// figure out the default properties of the parser
setDefaultOutputFactoryProperties(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, outputFactoryClassName);
// release the factory
factory = null;
// register the factory as a service
context.registerService(OUTPUTFACTORYNAME, 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.
*/
@SuppressWarnings("unchecked")
private void setDefaultEventFactoryProperties(XMLEventFactory factory, Hashtable props, int index) {
props.put(Constants.SERVICE_DESCRIPTION, EVENTFACTORYDESCRIPTION);
props.put(Constants.SERVICE_PID, EVENTFACTORYNAME + "." + context.getBundle().getBundleId() + "." + 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.
*/
@SuppressWarnings("unchecked")
private void setDefaultInputFactoryProperties(XMLInputFactory factory, Hashtable props, int index) {
props.put(Constants.SERVICE_DESCRIPTION, INPUTFACTORYDESCRIPTION);
props.put(Constants.SERVICE_PID, INPUTFACTORYNAME + "." + context.getBundle().getBundleId() + "." + 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.
*/
@SuppressWarnings("unchecked")
private void setDefaultOutputFactoryProperties(XMLOutputFactory factory, Hashtable props, int index) {
props.put(Constants.SERVICE_DESCRIPTION, OUTPUTFACTORYDESCRIPTION);
props.put(Constants.SERVICE_PID, OUTPUTFACTORYNAME + "." + context.getBundle().getBundleId() + "." + index);
}
/**
* 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);
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) {
}
}