[jbosscache-commits] JBoss Cache SVN: r4872 - in core/trunk/src: main/java/org/jboss/cache/config and 12 other directories.
jbosscache-commits at lists.jboss.org
jbosscache-commits at lists.jboss.org
Tue Dec 18 14:58:11 EST 2007
Author: manik.surtani at jboss.com
Date: 2007-12-18 14:58:10 -0500 (Tue, 18 Dec 2007)
New Revision: 4872
Added:
core/trunk/src/main/java/org/jboss/cache/util/reflect/
core/trunk/src/main/java/org/jboss/cache/util/reflect/ClasspathScanner.java
core/trunk/src/main/java/org/jboss/cache/util/reflect/ReflectionUtil.java
core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryFunctionalTest.java
core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryUnitTest.java
core/trunk/src/test/java/org/jboss/cache/factories/DependencyGraphTest.java
core/trunk/src/test/java/org/jboss/cache/util/reflect/
core/trunk/src/test/java/org/jboss/cache/util/reflect/ClasspathScannerTest.java
Removed:
core/trunk/src/test/java/org/jboss/cache/factories/annotations/
Modified:
core/trunk/src/main/java/org/jboss/cache/CacheImpl.java
core/trunk/src/main/java/org/jboss/cache/DefaultCacheFactory.java
core/trunk/src/main/java/org/jboss/cache/config/ConfigurationComponent.java
core/trunk/src/main/java/org/jboss/cache/factories/CacheMarshallerFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/ComponentRegistry.java
core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java
core/trunk/src/main/java/org/jboss/cache/interceptors/CacheLoaderInterceptor.java
core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java
core/trunk/src/test/java/org/jboss/cache/interceptors/EvictionInterceptorTest.java
core/trunk/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java
core/trunk/src/test/java/org/jboss/cache/loader/UnnecessaryLoadingTest.java
core/trunk/src/test/java/org/jboss/cache/misc/TestingUtil.java
core/trunk/src/test/java/org/jboss/cache/passivation/PassivationToDummyInMemoryCacheLoaderTest.java
Log:
Updated injection framework and component registry internals and bootstrap
Modified: core/trunk/src/main/java/org/jboss/cache/CacheImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/CacheImpl.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/main/java/org/jboss/cache/CacheImpl.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -502,6 +502,8 @@
// Include our clusterName in our log category
configureLogCategory();
+ componentRegistry.wire();
+
// initialise the node factory and set this in the runtime.
// NodeFactory nf;
// if ((nf = configuration.getRuntimeConfig().getNodeFactory()) == null)
@@ -617,9 +619,11 @@
// remove the Interceptor.class component though, since it may pertain to an old config
componentRegistry.unregisterComponent(Interceptor.class);
+ // re-do the bootstrap sequence
componentRegistry.registerComponent(configuration);
componentRegistry.registerComponent(this);
- componentRegistry.registerComponent(CacheSPI.class, spi);
+ componentRegistry.registerComponent(CacheSPI.class.getName(), spi);
+ componentRegistry.registerComponent(Cache.class.getName(), spi);
componentRegistry.registerComponent(componentRegistry);
componentRegistry.updateDependencies();
@@ -628,7 +632,7 @@
cacheStatus = CacheStatus.STARTING;
// start all internal components
- componentRegistry.startComponents();
+ componentRegistry.start();
correctRootNodeType();
@@ -873,7 +877,7 @@
{
cacheStatus = CacheStatus.STOPPING;
- componentRegistry.stopComponents();
+ componentRegistry.stop();
// before closing the channel stop the buddy manager
// if (buddyManager != null && buddyManager.isEnabled())
@@ -3773,7 +3777,7 @@
private void setInterceptorChain(Interceptor startOfNewChain)
{
- componentRegistry.registerComponent(Interceptor.class, startOfNewChain);
+ componentRegistry.registerComponent(Interceptor.class.getName(), startOfNewChain);
componentRegistry.updateDependencies();
}
@@ -3781,21 +3785,22 @@
{
List<Interceptor> interceptors = getInterceptors();
- InterceptorChainFactory factory = InterceptorChainFactory.getInstance();
+ InterceptorChainFactory factory = componentRegistry.getComponent(InterceptorChainFactory.class);
interceptors.add(position, i);
// now correct the chaining of interceptors...
- Interceptor linkedChain = factory.correctInterceptorChaining(interceptors, configuration, componentRegistry);
+ Interceptor linkedChain = factory.correctInterceptorChaining(interceptors);
setInterceptorChain(linkedChain);
}
public synchronized void removeInterceptor(int position)
{
+ InterceptorChainFactory factory = componentRegistry.getComponent(InterceptorChainFactory.class);
List<Interceptor> i = getInterceptors();
i.remove(position);
- setInterceptorChain(InterceptorChainFactory.getInstance().correctInterceptorChaining(i, configuration, componentRegistry));
+ setInterceptorChain(factory.correctInterceptorChaining(i));
}
public RPCManager getRPCManager()
Modified: core/trunk/src/main/java/org/jboss/cache/DefaultCacheFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/DefaultCacheFactory.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/main/java/org/jboss/cache/DefaultCacheFactory.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -11,7 +11,6 @@
import org.jboss.cache.factories.ComponentFactory;
import org.jboss.cache.factories.ComponentRegistry;
import org.jboss.cache.factories.XmlConfigurationParser;
-import org.jboss.cache.factories.annotations.DefaultFactoryFor;
import org.jboss.cache.invocation.CacheInvocationDelegate;
import java.io.InputStream;
@@ -23,34 +22,27 @@
* This is a special instance of a {@link ComponentFactory} which contains bootstrap information for the
* {@link ComponentRegistry}.
* <p/>
- * E.g., {@link #bootstrap()} is used to create a cache, a {@link ComponentRegistry}, and then wire dependencies as needed.
+ * E.g., {@link #bootstrap(CacheImpl, CacheSPI, org.jboss.cache.config.Configuration)} is used to create a cache, a {@link ComponentRegistry}, and then wire dependencies as needed.
*
* @author <a href="mailto:manik at jboss.org">Manik Surtani (manik at jboss.org)</a>
* @see org.jboss.cache.factories.ComponentFactory
*/
- at DefaultFactoryFor(classes = {CacheSPI.class})
+//@DefaultFactoryFor(classes = {CacheSPI.class})
public class DefaultCacheFactory<K, V> extends ComponentFactory implements CacheFactory<K, V>
{
private volatile static CacheFactory<?, ?> singleton = new DefaultCacheFactory();
- protected DefaultCacheFactory()
- {
- }
-
/**
- * @return a singleton instance of this class.
+ * Note - this method used to return a singleton instance, and since 2.1.0 returns a new instance. The method is
+ * deprecated and you should use the no-arg constructor to create a new instance of this factory.
+ *
+ * @return a NEW instance of this class.
*/
@SuppressWarnings("unchecked")
+ @Deprecated
public static <K, V> CacheFactory<K, V> getInstance()
{
- if (singleton == null)
- {
- synchronized (DefaultCacheFactory.class)
- {
- if (singleton == null) singleton = new DefaultCacheFactory();
- }
- }
- return (CacheFactory<K, V>) singleton;
+ return new DefaultCacheFactory();
}
public Cache<K, V> createCache() throws ConfigurationException
@@ -124,11 +116,7 @@
bootstrap(cache, spi, configuration);
- // wire dependencies into cache
- componentRegistry.wireDependencies(cache);
- // wire dependencies into SPI delegate
- componentRegistry.wireDependencies(spi);
-
+ componentRegistry.wire();
return spi;
}
@@ -143,17 +131,18 @@
// injection bootstrap stuff
componentRegistry = cache.getComponentRegistry();
- // make sure we set the CacheImpl and CacheSPI instance in the component registry.
- componentRegistry.registerComponent(cache);
- componentRegistry.registerComponent(spi);
-
// and the configuration
componentRegistry.registerComponent(configuration);
this.configuration = configuration;
- componentRegistry.wireDependencies(configuration);
+
// and the component registry itself. This bit of recursiveness is needed for factories that are also components.
// See NodeFactory for example, which is created by an EmptyConstructorFactory
componentRegistry.registerComponent(componentRegistry);
+
+ // make sure we set the CacheImpl and CacheSPI instance in the component registry.
+ componentRegistry.registerComponent(cache);
+ componentRegistry.registerComponent(CacheSPI.class.getName(), spi);
+ componentRegistry.registerComponent(Cache.class.getName(), spi);
}
public Cache<K, V> createCache(InputStream is) throws ConfigurationException
Modified: core/trunk/src/main/java/org/jboss/cache/config/ConfigurationComponent.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/config/ConfigurationComponent.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/main/java/org/jboss/cache/config/ConfigurationComponent.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -10,7 +10,9 @@
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.CacheStatus;
+import org.jboss.cache.factories.ComponentRegistry;
import org.jboss.cache.factories.annotations.Inject;
+import org.jboss.cache.factories.annotations.Start;
import java.io.Serializable;
import java.util.Collection;
@@ -34,6 +36,7 @@
private transient CacheSPI cache; // back-reference to test whether the cache is running.
private final Set<ConfigurationComponent> children =
Collections.synchronizedSet(new HashSet<ConfigurationComponent>());
+ private ComponentRegistry cr;
protected ConfigurationComponent()
{
@@ -117,7 +120,6 @@
*
* @param cache
*/
- @Inject
public void setCache(CacheSPI cache)
{
this.cache = cache;
@@ -130,6 +132,18 @@
}
}
+ @Inject
+ private void injectDependencies(ComponentRegistry cr)
+ {
+ this.cr = cr;
+ }
+
+ @Start
+ private void start()
+ {
+ setCache(cr.getComponent(CacheSPI.class));
+ }
+
public ConfigurationComponent clone() throws CloneNotSupportedException
{
ConfigurationComponent c = (ConfigurationComponent) super.clone();
Modified: core/trunk/src/main/java/org/jboss/cache/factories/CacheMarshallerFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/CacheMarshallerFactory.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/main/java/org/jboss/cache/factories/CacheMarshallerFactory.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -42,7 +42,7 @@
throw new ConfigurationException("Unable to instantiate marshaller of type " + marshallerClass, e);
}
- componentRegistry.wireDependencies(m);
+ //componentRegistry.wireDependencies(m);
if (log.isDebugEnabled()) log.debug("Instantiated " + marshallerClass + "; wrapping in a VersionAwareMarshaller");
Modified: core/trunk/src/main/java/org/jboss/cache/factories/ComponentRegistry.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/ComponentRegistry.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/main/java/org/jboss/cache/factories/ComponentRegistry.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -2,23 +2,25 @@
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.Cache;
+import org.jboss.cache.CacheImpl;
+import org.jboss.cache.CacheSPI;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.ConfigurationException;
import org.jboss.cache.config.RuntimeConfig;
-import org.jboss.cache.factories.annotations.ClasspathScanner;
+import static org.jboss.cache.factories.ComponentRegistry.State.*;
import org.jboss.cache.factories.annotations.ComponentName;
import org.jboss.cache.factories.annotations.DefaultFactoryFor;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.factories.annotations.Start;
import org.jboss.cache.factories.annotations.Stop;
import org.jboss.cache.util.BeanUtils;
+import org.jboss.cache.util.reflect.ClasspathScanner;
+import org.jboss.cache.util.reflect.ReflectionUtil;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -34,17 +36,36 @@
* Components can be retrieved from the registry using {@link #getComponent(Class)}, or they can be constructed using
* {@link #getOrCreateComponent(String, Class)} which will scan for default factories and attempt to use such
* factpries to create the component if needed.
+ * <p/>
+ * Default factories are treated as components too and will need to be wired and started before being used.
+ * <p/>
+ * Components can exist in one of 4 states, as defined by the {@link State} enumeration. The component registry also
+ * has a state for the overall component set. While some components may move to the {@link org.jboss.cache.factories.ComponentRegistry.State#STARTED}
+ * state before others (such as factories to create other components) the ComponentRegistry's overall state depicts the lowest
+ * possible form for all components.
+ * <p/>
+ * In terms of the cache, overall state changes in the following manner:
+ * <ul>
+ * <li>CONSTRUCTED - when created using the DefaultCacheFactory</li>
+ * <li>WIRED - when {@link org.jboss.cache.Cache#create()} is called</li>
+ * <li>STARTED - when {@link org.jboss.cache.Cache#start()} is called</li>
+ * <li>STOPPED - when {@link org.jboss.cache.Cache#stop()} is called</li>
+ * </ul>
+ * <p/>
+ * Cache configuration can only be changed and will only be reinjected if the cache is not in the {@link org.jboss.cache.CacheStatus#STARTED} state.
*
* @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
* @since 2.1.0
*/
public class ComponentRegistry
{
+ State overallState = CONSTRUCTED;
+
/**
- * Can contain either unnamed singletons (in which case the key is the class of the instance) or named singletons, keyed by a
- * String representing the name of the instance.
+ * The registry of components. Components are stored under their name.
*/
- Map<Object, Object> registry = new HashMap<Object, Object>();
+ Map<String, Component> componentLookup = new HashMap<String, Component>();
+
/**
* Contains class definitions of component factories that can be used to construct certain components
*/
@@ -52,6 +73,11 @@
private static Log log = LogFactory.getLog(ComponentRegistry.class);
+ /**
+ * Creates an instance of the component registry. The configuration passed in is automatically registered.
+ *
+ * @param configuration
+ */
public ComponentRegistry(Configuration configuration)
{
// bootstrap.
@@ -61,72 +87,50 @@
/**
* Adds a singleton instance to the registry. Note that if an instance of this component already exists in the
* registry it is overwritten. The instance is registered under type <tt>component.getClass()</tt>. If the component
- * implements an interface or extends an abstract class, it may make more sense to use {@link #registerComponent(Class, Object)}
+ * implements an interface or extends an abstract class, it may make more sense to use {@link #registerComponent(String, Object)}
+ * <p/>
+ * The status of the component is updated th the overall state of the registry, which may involve calling of methods annotated
+ * with {@link org.jboss.cache.factories.annotations.Inject}, {@link org.jboss.cache.factories.annotations.Start} or
+ * {@link org.jboss.cache.factories.annotations.Stop}.
+ * <p/>
*
* @param component component to add to the registry.
*/
public void registerComponent(Object component)
{
- registerComponent(component.getClass(), component);
+ registerComponent(component.getClass().getName(), component);
}
/**
- * Adds a singleton instance to the registry. Note that if an instance of this component already exists in the
- * registry it is overwritten.
- *
- * @param componentType the type to register the component under.
- * @param component component to add to the registry.
- */
- public void registerComponent(Class componentType, Object component)
- {
- registry.put(componentType, component);
- }
-
- /**
* Adds an instance component to the registry, registering the component under the given name. If an instance already
* exists in the registry under the given name, it will be overwritten.
+ * <p/>
+ * The status of the component is updated th the overall state of the registry, which may involve calling of methods annotated
+ * with {@link org.jboss.cache.factories.annotations.Inject}, {@link org.jboss.cache.factories.annotations.Start} or
+ * {@link org.jboss.cache.factories.annotations.Stop}.
+ * <p/>
*
* @param name name of the instance
* @param component component to add
*/
public void registerComponent(String name, Object component)
{
- registry.put(name, component);
+ // this will make sure all dependent components are stopped or set to CONSTRUCTED so they can be re-wired later.
+ unregisterComponent(name);
+
+ Component c = new Component(name, component);
+ componentLookup.put(name, c);
+ // build any dependent components if necessary
+ for (Dependency d : c.dependencies)
+ {
+ getOrCreateComponent(d.name, d.type);
+ }
+ c.changeState(overallState);
}
- /**
- * Retrieves a singleton component which can be assigned to the type passed in. Will return a null if no such component
- * is registered
- *
- * @param c type to be able to assign component to.
- * @return component, if found, or a null otherwise.
- */
- @SuppressWarnings("unchecked")
public <T> T getComponent(Class<T> c)
{
- if (c == null)
- {
- log.error("Scanning for component null!! This is a bug!!", new Throwable());
- return null;
- }
-
- //if (log.isTraceEnabled()) log.trace("Scanning registry for an instance that can be assigned to class " + c);
- T component = (T) registry.get(c);
-
- if (component == null)
- {
- // search for subclasses.
- for (Object object : registry.keySet())
- {
- if (object instanceof Class)
- {
- if (c.isAssignableFrom((Class) object)) component = (T) registry.get(object);
- }
-
- }
- }
-
- return component;
+ return getComponent(c.getName(), c);
}
/**
@@ -139,8 +143,11 @@
@SuppressWarnings("unchecked")
public <T> T getComponent(String name, Class<T> c)
{
- T component = (T) registry.get(name);
- if (component == null) return null;
+ Component wrapper = componentLookup.get(name);
+ if (wrapper == null) return null;
+
+ T component = (T) wrapper.instance;
+
if (c.isAssignableFrom(component.getClass())) return component;
else
throw new ConfigurationException("Component registered under " + name + " is of type " + component.getClass() + " and cannot be assigned to " + c);
@@ -160,6 +167,29 @@
* 3. Attempt to create it by looking for an appropriate factory (annotated with {@link org.jboss.cache.factories.annotations.DefaultFactoryFor})
* <p/>
*
+ * @param componentClass type of component to be retrieved. Should not be null.
+ * @return a fully wired component instance, or null if one cannot be found or constructed.
+ * @throws ConfigurationException if there is a problem with consructing or wiring the instance.
+ */
+ public <T> T getOrCreateComponent(Class<T> componentClass)
+ {
+ return getOrCreateComponent(null, componentClass);
+ }
+
+ /**
+ * Retrieves a named component if one exists, and if not, attempts to find a factory capable of constructing the component
+ * (factories annotated with the {@link org.jboss.cache.factories.annotations.DefaultFactoryFor} annotation that is capable
+ * of creating the component class).
+ * <p/>
+ * If an instance needs to be constructed, dependencies are then automatically wired into the instance, based on methods
+ * on the component type annotated with {@link org.jboss.cache.factories.annotations.Inject}.
+ * <p/>
+ * Summing it up, component retrieval happens in the following order:<br />
+ * 1. Look for an appropriate component that exists in the {@link Configuration} that may be injected from an external system.
+ * 2. Look for a class definition passed in to the {@link org.jboss.cache.config.Configuration} - such as an EvictionPolicy implementation
+ * 3. Attempt to create it by looking for an appropriate factory (annotated with {@link org.jboss.cache.factories.annotations.DefaultFactoryFor})
+ * <p/>
+ *
* @param componentName name of component to be created. If null, uses the fully qualified class name as component name.
* @param componentClass type of component to be retrieved. Should not be null.
* @return a fully wired component instance, or null if one cannot be found or constructed.
@@ -167,14 +197,14 @@
*/
public <T> T getOrCreateComponent(String componentName, Class<T> componentClass)
{
- T component = (componentName == null) ? getComponent(componentClass) : getComponent(componentName, componentClass);
+ T component = getComponent(componentName == null ? componentClass.getName() : componentName, componentClass);
if (component == null)
{
// first see if this has been injected externally.
component = getFromConfiguration(componentClass);
- if (component == null)
+ if (component == null && isNonBootstrap(componentClass))
{
// create this component and add it to the registry
ComponentFactory factory = getFactory(componentClass);
@@ -184,9 +214,9 @@
if (component != null)
{
// wire dependencies
- wireDependencies(component);
+// wireDependencies(component);
if (componentName == null)
- registerComponent(componentClass, component);
+ registerComponent(componentClass.getName(), component);
else
registerComponent(componentName, component);
}
@@ -194,8 +224,14 @@
return component;
}
+ private boolean isNonBootstrap(Class<?> componentClass)
+ {
+ return !(componentClass.equals(CacheSPI.class) || componentClass.equals(CacheImpl.class) || componentClass.equals(Cache.class)
+ || componentClass.equals(ComponentRegistry.class));
+ }
+
@SuppressWarnings("unchecked")
- private <T> T getFromConfiguration(Class<T> componentClass)
+ <T> T getFromConfiguration(Class<T> componentClass)
{
//if (log.isDebugEnabled()) log.debug("Looking in configuration for an instance of " + componentClass + " that may have been injected from an external source.");
Method getter = BeanUtils.getterMethod(Configuration.class, componentClass);
@@ -243,12 +279,9 @@
*/
public void updateDependencies()
{
- // make sure we work off a copy of the values set since wireDependencies may add components to the registry
- // and this may otherwise end up in a CME.
-
- Set<Object> components = new HashSet<Object>(registry.values());
-
- for (Object component : components) wireDependencies(component);
+ State originalState = overallState;
+ moveComponentsToState(overallState == STARTED ? CONSTRUCTED : STOPPED);
+ moveComponentsToState(originalState);
}
/**
@@ -259,7 +292,7 @@
*/
public void unregisterComponent(Class<?> clazz)
{
- registry.remove(clazz);
+ unregisterComponent(clazz.getName());
}
/**
@@ -270,13 +303,14 @@
*/
public void unregisterComponent(String name)
{
- registry.remove(name);
+ Component c = componentLookup.remove(name);
+ if (c != null) c.changeState(c.state == STARTED ? STOPPED : CONSTRUCTED);
}
/**
* Wires an object instance with dependencies annotated with the {@link org.jboss.cache.factories.annotations.Inject} annotation, creating more components
* as needed based on the Configuration passed in if these additional components don't exist in the
- * {@link ComponentRegistry}.
+ * {@link ComponentRegistry}. Strictly for components that don't otherwise live in the registry and have a lifecycle, such as Nodes.
*
* @param target object to wire
* @throws ConfigurationException if there is a problem wiring the instance
@@ -286,7 +320,7 @@
//if (log.isTraceEnabled()) log.trace("Inspecting class " + target.getClass());
try
{
- List<Method> methods = getAllMethods(target.getClass(), Inject.class);
+ List<Method> methods = ReflectionUtil.getAllMethods(target.getClass(), Inject.class);
//if (log.isTraceEnabled()) log.trace("Found method set containing " + methods.size() + " methods that need injection: " + methods);
// search for anything we need to inject
@@ -316,14 +350,13 @@
private <T> void performInjection(Method method, T target) throws IllegalAccessException, InvocationTargetException
{
Class[] parameterTypes = method.getParameterTypes();
- Annotation[][] annotationsOnParams = method.getParameterAnnotations();
+ List<Dependency> componentsToInject = getDeclaredDependencies(method);
Object[] parameters = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++)
{
- String componentName = extractComponentName(annotationsOnParams[i]);
- parameters[i] = getOrCreateComponent(componentName, parameterTypes[i]);
+ parameters[i] = getComponent(componentsToInject.get(i).name, parameterTypes[i]);
}
// make sure we set this method to be accessible, so we can call private, package and protected
@@ -347,53 +380,23 @@
return null;
}
- /**
- * Returns a set of Methods that require components injected. This includes all public, protected, package and private
- * methods, as well as those of superclasses. Note that this does *not* include overridden methods.
- * <p/>
- * Includes all methods annotated with &Inject as well as methods that are <i>not</i> annotated, but instead
- * have parameters annotated accordingly.
- *
- * @param c class to inspect
- * @param annotationType the type of annotation to look for
- * @return Set of Method objects that require injection.
- */
- private List<Method> getAllMethods(Class c, Class<? extends Annotation> annotationType)
+ private List<Dependency> getDeclaredDependencies(Method method)
{
- List<Method> annotated = new LinkedList<Method>();
- inspectRecursively(c, annotated, annotationType);
- return annotated;
- }
-
- private void inspectRecursively(Class c, List<Method> s, Class<? extends Annotation> annotationType)
- {
- // Superclass first
- if (!c.equals(Object.class)) inspectRecursively(c.getSuperclass(), s, annotationType);
-
- for (Method m : c.getDeclaredMethods())
+ List<Dependency> dependencies = new LinkedList<Dependency>();
+ Class[] parameterTypes = method.getParameterTypes();
+ Annotation[][] annotationsOnParams = method.getParameterAnnotations();
+ for (int i = 0; i < parameterTypes.length; i++)
{
- // don't bother if this method has already been overridden by a subclass
- if (!alreadyFound(m, s) && m.isAnnotationPresent(annotationType))
- {
- s.add(m);
- }
+ String componentName = extractComponentName(annotationsOnParams[i]);
+ Dependency d = new Dependency(componentName == null ? parameterTypes[i].getName() : componentName, parameterTypes[i]);
+ dependencies.add(d);
}
+ return dependencies;
}
- private boolean alreadyFound(Method m, Collection<Method> s)
- {
- for (Method found : s)
- {
- if (m.getName().equals(found.getName()) &&
- Arrays.equals(m.getParameterTypes(), found.getParameterTypes()))
- return true;
- }
- return false;
- }
-
-
/**
- * Retrieves a component factory instance capable of constructing components of a specified type.
+ * Retrieves a component factory instance capable of constructing components of a specified type. If the factory doesn't
+ * exist in the registry, one is created. Always changes the state of any factory to {@link State#STARTED} before returning.
*
* @param componentClass type of component to construct
* @return component factory capable of constructing such components
@@ -412,15 +415,19 @@
cf = instantiateFactory(cfClass);
if (cf != null)
{
- // wire factory deps
- wireDependencies(cf);
- // register this factory
+ // we simply register this factory. Registration will take care of constructing any dependencies.
registerComponent(cf);
}
}
if (cf == null)
throw new ConfigurationException("Unable to locate component factory for component " + componentClass);
+
+ // ensure the component factory is in the STARTED state!
+ Component c = componentLookup.get(cfClass.getName());
+ if (c.instance != cf)
+ throw new ConfigurationException("Component factory " + cfClass + " incorrectly registered!");
+ c.changeState(STARTED);
return cf;
}
@@ -445,111 +452,339 @@
/**
* No such thing as a meta factory yet. Factories are created using this method which attempts to use an empty public
- * constructor, or a "getInstance" method.
+ * constructor.
*
* @param factory class of factory to be created
* @return factory instance
*/
ComponentFactory instantiateFactory(Class<? extends ComponentFactory> factory)
{
- Constructor ctor = null;
- Method singletonAccessor = null;
try
{
- ctor = factory.getConstructor();
+ return factory.newInstance();
}
- catch (NoSuchMethodException e)
+ catch (Exception e)
{
- log.trace("No default, public constructor on " + factory);
- // perhaps this factory is a singleton.
- try
- {
- singletonAccessor = factory.getMethod("getInstance");
- }
- catch (NoSuchMethodException e1)
- {
- log.trace("No getInstance() method on " + factory);
- }
+ // unable to get a hold of an instance!!
+ throw new ConfigurationException("Unable to instantiate factory " + factory, e);
}
+ }
- if (ctor == null && singletonAccessor == null)
+ /**
+ * Wipes everything in the registry. Use with care.
+ */
+ public void reset()
+ {
+ componentLookup.clear();
+ overallState = CONSTRUCTED;
+ }
+
+ /**
+ * Starts all components that contain the {@link Start} annotation.
+ */
+ public void start()
+ {
+ moveComponentsToState(STARTED);
+ }
+
+ /**
+ * Stops all components that contain the {@link Stop} annotation.
+ */
+ public void stop()
+ {
+ moveComponentsToState(STOPPED);
+ }
+
+ /**
+ * Injects dependencies to all components that require injection.
+ */
+ public void wire()
+ {
+ moveComponentsToState(WIRED);
+ }
+
+ void moveComponentsToState(State state)
+ {
+ for (Component c : componentLookup.values())
{
- // unable to get a hold of an instance!!
- throw new ConfigurationException("Unable to instantiate factory " + factory + ": no public, default constructor or getInstance() static method!");
+ c.changeState(state);
}
+ overallState = state;
+ }
- ComponentFactory instance;
- try
+ /**
+ * Represents the state of a component
+ */
+ enum State
+ {
+ STOPPED, CONSTRUCTED, WIRED, STARTED;
+
+ /**
+ * Tests whether the current state is "greater" than the state passed in
+ *
+ * @param other state to compare with
+ * @return true if the current state is "greater" than the state passed in
+ */
+ boolean isGreaterThan(State other)
{
- if (ctor == null)
- {
- instance = (ComponentFactory) singletonAccessor.invoke(null);
- }
- else
- {
- instance = (ComponentFactory) ctor.newInstance();
- }
+ return this.ordinal() > other.ordinal();
}
- catch (Exception e)
+
+ /**
+ * Tests whether the current state is "less" than the state passed in
+ *
+ * @param other state to compare with
+ * @return true if the current state is "less" than the state passed in
+ */
+ boolean isLessThan(State other)
{
- throw new ConfigurationException("Unable to instantiate factory " + factory, e);
+ return this.ordinal() < other.ordinal();
}
- return instance;
+ /**
+ * @param other state to compare with
+ * @return the absolute difference in ordinal places between 2 states
+ */
+ int absoluteDifference(State other)
+ {
+ return Math.abs(this.ordinal() - other.ordinal());
+ }
}
/**
- * Wipes everything in the registry. Use with care.
+ * Represents a potentially unconstructed dependency. A simple wrapper around a name and type pair.
*/
- public void reset()
+ class Dependency
{
- registry.clear();
+ String name;
+ Class<?> type;
+
+ public Dependency(String name, Class type)
+ {
+ this.name = name;
+ this.type = type;
+ }
+
+ public int hashCode()
+ {
+ return 31 * name.hashCode();
+ }
+
+ public boolean equals(Object other)
+ {
+ return other instanceof Dependency && name.equals(((Dependency) other).name);
+ }
+
+ public String toString()
+ {
+ return "Dependency (name = " + name + ")";
+ }
}
/**
- * Starts all components that contain the {@link Start} annotation.
+ * Represents a component in the registry, along with state and dependencies.
*/
- public void startComponents()
+ class Component
{
- for (Object component : registry.values())
+ Object instance;
+ String name;
+ State state = CONSTRUCTED;
+ Set<Dependency> dependencies = new HashSet<Dependency>(3);
+ Set<Dependency> dependencyFor = new HashSet<Dependency>(3);
+ Dependency me;
+ boolean deepRecursionDetector = false;
+
+ /**
+ * Constructs a Component out of an instance and a name. Scans instance for dependencies and populates
+ * the "dependencies" and "dependencyFor" collections.
+ *
+ * @param name name of component
+ * @param instance component instance
+ */
+ public Component(String name, Object instance)
{
- List<Method> methods = getAllMethods(component.getClass(), Start.class);
- for (Method m : methods)
+ this.instance = instance;
+ this.name = name;
+
+ // now scan the instance for all dependencies.
+ List<Method> injectionMethods = ReflectionUtil.getAllMethods(instance.getClass(), Inject.class);
+
+ // now for each injection method, get dependencies
+ for (Method m : injectionMethods) dependencies.addAll(getDeclaredDependencies(m));
+
+ // backward-chaining - make sure I know about components that depend on me.
+ me = asDependency();
+ for (Dependency d : dependencies)
{
- try
+ Component c = componentLookup.get(d.name);
+ if (c != null) c.dependencyFor.add(me);
+ }
+ }
+
+ /**
+ * @return a {@link org.jboss.cache.factories.ComponentRegistry.Dependency} object that represents the current component
+ */
+ Dependency asDependency()
+ {
+ if (me == null) me = new Dependency(name, null);
+ return me;
+ }
+
+ /**
+ * Changes the state of a component - along with all dependent components - to a new state. This method is recursion
+ * and cyclic dependency safe.
+ *
+ * @param newState new state to move component to
+ */
+ void changeState(State newState)
+ {
+ // Deep recursion is when this component depends on another component which in turn may depend on this component again.
+ // if this is encountered then break out of the recursive loop.
+ if (state != newState && !deepRecursionDetector)
+ {
+ // Step by step. If the new state is > 1 step away from the current state, change state gradually.
+ boolean increase = newState.isGreaterThan(state);
+ int numSteps = newState.absoluteDifference(state);
+
+ while (numSteps > 1)
{
- m.setAccessible(true);
- m.invoke(component);
+ changeState(State.values()[state.ordinal() + (increase ? 1 : -1)]);
+ numSteps = newState.absoluteDifference(state);
}
- catch (Exception e)
+
+ // now we update the state of dependent components accordingly.
+ Set<Dependency> dependentComponents = new HashSet<Dependency>();
+ Set<Component> shallowCyclic = new HashSet<Component>();
+
+ if (increase)
{
- log.warn("Unable to invoke @Start annotated method " + m, e);
+ // if I am moving to a "higher" state make sure all components that I depend on have moved to that state as well.
+ dependentComponents.addAll(dependencies);
}
+ else
+ {
+ // if I am moving to a "lower" state make sure all components depend on me have moved to that state as well.
+ dependentComponents.addAll(dependencyFor);
+ }
+
+ // switch on the deep recursion detector
+ deepRecursionDetector = true;
+ for (Dependency d : dependentComponents)
+ {
+ Component c = componentLookup.get(d.name);
+ if (c != null)
+ {
+ if (isShallowCyclic(c))
+ {
+ // don't process shallow cyclic deps here - shoud do that after we set our state.
+ shallowCyclic.add(c);
+ }
+ else
+ {
+ // of we are "moving up" - only do this if the component is lower than what is needed.
+ if ((increase && newState.isGreaterThan(c.state)) || (!increase && newState.isLessThan(c.state)))
+ c.changeState(newState);
+
+ }
+ }
+ }
+
+ // NOW we update our state.
+ switch (newState)
+ {
+ case STOPPED:
+ stop();
+ break;
+ case WIRED:
+ wire();
+ break;
+ case STARTED:
+ start();
+ break;
+ case CONSTRUCTED:
+ // nothing to do here.
+ }
+
+ state = newState;
+
+ // now process the shallow cyclic deps
+ for (Component c : shallowCyclic)
+ {
+ // of we are "moving up" - only do this if the component is lower than what is needed.
+ if ((increase && newState.isGreaterThan(c.state)) || (!increase && newState.isLessThan(c.state)))
+ c.changeState(newState);
+ }
+
+ // make sure we switch off the deep recursion detector now.
+ deepRecursionDetector = false;
}
}
- }
- /**
- * Stops all components that contain the {@link Stop} annotation.
- */
- public void stopComponents()
- {
- for (Object component : registry.values())
+ /**
+ * Tests whether there is an immediate cyclic dependency (i.e., both components depend on each other) between the current component and another one.
+ *
+ * @param c other component to test with
+ * @return true if there is an immediate cyclic dependency between the 2 components
+ */
+ private boolean isShallowCyclic(Component c)
{
- List<Method> methods = getAllMethods(component.getClass(), Stop.class);
+ return (dependencies.contains(c.asDependency()) && c.dependencies.contains(asDependency()));
+ }
+
+ /**
+ * Used to wire dependencies into this component instance.
+ */
+ void wire()
+ {
+ try
+ {
+ List<Method> methods = ReflectionUtil.getAllMethods(instance.getClass(), Inject.class);
+
+ // search for anything we need to inject
+ for (Method method : methods) performInjection(method, instance);
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to configure component (type: " + instance.getClass() + ", instance " + instance + ")", e);
+ }
+ }
+
+ /**
+ * Used to call all methods annotated with {@link Start} on component instance
+ */
+ void start()
+ {
+ invokeMethods(Start.class);
+ }
+
+ /**
+ * Used to call all methods annotated with {@link Stop} on component instance
+ */
+ void stop()
+ {
+ invokeMethods(Stop.class);
+ }
+
+ private void invokeMethods(Class<? extends Annotation> annotation)
+ {
+ List<Method> methods = ReflectionUtil.getAllMethods(instance.getClass(), annotation);
for (Method m : methods)
{
try
{
m.setAccessible(true);
- m.invoke(component);
+ m.invoke(instance);
}
catch (Exception e)
{
- log.warn("Unable to invoke @Stop annotated method " + m, e);
+ log.warn("Unable to invoke annotated method " + m, e);
}
}
}
+
+ public String toString()
+ {
+ return "Component (name = " + name + ", state = " + state + ")";
+ }
}
-
-}
+}
\ No newline at end of file
Modified: core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -58,7 +58,7 @@
if (i == null)
{
i = clazz.newInstance();
- componentRegistry.wireDependencies(i);
+// componentRegistry.wireDependencies(i);
// add this interceptor as a NAMED component into the registry
componentRegistry.registerComponent(clazz.getName(), i);
}
@@ -584,41 +584,37 @@
}
/**
- * The same as {@link #correctInterceptorChaining(java.util.List, org.jboss.cache.config.Configuration, ComponentRegistry)}
+ * The same as {@link #correctInterceptorChaining(java.util.List)}
* except that it takes in the first interceptor in the chain instead of a list of interceptors, and traverses the chain to
* generate a list. Identical to calling
* <pre>
* InterceptorChainFactory.correctInterceptorChaining( InterceptorChainFactory.asList(first), configuration, componentRegistry );
* </pre>
*
- * @param first first interceptor in the chain to correct.
- * @param configuration cache configuration
- * @param componentRegistry component registry
+ * @param first first interceptor in the chain to correct.
* @return the first interceptor in the chain.
*/
- public Interceptor correctInterceptorChaining(Interceptor first, Configuration configuration, ComponentRegistry componentRegistry)
+ public Interceptor correctInterceptorChaining(Interceptor first)
{
- return correctInterceptorChaining(asList(first), configuration, componentRegistry);
+ return correctInterceptorChaining(asList(first));
}
/**
* "Fixes" the next() and last() pointers for each interceptor, based on the order presented in the list passed in, and
* also re-assesses dependencies for each interceptor, injecting dependencies accordingingly.
*
- * @param interceptors interceptor chain to correct
- * @param configuration cache configuration
- * @param componentRegistry component registry
+ * @param interceptors interceptor chain to correct
* @return the first interceptor in the chain.
*/
- public Interceptor correctInterceptorChaining(List<Interceptor> interceptors, Configuration configuration, ComponentRegistry componentRegistry)
+ public Interceptor correctInterceptorChaining(List<Interceptor> interceptors)
{
+ // re-wire
+ componentRegistry.updateDependencies();
+
Interceptor first = null, last = null;
for (Interceptor next : interceptors)
{
- // re-wire
- componentRegistry.wireDependencies(next);
-
if (first == null)
{
first = last = next;
Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/CacheLoaderInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/CacheLoaderInterceptor.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/CacheLoaderInterceptor.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -316,6 +316,7 @@
if (node != null && node.isChildrenLoaded())
{
+ log.trace("Children already loaded!");
return;
}
Set children_names = loader.getChildrenNames(fqn);
Modified: core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -414,7 +414,6 @@
public Node getNode(Fqn<?> fqn)
{
- GlobalTransaction tx = cache.getCurrentTransaction();
MethodCall m = MethodCallFactory.create(MethodDeclarations.getNodeMethodLocal, fqn);
return (Node) invoke(m);
}
Copied: core/trunk/src/main/java/org/jboss/cache/util/reflect/ClasspathScanner.java (from rev 4871, core/trunk/src/main/java/org/jboss/cache/factories/annotations/ClasspathScanner.java)
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/util/reflect/ClasspathScanner.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/util/reflect/ClasspathScanner.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -0,0 +1,171 @@
+package org.jboss.cache.util.reflect;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Class for scanning archives and classpaths in the current JBoss Cache classpath for classes annotated with a given annotation. Inspired by a similar class in
+ * JBoss SEAM.
+ *
+ * @author Manik Surtani
+ */
+public class ClasspathScanner
+{
+ private Log log = LogFactory.getLog(ClasspathScanner.class);
+ private URLClassLoader classLoader;
+
+ /**
+ * Constructor with the type of annotation to scan for.
+ */
+ public ClasspathScanner()
+ {
+ classLoader = (URLClassLoader) ClasspathScanner.class.getClassLoader();
+ }
+
+ /**
+ * Scans the class path element that contains JBoss Cache for all classes that contain the annotation type this class is
+ * initialised with. Note that this only scans CLASSES for the annotation; not methods, etc.
+ *
+ * @param annotationType the type of annotation to scan for.
+ * @param classType the type of class to scan for. Subclasses will be scanned, others will not.
+ * @return a set of Classes that contain the specified annotation on the class.
+ */
+ public <T> Set<Class<? extends T>> scan(Class<? extends Annotation> annotationType, Class<T> classType)
+ {
+ Set<Class<? extends T>> classes = Collections.emptySet();
+
+ try
+ {
+ // only scan the current ClassPath location that contains this file. Could be a directory or a JAR file.
+ URL url = getURLPathFromClassLoader();
+ String urlPath = url.getFile();
+ if (urlPath.endsWith("/"))
+ {
+ urlPath = urlPath.substring(0, urlPath.length() - 1);
+ }
+
+ if (log.isDebugEnabled()) log.debug("scanning: " + urlPath);
+ File file = new File(urlPath);
+ if (file.isDirectory())
+ {
+ classes = handleDirectory(file, null, classType, annotationType);
+ }
+ else
+ {
+ classes = handleArchive(file, classType, annotationType);
+ }
+ }
+ catch (IOException ioe)
+ {
+ log.warn("could not read entries", ioe);
+ }
+ catch (ClassNotFoundException e)
+ {
+ log.warn("Unable to load class", e);
+ }
+
+ return classes;
+ }
+
+ private URL getURLPathFromClassLoader() throws MalformedURLException
+ {
+ URL u2 = classLoader.findResource("org/jboss/cache/Version.class");
+
+ for (URL u : classLoader.getURLs())
+ {
+ String urlString = u.toString().replaceAll("\\/\\.\\/", "/");
+ if (u2.toString().startsWith(urlString))
+ {
+ return new URL(urlString);
+ }
+ }
+
+ return null;
+ }
+
+
+ private <T> Set<Class<? extends T>> handleArchive(File file, Class<T> classType, Class<? extends Annotation> annotationType) throws IOException, ClassNotFoundException
+ {
+ Set<Class<? extends T>> classesWithAnnotations = new HashSet<Class<? extends T>>();
+ ZipFile zip = new ZipFile(file);
+ Enumeration<? extends ZipEntry> entries = zip.entries();
+ while (entries.hasMoreElements())
+ {
+ ZipEntry entry = entries.nextElement();
+ String name = entry.getName();
+ Class<? extends T> c = handleItem(name, classType, annotationType);
+ if (c != null) classesWithAnnotations.add(c);
+ }
+
+ return classesWithAnnotations;
+ }
+
+ private <T> Set<Class<? extends T>> handleDirectory(File file, String path, Class<T> classType, Class<? extends Annotation> annotationType) throws IOException, ClassNotFoundException
+ {
+ Set<Class<? extends T>> classesWithAnnotations = new HashSet<Class<? extends T>>();
+ for (File child : file.listFiles())
+ {
+ String newPath = path == null ? child.getName() : path + '/' + child.getName();
+ if (child.isDirectory())
+ {
+ classesWithAnnotations.addAll(handleDirectory(child, newPath, classType, annotationType));
+ }
+ else
+ {
+ Class<? extends T> c = handleItem(newPath, classType, annotationType);
+ if (c != null)
+ {
+ classesWithAnnotations.add(c);
+ }
+ }
+ }
+
+ return classesWithAnnotations;
+ }
+
+ private <T> Class<? extends T> handleItem(String name, Class<T> classType, Class<? extends Annotation> annotationType) throws IOException, ClassNotFoundException
+ {
+ if (!name.endsWith(".class")) return null;
+
+ Class<? extends T> c = getClassFile(filenameToClassname(name), classType);
+ if (c != null && hasAnnotation(c, annotationType))
+ {
+ return c;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private <T> Class<? extends T> getClassFile(String name, Class<T> classType) throws IOException, ClassNotFoundException
+ {
+ Class c = classLoader.loadClass(name);
+ if (c != null && classType.isAssignableFrom(c)) return c;
+ else return null;
+ }
+
+ private boolean hasAnnotation(Class clazz, Class<? extends Annotation> annotationType)
+ {
+ return (clazz.isAnnotationPresent(annotationType));
+ }
+
+ private static String filenameToClassname(String filename)
+ {
+ return filename.substring(0, filename.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
+ }
+
+}
Added: core/trunk/src/main/java/org/jboss/cache/util/reflect/ReflectionUtil.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/util/reflect/ReflectionUtil.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/util/reflect/ReflectionUtil.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -0,0 +1,73 @@
+package org.jboss.cache.util.reflect;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Basic reflection utilities to enhance what the JDK provides.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+public class ReflectionUtil
+{
+ /**
+ * Returns a set of Methods that contain the given method annotation. This includes all public, protected, package and private
+ * methods, as well as those of superclasses. Note that this does *not* include overridden methods.
+ *
+ * @param c class to inspect
+ * @param annotationType the type of annotation to look for
+ * @return List of Method objects that require injection.
+ */
+ public static List<Method> getAllMethods(Class c, Class<? extends Annotation> annotationType)
+ {
+ List<Method> annotated = new LinkedList<Method>();
+ inspectRecursively(c, annotated, annotationType);
+ return annotated;
+ }
+
+ /**
+ * Inspects a class and it's superclasses (all the way to {@link Object} for method instances that contain a given annotation.
+ * This even identifies private, package and protected methods, not just public ones.
+ *
+ * @param c
+ * @param s
+ * @param annotationType
+ */
+ private static void inspectRecursively(Class c, List<Method> s, Class<? extends Annotation> annotationType)
+ {
+ // Superclass first
+ if (!c.equals(Object.class)) inspectRecursively(c.getSuperclass(), s, annotationType);
+
+ for (Method m : c.getDeclaredMethods())
+ {
+ // don't bother if this method has already been overridden by a subclass
+ if (!alreadyFound(m, s) && m.isAnnotationPresent(annotationType))
+ {
+ s.add(m);
+ }
+ }
+ }
+
+ /**
+ * Tests whether a method has already been found, i.e., overridden.
+ *
+ * @param m method to inspect
+ * @param s collection of methods found
+ * @return true a method with the same signature already exists.
+ */
+ private static boolean alreadyFound(Method m, Collection<Method> s)
+ {
+ for (Method found : s)
+ {
+ if (m.getName().equals(found.getName()) &&
+ Arrays.equals(m.getParameterTypes(), found.getParameterTypes()))
+ return true;
+ }
+ return false;
+ }
+}
Copied: core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryFunctionalTest.java (from rev 4851, core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryTest.java)
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryFunctionalTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryFunctionalTest.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -0,0 +1,227 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.CacheImpl;
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.RPCManager;
+import org.jboss.cache.RegionManager;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.buddyreplication.NextMemberBuddyLocator;
+import org.jboss.cache.config.BuddyReplicationConfig;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.factories.annotations.Inject;
+import org.jboss.cache.invocation.CacheInvocationDelegate;
+import org.jboss.cache.marshall.CacheMarshaller200;
+import org.jboss.cache.marshall.CacheMarshaller210;
+import org.jboss.cache.marshall.Marshaller;
+import org.jboss.cache.marshall.VersionAwareMarshaller;
+import org.jboss.cache.misc.TestingUtil;
+import org.jboss.cache.notifications.Notifier;
+import org.jboss.cache.statetransfer.StateTransferManager;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Test(groups = {"functional"})
+public class ComponentRegistryFunctionalTest
+{
+ private ComponentFactory cf;
+ private ComponentRegistry cr;
+ private Configuration configuration;
+
+ @BeforeMethod
+ public void setUp() throws Exception
+ {
+ cf = (ComponentFactory) DefaultCacheFactory.getInstance();
+
+ CacheSPI spi = new CacheInvocationDelegate();
+ Constructor ctor = CacheImpl.class.getDeclaredConstructor();
+ ctor.setAccessible(true);
+ CacheImpl ci = (CacheImpl) ctor.newInstance();
+
+ configuration = ci.getConfiguration();
+ cr = TestingUtil.extractComponentRegistry(ci);
+
+ cf.componentRegistry = cr;
+ cf.configuration = configuration;
+
+ cr.registerComponent(cr); // register self
+ cr.registerComponent(ci);
+ cr.registerComponent(spi);
+ cr.registerComponent(configuration);
+
+ cr.wire();
+ }
+
+ public void testDefaultFactoryScanning()
+ {
+ cr.scanDefaultFactories();
+
+ assert cr.defaultFactories != null : "Should be populated";
+
+ // at very least, expecting a Marshaller factory and a DefaultCacheFactory.
+ assert cr.defaultFactories.containsKey(Marshaller.class);
+ assert cr.defaultFactories.get(Marshaller.class).equals(CacheMarshallerFactory.class);
+ assert cr.defaultFactories.containsKey(Notifier.class);
+ assert cr.defaultFactories.get(Notifier.class).equals(EmptyConstructorFactory.class);
+
+ }
+
+ public void testNamedComponents()
+ {
+ cr.registerComponent("blah", new Object());
+ Object namedComponent1 = cr.getOrCreateComponent("blah", Object.class);
+ Object namedComponent2 = cr.getOrCreateComponent("blah", Object.class);
+
+ assert namedComponent1 == namedComponent2;
+ }
+
+ /**
+ * Case 1:
+ * nothing injected, nothing specified in Configuration. Should use default factory.
+ */
+ public void testConstructionOrder1()
+ {
+ Class<Marshaller> componentToTest = Marshaller.class;
+ Marshaller m = cr.getOrCreateComponent(null, componentToTest);
+ assert m instanceof VersionAwareMarshaller;
+ VersionAwareMarshaller vam = (VersionAwareMarshaller) m;
+ m = (Marshaller) TestingUtil.extractField(vam, "defaultMarshaller");
+ assert m instanceof CacheMarshaller210;
+ }
+
+ /**
+ * Case 2:
+ * instance injected, class specified in Configuration. Should use injected.
+ */
+ public void testConstructionOrder2()
+ {
+ Class<Marshaller> componentToTest = Marshaller.class;
+ configuration.setMarshallerClass(CacheMarshaller200.class.getName());
+ Marshaller instance = new CacheMarshaller210(null, false, false);
+ configuration.setCacheMarshaller(instance);
+
+ // the setup() would have wired the default marshaller. Need to update deps.
+ cr.unregisterComponent(Marshaller.class);
+ cr.updateDependencies();
+
+ Marshaller m = cr.getOrCreateComponent(null, componentToTest);
+ assert m == instance : "m is " + m + " but expected " + instance;
+ }
+
+ /**
+ * Case 3:
+ * instance injected, no class specified in Configuration. Should use injected.
+ */
+ public void testConstructionOrder3()
+ {
+ Class<Marshaller> componentToTest = Marshaller.class;
+ Marshaller instance = new CacheMarshaller210(null, false, false);
+ configuration.setCacheMarshaller(instance);
+
+ // the setup() would have wired the default marshaller. Need to update deps.
+ cr.unregisterComponent(Marshaller.class);
+ cr.updateDependencies();
+
+ Marshaller m = cr.getOrCreateComponent(null, componentToTest);
+ assert m == instance : "m is " + m + " but expected " + instance;
+ }
+
+ /**
+ * Case 4:
+ * nothing injected, class specified in Configuration. Should use class specified.
+ */
+ public void testConstructionOrder4()
+ {
+ Class<Marshaller> componentToTest = Marshaller.class;
+ configuration.setMarshallerClass(CacheMarshaller200.class.getName());
+ Marshaller m = cr.getOrCreateComponent(null, componentToTest);
+ assert m instanceof VersionAwareMarshaller;
+ VersionAwareMarshaller vam = (VersionAwareMarshaller) m;
+ m = (Marshaller) TestingUtil.extractField(vam, "defaultMarshaller");
+ assert m instanceof CacheMarshaller200;
+ }
+
+ public void testTransitiveDependencies()
+ {
+ Class<BuddyManager> componentToTest = BuddyManager.class;
+
+ // configure the cfg to use BR
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ BuddyReplicationConfig.BuddyLocatorConfig blc = new BuddyReplicationConfig.BuddyLocatorConfig();
+ blc.setBuddyLocatorClass(NextMemberBuddyLocator.class.getName());
+ brc.setBuddyLocatorConfig(blc);
+ configuration.setBuddyReplicationConfig(brc);
+
+ BuddyManager bm = cr.getOrCreateComponent(null, componentToTest);
+ assert bm != null;
+
+ StateTransferManager stm = (StateTransferManager) TestingUtil.extractField(bm, "stateTransferManager");
+ assert stm != null;
+
+ RPCManager rpcm = (RPCManager) TestingUtil.extractField(bm, "rpcManager");
+ assert rpcm != null;
+
+ RegionManager rm = (RegionManager) TestingUtil.extractField(bm, "regionManager");
+ assert rm != null;
+
+ Configuration cfg = (Configuration) TestingUtil.extractField(bm, "configuration");
+ assert cfg == configuration;
+ }
+
+ public void testInjectionOrder()
+ {
+ // injection should only occur after dependent components have been fully wired.
+
+ // E.g. Test1 depends on Test2 and Test2 depends on Test3.
+ //cr.reset();
+
+ // DefaultFactoryFor annotation won't work since tests are compiled into a separate classpath
+ cr.defaultFactories.put(Test1.class, EmptyConstructorFactory.class);
+ cr.defaultFactories.put(Test2.class, EmptyConstructorFactory.class);
+ cr.defaultFactories.put(Test3.class, EmptyConstructorFactory.class);
+
+ Test1 t1 = cr.getOrCreateComponent(null, Test1.class);
+
+ assert t1 != null;
+ assert t1.test2 != null;
+ assert t1.test2.test3 != null;
+ assert t1.someValue == t1.test2.test3.someValue;
+ }
+
+ public static class Test1
+ {
+ private Test2 test2;
+ private boolean someValue = false;
+
+ @Inject
+ public void setTest2(Test2 test2)
+ {
+ this.test2 = test2;
+ someValue = test2.test3.someValue;
+ }
+ }
+
+ public static class Test2
+ {
+ private Test3 test3;
+
+ @Inject
+ public void setTest3(Test3 test3)
+ {
+ this.test3 = test3;
+ }
+ }
+
+ public static class Test3
+ {
+ private boolean someValue = true;
+ }
+}
+
Added: core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryUnitTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryUnitTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/factories/ComponentRegistryUnitTest.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -0,0 +1,107 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.factories.annotations.ComponentName;
+import org.jboss.cache.factories.annotations.Inject;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Test(groups = "functional")
+public class ComponentRegistryUnitTest
+{
+ ComponentRegistry cr;
+ Configuration cfg;
+
+ @BeforeMethod
+ public void setUp()
+ {
+ cr = new ComponentRegistry(new Configuration());
+ cfg = cr.getConfiguration();
+ }
+
+ public void testChangingComponentState()
+ {
+ cr.registerComponent("c2", new C2());
+ cr.registerComponent("c1", new C1());
+ cr.registerComponent("c3", new C3());
+
+ ComponentRegistry.Component c1 = cr.componentLookup.get("c1");
+ ComponentRegistry.Component c2 = cr.componentLookup.get("c2");
+ ComponentRegistry.Component c3 = cr.componentLookup.get("c3");
+
+ // add some dependencies
+ ComponentRegistry.Dependency d1 = cr.new Dependency("c1", null);
+ ComponentRegistry.Dependency d2 = cr.new Dependency("c2", null);
+ ComponentRegistry.Dependency d3 = cr.new Dependency("c3", null);
+
+ // c1 depends on c2
+ // c3 depends on c1
+
+ // test dependency and dependencyFor
+
+ assert c2.dependencies.isEmpty();
+ assert c1.dependencies.contains(d2);
+ assert c1.dependencies.size() == 1;
+ assert c3.dependencies.contains(d1);
+ assert c3.dependencies.size() == 1;
+
+ assert c2.dependencyFor.contains(d1);
+ assert c2.dependencyFor.size() == 1;
+ assert c1.dependencyFor.contains(d3);
+ assert c1.dependencyFor.size() == 1;
+ assert c3.dependencyFor.isEmpty();
+
+ assert c1.state == ComponentRegistry.State.CONSTRUCTED;
+ assert c2.state == ComponentRegistry.State.CONSTRUCTED;
+ assert c3.state == ComponentRegistry.State.CONSTRUCTED;
+
+ c1.changeState(ComponentRegistry.State.WIRED);
+
+ assert c1.state == ComponentRegistry.State.WIRED;
+ assert c2.state == ComponentRegistry.State.WIRED;
+ assert c3.state == ComponentRegistry.State.CONSTRUCTED;
+
+ c3.changeState(ComponentRegistry.State.STARTED);
+
+ assert c1.state == ComponentRegistry.State.STARTED;
+ assert c2.state == ComponentRegistry.State.STARTED;
+ assert c3.state == ComponentRegistry.State.STARTED;
+
+ c1.changeState(ComponentRegistry.State.CONSTRUCTED);
+
+ assert c1.state == ComponentRegistry.State.CONSTRUCTED;
+ assert c2.state == ComponentRegistry.State.STARTED;
+ assert c3.state == ComponentRegistry.State.CONSTRUCTED;
+ }
+
+ public static class C1
+ {
+ C2 c2;
+
+ @Inject
+ private void inject(@ComponentName("c2")C2 c2)
+ {
+ this.c2 = c2;
+ }
+ }
+
+ public static class C2
+ {
+
+ }
+
+ public static class C3
+ {
+ C1 c1;
+
+ @Inject
+ private void inject(@ComponentName("c1")C1 c1)
+ {
+ this.c1 = c1;
+ }
+ }
+}
Added: core/trunk/src/test/java/org/jboss/cache/factories/DependencyGraphTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/factories/DependencyGraphTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/factories/DependencyGraphTest.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -0,0 +1,78 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.factories.annotations.Inject;
+import org.testng.annotations.Test;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Test(groups = "functional")
+public class DependencyGraphTest
+{
+ public void testDependencies()
+ {
+ Map<Object, Object> components = new HashMap<Object, Object>();
+ components.put(Test1.class, new Test1());
+ components.put(Test2.class, new Test2());
+ components.put(Test3.class, new Test3());
+ components.put(Test4.class, new Test4());
+
+ // expected order - Test4, Test3, Test2, Test1
+ DependencyGraph dg = new DependencyGraph(components);
+ List<Object> ordered = dg.getOrderedComponentKeys();
+
+ assert ordered.size() == 4;
+
+ System.out.println(ordered);
+
+ int n = 0;
+ assert ordered.get(n++) == Test4.class;
+ assert ordered.get(n++) == Test3.class;
+ assert ordered.get(n++) == Test2.class;
+ assert ordered.get(n) == Test1.class;
+ }
+
+ public static class Test1
+ {
+ private Test2 test2;
+
+ @Inject
+ private void inject(Test2 test2)
+ {
+ this.test2 = test2;
+ }
+ }
+
+ public static class Test2
+ {
+ private Test3 test3;
+ private Test4 test4;
+
+ @Inject
+ private void inject(Test3 test3, Test4 test4)
+ {
+ this.test3 = test3;
+ this.test4 = test4;
+ }
+ }
+
+ public static class Test3
+ {
+ private Test4 test4;
+
+ @Inject
+ private void inject(Test4 test4)
+ {
+ this.test4 = test4;
+ }
+ }
+
+ public static class Test4
+ {
+ }
+}
Modified: core/trunk/src/test/java/org/jboss/cache/interceptors/EvictionInterceptorTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/interceptors/EvictionInterceptorTest.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/test/java/org/jboss/cache/interceptors/EvictionInterceptorTest.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -17,6 +17,7 @@
import org.jboss.cache.eviction.DummyEvictionConfiguration;
import org.jboss.cache.eviction.EvictedEventNode;
import org.jboss.cache.eviction.NodeEventType;
+import org.jboss.cache.factories.ComponentRegistry;
import org.jboss.cache.factories.InterceptorChainFactory;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.marshall.MethodCall;
@@ -88,10 +89,12 @@
CallInterceptor ci = new CallInterceptor();
ei.setNext(ci);
- InterceptorChainFactory.getInstance().correctInterceptorChaining(interceptor, cache.getConfiguration(), TestingUtil.extractComponentRegistry(cache));
-
cache.getConfiguration().setCacheMode("LOCAL");
cache.start();
+
+ ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache);
+
+ cr.getComponent(InterceptorChainFactory.class).correctInterceptorChaining(interceptor);
}
@AfterMethod(alwaysRun = true)
Modified: core/trunk/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -44,7 +44,6 @@
@Test(groups = {"functional"})
abstract public class CacheLoaderTestsBase extends AbstractCacheLoaderTestBase
{
-
private static final Log log = LogFactory.getLog(CacheLoaderTestsBase.class);
CacheSPI<Object, Object> cache;
CacheLoader loader = null;
@@ -80,6 +79,7 @@
@AfterMethod(alwaysRun = true)
public void tearDown() throws Exception
{
+ log.warn("********* Starting tearDown()");
cleanup();
try
{
@@ -755,7 +755,11 @@
cache.evict(Fqn.fromString("/a"));
// now load the children - this set childrenLoaded in /a/b to true
- cache.getNode("/a/b").getChildrenNames();
+// cache.getNode("/a/b").getChildrenNames();
+
+ NodeSPI n = (NodeSPI) cache.getNode("/a/b");
+ assert !n.isChildrenLoaded();
+
children = cache.getNode("/a/b").getChildrenNames();
assertEquals(3, children.size());
Modified: core/trunk/src/test/java/org/jboss/cache/loader/UnnecessaryLoadingTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/loader/UnnecessaryLoadingTest.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/test/java/org/jboss/cache/loader/UnnecessaryLoadingTest.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -48,7 +48,7 @@
cache.getCacheLoaderManager().setCacheLoader(mockCacheLoader);
// re-set the cache so that the mock CL is registered with the inerceptors.
- InterceptorChainFactory.getInstance().correctInterceptorChaining(cache.getInterceptorChain(), cache.getConfiguration(), TestingUtil.extractComponentRegistry(cache));
+ TestingUtil.extractComponentRegistry(cache).getComponent(InterceptorChainFactory.class).correctInterceptorChaining(cache.getInterceptorChain());
// lifecycle stuff
mockCacheLoader.stop();
Modified: core/trunk/src/test/java/org/jboss/cache/misc/TestingUtil.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/misc/TestingUtil.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/test/java/org/jboss/cache/misc/TestingUtil.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -94,7 +94,7 @@
System.out.println("Old interceptor chain: " + CachePrinter.printCacheInterceptors(cache));
// make sure the new interceptor is wired
ComponentRegistry cr = extractComponentRegistry(cache);
- cr.wireDependencies(interceptorToInject);
+ cr.registerComponent(interceptorToInject);
interceptorToInject.setCache(cache);
int index = -1;
for (Interceptor i : cache.getInterceptorChain())
@@ -527,13 +527,15 @@
ComponentRegistry cr = extractComponentRegistry(cache);
// This will replace the previous interceptor chain in the component registry
- cr.registerComponent(Interceptor.class, interceptor);
+ cr.registerComponent(Interceptor.class.getName(), interceptor);
// update all component dependencies
cr.updateDependencies();
+ InterceptorChainFactory factory = cr.getComponent(InterceptorChainFactory.class);
+
// make sure the new interceptors have their deps satisfied.
- InterceptorChainFactory.getInstance().correctInterceptorChaining(interceptor, cache.getConfiguration(), cr);
+ factory.correctInterceptorChaining(interceptor);
}
/**
Modified: core/trunk/src/test/java/org/jboss/cache/passivation/PassivationToDummyInMemoryCacheLoaderTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/passivation/PassivationToDummyInMemoryCacheLoaderTest.java 2007-12-18 15:03:42 UTC (rev 4871)
+++ core/trunk/src/test/java/org/jboss/cache/passivation/PassivationToDummyInMemoryCacheLoaderTest.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -2,7 +2,9 @@
import org.jboss.cache.config.CacheLoaderConfig;
import org.jboss.cache.loader.DummyInMemoryCacheLoader;
+import org.testng.annotations.Test;
+ at Test(groups = {"functional"})
public class PassivationToDummyInMemoryCacheLoaderTest extends PassivationTestsBase
{
protected void configureCache() throws Exception
Copied: core/trunk/src/test/java/org/jboss/cache/util/reflect/ClasspathScannerTest.java (from rev 4836, core/trunk/src/test/java/org/jboss/cache/factories/annotations/ClasspathScannerTest.java)
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/util/reflect/ClasspathScannerTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/util/reflect/ClasspathScannerTest.java 2007-12-18 19:58:10 UTC (rev 4872)
@@ -0,0 +1,27 @@
+package org.jboss.cache.util.reflect;
+
+import org.jboss.cache.factories.CacheMarshallerFactory;
+import org.jboss.cache.factories.ComponentFactory;
+import org.jboss.cache.factories.TransactionManagerFactory;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+import org.testng.annotations.Test;
+
+import java.util.Set;
+
+/**
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Test(groups = {"functional"})
+public class ClasspathScannerTest
+{
+ public void testScanning()
+ {
+ ClasspathScanner cs = new ClasspathScanner();
+ Set<Class<? extends ComponentFactory>> sc = cs.scan(DefaultFactoryFor.class, ComponentFactory.class);
+
+ // should at least contain these 2.
+ assert sc.contains(TransactionManagerFactory.class);
+ assert sc.contains(CacheMarshallerFactory.class);
+ }
+}
More information about the jbosscache-commits
mailing list