com.caucho.config.Config Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of resin Show documentation
Show all versions of resin Show documentation
Resin Java Application Server
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.config;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
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.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ELContext;
import javax.el.ELException;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import com.caucho.config.attribute.Attribute;
import com.caucho.config.inject.InjectManager;
import com.caucho.config.type.ConfigType;
import com.caucho.config.type.TypeFactory;
import com.caucho.config.types.DirVar;
import com.caucho.config.types.FileVar;
import com.caucho.config.xml.XmlConfigContext;
import com.caucho.el.EL;
import com.caucho.el.EnvironmentContext;
import com.caucho.loader.*;
import com.caucho.relaxng.CompactVerifierFactoryImpl;
import com.caucho.relaxng.Schema;
import com.caucho.relaxng.Verifier;
import com.caucho.relaxng.VerifierFilter;
import com.caucho.util.DisplayableException;
import com.caucho.util.L10N;
import com.caucho.vfs.Path;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.Vfs;
import com.caucho.xml.DOMBuilder;
import com.caucho.xml.QAttr;
import com.caucho.xml.QDocument;
import com.caucho.xml.QName;
import com.caucho.xml.Xml;
/**
* Facade for Resin's configuration builder.
*/
public class Config {
private static final L10N L = new L10N(Config.class);
private static final Logger log
= Logger.getLogger(Config.class.getName());
private static final EnvironmentLocal _envProperties
= new EnvironmentLocal();
// the context class loader of the config
private ClassLoader _classLoader;
private boolean _isEL = true;
private boolean _isIgnoreEnvironment;
private boolean _allowResinInclude;
public Config()
{
this(Thread.currentThread().getContextClassLoader());
}
/**
* @param loader the class loader environment to use.
*/
public Config(ClassLoader loader)
{
_classLoader = loader;
}
/**
* Set true if resin:include should be allowed.
*/
public void setResinInclude(boolean useResinInclude)
{
_allowResinInclude = useResinInclude;
}
/**
* True if EL expressions are allowed
*/
public boolean isEL()
{
return _isEL;
}
/**
* True if EL expressions are allowed
*/
public void setEL(boolean isEL)
{
_isEL = isEL;
}
/**
* True if environment tags are ignored
*/
public boolean isIgnoreEnvironment()
{
return _isIgnoreEnvironment;
}
/**
* True if environment tags are ignored
*/
public void setIgnoreEnvironment(boolean isIgnore)
{
_isIgnoreEnvironment = isIgnore;
}
/**
* Returns an environment property
*/
public static Object getProperty(String key)
{
ConfigProperties props = _envProperties.get();
if (props != null)
return props.get(key);
else
return null;
}
public static ConfigProperties getConfigProperties()
{
return _envProperties.get();
}
/**
* Sets a environment property
*/
public static void setProperty(String key, Object value)
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
setProperty(key, value, loader);
}
/**
* Sets a environment property
*/
public static void setProperty(String key, Object value, ClassLoader loader)
{
ConfigProperties props = _envProperties.getLevel(loader);
if (props == null) {
props = createConfigProperties(loader);
}
props.put(key, value);
}
private static ConfigProperties createConfigProperties(ClassLoader loader)
{
EnvironmentClassLoader envLoader
= Environment.getEnvironmentClassLoader(loader);
ConfigProperties props = _envProperties.getLevel(envLoader);
if (props != null)
return props;
if (envLoader != null) {
ConfigProperties parent = createConfigProperties(envLoader.getParent());
props = new ConfigProperties(parent);
}
else
props = new ConfigProperties(null);
_envProperties.set(props, envLoader);
return props;
}
/**
* Configures a bean with a configuration file.
*/
public Object configure(Object obj, Path path)
throws ConfigException, IOException
{
try {
QDocument doc = parseDocument(path, null);
return configure(obj, doc.getDocumentElement());
} catch (RuntimeException e) {
throw e;
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw ConfigException.create(e);
}
}
/**
* Configures a bean with a configuration file.
*/
public Object configure(Object obj, InputStream is)
throws Exception
{
QDocument doc = parseDocument(is, null);
return configure(obj, doc.getDocumentElement());
}
/**
* Configures a bean with a configuration file and schema.
*/
public Object configure(Object obj, Path path, String schemaLocation)
throws ConfigException
{
try {
Schema schema = findCompactSchema(schemaLocation);
QDocument doc = parseDocument(path, schema);
return configure(obj, doc.getDocumentElement());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw LineConfigException.create(e);
}
}
/**
* Configures a bean with a configuration file and schema.
*/
public Object configure(Object obj, Path path, Schema schema)
throws ConfigException
{
try {
QDocument doc = parseDocument(path, schema);
return configure(obj, doc.getDocumentElement());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw ConfigException.create(e);
}
}
/**
* Configures a bean with a configuration file.
*/
public Object configure(Object obj,
InputStream is,
String schemaLocation)
throws Exception
{
Schema schema = findCompactSchema(schemaLocation);
QDocument doc = parseDocument(is, schema);
return configure(obj, doc.getDocumentElement());
}
/**
* Configures a bean with a configuration file.
*/
public Object configure(Object obj,
InputStream is,
Schema schema)
throws Exception
{
QDocument doc = parseDocument(is, schema);
return configure(obj, doc.getDocumentElement());
}
/**
* Configures a bean with a DOM.
*/
public Object configure(Object obj, Node topNode)
throws Exception
{
Thread thread = Thread.currentThread();
ClassLoader oldLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(_classLoader);
XmlConfigContext builder = createBuilder();
setProperty("__FILE__", FileVar.__FILE__);
setProperty("__DIR__", DirVar.__DIR__);
return builder.configure(obj, topNode);
} finally {
thread.setContextClassLoader(oldLoader);
}
}
/**
* Configures a bean with a configuration file and schema.
*/
public void configureBean(Object obj,
Path path,
String schemaLocation)
throws Exception
{
Schema schema = findCompactSchema(schemaLocation);
QDocument doc = parseDocument(path, schema);
configureBean(obj, doc.getDocumentElement());
}
/**
* Configures a bean with a configuration file and schema.
*/
public void configureBean(Object obj, Path path)
throws Exception
{
QDocument doc = parseDocument(path, null);
configureBean(obj, doc.getDocumentElement());
}
/**
* Configures a bean with a DOM. configureBean does not
* apply init() or replaceObject().
*/
public void configureBean(Object obj, Node topNode)
throws Exception
{
Thread thread = Thread.currentThread();
ClassLoader oldLoader = thread.getContextClassLoader();
try {
thread.setContextClassLoader(_classLoader);
XmlConfigContext builder = createBuilder();
InjectManager webBeans = InjectManager.create();
setProperty("__FILE__", FileVar.__FILE__);
setProperty("__DIR__", DirVar.__DIR__);
builder.configureBean(obj, topNode);
} finally {
thread.setContextClassLoader(oldLoader);
}
}
private XmlConfigContext createBuilder()
{
return new XmlConfigContext(this);
}
/**
* Configures a bean with a configuration file and schema.
*/
public void configureBean(Object obj,
Path path,
Schema schema)
throws Exception
{
QDocument doc = parseDocument(path, schema);
configureBean(obj, doc.getDocumentElement());
}
/**
* Configures the bean from a path
*/
private QDocument parseDocument(Path path, Schema schema)
throws LineConfigException, IOException, org.xml.sax.SAXException
{
// server/2d33
SoftReference docRef = null;//_parseCache.get(path);
QDocument doc;
if (docRef != null) {
doc = docRef.get();
if (doc != null && ! doc.isModified())
return doc;
}
ReadStream is = path.openRead();
try {
doc = parseDocument(is, schema);
// _parseCache.put(path, new SoftReference(doc));
return doc;
} finally {
is.close();
}
}
/**
* Configures the bean from an input stream.
*/
private QDocument parseDocument(InputStream is, Schema schema)
throws LineConfigException,
IOException,
org.xml.sax.SAXException
{
QDocument doc = new QDocument();
DOMBuilder builder = new DOMBuilder();
builder.init(doc);
String systemId = null;
String filename = null;
Path path = null;
if (is instanceof ReadStream) {
path = ((ReadStream) is).getPath();
systemId = path.getURL();
filename = path.getUserPath();
}
doc.setSystemId(systemId);
builder.setSystemId(systemId);
doc.setRootFilename(filename);
builder.setFilename(filename);
builder.setSkipWhitespace(true);
InputSource in = new InputSource();
in.setByteStream(is);
in.setSystemId(systemId);
Xml xml = new Xml();
xml.setOwner(doc);
xml.setResinInclude(_allowResinInclude);
xml.setFilename(filename);
if (schema != null) {
Verifier verifier = schema.newVerifier();
VerifierFilter filter = verifier.getVerifierFilter();
filter.setParent(xml);
filter.setContentHandler(builder);
filter.setErrorHandler(builder);
filter.parse(in);
}
else {
xml.setContentHandler(builder);
xml.parse(in);
}
if (path != null) {
ConfigAdmin.registerPath(path);
}
return doc;
}
private Schema findCompactSchema(String location)
throws IOException, ConfigException
{
try {
if (location == null)
return null;
Thread thread = Thread.currentThread();
ClassLoader loader = thread.getContextClassLoader();
if (loader == null)
loader = ClassLoader.getSystemClassLoader();
URL url = loader.getResource(location);
if (url == null)
return null;
Path path = Vfs.lookup(URLDecoder.decode(url.toString()));
// VerifierFactory factory = VerifierFactory.newInstance("http://caucho.com/ns/compact-relax-ng/1.0");
CompactVerifierFactoryImpl factory;
factory = new CompactVerifierFactoryImpl();
return factory.compileSchema(path);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw ConfigException.create(e);
}
}
/**
* Returns true if the class can be instantiated.
*/
public static void checkCanInstantiate(Class beanClass)
throws ConfigException
{
if (beanClass == null)
throw new ConfigException(L.l("null classes can't be instantiated."));
else if (beanClass.isInterface())
throw new ConfigException(L.l("'{0}' must be a concrete class. Interfaces cannot be instantiated.", beanClass.getName()));
else if (! Modifier.isPublic(beanClass.getModifiers()))
throw new ConfigException(L.l("Custom bean class '{0}' is not public. Bean classes must be public, concrete, and have a zero-argument constructor.", beanClass.getName()));
else if (Modifier.isAbstract(beanClass.getModifiers()))
throw new ConfigException(L.l("Custom bean class '{0}' is abstract. Bean classes must be public, concrete, and have a zero-argument constructor.", beanClass.getName()));
Constructor []constructors = beanClass.getDeclaredConstructors();
Constructor constructor = null;
for (int i = 0; i < constructors.length; i++) {
if (constructors[i].getParameterTypes().length == 0) {
constructor = constructors[i];
break;
}
}
if (constructor == null)
throw new ConfigException(L.l("Custom bean class '{0}' doesn't have a zero-arg constructor. Bean classes must be have a zero-argument constructor.", beanClass.getName()));
// #5701, server/1768
/*
if (! Modifier.isPublic(constructor.getModifiers())) {
throw new ConfigException(L.l("The zero-argument constructor for '{0}' isn't public. Bean classes must have a public zero-argument constructor.", beanClass.getName()));
}
*/
}
/**
* Returns true if the class can be instantiated.
*/
public static void validate(Class cl, Class api)
throws ConfigException
{
checkCanInstantiate(cl);
if (! api.isAssignableFrom(cl)) {
throw new ConfigException(L.l("{0} must implement {1}.",
cl.getName(), api.getName()));
}
}
/**
* Returns true if the class can be instantiated using zero args constructor
* or constructor that accepts an instance of class passed in type argument
*/
public static void checkCanInstantiate(Class beanClass,
Class type)
throws ConfigException
{
if (beanClass == null)
throw new ConfigException(L.l("null classes can't be instantiated."));
else if (beanClass.isInterface())
throw new ConfigException(L.l(
"'{0}' must be a concrete class. Interfaces cannot be instantiated.",
beanClass.getName()));
else if (! Modifier.isPublic(beanClass.getModifiers()))
throw new ConfigException(L.l(
"Custom bean class '{0}' is not public. Bean classes must be public, concrete, and have a zero-argument constructor.",
beanClass.getName()));
else if (Modifier.isAbstract(beanClass.getModifiers()))
throw new ConfigException(L.l(
"Custom bean class '{0}' is abstract. Bean classes must be public, concrete, and have a zero-argument constructor.",
beanClass.getName()));
Constructor [] constructors = beanClass.getDeclaredConstructors();
Constructor zeroArgsConstructor = null;
Constructor singleArgConstructor = null;
for (int i = 0; i < constructors.length; i++) {
if (constructors [i].getParameterTypes().length == 0) {
zeroArgsConstructor = constructors [i];
if (singleArgConstructor != null)
break;
}
else if (type != null
&& constructors [i].getParameterTypes().length == 1 &&
type.isAssignableFrom(constructors[i].getParameterTypes()[0])) {
singleArgConstructor = constructors [i];
if (zeroArgsConstructor != null)
break;
}
}
if (zeroArgsConstructor == null
&& singleArgConstructor == null)
if (type != null)
throw new ConfigException(L.l(
"Custom bean class '{0}' doesn't have a zero-arg constructor, or a constructor accepting parameter of type '{1}'. Bean class '{0}' must have a zero-argument constructor, or a constructor accepting parameter of type '{1}'",
beanClass.getName(),
type.getName()));
else
throw new ConfigException(L.l(
"Custom bean class '{0}' doesn't have a zero-arg constructor. Bean classes must have a zero-argument constructor.",
beanClass.getName()));
if (singleArgConstructor != null) {
if (! Modifier.isPublic(singleArgConstructor.getModifiers()) &&
(zeroArgsConstructor == null ||
! Modifier.isPublic(zeroArgsConstructor.getModifiers()))) {
throw new ConfigException(L.l(
"The constructor for bean '{0}' accepting parameter of type '{1}' is not public. Constructor accepting parameter of type '{1}' must be public.",
beanClass.getName(),
type.getName()));
}
}
else if (zeroArgsConstructor != null) {
if (! Modifier.isPublic(zeroArgsConstructor.getModifiers()))
throw new ConfigException(L.l(
"The zero-argument constructor for '{0}' isn't public. Bean classes must have a public zero-argument constructor.",
beanClass.getName()));
}
}
public static void validate(Class cl, Class api, Class type)
throws ConfigException
{
checkCanInstantiate(cl, type);
if (! api.isAssignableFrom(cl)) {
throw new ConfigException(L.l("{0} must implement {1}.",
cl.getName(), api.getName()));
}
}
/**
* Sets an attribute with a value.
*
* @param obj the bean to be set
* @param attr the attribute name
* @param value the attribute value
*/
public static void setAttribute(Object obj, String attr, Object value)
{
ConfigType> type = TypeFactory.getType(obj.getClass());
QName attrName = new QName(attr);
Attribute attrStrategy = type.getAttribute(attrName);
if (attrStrategy == null)
throw new ConfigException(L.l("{0}: '{1}' is an unknown attribute.",
obj.getClass().getName(),
attrName.getName()));
value = attrStrategy.getConfigType().valueOf(value);
attrStrategy.setValue(obj, attrName, value);
}
/**
* Sets an attribute with a value.
*
* @param obj the bean to be set
* @param attr the attribute name
* @param value the attribute value
*/
public static void setStringAttribute(Object obj, String attr, String value)
throws Exception
{
XmlConfigContext builder = new XmlConfigContext();
QAttr qAttr = new QAttr(attr);
qAttr.setValue(value);
builder.configureAttribute(obj, qAttr);
}
public static void init(Object bean)
throws ConfigException
{
try {
ConfigType type = TypeFactory.getType(bean.getClass());
type.init(bean);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw ConfigException.create(e);
}
}
public static void inject(Object bean)
throws ConfigException
{
try {
ConfigType type = TypeFactory.getType(bean.getClass());
type.inject(bean);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw ConfigException.create(e);
}
}
public static Object replaceObject(Object bean) throws Exception
{
ConfigType type = TypeFactory.getType(bean.getClass());
return type.replaceObject(bean);
}
/**
* Returns the variable resolver.
*/
public static ELContext getEnvironment()
{
XmlConfigContext builder = XmlConfigContext.getCurrentBuilder();
if (builder != null) {
return builder.getELContext();
}
else
return EL.getEnvironment();
}
/**
* Returns the variable resolver.
*/
public static ConfigELContext getELContext()
{
XmlConfigContext builder = XmlConfigContext.getCurrentBuilder();
if (builder != null) {
return builder.getELContext();
}
else
return null;
}
public static Object getElVar(String var)
{
ELContext context = getEnvironment();
if (context != null)
return context.getELResolver().getValue(context, null, var);
return getProperty(var);
}
/**
* Sets an EL configuration variable.
*/
public static Object getCurrentVar(String var)
{
// return InjectManager.create().findByName(var);
return getProperty(var);
}
/**
* Evaluates an EL string in the context.
*/
public static String evalString(String str)
throws ELException
{
ELContext elContext = ConfigELContext.EL_CONTEXT;
if (elContext == null) {
elContext = getEnvironment();
}
return EL.evalString(str, elContext);
}
/**
* Evaluates an EL string in the context.
*/
public static String evalString(String str, HashMap varMap)
throws ELException
{
return EL.evalString(str, getEnvironment(varMap));
}
/**
* Evaluates an EL boolean in the context.
*/
public static boolean evalBoolean(String str)
throws ELException
{
return EL.evalBoolean(str, getEnvironment());
}
public static ELContext getEnvironment(HashMap varMap)
{
if (varMap != null)
return new EnvironmentContext(varMap);
else
return new EnvironmentContext();
}
public static ConfigException error(Field field, String msg)
{
return new ConfigException(location(field) + msg);
}
public static ConfigException error(Method method, String msg)
{
return new ConfigException(location(method) + msg);
}
public static RuntimeException createLine(String systemId, int line,
Throwable e)
{
while (e.getCause() != null
&& (e instanceof InstantiationException
|| e instanceof InvocationTargetException
|| e.getClass().equals(ConfigRuntimeException.class))) {
e = e.getCause();
}
if (e instanceof LineConfigException)
throw (LineConfigException) e;
String lines = getSourceLines(systemId, line);
String loc = systemId + ":" + line + ": ";
if (e instanceof DisplayableException) {
return new LineConfigException(loc + e.getMessage() + "\n" + lines, e);
}
else
return new LineConfigException(loc + e + "\n" + lines, e);
}
public static String location(Field field)
{
String className = field.getDeclaringClass().getName();
return className + "." + field.getName() + ": ";
}
public static String location(Method method)
{
String className = method.getDeclaringClass().getName();
return className + "." + method.getName() + ": ";
}
private static String getSourceLines(String systemId, int errorLine)
{
if (systemId == null)
return "";
ReadStream is = null;
try {
is = Vfs.lookup().lookup(systemId).openRead();
int line = 0;
StringBuilder sb = new StringBuilder("\n\n");
String text;
while ((text = is.readLine()) != null) {
line++;
if (errorLine - 2 <= line && line <= errorLine + 2) {
sb.append(line);
sb.append(": ");
sb.append(text);
sb.append("\n");
}
}
return sb.toString();
} catch (IOException e) {
log.log(Level.FINEST, e.toString(), e);
return "";
} finally {
if (is != null)
is.close();
}
}
static class ConfigProperties {
private ConfigProperties _parent;
private HashMap _properties = new HashMap(8);
ConfigProperties(ConfigProperties parent)
{
_parent = parent;
}
public Object get(String key)
{
Object value = _properties.get(key);
if (value != null)
return value;
else if (_parent != null)
return _parent.get(key);
else
return null;
}
public void put(String key, Object value)
{
_properties.put(key, value);
}
public String toString()
{
return getClass().getSimpleName() + "[]";
}
}
}