org.directwebremoting.impl.DefaultContainer Maven / Gradle / Ivy
package org.directwebremoting.impl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import org.directwebremoting.Container;
import org.directwebremoting.extend.ContainerConfigurationException;
import org.directwebremoting.extend.UninitializingBean;
import org.directwebremoting.util.LocalUtil;
import org.directwebremoting.util.Loggers;
/**
* DefaultContainer is like a mini-IoC container for DWR.
* At least it is an IoC container by interface (check: no params that have
* anything to do with DWR), but it is hard coded specifically for DWR. If we
* want to make more of it we can later, but this is certainly not going to
* become a full blown IoC container.
* @author Joe Walker [joe at getahead dot ltd dot uk]
*/
public class DefaultContainer extends AbstractContainer implements Container {
/**
* Set the class that should be used to implement the given interface
* @param ...
* @param base The interface to implement
* @param bean The new implementation
* @throws ContainerConfigurationException If the specified beans could not be used
*/
public void addBean(Class base, T bean) {
addParameter(LocalUtil.originalDwrClassName(base.getName()), bean);
}
/**
* Set the class that should be used to implement the given interface
* @param ...
* @param base The interface to implement
* @param implementation The new implementation
* @throws ContainerConfigurationException If the specified beans could not be used
*/
public void addImplementation(Class base, Class extends T> implementation) {
addParameter(LocalUtil.originalDwrClassName(base.getName()), implementation.getName());
}
/**
* Set the class that should be used to implement the given interface
* @param ...
* @param base The interface to implement
* @param implementation The new implementation
* @throws ContainerConfigurationException If the specified beans could not be used
*/
public void addImplementationOption(Class base, Class extends T> implementation) {
Object existingOptions = beans.get(base.getName());
if (existingOptions == null) {
beans.put(LocalUtil.originalDwrClassName(base.getName()), implementation.getName());
} else {
beans.put(LocalUtil.originalDwrClassName(base.getName()), existingOptions + " " + implementation.getName());
}
}
/**
* Add a parameter to the container as a possibility for injection
* @param askFor The key that will be looked up
* @param valueParam The value to be returned from the key lookup
* @throws ContainerConfigurationException If the specified beans could not be used
*/
public void addParameter(String askFor, Object valueParam) throws ContainerConfigurationException {
Object value = valueParam;
// Maybe the value is a classname that needs instantiating
if (value instanceof String) {
try {
Class> impl = LocalUtil.classForName((String) value);
value = impl.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException ex) {
// it's not a classname, leave it
} catch (InstantiationException ex) {
throw new ContainerConfigurationException("Unable to instantiate " + value);
} catch (IllegalAccessException ex) {
throw new ContainerConfigurationException("Unable to access " + value);
} catch (InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
// If we have an instantiated value object and askFor is an interface
// then we can check that one implements the other
if (!(value instanceof String)) {
try {
Class> iface = LocalUtil.classForName(askFor);
if (!iface.isAssignableFrom(value.getClass())) {
Loggers.STARTUP.error("Can't cast: " + value + " to " + askFor);
}
} catch (ClassNotFoundException ex) {
// it's not a classname, leave it
}
}
if (Loggers.STARTUP.isDebugEnabled()) {
if (value instanceof String) {
Loggers.STARTUP.debug("- value: " + askFor + " = " + value);
} else {
Loggers.STARTUP.debug("- impl: " + askFor + " = " + value.getClass().getName());
}
}
beans.put(askFor, value);
}
/**
* Retrieve a previously set parameter
* @param name The parameter name to retrieve
* @return The value of the specified parameter, or null if one is not set
*/
public String getParameter(String name) {
Object value = beans.get(name);
return (value == null) ? null : value.toString();
}
/**
* Called to indicate that we finished adding parameters.
* The thread safety of a large part of DWR depends on this function only
* being called from {@link Servlet#init(javax.servlet.ServletConfig)},
* where all the setup is done, and where we depend on the undocumented
* feature of all servlet containers that they complete the init process
* of a Servlet before they begin servicing requests.
* @see DefaultContainer#addParameter(String, Object)
*/
public void setupFinished() {
// We try to autowire each bean in turn
for (Object bean : beans.values()) {
initializeBean(bean);
}
callInitializingBeans();
}
public T newInstance(Class type) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
T t = type.getDeclaredConstructor().newInstance();
initializeBean(t);
return t;
}
public void initializeBean(Object bean) {
// HACK: It wouldn't be a good idea to start injecting into objects
// created by the app-server. Currently this is just ServletContext
// and ServletConfig. If it is others in the future then we'll need a
// better way of marking objects that should not be injected into.
// It's worth remembering that the Container is itself in the container
// so there is a vague risk of recursion here if we're not careful
if (bean instanceof ServletContext || bean instanceof ServletConfig) {
Loggers.STARTUP.debug("- skipping injecting into: " + bean.getClass().getName());
return;
}
if (!(bean instanceof String)) {
Loggers.STARTUP.debug("- autowire: " + bean.getClass().getName());
Method[] methods = bean.getClass().getMethods();
methods:
for (Method setter : methods) {
if (setter.getName().startsWith("set") &&
setter.getName().length() > 3 &&
setter.getParameterTypes().length == 1)
{
String name = Character.toLowerCase(setter.getName().charAt(3)) + setter.getName().substring(4);
Class> propertyType = setter.getParameterTypes()[0];
// First we try auto-wire by name
Object setting = beans.get(name);
if (setting != null) {
if (propertyType.isAssignableFrom(setting.getClass())) {
Loggers.STARTUP.debug(" - by name: " + name + " = " + setting);
invoke(setter, bean, setting);
continue methods;
} else if (setting.getClass() == String.class) {
try {
Object value = LocalUtil.simpleConvert((String) setting, propertyType);
Loggers.STARTUP.debug(" - by name: " + name + " = " + value);
invoke(setter, bean, value);
} catch (IllegalArgumentException ex) {
// Ignore - this was a speculative convert anyway
}
continue methods;
}
}
// Next we try autowire-by-type
Object value = beans.get(LocalUtil.originalDwrClassName(propertyType.getName()));
if (value != null) {
Loggers.STARTUP.debug(" - by type: " + name + " = " + value.getClass().getName());
invoke(setter, bean, value);
continue methods;
}
Loggers.STARTUP.debug(" - no properties for: " + name);
}
}
}
}
/**
* A helper to do the reflection.
* This helper throws away all exceptions, preferring to log.
* @param setter The method to invoke
* @param bean The object to invoke the method on
* @param value The value to assign to the property using the setter method
*/
private static void invoke(Method setter, Object bean, Object value) {
try {
setter.invoke(bean, value);
} catch (InvocationTargetException ex) {
Loggers.STARTUP.error(" - Exception during auto-wire: ", ex.getTargetException());
} catch (Exception ex) {
Loggers.STARTUP.error(" - Error calling setter: " + setter, ex);
}
}
public Object getBean(String id)
{
return beans.get(id);
}
public Collection getBeanNames()
{
return Collections.unmodifiableCollection(beans.keySet());
}
public synchronized void destroy() {
if (isDestroyed) {
return;
}
isDestroyed = true;
destroy(getBeanNames());
}
public void destroy(Collection beanNames) {
Loggers.STARTUP.debug("Destroy for container: " + getClass().getSimpleName());
for (String beanName : beanNames) {
Object bean = getBean(beanName);
if (bean instanceof UninitializingBean && !(bean instanceof Container)) {
UninitializingBean scl = (UninitializingBean) bean;
Loggers.STARTUP.debug("- For contained bean: " + beanName);
scl.destroy();
}
}
}
/**
* The beans that we know of indexed by type
*/
protected Map beans = new TreeMap();
/**
* Keep track of whether we have already been destroyed
*/
boolean isDestroyed = false;
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy