Hi Johen,
I modified the following three classes and JaxME now supports JAXB
objects in children classloaders while JaxME sits in a parent
classloader. Luckily, I did not have to write a custom classloader at
all :).
Can you look this over and give me your thoughts? Basically, the way
I use JaxME now is like so
//first few lines are JAXB compliant
JAXBContext ctx =
JAXBContext.newInstance("biz.xsoftware.impl.router.jaxme",
getClass().getClassLoader());
unmarshal = ctx.createUnmarshaller();
Classloader cl = newBean.getClassLoader(); //get the new beans
classloader
* //now we get into non-compliant stuff but there is not too
much non-compliance....*
jaxmeCtx = (JAXBContextImpl)ctx;
jaxmeCtx.registerPackages("biz.xsoftware.impl.new.dynamicly.deployedbeans",
cl);
I can now dynamically now deploy new jars with JaxME objects into a
VM. I bet you are probably the only JAXB implementation that can do
so too. Castor can, but it is not JAXB compliant.....and of course,
using this feature leaves you non-compliant, but heh, I think it is a
cool feature :). Let me know what you think.
thanks,
dean
------------------------------------------------------------------------
/*
* Copyright 2003, 2004 The Apache Software Foundation
*
* 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.apache.ws.jaxme.impl;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import javax.xml.bind.DatatypeConverter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.MarshalException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationException;
import javax.xml.bind.Validator;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.ws.jaxme.JMManager;
import org.apache.ws.jaxme.JMMarshaller;
import org.apache.ws.jaxme.JMUnmarshaller;
import org.apache.ws.jaxme.JMValidator;
import org.apache.ws.jaxme.PM;
import org.apache.ws.jaxme.PMException;
import org.apache.ws.jaxme.util.Configurator;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
/** <p>JaxMe's implementation of a JAXBContext.</p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Jochen Wiedmann</a>
* @version $Id: JAXBContextImpl.java,v 1.9 2005/03/10 10:14:03 jochen Exp $
*/
public class JAXBContextImpl extends JAXBContext {
/** The namespace of JaxMe's configuration files.
*/
public static final String CONFIGURATION_URI =
"http://ws.apache.org/jaxme/namespaces/jaxme2/configuration";
private static final SAXParserFactory spf;
private static final DatatypeConverterImpl datatypeConverter = new
DatatypeConverterImpl();
private ClassLoader cl;
private String packageNames;
private Map managersByQName = new HashMap();
private Map managersByInterface = new HashMap();
private Class jmMarshallerClass;
private Class jmUnmarshallerClass;
private Class jmValidatorClass;
static {
spf = SAXParserFactory.newInstance();
spf.setValidating(false);
spf.setNamespaceAware(true);
DatatypeConverter.setDatatypeConverter(datatypeConverter);
}
protected JAXBContextImpl() {}
/** <p>Sets the ClassLoader to use.</p>
*/
protected void setClassLoader(ClassLoader pClassLoader) {
cl = pClassLoader;
}
/** <p>Returns the ClassLoader to use.</p>
*/
public ClassLoader getClassLoader() {
return cl;
}
/** <p>Sets the package names managed by this context.</p>
*/
protected void setPackageNames(String pPackageNames) {
packageNames = pPackageNames;
}
/** <p>Returns the package names managed by this context.</p>
*/
public String getPackageNames() {
return packageNames;
}
/** <p>Sets the JMMarshaller class to use.</p>
*/
protected void setJMMarshallerClass(Class pClass) {
jmMarshallerClass = pClass;
}
/** <p>Returns the JMMarshaller class to use.</p>
*/
public Class getJMMarshallerClass() {
return jmMarshallerClass;
}
/** <p>Sets the JMUnmarshaller class to use.</p>
*/
protected void setJMUnmarshallerClass(Class pClass) {
jmUnmarshallerClass = pClass;
}
/** <p>Sets the JMUnmarshaller class to use.</p>
*/
public Class getJMUnmarshallerClass() {
return jmUnmarshallerClass;
}
/** <p>Sets the JMValidator class to use.</p>
*/
protected void setJMValidatorClass(Class pClass) {
jmValidatorClass = pClass;
}
/** <p>Returns the JMValidator class to use.</p>
*/
public Class getJMValidatorClass() {
return jmValidatorClass;
}
public Marshaller createMarshaller() throws JAXBException {
Class c = getJMMarshallerClass();
try {
JMMarshaller marshaller = (JMMarshaller)
c.newInstance();
marshaller.setJAXBContextImpl(this);
return marshaller;
} catch (InstantiationException e) {
throw new JAXBException("Failed to instantiate class "
+ c.getName(), e);
} catch (IllegalAccessException e) {
throw new JAXBException("Illegal access to class " +
c.getName(), e);
} catch (ClassCastException e) {
throw new JAXBException("Class " + c.getName() +
" is not implementing " +
JMMarshaller.class.getName());
}
}
public Unmarshaller createUnmarshaller() throws JAXBException {
Class c = getJMUnmarshallerClass();
try {
JMUnmarshaller unmarshaller = (JMUnmarshaller)
c.newInstance();
unmarshaller.setJAXBContextImpl(this);
return unmarshaller;
} catch (InstantiationException e) {
throw new JAXBException("Failed to instantiate class "
+ c.getName(), e);
} catch (IllegalAccessException e) {
throw new JAXBException("Illegal access to class " +
c.getName(), e);
} catch (ClassCastException e) {
throw new JAXBException("Class " + c.getName() +
" is not implementing " +
JMUnmarshaller.class.getName());
}
}
public Validator createValidator() throws JAXBException {
Class c = getJMValidatorClass();
try {
JMValidator validator = (JMValidator) c.newInstance();
validator.setJAXBContextImpl(this);
return validator;
} catch (InstantiationException e) {
throw new JAXBException("Failed to instantiate class "
+ c.getName(), e);
} catch (IllegalAccessException e) {
throw new JAXBException("Illegal access to class " +
c.getName(), e);
} catch (ClassCastException e) {
throw new JAXBException("Class " + c.getName() +
" is not implementing " +
JMValidator.class.getName());
}
}
protected JMManager getManagerByQName(QName pQName) {
return (JMManager) managersByQName.get(pQName);
}
protected JMManager getManagerByInterface(Class pElementInterface) {
return (JMManager) managersByInterface.get(pElementInterface);
}
/** Returns a Manager for the given QName.
*
* @throws JAXBException No Manager is registered for the
* given QName.
*/
public JMManager getManager(QName pQName) throws JAXBException {
JMManager manager = getManagerByQName(pQName);
if (manager == null) {
throw new JAXBException("A Manager for " + pQName + " is not
declared.");
}
return manager;
}
/** Returns a Manager for the given element interface.
* Same method than [EMAIL PROTECTED] #getManager(Class)}, except that
it
* throws a [EMAIL PROTECTED] JAXBException}.
* @throws JAXBException No Manager is registered for the
* given QName.
* @see #getManagerS(Class)
*/
public JMManager getManager(Class pElementInterface) throws
JAXBException {
JMManager manager = getManagerByInterface(pElementInterface);
if (manager == null) {
throw new JAXBException("A Manager for " +
pElementInterface.getName() +
" is not declared.");
}
return manager;
}
/** Returns a Manager for the given element interface.
* Same method than [EMAIL PROTECTED] #getManager(Class)}, except that
it
* throws a [EMAIL PROTECTED] SAXException}.
* @throws SAXException No Manager is registered for the
* given QName.
* @see #getManager(Class)
*/
public JMManager getManagerS(Class pElementInterface) throws
SAXException {
JMManager manager = getManagerByInterface(pElementInterface);
if (manager == null) {
throw new SAXException("A Manager for " +
pElementInterface.getName() +
" is not
declared.");
}
return manager;
}
/** <p>Returns a new JMMarshaller.</p>
*/
public JMMarshaller getJMMarshaller() throws MarshalException {
Class c = getJMMarshallerClass();
if (c == null) {
throw new MarshalException("A JMMarshaller class is not set.");
}
try {
return (JMMarshaller) c.newInstance();
} catch (Exception e) {
throw new MarshalException("Failed to instantiate JMMarshaller class " +
c, e);
}
}
/** <p>Returns a new JMUnmarshaller.</p>
*/
public JMUnmarshaller getJMUnmarshaller() throws UnmarshalException {
Class c = getJMUnmarshallerClass();
if (c == null) {
throw new UnmarshalException("A JMUnmarshaller class is not set.");
}
try {
return (JMUnmarshaller) c.newInstance();
} catch (Exception e) {
throw new UnmarshalException("Failed to instantiate JMUnmarshaller class "
+ c, e);
}
}
/** <p>Returns a new JMValidator.</p>
*/
public JMValidator getJMValidator() throws ValidationException {
Class c = getJMValidatorClass();
if (c == null) {
throw new ValidationException("A JMValidator class is not set.");
}
try {
return (JMValidator) c.newInstance();
} catch (Exception e) {
throw new ValidationException("Failed to instantiate JMValidator class " +
c, e);
}
}
/** <p>Returns a new instance of JMPM.</p>
*/
public PM getJMPM(Class pElementInterface) throws PMException {
JMManager manager = getManagerByInterface(pElementInterface);
Class c = manager.getPmClass();
if (c == null) {
throw new PMException("No persistency class configured for " +
pElementInterface.getName());
}
try {
PM pm = (PM) c.newInstance();
pm.init(manager);
return pm;
} catch (Exception e) {
e.printStackTrace(System.err);
throw new PMException("Could not instantiate persistence manager class " +
c.getName(), e);
}
}
/** <p>Returns a new instance of JMPM.</p>
*/
public PM getJMPM(QName pQName) throws PMException {
JMManager manager = getManagerByQName(pQName);
Class c = manager.getPmClass();
if (c == null) {
throw new PMException("No persistency class configured for " + pQName);
}
try {
PM pm = (PM) c.newInstance();
pm.init(manager);
return pm;
} catch (Exception e) {
throw new PMException("Could not instantiate persistence manager class " +
c.getName(), e);
}
}
private boolean first = true;
public void registerPackages(String packageNames, ClassLoader cl)
throws JAXBException {
for (StringTokenizer st = new StringTokenizer(packageNames, ":"); st
.hasMoreTokens();) {
String packageName = st.nextToken();
String configFileName = ((packageName.length() > 0) ?
(packageName
.replace('.', '/') + '/') : "")
+ "Configuration.xml";
URL url = cl.getResource(configFileName);
if (url != null) {
InputStream istream = null;
try {
Configuration c = new
Configuration(this, cl);
Configurator configurator = new
Configurator();
configurator.setNamespace(CONFIGURATION_URI);
configurator.setRootObject(c);
SAXParser sp = spf.newSAXParser();
XMLReader xr = sp.getXMLReader();
xr.setContentHandler(configurator);
istream = url.openStream();
InputSource isource = new
InputSource(istream);
isource.setSystemId(url.toString());
xr.parse(isource);
istream.close();
istream = null;
if (first) {
first = false;
setJMMarshallerClass(c.getJMMarshallerClass());
setJMUnmarshallerClass(c.getJMUnmarshallerClass());
setJMValidatorClass(c.getJMValidatorClass());
}
} catch (IOException e) {
throw new JAXBException(
"Failed to load config file
" + url, e);
} catch (SAXParseException e) {
Exception f = e.getException() == null
? e : e
.getException();
JAXBException jaxbExc = new
JAXBException("Failed to parse config file "
+ url + " at line " +
e.getLineNumber()
+ ", column " +
e.getColumnNumber() + ": "
+ f.getMessage(), f);
jaxbExc.initCause(e);
throw jaxbExc;
} catch (SAXException e) {
Exception f = e.getException() == null
? e : e
.getException();
String msg = "Failed to parse config file " + url
+ ": "
+ f.getMessage();
throw new JAXBException(msg, f);
} catch (ParserConfigurationException e) {
throw new JAXBException("Failed to create a
SAX Parser: "
+ e.getMessage(), e);
} finally {
if (istream != null) {
try {
istream.close();
} catch (Throwable ignore) {
}
}
}
}
}
if (first) {
throw new JAXBException("Unable to locate configuration file
Configuration.xml in " + packageNames);
}
}
public void unregisterPackages(String packageNames) {
}
/**
* <p>
* Initializes the context by loading the configuration or the
* configurations from the given classpath.
* </p>
*/
protected void init() throws JAXBException {
if (packageNames == null || packageNames.length() == 0) {
packageNames = JAXBContextImpl.class.getName();
packageNames = packageNames.substring(0, packageNames.lastIndexOf('.'));
}
registerPackages(packageNames, getClassLoader());
}
/** Creates a new instance of [EMAIL PROTECTED] javax.xml.bind.JAXBContext}.
* Invoked implicitly by
* [EMAIL PROTECTED] javax.xml.bind.JAXBContext#newInstance(java.lang.String)}.
*/
public static JAXBContextImpl createContext() throws JAXBException {
return createContext(null, JAXBContextImpl.class.getClassLoader());
}
/** Invoked from the SAX handler when loading the config file.
*/
public Configuration createConfiguration(Attributes pAttributes) throws
JAXBException {
String className = pAttributes.getValue("", "className");
if (className == null || className.length() == 0) {
return new Configuration(this, cl);
} else {
try {
return (Configuration) cl.loadClass(className).newInstance();
} catch (Exception e) {
throw new JAXBException("Failed to instantiate Configuration class " +
className, e);
}
}
}
private static boolean verbose = true;
private static void showException(Exception e) {
if (!verbose) { return; }
System.err.println("Exception catched in " + JAXBContextImpl.class.getName()
+
".createContext().");
System.err.println("Set " + JAXBContextImpl.class.getName() +
".verbose = false to suppress this message.");
e.printStackTrace(System.err);
}
/** Creates a new instance of [EMAIL PROTECTED] javax.xml.bind.JAXBContext}.
* Invoked implicitly by
* [EMAIL PROTECTED] javax.xml.bind.JAXBContext#newInstance(String,
ClassLoader)}
*/
public static JAXBContextImpl createContext(String pPackageNames,
ClassLoader pClassLoader)
throws JAXBException {
try {
JAXBContextImpl result = new JAXBContextImpl();
result.setClassLoader(pClassLoader);
result.setPackageNames(pPackageNames);
result.init();
return result;
} catch (RuntimeException e) {
showException(e);
throw e;
} catch (JAXBException e) {
showException(e);
throw e;
}
}
/** Invoked from the SAX handler when reading the config file
* for adding another instance of JMManager.
*/
public void addManager(JMManager pManager) throws JAXBException {
Class elementInterface = pManager.getElementInterface();
if (elementInterface == null) {
throw new JAXBException("The Manager must have its elementInterface set.");
}
if (managersByInterface.containsKey(elementInterface)) {
throw new JAXBException("A Manager for interface " +
elementInterface.getName() +
" is already set.");
}
if (pManager.getDriverClass() == null) {
throw new IllegalStateException("Missing driver class for " +
pManager.getQName());
}
if (pManager.getHandlerClass() == null) {
throw new IllegalStateException("Missing driver class for " +
pManager.getQName());
}
QName qName = pManager.getQName();
if (qName != null && managersByQName.containsKey(qName)) {
throw new JAXBException("A Manager for document type " + qName +
" is already set.");
}
managersByInterface.put(elementInterface, pManager);
if (qName != null) {
managersByQName.put(qName, pManager);
}
}
}
------------------------------------------------------------------------
/*
* Copyright 2003, 2004 The Apache Software Foundation
*
* 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.apache.ws.jaxme.impl;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBException;
import javax.xml.namespace.QName;
import org.apache.ws.jaxme.JMManager;
import org.apache.ws.jaxme.xs.xml.XsQName;
import org.xml.sax.SAXException;
/** <p>An instance of this class represents a config file.
* A JAXBContext requires an associated Configuration which
* is located through the classpath.</p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Jochen Wiedmann</a>
* @version $Id: Configuration.java,v 1.7 2005/03/10 10:14:03 jochen Exp $
*/
public class Configuration {
JAXBContextImpl context;
Manager currentManager;
Map managers = new HashMap();
private Class jmMarshallerClass = JMMarshallerImpl.class;
private Class jmUnmarshallerClass = JMUnmarshallerImpl.class;
private Class jmValidatorClass = JMValidatorImpl.class;
private ClassLoader classLoader;
public Configuration(JAXBContextImpl pContext, ClassLoader cl) {
context = pContext;
this.classLoader = cl;
}
public class Manager implements JMManager {
public class Property {
private String managerName;
private String value;
public String getName() { return managerName; }
public void setName(String pName) { managerName = pName; }
public String getValue() { return value; }
public void setValue(String pValue) { value = pValue; }
public void finish() throws SAXException {
if (managerName == null) {
throw new NullPointerException("Missing 'name' attribute in 'property'
element.");
}
if (value == null) {
throw new NullPointerException("Missing 'value' attribute in 'property'
element.");
}
if (properties == null) {
properties = new HashMap();
}
if (properties.put(managerName, value) != null) {
throw new IllegalStateException("The property " + managerName + " was
specified more than once.");
}
}
}
private QName name;
private Class elementInterface;
private Class elementClass;
private Class handlerClass;
private Class driverClass;
private Class pmClass;
private String prefix;
private Map properties;
private ClassLoader classLoader;
public Manager(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public String getPrefix() {
return prefix;
}
/** Sets the suggested prefix for the elements namespace.
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public void setQName(QName pName) { name = pName; }
public QName getQName() { return name; }
public void setElementClass(String pElementClass) throws
ClassNotFoundException {
elementClass = classLoader.loadClass(pElementClass);
}
public Class getElementClass() { return elementClass; }
public void setElementInterface(String pElementInterface) throws
ClassNotFoundException {
elementInterface = classLoader.loadClass(pElementInterface);
}
public Class getElementInterface() { return elementInterface; }
public Class getHandlerClass() { return handlerClass; }
public void setHandlerClass(String pHandlerClass) throws
ClassNotFoundException {
handlerClass = classLoader.loadClass(pHandlerClass);
if (!JMSAXElementParser.class.isAssignableFrom(handlerClass)) {
throw new IllegalStateException("The class " +
handlerClass.getName()
+
" is not implementing "
+ JMSAXElementParser.class.getName());
}
}
public void setDriverClass(String pMarshallerClass) throws
ClassNotFoundException {
driverClass = classLoader.loadClass(pMarshallerClass);
if (!JMSAXDriver.class.isAssignableFrom(driverClass)) {
throw new IllegalStateException("The class " +
driverClass.getName()
+
" is not implementing "
+ JMSAXDriver.class.getName());
}
}
public Class getDriverClass() { return driverClass; }
public JMSAXDriver getDriver() throws SAXException {
try {
return (JMSAXDriver) driverClass.newInstance();
} catch (InstantiationException e) {
throw new SAXException("Unable to instantiate driver class
" + driverClass.getName(), e);
} catch (IllegalAccessException e) {
throw new SAXException("Illegal access to driver class
" + driverClass.getName(), e);
}
}
/** Sets the persistence manager class.
*/
public void setPmClass(String pPersistencyClass) throws
ClassNotFoundException {
pmClass = classLoader.loadClass(pPersistencyClass);
}
public Class getPmClass() { return pmClass; }
public JAXBContextImpl getFactory() { return context; }
public Property createProperty() {
return new Property();
}
public String getProperty(String pName) {
if (pName == null) {
throw new IllegalArgumentException("The property name must not be
null.");
}
if (properties == null) {
return null;
}
return (String) properties.get(pName);
}
public void finish() throws SAXException {
if (currentManager != this) {
throw new IllegalStateException("currentManager != this");
}
try {
if (prefix != null) {
name = new QName(name.getNamespaceURI(),
name.getLocalPart(), prefix);
}
context.addManager(currentManager);
currentManager = null;
} catch (Exception e) {
throw new SAXException(e.getMessage(), e);
}
}
public JMSAXElementParser getHandler() throws SAXException {
try {
return (JMSAXElementParser) handlerClass.newInstance();
} catch (InstantiationException e) {
throw new SAXException("Unable to instantiate handler class
"
+
jmUnmarshallerClass.getName(), e);
} catch (IllegalAccessException e) {
throw new SAXException("Illegal access to handler class
"
+
jmUnmarshallerClass.getName(), e);
}
}
public Object getElementJ() throws JAXBException {
try {
return elementClass.newInstance();
} catch (InstantiationException e) {
throw new JAXBException("Unable to instantiate handler class
"
+
jmUnmarshallerClass.getName(), e);
} catch (IllegalAccessException e) {
throw new JAXBException("Illegal access to handler class
"
+
jmUnmarshallerClass.getName(), e);
}
}
public Object getElementS() throws SAXException {
try {
return elementClass.newInstance();
} catch (InstantiationException e) {
throw new SAXException("Unable to instantiate handler class
"
+
jmUnmarshallerClass.getName(), e);
} catch (IllegalAccessException e) {
throw new SAXException("Illegal access to handler class
"
+
jmUnmarshallerClass.getName(), e);
}
}
}
/** <p>Creates a new Manager.</p>
*/
public Manager createManager() {
if (currentManager != null) {
throw new IllegalStateException("currentManager != null");
}
currentManager = new Manager(classLoader);
return currentManager;
}
/** <p>Sets the JMMarshaller class.</p>
*/
public void setJMMarshallerClass(Class pJMMarshallerClass) {
jmMarshallerClass = pJMMarshallerClass;
}
/** <p>Returns the JMMarshaller class.</p>
*/
public Class getJMMarshallerClass() {
return jmMarshallerClass;
}
/** <p>Sets the JMUnmarshaller class.</p>
*/
public void setJMUnmarshallerClass(Class pJMUnmarshallerClass) {
jmUnmarshallerClass = pJMUnmarshallerClass;
}
/** <p>Returns the JMUnmarshaller class.</p>
*/
public Class getJMUnmarshallerClass() {
return jmUnmarshallerClass;
}
/** <p>Sets the JMValidator class.</p>
*/
public void setJMValidatorClass(Class pJMValidatorClass) {
jmValidatorClass = pJMValidatorClass;
}
public Class getJMValidatorClass() {
return jmValidatorClass;
}
}
------------------------------------------------------------------------
/*
* Copyright 2003, 2004 The Apache Software Foundation
*
* 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.apache.ws.jaxme.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import javax.xml.namespace.QName;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/** <p>The Configurator is an idea borrowed by the Ant project.
* It is a SAX2 handler that reads a config file which is
* represented by a hierarchy of Java beans. For example:</p>
* <pre>
* <outerBean foo="true" bar="Quite right">
* <innerBean whatever="57">
* </innerBean
* </outerBean>
* </pre>
* The example would create an object outerBean and call its
* methods <code>setFoo(boolean)</code> and
* <code>setBar(String)</code> to process the attributes.
* It would also create a bean innerBean by calling the
* outerBeans method <code>createInnerBean()</code>.
* Finally the innerBean is configured by calling
* <code>setWhatever(int)</code>.</p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Jochen Wiedmann</a>
* @version $Id: Configurator.java,v 1.4 2005/03/10 10:14:03 jochen Exp $
*/
public class Configurator implements ContentHandler, NamespaceResolver {
private static final Class[] zeroClasses = new Class[0];
private static final Object[] zeroObjects = new Object[0];
private static final Class[] oneClassString = new Class[]{String.class};
private static final Class[] oneClassAttributes = new
Class[]{Attributes.class};
private String[] namespaces;
private List beans = new ArrayList();
private List names = new ArrayList();
private Object currentBean;
private String currentName;
private Locator locator;
private Object beanFactory;
private Object rootObject;
private Object resultBean;
private int ignoreLevel = 0;
private NamespaceSupport nss = new NamespaceSupport();
boolean nssNeedsContext;
/** <p>Sets the namespace handled by the configurator. Defaults
* to no namespace. Shortcut for
* <code>setNamespace(new String[]{pNamespace})</code>.</p>
*
* @param pNamespace The namespace being set
*/
public void setNamespace(String pNamespace) {
setNamespaces(new String[]{pNamespace});
}
/** <p>Sets the namespaces handled by the configurator. Defaults
* to no namespace.</p>
*
* @param pNamespaces The namespaces being set
*/
public void setNamespaces(String[] pNamespaces) {
namespaces = pNamespaces;
}
/** <p>Returns the namespaces handled by the configurator. Defaults
* to no namespace.</p>
*/
public String[] getNamespaces() {
return namespaces;
}
/** <p>Sets the Locator being used in error messages.</p>
*/
public void setDocumentLocator(Locator pLocator) {
locator = pLocator;
}
/** <p>Returns the Locator being used in error messages.</p>
*/
public Locator getDocumentLocator() {
return locator;
}
/** <p>Sets the bean factory, creating the outermost element.
* The bean factory must have a matching <code>createElementName()</code>
* method, with <samp>ElementName</samp> being the element name
* of the document element.</p>
*/
public void setBeanFactory(Object pFactory) {
beanFactory = pFactory;
}
/** <p>Returns the bean factory, creating the outermost element.
* The bean factory must have a matching <code>createElementName()</code>
* method, with <samp>ElementName</samp> being the element name
* of the document element.</p>
*/
public Object getBeanFactory() {
return beanFactory == null ? this : beanFactory;
}
/** <p>An alternative to using the bean factory. This object
* is used as the root object, regardless of its name.</p>
*/
public void setRootObject(Object pRootObject) {
rootObject = pRootObject;
}
/** <p>An alternative to using the bean factory. This object
* is used as the root object, regardless of its name.</p>
*/
public Object getRootObject() {
return rootObject;
}
public void startDocument() throws SAXException {
currentBean = null;
currentName = null;
beans.clear();
names.clear();
ignoreLevel = 0;
nss.reset();
nssNeedsContext = true;
}
public void endDocument() throws SAXException {}
public void startPrefixMapping(String pPrefix, String pURI)
throws SAXException {
nss.declarePrefix(pPrefix, pURI);
}
public void endPrefixMapping(String pPrefix)
throws SAXException {
nss.undeclarePrefix(pPrefix);
}
/** <p>Returns whether a namespace is matching the configured
* namespace.</p>
*/
protected boolean isNamespaceMatching(String pNamespace) {
if (pNamespace == null) {
pNamespace = "";
}
String[] myNamespaces = getNamespaces();
if (myNamespaces == null || myNamespaces.length == 0) {
return pNamespace.length() == 0;
} else {
for (int i = 0; i < myNamespaces.length; i++) {
String s = myNamespaces[i];
if (s == null) {
s = "";
}
if (s.equals(pNamespace)) {
return true;
}
}
return false;
}
}
/** <p>Given a prefix and a name, creates a method name matching
* the prefix and the name.</p>
*/
protected String getMethodNameFor(String pPrefix, String pName) {
StringBuffer result = new StringBuffer(pPrefix);
for (int i = 0; i < pName.length(); i++) {
char c = pName.charAt(i);
if (i == 0) {
if (Character.isJavaIdentifierStart(c)) {
result.append(Character.toUpperCase(c));
} else {
result.append('_');
}
} else {
if (Character.isJavaIdentifierPart(c)) {
result.append(c);
} else {
result.append('_');
}
}
}
return result.toString();
}
/** <p>Creates a new bean, matching the element name <code>pLocalName</code>.
* If this is the outermost bean, calls the bean factorys
* <code>createBeanName()</code> method, otherwise calls the current beans
* <code>createBeanName()</code> method, with <samp>beanName</samp>
* being the value of the <code>pLocalName</code> parameter.</p>
*/
public void startElement(String pNamespaceURI, String pQName,
String pLocalName, Attributes pAttr) throws
SAXException {
if (ignoreLevel > 0) {
++ignoreLevel;
return;
}
if (!isNamespaceMatching(pNamespaceURI)) {
if (currentBean == null) {
String[] myNamespaces = getNamespaces();
if (myNamespaces == null || myNamespaces.length == 0) {
throw new SAXParseException("The document element must have the default
namespace.",
getDocumentLocator());
} else {
StringBuffer sb = new StringBuffer("The document element must have either
of the namespaces: ");
for (int i = 0; i < myNamespaces.length; i++) {
if (i > 0) sb.append(",");
String s = myNamespaces[i];
if (s == null) {
s = "";
}
sb.append('"').append(s).append('"');
}
throw new SAXParseException(sb.toString(), getDocumentLocator());
}
}
++ignoreLevel;
return; // Namespace not matching, ignore this element
}
Object o;
if (currentBean == null && rootObject != null) {
o = rootObject;
} else {
o = null;
Object factory = (currentBean == null) ? beanFactory : currentBean;
String methodName = getMethodNameFor("create", pLocalName);
try {
o = invokeMethod(methodName, factory, oneClassAttributes,
new Object[]{pAttr});
} catch (SAXParseException e) {
if (e.getException() != null &&
e.getException() instanceof NoSuchMethodException) {
try {
o = invokeMethod(methodName, factory, zeroClasses, zeroObjects);
e = null;
} catch (SAXParseException f) {
if (f.getException() != null &&
f.getException() instanceof NoSuchMethodException) {
if (currentBean == null) {
throw new SAXParseException("Invalid document element: " +
pQName,
getDocumentLocator());
} else {
throw new SAXParseException("Invalid child element name: " +
pQName,
getDocumentLocator());
}
}
throw f;
}
}
if (e != null) {
throw e;
}
}
if (o == null) {
throw new SAXParseException("Method " + methodName + " of class " +
factory.getClass().getName() +
" did not return an object.",
getDocumentLocator());
}
}
if (currentBean == null) {
resultBean = o;
} else {
beans.add(currentBean);
names.add(currentName);
}
currentBean = o;
currentName = pQName;
if (pAttr != null) {
for (int i = 0; i < pAttr.getLength(); i++) {
String uri = pAttr.getURI(i);
if (uri == null || uri.length() == 0 || isNamespaceMatching(uri)) {
String value = pAttr.getValue(i);
String qName = pAttr.getQName(i);
String setMethodName = getMethodNameFor("set", pAttr.getLocalName(i));
Method[] methods = currentBean.getClass().getMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
if (!setMethodName.equals(method.getName())) {
continue;
}
Class[] argClasses = method.getParameterTypes();
if (argClasses.length != 1) {
continue;
}
Object[] args = null;
if (argClasses[0] == String.class) {
args = new Object[]{value};
} else if (argClasses[0] == Integer.class ||
argClasses[0] == Integer.TYPE) {
try {
args = new Object[]{new Integer(value)};
} catch (Exception e) {
throw new SAXParseException("Attribute " + qName +
" contains an invalid Integer value:
" +
value, getDocumentLocator());
}
} else if (argClasses[0] == Long.class ||
argClasses[0] == Long.TYPE) {
try {
args = new Object[]{new Long(value)};
} catch (Exception e) {
throw new SAXParseException("Attribute " + qName +
" contains an invalid Long value: "
+
value, getDocumentLocator());
}
} else if (argClasses[0] == Short.class ||
argClasses[0] == Short.TYPE) {
try {
args = new Object[]{new Short(value)};
} catch (Exception e) {
throw new SAXParseException("Attribute " + qName +
" contains an invalid Short value:
" +
value, getDocumentLocator());
}
} else if (argClasses[0] == Byte.class ||
argClasses[0] == Byte.TYPE) {
try {
args = new Object[]{new Byte(value)};
} catch (Exception e) {
throw new SAXParseException("Attribute " + qName +
" contains an invalid Byte value: "
+
value, getDocumentLocator());
}
} else if (argClasses[0] == Boolean.class ||
argClasses[0] == Boolean.TYPE) {
try {
args = new Object[]{Boolean.valueOf(value)};
} catch (Exception e) {
throw new SAXParseException("Attribute " + qName +
" contains an invalid Boolean value:
" +
value, getDocumentLocator());
}
} else if (argClasses[0] == Character.class ||
argClasses[0] == Character.TYPE) {
if (value.length() != 1) {
throw new SAXParseException("Attribute " + qName +
" contains an invalid Character value:
" +
value, getDocumentLocator());
}
args = new Object[]{new Character(value.charAt(0))};
} else if (argClasses[0] == Class.class) {
Class c;
try {
c = ClassLoader.getClass(value);
} catch (Exception e) {
throw new SAXParseException("Failed to load class " + value,
getDocumentLocator(), e);
}
args = new Object[]{c};
} else if (argClasses[0] == QName.class) {
try {
QName name = QName.valueOf(value);
args = new Object[]{name};
} catch (Exception e) {
throw new SAXParseException("Failed to parse QName " + value,
getDocumentLocator());
}
} else {
// Try a constructor class(String)
try {
Constructor con = argClasses[0].getConstructor(oneClassString);
args = new Object[]{con.newInstance(new Object[]{value})};
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
throw new SAXParseException("Failed to invoke constructor of class
" +
argClasses[0].getClass().getName(),
getDocumentLocator(),
(t instanceof Exception) ?
((Exception) t) : e);
} catch (NoSuchMethodException e) {
throw new SAXParseException("Attribute " + qName +
" has an invalid type: " +
argClasses[0].getClass().getName(),
getDocumentLocator());
} catch (IllegalAccessException e) {
throw new SAXParseException("Illegal access to constructor of class
" +
argClasses[0].getClass().getName(),
getDocumentLocator(), e);
} catch (InstantiationException e) {
throw new SAXParseException("Failed to instantiate class " +
argClasses[0].getClass().getName(),
getDocumentLocator(), e);
}
}
try {
method.invoke(currentBean, args);
} catch (IllegalAccessException e) {
throw new SAXParseException("Illegal access to method " +
setMethodName +
" of class " +
currentBean.getClass().getName(),
getDocumentLocator(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
SAXParseException saxExc = new SAXParseException("Failed to invoke
method " + setMethodName +
" of class " +
currentBean.getClass().getName(),
getDocumentLocator(),
(t instanceof Exception) ?
((Exception) t) : e);
saxExc.initCause(e);
throw saxExc;
}
}
}
}
}
}
protected Object invokeMethod(String pMethodName, Object pBean,
Class[] pSignature, Object[] pArgs)
throws SAXException {
try {
Method m = pBean.getClass().getMethod(pMethodName, pSignature);
return m.invoke(pBean, pArgs);
} catch (IllegalAccessException e) {
throw new SAXParseException("Illegal access to method " + pMethodName +
" of class " + pBean.getClass().getName(),
getDocumentLocator(), e);
} catch (NoSuchMethodException e) {
throw new SAXParseException("No such method in class " +
pBean.getClass().getName() + ": " +
pMethodName,
getDocumentLocator(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getTargetException();
throw new SAXParseException("Failed to invoke method " + pMethodName +
" of class " + pBean.getClass().getName(),
getDocumentLocator(),
(t instanceof Exception) ? ((Exception) t) :
e);
}
}
/** <p>Terminates parsing the current bean by calling its
* <code>finish()</code> method, if any.</p>
*/
public void endElement(String namespaceURI, String qName, String localName)
throws SAXException {
if (ignoreLevel > 0) {
--ignoreLevel;
return;
}
Object previousBean = currentBean;
if (beans.size() > 0) {
currentBean = beans.remove(beans.size()-1);
currentName = names.remove(names.size()-1).toString();
} else {
currentBean = null;
currentName = null;
}
try {
invokeMethod("finish", previousBean, zeroClasses, zeroObjects);
} catch (SAXParseException e) {
if (e.getException() == null ||
!(e.getException() instanceof NoSuchMethodException)) {
throw e;
}
}
}
/** <p>Handles atomic child elements by invoking their method
* <code>addText(String pText)</code>. Note that it may happen,
* that this method is invoked multiple times, if the parser
* splits a piece of text into multiple SAX events.</p>
*/
public void characters(char[] ch, int start, int length)
throws SAXException {
if (ignoreLevel > 0) {
return;
}
String s = new String(ch, start, length);
try {
invokeMethod("addText", currentBean, oneClassString, new String[]{s});
} catch (SAXParseException e) {
if (e.getException() != null &&
(e.getException() instanceof NoSuchMethodException)) {
boolean allWhitespace = true;
for (int i = 0; i < length; i++) {
if (!Character.isWhitespace(ch[start+i])) {
allWhitespace = false;
break;
}
}
if (allWhitespace) {
// Ignore this
} else {
throw new SAXParseException("Element " + currentName +
" doesn't support embedded text.",
getDocumentLocator());
}
} else {
throw e;
}
}
}
public void ignorableWhitespace(char[] ch, int start, int length)
throws SAXException {
}
public void processingInstruction(String target, String data)
throws SAXException {
}
public void skippedEntity(String name) throws SAXException {
}
/** Returns the parsed result bean.
*/
public Object getResult() { return resultBean; }
public boolean processName(String pName, String[] parts) {
int offset = pName.indexOf(':');
if (offset == -1) {
String uri = nss.getNamespaceURI("");
if (uri == null) {
parts[0] = "";
} else {
parts[0] = uri;
}
parts[1] = pName;
parts[2] = pName;
return true;
} else {
String uri = nss.getNamespaceURI(pName.substring(0, offset));
if (uri == null) {
return false;
} else {
parts[0] = uri;
parts[1] = pName.substring(offset+1);
parts[2] = pName;
return true;
}
}
}
}