org.eclipse.jetty.xml.XmlConfiguration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of testatoo-container-jetty-full Show documentation
Show all versions of testatoo-container-jetty-full Show documentation
Testatoo Jetty Container with JSP support
// ========================================================================
// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.xml;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.AccessControlException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.resource.Resource;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/* ------------------------------------------------------------ */
/**
* Configure Objects from XML. This class reads an XML file conforming to the configure.dtd DTD and
* uses it to configure and object by calling set, put or other methods on the object.
*
*
*/
public class XmlConfiguration
{
private static Class[] __primitives = { Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE, Void.TYPE};
private static Class[] __primitiveHolders = { Boolean.class, Character.class, Byte.class,
Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class};
private static final Integer ZERO=new Integer(0);
/* ------------------------------------------------------------ */
private static XmlParser __parser;
private XmlParser.Node _config;
private final Map _idMap = new HashMap();
private final Map _propertyMap = new HashMap();
/* ------------------------------------------------------------ */
private synchronized static void initParser() throws IOException
{
if (__parser != null) return;
__parser = new XmlParser();
try
{
URL configURL = Loader.getResource(XmlConfiguration.class, "org/eclipse/jetty/xml/configure_6_0.dtd", true);
__parser.redirectEntity("configure.dtd", configURL);
__parser.redirectEntity("configure_1_0.dtd", configURL);
__parser.redirectEntity("configure_1_1.dtd", configURL);
__parser.redirectEntity("configure_1_2.dtd", configURL);
__parser.redirectEntity("configure_1_3.dtd", configURL);
__parser.redirectEntity("configure_6_0.dtd", configURL);
__parser.redirectEntity("http://jetty.mortbay.org/configure.dtd", configURL);
__parser.redirectEntity("http://jetty.eclipse.org/configure.dtd", configURL);
__parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd", configURL);
__parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN", configURL);
__parser.redirectEntity("-//Jetty//Configure//EN", configURL);
}
catch (ClassNotFoundException e)
{
Log.warn(e.toString());
Log.debug(e);
}
}
/* ------------------------------------------------------------ */
/**
* Constructor. Reads the XML configuration file.
*
* @param configuration
*/
public XmlConfiguration(URL configuration) throws SAXException, IOException
{
initParser();
synchronized (__parser)
{
_config = __parser.parse(configuration.toString());
}
}
/* ------------------------------------------------------------ */
/**
* Constructor.
*
* @param configuration String of XML configuration commands excluding the normal XML preamble.
* The String should start with a " \n"
+ configuration;
InputSource source = new InputSource(new StringReader(configuration));
synchronized (__parser)
{
_config = __parser.parse(source);
}
}
/* ------------------------------------------------------------ */
/**
* Constructor.
*
* @param configuration An input stream containing a complete e.g. configuration file
* @exception SAXException
* @exception IOException
*/
public XmlConfiguration(InputStream configuration) throws SAXException, IOException
{
initParser();
InputSource source = new InputSource(configuration);
synchronized (__parser)
{
_config = __parser.parse(source);
}
}
/* ------------------------------------------------------------ */
public Map getIdMap()
{
return _idMap;
}
/* ------------------------------------------------------------ */
/**
* @deprecated use {@link #getIdMap()}.put(...)
*/
public void setIdMap(Map map)
{
_idMap.clear();
_idMap.putAll(map);
}
/* ------------------------------------------------------------ */
/**
* @deprecated use {@link #getProperties()}.put(...)
*/
public void setProperties (Map map)
{
_propertyMap.clear();
_propertyMap.putAll(map);
}
/* ------------------------------------------------------------ */
public Map getProperties ()
{
return _propertyMap;
}
/* ------------------------------------------------------------ */
/**
* Configure an object. If the object is of the approprate class, the XML configuration script
* is applied to the object.
*
* @param obj The object to be configured.
* @exception Exception
*/
public void configure(Object obj) throws Exception
{
//Check the class of the object
Class oClass = nodeClass(_config);
if (!oClass.isInstance(obj))
throw new IllegalArgumentException("Object is not of type " + oClass);
configure(obj, _config, 0);
}
/* ------------------------------------------------------------ */
/**
* Configure an object. If the configuration has an ID, an object is looked up
* by ID and it's type check. Otherwise a new object is created.
*
* @return The newly created configured object.
* @exception Exception
*/
public Object configure() throws Exception
{
Class oClass = nodeClass(_config);
String id = _config.getAttribute("id");
Object obj = id==null?null:_idMap.get(id);
if (obj==null && oClass !=null)
obj = oClass.newInstance();
if (oClass!=null && !oClass.isInstance(obj))
throw new ClassCastException(oClass.toString());
configure(obj, _config, 0);
return obj;
}
/* ------------------------------------------------------------ */
private Class nodeClass(XmlParser.Node node) throws ClassNotFoundException
{
String className = node.getAttribute("class");
if (className == null) return null;
return Loader.loadClass(XmlConfiguration.class, className,true);
}
/* ------------------------------------------------------------ */
/*
* Recursive configuration step. This method applies the remaining Set, Put and Call elements to
* the current object. @param obj @param cfg @param i @exception Exception
*/
private void configure(Object obj, XmlParser.Node cfg, int i) throws Exception
{
String id = cfg.getAttribute("id");
if (id!=null)
_idMap.put(id,obj);
for (; i < cfg.size(); i++)
{
Object o = cfg.get(i);
if (o instanceof String) continue;
XmlParser.Node node = (XmlParser.Node) o;
try
{
String tag = node.getTag();
if ("Set".equals(tag))
set(obj, node);
else if ("Put".equals(tag))
put(obj, node);
else if ("Call".equals(tag))
call(obj, node);
else if ("Get".equals(tag))
get(obj, node);
else if ("New".equals(tag))
newObj(obj, node);
else if ("Array".equals(tag))
newArray(obj, node);
else if ("Ref".equals(tag))
refObj(obj, node);
else if ("Property".equals(tag))
propertyObj(obj, node);
else
throw new IllegalStateException("Unknown tag: " + tag);
}
catch (Exception e)
{
Log.warn("Config error at " + node, e.toString());
throw e;
}
}
}
/* ------------------------------------------------------------ */
/*
* Call a set method. This method makes a best effort to find a matching set method. The type of
* the value is used to find a suitable set method by 1. Trying for a trivial type match. 2.
* Looking for a native type match. 3. Trying all correctly named methods for an auto
* conversion. 4. Attempting to construct a suitable value from original value. @param obj
* @param node
*/
private void set(Object obj, XmlParser.Node node) throws Exception
{
String attr = node.getAttribute("name");
String name = "set" + attr.substring(0, 1).toUpperCase() + attr.substring(1);
Object value = value(obj, node);
Object[] arg = { value};
Class oClass = nodeClass(node);
if (oClass != null)
obj = null;
else
oClass = obj.getClass();
Class[] vClass = { Object.class};
if (value != null) vClass[0] = value.getClass();
if (Log.isDebugEnabled())
Log.debug("XML "+(obj!=null?obj.toString():oClass.getName()) + "." + name + "(" + value + ")");
// Try for trivial match
try
{
Method set = oClass.getMethod(name, vClass);
set.invoke(obj, arg);
return;
}
catch (IllegalArgumentException e)
{
Log.ignore(e);
}
catch (IllegalAccessException e)
{
Log.ignore(e);
}
catch (NoSuchMethodException e)
{
Log.ignore(e);
}
// Try for native match
try
{
Field type = vClass[0].getField("TYPE");
vClass[0] = (Class) type.get(null);
Method set = oClass.getMethod(name, vClass);
set.invoke(obj, arg);
return;
}
catch (NoSuchFieldException e)
{
Log.ignore(e);
}
catch (IllegalArgumentException e)
{
Log.ignore(e);
}
catch (IllegalAccessException e)
{
Log.ignore(e);
}
catch (NoSuchMethodException e)
{
Log.ignore(e);
}
// Try a field
try
{
Field field = oClass.getField(attr);
if (Modifier.isPublic(field.getModifiers()))
{
field.set(obj, value);
return;
}
}
catch (NoSuchFieldException e)
{
Log.ignore(e);
}
// Search for a match by trying all the set methods
Method[] sets = oClass.getMethods();
Method set = null;
for (int s = 0; sets != null && s < sets.length; s++)
{
Class>[] paramTypes= sets[s].getParameterTypes();
if (name.equals(sets[s].getName()) && paramTypes.length == 1)
{
// lets try it
try
{
set = sets[s];
sets[s].invoke(obj, arg);
return;
}
catch (IllegalArgumentException e)
{
Log.ignore(e);
}
catch (IllegalAccessException e)
{
Log.ignore(e);
}
// Can we convert to a collection
if (paramTypes[0].isAssignableFrom(Collection.class) && value.getClass().isArray())
{
try
{
if (paramTypes[0].isAssignableFrom(Set.class))
sets[s].invoke(obj, new Object[]{new HashSet