[jbosscache-commits] JBoss Cache SVN: r4832 - in core/trunk/src/main/java/org/jboss/cache: factories and 2 other directories.
jbosscache-commits at lists.jboss.org
jbosscache-commits at lists.jboss.org
Tue Dec 11 12:25:09 EST 2007
Author: manik.surtani at jboss.com
Date: 2007-12-11 12:25:09 -0500 (Tue, 11 Dec 2007)
New Revision: 4832
Added:
core/trunk/src/main/java/org/jboss/cache/factories/BuddyManagerFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/CacheLoaderConfigFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/CacheMarshallerFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/ComponentFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/ComponentRegistry.java
core/trunk/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/LockTableFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/RuntimeConfigAwareFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/TransactionManagerFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/annotations/
core/trunk/src/main/java/org/jboss/cache/factories/annotations/ClasspathScanner.java
core/trunk/src/main/java/org/jboss/cache/factories/annotations/ComponentName.java
core/trunk/src/main/java/org/jboss/cache/factories/annotations/DefaultFactoryFor.java
core/trunk/src/main/java/org/jboss/cache/factories/annotations/Inject.java
core/trunk/src/main/java/org/jboss/cache/invocation/
core/trunk/src/main/java/org/jboss/cache/invocation/AbstractInvocationDelegate.java
core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java
core/trunk/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java
core/trunk/src/main/java/org/jboss/cache/invocation/RemoteCacheInvocationDelegate.java
Modified:
core/trunk/src/main/java/org/jboss/cache/DefaultCacheFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java
core/trunk/src/main/java/org/jboss/cache/factories/XmlConfigurationParser.java
Log:
Initial check in of injection and aop code
Modified: core/trunk/src/main/java/org/jboss/cache/DefaultCacheFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/DefaultCacheFactory.java 2007-12-11 17:19:55 UTC (rev 4831)
+++ core/trunk/src/main/java/org/jboss/cache/DefaultCacheFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -6,41 +6,50 @@
*/
package org.jboss.cache;
-import org.jboss.beans.metadata.api.annotations.Inject;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.factories.ComponentFactory;
+import org.jboss.cache.factories.ComponentRegistry;
import org.jboss.cache.factories.XmlConfigurationParser;
-import org.jboss.kernel.Kernel;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+import org.jboss.cache.invocation.CacheInvocationDelegate;
import java.io.InputStream;
/**
* Default (singleton) implementation of the {@link org.jboss.cache.CacheFactory} interface.
* Use {@link #getInstance()} to obtain an instance.
+ * <p/>
+ * 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.
*
* @author <a href="mailto:manik at jboss.org">Manik Surtani (manik at jboss.org)</a>
+ * @see org.jboss.cache.factories.ComponentFactory
*/
-public class DefaultCacheFactory<K, V> implements CacheFactory<K, V>
+ at DefaultFactoryFor(classes = {CacheSPI.class})
+public class DefaultCacheFactory<K, V> extends ComponentFactory implements CacheFactory<K, V>
{
- private static CacheFactory<?, ?> singleton = new DefaultCacheFactory();
- private Kernel kernel;
+ private volatile static CacheFactory<?, ?> singleton = new DefaultCacheFactory();
- public DefaultCacheFactory()
+ protected DefaultCacheFactory()
{
- this(new Kernel());
}
- public DefaultCacheFactory(@Inject Kernel kernel)
- {
- this.kernel = kernel;
- }
-
/**
* @return a singleton instance of this class.
*/
@SuppressWarnings("unchecked")
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;
}
@@ -90,8 +99,7 @@
{
try
{
- CacheImpl<K, V> cache = new CacheImpl<K, V>();
- cache.setConfiguration(configuration);
+ CacheSPI<K, V> cache = createAndWire(configuration);
if (start) cache.start();
return cache;
}
@@ -109,6 +117,45 @@
}
}
+ protected CacheSPI<K, V> createAndWire(Configuration configuration) throws Exception
+ {
+ CacheImpl cache = new CacheImpl(configuration);
+ CacheSPI<K, V> spi = new CacheInvocationDelegate<K, V>();
+
+ bootstrap(cache, spi, configuration);
+
+ // wire dependencies into cache
+ componentRegistry.wireDependencies(cache);
+ // wire dependencies into SPI delegate
+ componentRegistry.wireDependencies(spi);
+
+ return spi;
+ }
+
+ /**
+ * Bootstraps this factory with a Configuration and a ComponentRegistry
+ *
+ * @param cache
+ * @param configuration
+ */
+ protected void bootstrap(CacheImpl cache, CacheSPI spi, Configuration configuration)
+ {
+ // 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);
+ }
+
public Cache<K, V> createCache(InputStream is) throws ConfigurationException
{
XmlConfigurationParser parser = new XmlConfigurationParser();
@@ -121,5 +168,10 @@
XmlConfigurationParser parser = new XmlConfigurationParser();
Configuration c = parser.parseStream(is);
return createCache(c, start);
- }
+ }
+
+ protected <T> T construct(String componentName, Class<T> componentType)
+ {
+ throw new UnsupportedOperationException("Should never be invoked - this is a bootstrap factory.");
+ }
}
Added: core/trunk/src/main/java/org/jboss/cache/factories/BuddyManagerFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/BuddyManagerFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/BuddyManagerFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,27 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+
+/**
+ * Buddy manager factory
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at DefaultFactoryFor(classes = {BuddyManager.class})
+public class BuddyManagerFactory extends EmptyConstructorFactory
+{
+ @Override
+ public <T> T construct(String componentName, Class<T> componentType)
+ {
+ if (configuration.getBuddyReplicationConfig() != null && configuration.getBuddyReplicationConfig().isEnabled())
+ {
+ return super.construct(componentName, componentType);
+ }
+ else
+ {
+ return null;
+ }
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/CacheLoaderConfigFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/CacheLoaderConfigFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/CacheLoaderConfigFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,21 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.config.CacheLoaderConfig;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+
+/**
+ * Factory class to create cache loader managers
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at DefaultFactoryFor(classes = {CacheLoaderConfig.class})
+public class CacheLoaderConfigFactory extends ComponentFactory
+{
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T> T construct(String componentName, Class<T> componentType)
+ {
+ return (T) configuration.getCacheLoaderConfig();
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/CacheMarshallerFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/CacheMarshallerFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/CacheMarshallerFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,52 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.RegionManager;
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+import org.jboss.cache.marshall.CacheMarshaller210;
+import org.jboss.cache.marshall.Marshaller;
+import org.jboss.cache.marshall.VersionAwareMarshaller;
+import org.jboss.cache.util.Util;
+
+/**
+ * Factory for creating marshallers
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at DefaultFactoryFor(classes = {Marshaller.class})
+public class CacheMarshallerFactory extends ComponentFactory
+{
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T> T construct(String componentName, Class<T> componentType)
+ {
+ assertTypeConstructable(componentType, Marshaller.class);
+
+ Marshaller m;
+
+ String marshallerClass = configuration.getMarshallerClass();
+
+ if (marshallerClass == null)
+ {
+ marshallerClass = CacheMarshaller210.class.getName();
+ log.trace("Cache marshaller implementation not specified; using default " + marshallerClass);
+ }
+
+ try
+ {
+ m = (Marshaller) Util.loadClass(marshallerClass).newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to instantiate marshaller of type " + marshallerClass, e);
+ }
+
+ componentRegistry.wireDependencies(m);
+
+ if (log.isDebugEnabled()) log.debug("Instantiated " + marshallerClass + "; wrapping in a VersionAwareMarshaller");
+
+ VersionAwareMarshaller vam = new VersionAwareMarshaller(componentRegistry.getOrCreateComponent(null, RegionManager.class), configuration, m);
+ return (T) vam;
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/ComponentFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/ComponentFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/ComponentFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,60 @@
+package org.jboss.cache.factories;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.factories.annotations.Inject;
+
+/**
+ * Factory that creates components used internally within JBoss Cache, and also wires dependencies into the components.
+ * <p/>
+ * The {@link org.jboss.cache.DefaultCacheFactory} is a special subclass of this, which bootstraps the construction of
+ * other components. When this class is loaded, it maintains a static list of known default factories for known
+ * components, which it then delegates to, when actually performing the construction.
+ * <p/>
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @see Inject
+ * @see ComponentRegistry
+ * @since 2.1.0
+ */
+public abstract class ComponentFactory
+{
+ protected final Log log = LogFactory.getLog(getClass());
+ protected ComponentRegistry componentRegistry;
+ protected Configuration configuration;
+
+ @Inject
+ private void injectDependencies(Configuration configuration, ComponentRegistry componentRegistry)
+ {
+ this.configuration = configuration;
+ this.componentRegistry = componentRegistry;
+ }
+
+ /**
+ * Constructs a component.
+ *
+ * @param componentName optional, if the ComponentName annotation is used with the parameter, it's value is passed in here for the implementing factory to use.
+ * @param componentType type of component
+ * @param configuration configuration to use
+ * @param componentRegistry component registry to use
+ * @return a component
+ */
+ protected abstract <T> T construct(String componentName, Class<T> componentType);
+// {
+// // not making this method abstract since it is valid that classes extend ComponentFactory for the wiring and injection
+// // but not for component creation.
+// throw new UnsupportedOperationException("Meant to be overridden!");
+// }
+
+ protected void assertTypeConstructable(Class requestedType, Class... ableToConstruct)
+ {
+ boolean canConstruct = false;
+ for (Class c : ableToConstruct)
+ {
+ canConstruct = canConstruct || requestedType.isAssignableFrom(c);
+ }
+ if (!canConstruct) throw new ConfigurationException("Don't know how to construct " + requestedType);
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/ComponentRegistry.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/ComponentRegistry.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/ComponentRegistry.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,502 @@
+package org.jboss.cache.factories;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+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 org.jboss.cache.factories.annotations.ComponentName;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+import org.jboss.cache.factories.annotations.Inject;
+import org.jboss.cache.util.BeanUtils;
+
+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;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A registry where components which have been created are stored. Components are stored as singletons, registered under
+ * a specific name. When retrieving components from the registry, callers may specify a component name or just the type
+ * of component needed (in which case the fully qualified class name is used as the component name).
+ * <p/>
+ * 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.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+public class ComponentRegistry
+{
+ /**
+ * 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.
+ */
+ private Map<Object, Object> registry = new HashMap<Object, Object>();
+ /**
+ * Contains class definitions of component factories that can be used to construct certain components
+ */
+ Map<Class, Class<? extends ComponentFactory>> defaultFactories = null;
+
+ private static Log log = LogFactory.getLog(ComponentRegistry.class);
+
+ public ComponentRegistry(Configuration configuration)
+ {
+ // bootstrap.
+ registerComponent(configuration);
+ }
+
+ /**
+ * 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)}
+ *
+ * @param component component to add to the registry.
+ */
+ public void registerComponent(Object component)
+ {
+ registerComponent(component.getClass(), 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.
+ *
+ * @param name name of the instance
+ * @param component component to add
+ */
+ public void registerComponent(String name, Object component)
+ {
+ registry.put(name, component);
+ }
+
+ /**
+ * 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.
+ */
+ 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;
+ }
+
+ /**
+ * Retrieves a named 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.
+ */
+ public <T> T getComponent(String name, Class<T> c)
+ {
+ T component = (T) registry.get(c);
+ if (component == null) return null;
+ 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);
+ }
+
+ /**
+ * 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.
+ * @throws ConfigurationException if there is a problem with consructing or wiring the instance.
+ */
+ public <T> T getOrCreateComponent(String componentName, Class<T> componentClass)
+ {
+ T component = (componentName == null) ? getComponent(componentClass) : getComponent(componentName, componentClass);
+
+ if (component == null)
+ {
+ // first see if this has been injected externally.
+ component = getFromConfiguration(componentClass);
+
+ if (component == null)
+ {
+ // create this component and add it to the registry
+ ComponentFactory factory = getFactory(componentClass);
+ component = factory.construct(componentName, componentClass);
+ }
+
+ if (component != null)
+ {
+ // wire dependencies
+ wireDependencies(component);
+ if (componentName == null)
+ registerComponent(componentClass, component);
+ else
+ registerComponent(componentName, component);
+ }
+ }
+ return component;
+ }
+
+ @SuppressWarnings("unchecked")
+ private <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);
+ T returnValue = null;
+
+ if (getter != null)
+ {
+ try
+ {
+ returnValue = (T) getter.invoke(getConfiguration());
+ }
+ catch (Exception e)
+ {
+ log.warn("Unable to invoke getter " + getter + " on Configuration.class!", e);
+ }
+ }
+
+ // now try the RuntimeConfig - a legacy "registry" of sorts.
+ if (returnValue == null)
+ {
+ getter = BeanUtils.getterMethod(RuntimeConfig.class, componentClass);
+ if (getter != null)
+ {
+ try
+ {
+ returnValue = (T) getter.invoke(getConfiguration().getRuntimeConfig());
+ }
+ catch (Exception e)
+ {
+ log.warn("Unable to invoke getter " + getter + " on RuntimeConfig.class!", e);
+ }
+ }
+ }
+ return returnValue;
+ }
+
+ Configuration getConfiguration()
+ {
+ // this is assumed to always be present as a part of the bootstrap/construction of a ComponentRegistry.
+ return getComponent(Configuration.class);
+ }
+
+ /**
+ * Updates (re-injects) any dependencies needed by all components already in the registry.
+ */
+ 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 components = new HashSet(registry.values());
+
+ for (Object component : components)
+ {
+ wireDependencies(component);
+ }
+ }
+
+ /**
+ * Removes a component from the registry. If the component has already been injected into other components, you should
+ * call {@link #updateDependencies()} to ensure dependencies are updated.
+ *
+ * @param clazz class of component to remove.
+ */
+ public void unregisterComponent(Class<?> clazz)
+ {
+ registry.remove(clazz);
+ }
+
+ /**
+ * Removes a component from the registry. If the component has already been injected into other components, you should
+ * call {@link #updateDependencies()} to ensure dependencies are updated.
+ *
+ * @param name name of the component to remove.
+ */
+ public void unregisterComponent(String name)
+ {
+ registry.remove(name);
+ }
+
+ /**
+ * 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}.
+ *
+ * @param target object to wire
+ * @param configuration Configuration object with knowledge of what components to create and how to create them
+ * @throws ConfigurationException if there is a problem wiring the instance
+ */
+ public void wireDependencies(Object target) throws ConfigurationException
+ {
+ if (log.isTraceEnabled()) log.trace("Inspecting class " + target.getClass());
+ try
+ {
+ List<Method> methods = getAllMethods(target.getClass());
+ if (log.isTraceEnabled())
+ log.trace("Found method set containing " + methods.size() + " methods that need injection: " + methods);
+
+ // search for anything we need to inject
+ for (Method method : methods)
+ {
+ if (log.isTraceEnabled()) log.trace("Method " + method + " needs some other components injected!");
+ performInjection(method, target);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to configure component (type: " + target.getClass() + ", instance " + target + ")", e);
+ }
+ }
+
+ /**
+ * Looks through the parameter list of the given method, attempts to locate parameters that fit the types that may
+ * exist in the {@link ComponentRegistry}, and then calls the method on the target object with the necessary parameters.
+ *
+ * @param method Method to call
+ * @param target Instance on which to call the method
+ * @param configuration contains details of how to create components if they do not already exist.
+ * @throws IllegalAccessException if the method cannot be called
+ * @throws java.lang.reflect.InvocationTargetException
+ * if the method cannot be called
+ */
+ @SuppressWarnings("unchecked")
+ private <T> void performInjection(Method method, T target) throws IllegalAccessException, InvocationTargetException
+ {
+ Class[] parameterTypes = method.getParameterTypes();
+ Annotation[][] annotationsOnParams = method.getParameterAnnotations();
+
+ 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]);
+ }
+
+ // make sure we set this method to be accessible, so we can call private, package and protected
+ // methods rather than just public ones.
+ method.setAccessible(true);
+
+ // invoke the method with the parameters we've worked out.
+ method.invoke(target, parameters);
+ }
+
+ private String extractComponentName(Annotation[] annotationsOnParameter)
+ {
+ for (Annotation a : annotationsOnParameter)
+ {
+ if (a instanceof ComponentName)
+ {
+ ComponentName cn = (ComponentName) a;
+ return cn.value();
+ }
+ }
+ 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
+ * @return Set of Method objects that require injection.
+ */
+ private List<Method> getAllMethods(Class c)
+ {
+ List<Method> annotated = new LinkedList<Method>();
+ inspectRecursively(c, annotated);
+ return annotated;
+ }
+
+ private void inspectRecursively(Class c, List<Method> s)
+ {
+ // Superclass first
+ if (!c.equals(Object.class)) inspectRecursively(c.getSuperclass(), s);
+
+ for (Method m : c.getDeclaredMethods())
+ {
+ // don't bother if this method has already been overridden by a subclass
+ if (!alreadyFound(m, s) && m.isAnnotationPresent(Inject.class))
+ {
+ s.add(m);
+ }
+ }
+ }
+
+ 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.
+ *
+ * @param componentClass type of component to construct
+ * @return component factory capable of constructing such components
+ */
+ protected ComponentFactory getFactory(Class componentClass)
+ {
+ if (defaultFactories == null) scanDefaultFactories();
+ Class<? extends ComponentFactory> cfClass = defaultFactories.get(componentClass);
+
+ // a component factory is a component too! See if one has been created and exists in the registry
+ ComponentFactory cf = getComponent(cfClass);
+ if (cf == null)
+ {
+ // hasn't yet been created. Create and put in registry
+ cf = instantiateFactory(cfClass);
+ if (cf != null)
+ {
+ // wire factory deps
+ wireDependencies(cf);
+ // register this factory
+ registerComponent(cf);
+ }
+ }
+
+ if (cf == null)
+ throw new ConfigurationException("Unable to locate component factory for component " + componentClass);
+ return cf;
+ }
+
+ /**
+ * Scans the class path for classes annotated with {@link org.jboss.cache.factories.annotations.DefaultFactoryFor}, and
+ * analyses which components can be created by such factories.
+ */
+ void scanDefaultFactories()
+ {
+ defaultFactories = new HashMap<Class, Class<? extends ComponentFactory>>();
+
+ ClasspathScanner scanner = new ClasspathScanner();
+
+ Set<Class<? extends ComponentFactory>> factories = scanner.scan(DefaultFactoryFor.class, ComponentFactory.class);
+
+ for (Class<? extends ComponentFactory> factory : factories)
+ {
+ DefaultFactoryFor dFFAnnotation = factory.getAnnotation(DefaultFactoryFor.class);
+ for (Class targetClass : dFFAnnotation.classes()) defaultFactories.put(targetClass, factory);
+ }
+ }
+
+ /**
+ * 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.
+ *
+ * @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();
+ }
+ catch (NoSuchMethodException 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);
+ }
+ }
+
+ if (ctor == null && singletonAccessor == null)
+ {
+ // unable to get a hold of an instance!!
+ throw new ConfigurationException("Unable to instantiate factory " + factory + ": no public, default constructor or getInstance() static method!");
+ }
+
+ ComponentFactory instance;
+ try
+ {
+ if (ctor == null)
+ {
+ instance = (ComponentFactory) singletonAccessor.invoke(null);
+ }
+ else
+ {
+ instance = (ComponentFactory) ctor.newInstance();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to instantiate factory " + factory, e);
+ }
+
+ return instance;
+ }
+
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,43 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.RegionManager;
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+import org.jboss.cache.loader.CacheLoaderManager;
+import org.jboss.cache.notifications.Notifier;
+import org.jboss.cache.remoting.jgroups.CacheMessageListener;
+import org.jboss.cache.statetransfer.StateTransferManager;
+import org.jboss.cache.transaction.TransactionTable;
+
+/**
+ * Simple factory that just uses reflection and an empty constructor of the component type.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at DefaultFactoryFor(classes = {StateTransferManager.class, TransactionTable.class, RegionManager.class, Notifier.class, CacheMessageListener.class, CacheLoaderManager.class})
+public class EmptyConstructorFactory extends ComponentFactory
+{
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T> T construct(String componentName, Class<T> componentType)
+ {
+ try
+ {
+ if (componentType.isInterface())
+ {
+ // add an "Impl" to the end of the class name and try again
+ Class componentImpl = getClass().getClassLoader().loadClass(componentType.getName() + "Impl");
+ return (T) componentImpl.newInstance();
+ }
+ else
+ {
+ return componentType.newInstance();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to create component " + componentType, e);
+ }
+ }
+}
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-11 17:19:55 UTC (rev 4831)
+++ core/trunk/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -6,10 +6,10 @@
*/
package org.jboss.cache.factories;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.jboss.cache.CacheImpl;
-import org.jboss.cache.CacheSPI;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
import org.jboss.cache.interceptors.*;
import org.jboss.cache.loader.CacheLoaderManager;
import org.jboss.cache.util.Util;
@@ -22,9 +22,9 @@
*
* @author <a href="mailto:manik at jboss.org">Manik Surtani (manik at jboss.org)</a>
*/
-public class InterceptorChainFactory
+ at DefaultFactoryFor(classes = Interceptor.class)
+public class InterceptorChainFactory extends ComponentFactory
{
- private Log log = LogFactory.getLog(InterceptorChainFactory.class);
private static InterceptorChainFactory singleton;
private InterceptorChainFactory()
@@ -46,15 +46,15 @@
}
- public Interceptor buildInterceptorChain(CacheImpl cache) throws IllegalAccessException, ClassNotFoundException, InstantiationException
+ public Interceptor buildInterceptorChain() throws IllegalAccessException, ClassNotFoundException, InstantiationException
{
- if (cache.getConfiguration().isNodeLockingOptimistic())
+ if (configuration.isNodeLockingOptimistic())
{
- return createOptimisticInterceptorChain(cache);
+ return createOptimisticInterceptorChain();
}
else
{
- return createPessimisticInterceptorChain(cache);
+ return createPessimisticInterceptorChain();
}
}
@@ -70,17 +70,18 @@
}
- private Interceptor createInterceptor(String classname, CacheSPI cache) throws ClassNotFoundException, IllegalAccessException, InstantiationException
+ @SuppressWarnings("unchecked")
+ private Interceptor createInterceptor(String classname) throws ClassNotFoundException, IllegalAccessException, InstantiationException
{
Class<Interceptor> clazz = Util.loadClass(classname);
- return createInterceptor(clazz, cache);
+ return createInterceptor(clazz);
}
- private Interceptor createInterceptor(Class<? extends Interceptor> clazz, CacheSPI cache) throws IllegalAccessException, InstantiationException
+ private Interceptor createInterceptor(Class<? extends Interceptor> clazz) throws IllegalAccessException, InstantiationException
{
Interceptor i = clazz.newInstance();
- i.setCache(cache);
- i.setStatisticsEnabled(cache.getConfiguration().getExposeManagementStatistics());
+ componentRegistry.wireDependencies(i);
+ i.setStatisticsEnabled(configuration.getExposeManagementStatistics());
return i;
}
@@ -95,7 +96,7 @@
first.setNext(i);
}
- private Interceptor createPessimisticInterceptorChain(CacheImpl cache) throws IllegalAccessException, InstantiationException, ClassNotFoundException
+ private Interceptor createPessimisticInterceptorChain() throws IllegalAccessException, InstantiationException, ClassNotFoundException
{
Interceptor call_interceptor;
Interceptor lock_interceptor;
@@ -109,54 +110,54 @@
Interceptor txInterceptor;
Interceptor eviction_interceptor;
Interceptor dataGravitatorInterceptor = null;
- Interceptor invocationCtxInterceptor = createInterceptor(InvocationContextInterceptor.class, cache);
- Interceptor notificationInterceptor = createInterceptor(NotificationInterceptor.class, cache);
+ Interceptor invocationCtxInterceptor = createInterceptor(InvocationContextInterceptor.class);
+ Interceptor notificationInterceptor = createInterceptor(NotificationInterceptor.class);
Interceptor first = null;
- call_interceptor = createInterceptor(CallInterceptor.class, cache);
- ((CallInterceptor) call_interceptor).setTreeCacheInstance(cache);
+ call_interceptor = createInterceptor(CallInterceptor.class);
+ //((CallInterceptor) call_interceptor).setTreeCacheInstance();
- if (cache.getBuddyManager() != null)
+ if (componentRegistry.getOrCreateComponent(null, BuddyManager.class) != null)
{
- dataGravitatorInterceptor = createInterceptor(DataGravitatorInterceptor.class, cache);
+ dataGravitatorInterceptor = createInterceptor(DataGravitatorInterceptor.class);
}
- lock_interceptor = createInterceptor(PessimisticLockInterceptor.class, cache);
+ lock_interceptor = createInterceptor(PessimisticLockInterceptor.class);
- unlock_interceptor = createInterceptor(UnlockInterceptor.class, cache);
+ unlock_interceptor = createInterceptor(UnlockInterceptor.class);
- cacheMgmtInterceptor = createInterceptor(CacheMgmtInterceptor.class, cache);
+ cacheMgmtInterceptor = createInterceptor(CacheMgmtInterceptor.class);
- txInterceptor = createInterceptor(TxInterceptor.class, cache);
+ txInterceptor = createInterceptor(TxInterceptor.class);
- switch (cache.getConfiguration().getCacheMode())
+ switch (configuration.getCacheMode())
{
case REPL_SYNC:
case REPL_ASYNC:
- repl_interceptor = createInterceptor(ReplicationInterceptor.class, cache);
+ repl_interceptor = createInterceptor(ReplicationInterceptor.class);
break;
case INVALIDATION_SYNC:
case INVALIDATION_ASYNC:
- repl_interceptor = createInterceptor(InvalidationInterceptor.class, cache);
+ repl_interceptor = createInterceptor(InvalidationInterceptor.class);
break;
case LOCAL:
//Nothing...
}
- CacheLoaderManager cacheLoaderMgr = cache.getCacheLoaderManager();
+ CacheLoaderManager cacheLoaderMgr = componentRegistry.getOrCreateComponent(null, CacheLoaderManager.class);
if (cacheLoaderMgr != null && cacheLoaderMgr.getCacheLoader() != null)
{
if (cacheLoaderMgr.isPassivation())
{
- activation_interceptor = createInterceptor(ActivationInterceptor.class, cache);
- passivation_interceptor = createInterceptor(PassivationInterceptor.class, cache);
+ activation_interceptor = createInterceptor(ActivationInterceptor.class);
+ passivation_interceptor = createInterceptor(PassivationInterceptor.class);
}
else
{
- cache_loader_interceptor = createInterceptor(CacheLoaderInterceptor.class, cache);
- cache_store_interceptor = createInterceptor(CacheStoreInterceptor.class, cache);
+ cache_loader_interceptor = createInterceptor(CacheLoaderInterceptor.class);
+ cache_store_interceptor = createInterceptor(CacheStoreInterceptor.class);
}
}
@@ -164,7 +165,7 @@
if (first == null) first = invocationCtxInterceptor;
// load the cache management interceptor next
- if (cache.getConfiguration().getExposeManagementStatistics())
+ if (configuration.getExposeManagementStatistics())
{
if (first == null)
{
@@ -191,37 +192,6 @@
else
addInterceptor(first, notificationInterceptor);
- // create the stack from the bottom up
-// if (activation_interceptor != null)
-// {
-// if (!cacheLoaderMgr.isFetchPersistentState())
-// {
-// if (first == null)
-// {
-// first = passivation_interceptor;
-// }
-// else
-// {
-// addInterceptor(first, passivation_interceptor);
-// }
-// }
-// }
-//
-// if (cache_loader_interceptor != null)
-// {
-// if (!cacheLoaderMgr.isFetchPersistentState())
-// {
-// if (first == null)
-// {
-// first = cache_store_interceptor;
-// }
-// else
-// {
-// addInterceptor(first, cache_store_interceptor);
-// }
-// }
-// }
-
if (repl_interceptor != null)
{
if (first == null)
@@ -258,72 +228,42 @@
if (activation_interceptor != null)
{
-// if (!cacheLoaderMgr.isFetchPersistentState())
- if (false)
+ if (first == null)
{
- if (first == null)
- {
- first = activation_interceptor;
- }
- else
- {
- addInterceptor(first, activation_interceptor);
- }
+ first = activation_interceptor;
}
else
{
- if (first == null)
- {
- first = activation_interceptor;
- }
- else
- {
- addInterceptor(first, activation_interceptor);
- }
- if (first == null)
- {
- first = passivation_interceptor;
- }
- else
- {
- addInterceptor(first, passivation_interceptor);
- }
+ addInterceptor(first, activation_interceptor);
}
+ if (first == null)
+ {
+ first = passivation_interceptor;
+ }
+ else
+ {
+ addInterceptor(first, passivation_interceptor);
+ }
}
if (cache_loader_interceptor != null)
{
-// if (!cacheLoaderMgr.isFetchPersistentState())
- if (false)
+ if (first == null)
{
- if (first == null)
- {
- first = cache_loader_interceptor;
- }
- else
- {
- addInterceptor(first, cache_loader_interceptor);
- }
+ first = cache_loader_interceptor;
}
else
{
- if (first == null)
- {
- first = cache_loader_interceptor;
- }
- else
- {
- addInterceptor(first, cache_loader_interceptor);
- }
- if (first == null)
- {
- first = cache_store_interceptor;
- }
- else
- {
- addInterceptor(first, cache_store_interceptor);
- }
+ addInterceptor(first, cache_loader_interceptor);
}
+ if (first == null)
+ {
+ first = cache_store_interceptor;
+ }
+ else
+ {
+ addInterceptor(first, cache_store_interceptor);
+ }
}
if (dataGravitatorInterceptor != null)
@@ -337,19 +277,10 @@
addInterceptor(first, dataGravitatorInterceptor);
}
}
-//
-// if (first == null)
-// {
-// first = lock_interceptor;
-// }
-// else
-// {
-// addInterceptor(first, lock_interceptor);
-// }
- if (cache.getConfiguration().getEvictionConfig() != null && cache.getConfiguration().getEvictionConfig().isValidConfig())
+ if (configuration.getEvictionConfig() != null && configuration.getEvictionConfig().isValidConfig())
{
- eviction_interceptor = createInterceptor(cache.getEvictionInterceptorClass(), cache);
+ eviction_interceptor = createInterceptor(EvictionInterceptor.class);
if (first == null)
{
first = eviction_interceptor;
@@ -377,72 +308,71 @@
return setLastInterceptorPointer(first, call_interceptor);
}
- private Interceptor createOptimisticInterceptorChain(CacheImpl cache) throws IllegalAccessException, InstantiationException, ClassNotFoundException
+ private Interceptor createOptimisticInterceptorChain() throws IllegalAccessException, InstantiationException, ClassNotFoundException
{
Interceptor txInterceptor, replicationInterceptor = null, lockInterceptor, validationInterceptor;
Interceptor createIfNotExistsInterceptor, nodeInterceptor, invokerInterceptor, activationInterceptor = null;
Interceptor passivationInterceptor = null, cacheLoaderInterceptor = null, cacheStoreInterceptor = null, first = null;
Interceptor cacheMgmtInterceptor, evictionInterceptor = null, dataGravitatorInterceptor = null;
- Interceptor invocationCtxInterceptor = createInterceptor(InvocationContextInterceptor.class, cache);
- Interceptor notificationInterceptor = createInterceptor(NotificationInterceptor.class, cache);
+ Interceptor invocationCtxInterceptor = createInterceptor(InvocationContextInterceptor.class);
+ Interceptor notificationInterceptor = createInterceptor(NotificationInterceptor.class);
- CacheLoaderManager cacheLoaderManager = cache.getCacheLoaderManager();
+ CacheLoaderManager cacheLoaderManager = componentRegistry.getOrCreateComponent(null, CacheLoaderManager.class);
if (cacheLoaderManager != null && cacheLoaderManager.getCacheLoader() != null)
{
if (cacheLoaderManager.isPassivation())
{
- activationInterceptor = createInterceptor(ActivationInterceptor.class, cache);
- passivationInterceptor = createInterceptor(PassivationInterceptor.class, cache);
+ activationInterceptor = createInterceptor(ActivationInterceptor.class);
+ passivationInterceptor = createInterceptor(PassivationInterceptor.class);
}
else
{
- cacheLoaderInterceptor = createInterceptor(CacheLoaderInterceptor.class, cache);
- cacheStoreInterceptor = createInterceptor(CacheStoreInterceptor.class, cache);
+ cacheLoaderInterceptor = createInterceptor(CacheLoaderInterceptor.class);
+ cacheStoreInterceptor = createInterceptor(CacheStoreInterceptor.class);
}
}
- txInterceptor = createInterceptor(TxInterceptor.class, cache);
+ txInterceptor = createInterceptor(TxInterceptor.class);
- if (cache.getBuddyManager() != null)
+ if (componentRegistry.getOrCreateComponent(null, BuddyManager.class) != null)
{
- dataGravitatorInterceptor = createInterceptor(DataGravitatorInterceptor.class, cache);
+ dataGravitatorInterceptor = createInterceptor(DataGravitatorInterceptor.class);
}
- switch (cache.getConfiguration().getCacheMode())
+ switch (configuration.getCacheMode())
{
case REPL_SYNC:
case REPL_ASYNC:
- replicationInterceptor = createInterceptor(OptimisticReplicationInterceptor.class, cache);
+ replicationInterceptor = createInterceptor(OptimisticReplicationInterceptor.class);
break;
case INVALIDATION_SYNC:
case INVALIDATION_ASYNC:
- replicationInterceptor = createInterceptor(InvalidationInterceptor.class, cache);
+ replicationInterceptor = createInterceptor(InvalidationInterceptor.class);
break;
case LOCAL:
//Nothing...
}
- lockInterceptor = createInterceptor(OptimisticLockingInterceptor.class, cache);
+ lockInterceptor = createInterceptor(OptimisticLockingInterceptor.class);
- validationInterceptor = createInterceptor(OptimisticValidatorInterceptor.class, cache);
+ validationInterceptor = createInterceptor(OptimisticValidatorInterceptor.class);
- createIfNotExistsInterceptor = createInterceptor(OptimisticCreateIfNotExistsInterceptor.class, cache);
+ createIfNotExistsInterceptor = createInterceptor(OptimisticCreateIfNotExistsInterceptor.class);
- nodeInterceptor = createInterceptor(OptimisticNodeInterceptor.class, cache);
+ nodeInterceptor = createInterceptor(OptimisticNodeInterceptor.class);
- invokerInterceptor = createInterceptor(CallInterceptor.class, cache);
- ((CallInterceptor) invokerInterceptor).setTreeCacheInstance(cache);
+ invokerInterceptor = createInterceptor(CallInterceptor.class);
- if (cache.getConfiguration().getEvictionConfig() != null && cache.getConfiguration().getEvictionConfig().isValidConfig())
+ if (configuration.getEvictionConfig() != null && configuration.getEvictionConfig().isValidConfig())
{
- evictionInterceptor = createInterceptor(cache.getEvictionInterceptorClass(), cache);
+ evictionInterceptor = createInterceptor(EvictionInterceptor.class);
}
if (first == null) first = invocationCtxInterceptor;
- if (cache.getConfiguration().getExposeManagementStatistics())
+ if (configuration.getExposeManagementStatistics())
{
- cacheMgmtInterceptor = createInterceptor(CacheMgmtInterceptor.class, cache);
+ cacheMgmtInterceptor = createInterceptor(CacheMgmtInterceptor.class);
if (first == null)
{
first = cacheMgmtInterceptor;
@@ -669,17 +599,41 @@
}
/**
- * "Fixes" the next() and last() pointers for each interceptor, based on the order presented in the list passed in
+ * The same as {@link #correctInterceptorChaining(java.util.List, org.jboss.cache.config.Configuration, ComponentRegistry)}
+ * 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 interceptors
+ * @param first first interceptor in the chain to correct.
+ * @param configuration cache configuration
+ * @param componentRegistry component registry
* @return the first interceptor in the chain.
*/
- public Interceptor correctInterceptorChaining(List<Interceptor> interceptors)
+ public Interceptor correctInterceptorChaining(Interceptor first, Configuration configuration, ComponentRegistry componentRegistry)
{
+ return correctInterceptorChaining(asList(first), configuration, componentRegistry);
+ }
+
+ /**
+ * "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
+ * @return the first interceptor in the chain.
+ */
+ public Interceptor correctInterceptorChaining(List<Interceptor> interceptors, Configuration configuration, ComponentRegistry componentRegistry)
+ {
Interceptor first = null, last = null;
for (Interceptor next : interceptors)
{
+ // re-wire
+ componentRegistry.wireDependencies(next);
+
if (first == null)
{
first = last = next;
@@ -699,11 +653,23 @@
* Initialises the interceptors with a CacheSPI instance. Essentially calls setCache(). This is done AFTER the interceptor chain
* is created, and when Cache.start() is called, so the interceptors get a fully initialised cache.
*
- * @param firstInterceptor interceptor chain
- * @param cache cache instance
+ * @param componentName
*/
- public void initialiseInterceptors(Interceptor firstInterceptor, CacheSPI cache)
+// public void initialiseInterceptors(Interceptor firstInterceptor, CacheSPI cache)
+// {
+// for (Interceptor i : asList(firstInterceptor)) i.setCache(cache);
+// }
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T> T construct(String componentName, Class<T> componentType)
{
- for (Interceptor i : asList(firstInterceptor)) i.setCache(cache);
+ try
+ {
+ return (T) buildInterceptorChain();
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to build interceptor chain", e);
+ }
}
}
Added: core/trunk/src/main/java/org/jboss/cache/factories/LockTableFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/LockTableFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/LockTableFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,31 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Dead simple class to create a lock factory
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at DefaultFactoryFor(classes = Map.class)
+public class LockTableFactory extends EmptyConstructorFactory
+{
+ private static final String LOCK_MAP_COMPONENT_NAME = "LockTable";
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T> T construct(String componentName, Class<T> componentType)
+ {
+ if (componentName.equals(LOCK_MAP_COMPONENT_NAME))
+ return (T) new HashMap();
+ else
+ {
+ log.warn("Unknown component name " + componentName);
+ return null;
+ }
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/RuntimeConfigAwareFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/RuntimeConfigAwareFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/RuntimeConfigAwareFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,41 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.NodeFactory;
+import org.jboss.cache.RPCManager;
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.config.RuntimeConfig;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+import org.jboss.cache.util.BeanUtils;
+
+import java.lang.reflect.Method;
+
+/**
+ * An extension of the EmptyConstructorFactory that places a component in the {@link RuntimeConfig} after creating it.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at DefaultFactoryFor(classes = {RPCManager.class, NodeFactory.class})
+public class RuntimeConfigAwareFactory extends EmptyConstructorFactory
+{
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T> T construct(String componentName, Class<T> componentType)
+ {
+ T component = super.construct(componentName, componentType);
+
+ Method setter = BeanUtils.setterMethod(RuntimeConfig.class, componentType);
+ if (setter != null)
+ {
+ try
+ {
+ setter.invoke(configuration.getRuntimeConfig(), component);
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to put newly constructed component of type " + componentType + " in the RuntimeConfig", e);
+ }
+ }
+ return component;
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/TransactionManagerFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/TransactionManagerFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/TransactionManagerFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,69 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+import org.jboss.cache.transaction.TransactionManagerLookup;
+
+import javax.transaction.TransactionManager;
+
+/**
+ * Uses a number of mechanisms to retrieve a transaction manager.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at DefaultFactoryFor(classes = {TransactionManager.class})
+public class TransactionManagerFactory extends ComponentFactory
+{
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T> T construct(String componentName, Class<T> componentType)
+ {
+ // See if we had a TransactionManager injected into our config
+ TransactionManager transactionManager = configuration.getRuntimeConfig().getTransactionManager();
+ TransactionManagerLookup lookup = null;
+
+ if (transactionManager == null)
+ {
+ // Nope. See if we can look it up from JNDI
+ if (configuration.getTransactionManagerLookupClass() != null)
+ {
+ try
+ {
+ Class clazz = Thread.currentThread().getContextClassLoader().loadClass(configuration.getTransactionManagerLookupClass());
+ lookup = (TransactionManagerLookup) clazz.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Problems looking up transaction manager", e);
+ }
+ }
+
+ try
+ {
+ if (lookup != null)
+ {
+ transactionManager = lookup.getTransactionManager();
+ configuration.getRuntimeConfig().setTransactionManager(transactionManager);
+ }
+ else
+ {
+ if (configuration.getNodeLockingScheme() == Configuration.NodeLockingScheme.OPTIMISTIC)
+ {
+ log.fatal("No transaction manager lookup class has been defined. Transactions cannot be used and thus OPTIMISTIC locking cannot be used");
+ }
+ else
+ {
+ log.info("No transaction manager lookup class has been defined. Transactions cannot be used");
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ log.info("failed looking up TransactionManager, will not use transactions", e);
+ }
+ }
+ return (T) transactionManager;
+ }
+}
Modified: core/trunk/src/main/java/org/jboss/cache/factories/XmlConfigurationParser.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/XmlConfigurationParser.java 2007-12-11 17:19:55 UTC (rev 4831)
+++ core/trunk/src/main/java/org/jboss/cache/factories/XmlConfigurationParser.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -20,8 +20,8 @@
import org.jboss.cache.config.EvictionRegionConfig;
import org.jboss.cache.config.MissingPolicyException;
import org.jboss.cache.eviction.EvictionPolicy;
+import org.jboss.cache.util.BeanUtils;
import org.jboss.cache.util.Util;
-import org.jboss.cache.util.BeanUtils;
import org.jboss.cache.xml.XmlHelper;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -89,9 +89,10 @@
/**
* Parses an input stream containing XML text and returns a new configuration.
+ *
* @param stream input stream to parse. SHould not be null.
+ * @return a configured Configuration object representing the configuration in the stream
* @since 2.1.0
- * @return a configured Configuration object representing the configuration in the stream
*/
public Configuration parseStream(InputStream stream)
{
@@ -101,7 +102,7 @@
return parseConfiguration(mbeanElement);
}
-
+
public Configuration parseConfiguration(Element configurationRoot)
{
ParsedAttributes attributes = extractAttributes(configurationRoot);
@@ -117,7 +118,7 @@
// Special handling for XML elements -- we hard code the parsing
setXmlValues(c, attributes.xmlAttribs);
- return c;
+ return c;
}
/**
@@ -139,23 +140,23 @@
if (evictionPolicy != null)
{
throw new ConfigurationException("XmlConfigurationParser does not " +
- "support the JBC 1.x attribute EvictionPolicyClass. Set the default " +
- "eviction policy via the policyClass element in the EvictionConfig section");
+ "support the JBC 1.x attribute EvictionPolicyClass. Set the default " +
+ "eviction policy via the policyClass element in the EvictionConfig section");
}
String multiplexerService = attributes.stringAttribs.remove("MultiplexerService");
if (multiplexerService != null)
{
throw new ConfigurationException("XmlConfigurationParser does not " +
- "support the JBC 1.x attribute MultiplexerService. Inject the " +
- "multiplexer directly using Configuration.getRuntimeConfig().setMuxChannelFactory()");
+ "support the JBC 1.x attribute MultiplexerService. Inject the " +
+ "multiplexer directly using Configuration.getRuntimeConfig().setMuxChannelFactory()");
}
String serviceName = attributes.stringAttribs.remove("ServiceName");
if (serviceName != null)
{
log.warn("XmlConfigurationParser does not support the deprecated " +
- "attribute ServiceName. If JMX registration is needed, " +
- "register a CacheJmxWrapper or PojoCacheJmxWrapper in " +
- "JMX with the desired name");
+ "attribute ServiceName. If JMX registration is needed, " +
+ "register a CacheJmxWrapper or PojoCacheJmxWrapper in " +
+ "JMX with the desired name");
}
}
@@ -176,16 +177,16 @@
if (keepStats != null && attributes.stringAttribs.get("ExposeManagementStatistics") == null)
{
log.warn("Found non-existent JBC 1.x attribute 'UseInterceptorMbeans' and replaced " +
- "with 'ExposeManagementStatistics'. Please update your config " +
- "to use the new attribute name");
+ "with 'ExposeManagementStatistics'. Please update your config " +
+ "to use the new attribute name");
attributes.stringAttribs.put("ExposeManagementStatistics", keepStats);
}
Element clc = attributes.xmlAttribs.remove("CacheLoaderConfiguration");
if (clc != null && attributes.xmlAttribs.get("CacheLoaderConfig") == null)
{
log.warn("Found non-existent JBC 1.x attribute 'CacheLoaderConfiguration' and replaced " +
- "with 'CacheLoaderConfig'. Please update your config " +
- "to use the new attribute name");
+ "with 'CacheLoaderConfig'. Please update your config " +
+ "to use the new attribute name");
attributes.xmlAttribs.put("CacheLoaderConfig", clc);
}
}
@@ -237,13 +238,13 @@
{
if (isXmlAttribs)
{
- method = objectClass.getMethod(setter, new Class[]{Element.class});
- method.invoke(target, new Object[]{entry.getValue()});
+ method = objectClass.getMethod(setter, Element.class);
+ method.invoke(target, entry.getValue());
}
else
{
- method = objectClass.getMethod(setter, new Class[]{String.class});
- method.invoke(target, new Object[]{entry.getValue()});
+ method = objectClass.getMethod(setter, String.class);
+ method.invoke(target, entry.getValue());
}
continue;
@@ -284,7 +285,7 @@
try
{
- m.invoke(target, new Object[]{parameter});
+ m.invoke(target, parameter);
}
catch (Exception e)
{
@@ -301,19 +302,19 @@
{
String propname = entry.getKey();
if ("BuddyReplicationConfiguration".equals(propname)
- || "BuddyReplicationConfig".equals(propname))
+ || "BuddyReplicationConfig".equals(propname))
{
BuddyReplicationConfig brc = parseBuddyReplicationConfig(entry.getValue());
conf.setBuddyReplicationConfig(brc);
}
else if ("CacheLoaderConfiguration".equals(propname)
- || "CacheLoaderConfig".equals(propname))
+ || "CacheLoaderConfig".equals(propname))
{
CacheLoaderConfig clc = parseCacheLoaderConfig(entry.getValue());
conf.setCacheLoaderConfig(clc);
}
else if ("EvictionPolicyConfiguration".equals(propname)
- || "EvictionPolicyConfig".equals(propname))
+ || "EvictionPolicyConfig".equals(propname))
{
EvictionConfig ec = parseEvictionConfig(entry.getValue());
conf.setEvictionConfig(ec);
@@ -433,7 +434,7 @@
/* singletonStore element can only appear once in a cacheloader, so we just take the first one ignoring any
subsequent definitions in cacheloader element*/
Node singletonStoreNode = cacheLoaderelement.getElementsByTagName("singletonStore").item(0);
- if (singletonStoreNode!= null && singletonStoreNode.getNodeType() == Node.ELEMENT_NODE)
+ if (singletonStoreNode != null && singletonStoreNode.getNodeType() == Node.ELEMENT_NODE)
{
Element singletonStoreElement = (Element) singletonStoreNode;
boolean singletonStoreEnabled = XmlHelper.readBooleanContents(singletonStoreElement, "enabled");
@@ -467,14 +468,14 @@
// If they set the default eviction policy in the element, use that
// in preference to the external attribute
String temp = XmlHelper.getTagContents(element,
- EvictionConfig.EVICTION_POLICY_CLASS, ATTR, NAME);
+ EvictionConfig.EVICTION_POLICY_CLASS, ATTR, NAME);
if (temp != null && temp.length() > 0)
{
ec.setDefaultEvictionPolicyClass(temp);
}
temp = XmlHelper.getTagContents(element,
- EvictionConfig.WAKEUP_INTERVAL_SECONDS, ATTR, NAME);
+ EvictionConfig.WAKEUP_INTERVAL_SECONDS, ATTR, NAME);
int wakeupIntervalSeconds = 0;
if (temp != null)
@@ -491,7 +492,7 @@
int eventQueueSize = 0;
temp = XmlHelper.getTagContents(element,
- EvictionConfig.EVENT_QUEUE_SIZE, ATTR, NAME);
+ EvictionConfig.EVENT_QUEUE_SIZE, ATTR, NAME);
if (temp != null)
{
@@ -563,7 +564,7 @@
if (evictionClass == null || evictionClass.length() == 0)
{
throw new MissingPolicyException(
- "There is no Eviction Policy Class specified on the region or for the entire cache!");
+ "There is no Eviction Policy Class specified on the region or for the entire cache!");
}
}
@@ -593,7 +594,7 @@
catch (Exception e)
{
throw new RuntimeException("Failed to instantiate eviction configuration of class " +
- policy.getEvictionConfigurationClass(), e);
+ policy.getEvictionConfigurationClass(), e);
}
parseEvictionPolicyConfig(element, epc);
Added: core/trunk/src/main/java/org/jboss/cache/factories/annotations/ClasspathScanner.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/annotations/ClasspathScanner.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/annotations/ClasspathScanner.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,168 @@
+package org.jboss.cache.factories.annotations;
+
+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.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)
+ {
+ // 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);
+ }
+
+ Set<Class<? extends T>> classes = Collections.emptySet();
+ try
+ {
+ 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()
+ {
+ URL u2 = classLoader.findResource("org/jboss/cache/Version.class");
+
+ for (URL u : classLoader.getURLs())
+ {
+ if (u2.toString().startsWith(u.toString()))
+ {
+ return u;
+ }
+ }
+
+ 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/factories/annotations/ComponentName.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/annotations/ComponentName.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/annotations/ComponentName.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,52 @@
+package org.jboss.cache.factories.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used with the {@link org.jboss.cache.factories.annotations.Inject} annotation to specify the name of a component to inject.
+ * Note that you still need to annotate your method with {@link org.jboss.cache.factories.annotations.Inject} for this
+ * annotation to be of any use. You can also combine injections of named and singleton components, see example below.
+ * <p/>
+ * Usage example:
+ * <p/>
+ * <pre>
+ * public class MyClass
+ * {
+ * private Map blahMap;
+ * private Map anotherMap;
+ * private List someList;
+ * private TransactionManager tm;
+ * <p/>
+ * & Inject
+ * public void setMyMap(&ComponentName("BlahMap") Map blahMap)
+ * {
+ * this.blahMap = blahMap;
+ * }
+ * & Inject
+ * public void injectMoreStuff(&ComponentName("AnotherMap") Map anotherMap, &ComponentName("SomeList") List someList, TransactionManager singletonTM)
+ * {
+ * this.anotherMap = anotherMap;
+ * this.someList = someList;
+ * this.tm = singletonTM;
+ * }
+ * }
+ * <p/>
+ * </pre>
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @see org.jboss.cache.factories.annotations.Inject
+ * @since 2.1.0
+ */
+// ensure this annotation is available at runtime.
+ at Retention(RetentionPolicy.RUNTIME)
+
+// only applies to fields.
+ at Target(ElementType.PARAMETER)
+
+public @interface ComponentName
+{
+ String value();
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/annotations/DefaultFactoryFor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/annotations/DefaultFactoryFor.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/annotations/DefaultFactoryFor.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,23 @@
+package org.jboss.cache.factories.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that is used internally, for defining a DEFAULT factory to be used when constructing components. This
+ * annotation allows you to define which components can be constructed by the annotated factory.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at Target(TYPE)
+ at Retention(RetentionPolicy.RUNTIME)
+public @interface DefaultFactoryFor
+{
+ /**
+ * Components that may be constructed by a factory annotated with this annotation.
+ */
+ Class[] classes();
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/annotations/Inject.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/annotations/Inject.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/annotations/Inject.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,55 @@
+package org.jboss.cache.factories.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to annotate a method as one that is used to inject a registered component into another component. The component
+ * to be constructed must be built using the {@link org.jboss.cache.factories.ComponentFactory#construct(Class)} method, or if your object that needs
+ * components injected into it already exists, it can be built using the {@link org.jboss.cache.factories.ComponentFactory#wireComponents(Object)}
+ * method.
+ * <p/>
+ * Usage example:
+ * <pre>
+ * public class MyClass
+ * {
+ * private TransactionManager tm;
+ * private BuddyManager bm;
+ * private Notifier n;
+ * <p/>
+ * &Inject
+ * public void setTransactionManager(TransactionManager tm)
+ * {
+ * this.tm = tm;
+ * }
+ * <p/>
+ * &Inject
+ * public void injectMoreStuff(BuddyManager bm, Notifier n)
+ * {
+ * this.bm = bm;
+ * this.n = n;
+ * }
+ * }
+ * <p/>
+ * </pre>
+ * and an instance of this class can be constructed and wired using
+ * <pre>
+ * MyClass myClass = componentFactory.construct(MyClass.class); // instance will have dependencies injected.
+ * </pre>
+ * If you wish to use named components, you can use the optional {@link org.jboss.cache.factories.annotations.ComponentName}
+ * annotation on each parameter.
+ *
+ * @author Manik Surtani
+ * @see ComponentName
+ * @since 2.1.0
+ */
+// ensure this annotation is available at runtime.
+ at Retention(RetentionPolicy.RUNTIME)
+
+// only applies to fields.
+ at Target(ElementType.METHOD)
+public @interface Inject
+{
+}
Added: core/trunk/src/main/java/org/jboss/cache/invocation/AbstractInvocationDelegate.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/invocation/AbstractInvocationDelegate.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/invocation/AbstractInvocationDelegate.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,156 @@
+package org.jboss.cache.invocation;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.CacheImpl;
+import org.jboss.cache.CacheStatus;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.factories.annotations.Inject;
+import org.jboss.cache.interceptors.Interceptor;
+import org.jboss.cache.marshall.MethodCall;
+import org.jboss.cache.marshall.MethodDeclarations;
+
+/**
+ * The JBoss Cache hand-wired interceptor stack. A "minimal" AOP framework which uses delegation through an
+ * interceptor chain rather than any bytecode manipulation.
+ * <p/>
+ * This class provides some generic behaviour such as the construction of an {@link org.jboss.cache.InvocationContext}
+ * which is passed up the interceptor chain.
+ * <p/>
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @see org.jboss.cache.interceptors.Interceptor
+ * @see org.jboss.cache.InvocationContext
+ * @since 2.1.0
+ */
+public abstract class AbstractInvocationDelegate
+{
+ protected Interceptor interceptorChain;
+ protected CacheImpl cache;
+ protected Log log = LogFactory.getLog(AbstractInvocationDelegate.class);
+ protected Configuration configuration;
+ protected boolean originLocal = true;
+
+ protected ThreadLocal<InvocationContext> invocationContextContainer = new ThreadLocal<InvocationContext>()
+ {
+ @Override
+ protected InvocationContext initialValue()
+ {
+ // create if this is initially unset
+ return new InvocationContext();
+ }
+ };
+
+ /**
+ * Used by the interceptor chain factory to inject dependencies.
+ *
+ * @param interceptorChain interceptor chain to pass calls up
+ * @param cache cache instance
+ * @param configuration configuration for the cache
+ */
+ @Inject
+ private void initialise(Interceptor interceptorChain, CacheImpl cache, Configuration configuration)
+ {
+ this.interceptorChain = interceptorChain;
+ this.cache = cache;
+ this.configuration = configuration;
+ }
+
+ /**
+ * Passes a method call up the interceptor chain.
+ *
+ * @param call methodcall to pass
+ * @return an Object, the generic return type for the interceptors.
+ * @throws Throwable in the event of problems
+ */
+ protected Object invoke(MethodCall call) throws CacheException
+ {
+ // never create a new one directly; always let the container do this if needed.
+ InvocationContext ctx = invocationContextContainer.get();
+
+ // BR methods should NOT block on the cache being started, since the cache depends on these completing to start.
+ if (!MethodDeclarations.isBuddyGroupOrganisationMethod(call.getMethodId()) && !cache.getCacheStatus().allowInvocations() && !ctx.getOptionOverrides().isSkipCacheStatusCheck())
+ {
+ // only throw an exception if this is a locally originating call - JBCACHE-1179
+ if (originLocal)
+ {
+ throw new IllegalStateException("Cache not in STARTED state!");
+ }
+ else
+ {
+ if (cache.getCacheStatus() == CacheStatus.STARTING)
+ {
+ try
+ {
+ blockUntilCacheStarts();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ }
+ }
+ else
+ {
+ log.warn("Received a remote call but the cache is not in STARTED state - ignoring call.");
+ return null;
+ }
+ }
+ }
+
+ MethodCall oldCall = null;
+ try
+ {
+ // check if we had a method call lurking around
+ oldCall = ctx.getMethodCall();
+ ctx.setMethodCall(call);
+ // only set this if originLocal is EXPLICITLY passed in as FALSE. Otherwise leave it as a default.
+ if (!originLocal) ctx.setOriginLocal(false);
+ return interceptorChain.invoke(ctx);
+ }
+ catch (CacheException e)
+ {
+ throw e;
+ }
+ catch (RuntimeException e)
+ {
+ throw e;
+ }
+ catch (Throwable t)
+ {
+ throw new CacheException(t);
+ }
+ finally
+ {
+ if (!originLocal) ctx.setOriginLocal(true);
+ // reset old method call
+ ctx.setMethodCall(oldCall);
+ }
+ }
+
+ /**
+ * Blocks until the current cache instance is in it's {@link org.jboss.cache.CacheStatus#STARTED started} phase. Blocks
+ * for up to {@link org.jboss.cache.config.Configuration#getStateRetrievalTimeout()} milliseconds, throwing an IllegalStateException
+ * if the cache doesn't reach this state even after this maximum wait time.
+ *
+ * @throws InterruptedException if interrupted while waiting
+ * @throws IllegalStateException if even after waiting the cache has not started.
+ */
+ private void blockUntilCacheStarts() throws InterruptedException, IllegalStateException
+ {
+ int pollFrequencyMS = 100;
+ long startupWaitTime = configuration.getStateRetrievalTimeout();
+ long giveUpTime = System.currentTimeMillis() + startupWaitTime;
+
+ while (System.currentTimeMillis() < giveUpTime)
+ {
+ if (cache.getCacheStatus().allowInvocations()) break;
+ Thread.sleep(pollFrequencyMS);
+ }
+
+ // check if we have started.
+ if (cache.getCacheStatus().allowInvocations())
+ throw new IllegalStateException("Cache not in STARTED state, even after waiting " + configuration.getStateRetrievalTimeout() + " millis.");
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,567 @@
+package org.jboss.cache.invocation;
+
+import org.jboss.cache.*;
+import org.jboss.cache.buddyreplication.BuddyManager;
+import org.jboss.cache.buddyreplication.GravitateResult;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.config.Option;
+import org.jboss.cache.factories.InterceptorChainFactory;
+import org.jboss.cache.factories.annotations.ComponentName;
+import org.jboss.cache.factories.annotations.Inject;
+import org.jboss.cache.interceptors.Interceptor;
+import org.jboss.cache.loader.CacheLoaderManager;
+import org.jboss.cache.lock.NodeLock;
+import org.jboss.cache.marshall.Marshaller;
+import org.jboss.cache.marshall.MethodCall;
+import org.jboss.cache.marshall.MethodCallFactory;
+import org.jboss.cache.marshall.MethodDeclarations;
+import org.jboss.cache.notifications.Notifier;
+import org.jboss.cache.statetransfer.StateTransferManager;
+import org.jboss.cache.transaction.GlobalTransaction;
+import org.jboss.cache.transaction.TransactionTable;
+import org.jgroups.Address;
+
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The delegate that users (and interceptor authors) interact with when they create a cache by using a cache factory.
+ * This wrapper delegates calls down the interceptor chain.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at SuppressWarnings("unchecked")
+public class CacheInvocationDelegate<K, V> extends AbstractInvocationDelegate implements CacheSPI<K, V>
+{
+
+ // this stuff is needed since the SPI has methods to retrieve these.
+ private StateTransferManager stateTransferManager;
+ private CacheLoaderManager cacheLoaderManager;
+ private Notifier notifier;
+ private TransactionManager transactionManager;
+ private BuddyManager buddyManager;
+ private TransactionTable transactionTable;
+ private RPCManager rpcManager;
+ private RegionManager regionManager;
+ private Marshaller marshaller;
+ private Map<Thread, List<NodeLock>> lockTable;
+
+ @Inject
+ private void injectDependencies(StateTransferManager stateTransferManager, CacheLoaderManager cacheLoaderManager, Notifier notifier,
+ TransactionManager transactionManager, BuddyManager buddyManager, TransactionTable transactionTable,
+ RPCManager rpcManager, RegionManager regionManager, Marshaller marshaller,
+ @ComponentName("LockTable")Map<Thread, List<NodeLock>> lockTable)
+ {
+ this.stateTransferManager = stateTransferManager;
+ this.cacheLoaderManager = cacheLoaderManager;
+ this.notifier = notifier;
+ this.transactionManager = transactionManager;
+ this.buddyManager = buddyManager;
+ this.transactionTable = transactionTable;
+ this.rpcManager = rpcManager;
+ this.regionManager = regionManager;
+ this.marshaller = marshaller;
+ }
+
+ @Override
+ public String toString()
+ {
+ return cache == null ? super.toString() : cache.toString();
+ }
+
+ public Configuration getConfiguration()
+ {
+ return configuration;
+ }
+
+ public NodeSPI<K, V> getRoot()
+ {
+ return (NodeSPI<K, V>) cache.getRoot();
+ }
+
+ public TransactionManager getTransactionManager()
+ {
+ return transactionManager;
+ }
+
+ public List<Interceptor> getInterceptorChain()
+ {
+ return InterceptorChainFactory.getInstance().asList(interceptorChain);
+ }
+
+ public void addInterceptor(Interceptor i, int position)
+ {
+ cache.addInterceptor(i, position);
+ }
+
+ public void removeInterceptor(int position)
+ {
+ cache.removeInterceptor(position);
+ }
+
+ public CacheLoaderManager getCacheLoaderManager()
+ {
+ return cacheLoaderManager;
+ }
+
+ public BuddyManager getBuddyManager()
+ {
+ return buddyManager;
+ }
+
+ public TransactionTable getTransactionTable()
+ {
+ return transactionTable;
+ }
+
+ public RPCManager getRPCManager()
+ {
+ return rpcManager;
+ }
+
+ public StateTransferManager getStateTransferManager()
+ {
+ return stateTransferManager;
+ }
+
+ public String getClusterName()
+ {
+ return cache.getClusterName();
+ }
+
+ public int getNumberOfAttributes()
+ {
+ return cache.getNumberOfAttributes();
+ }
+
+ public int getNumberOfNodes()
+ {
+ return cache.getNumberOfNodes();
+ }
+
+ public Map<Thread, List<NodeLock>> getLockTable()
+ {
+ return lockTable;
+ }
+
+ public RegionManager getRegionManager()
+ {
+ return regionManager;
+ }
+
+ public GlobalTransaction getCurrentTransaction(Transaction tx, boolean createIfNotExists)
+ {
+ return cache.getCurrentTransaction(tx, createIfNotExists);
+ }
+
+ public GlobalTransaction getCurrentTransaction()
+ {
+ return cache.getCurrentTransaction();
+ }
+
+ public Set<Fqn> getInternalFqns()
+ {
+ return cache.getInternalFqns();
+ }
+
+ public void fetchPartialState(List<Address> members, Fqn subtreeRoot) throws Exception
+ {
+ cache.fetchPartialState(members, subtreeRoot);
+ }
+
+ public void fetchPartialState(List<Address> members, Fqn subtreeRoot, Fqn integrationPoint) throws Exception
+ {
+ cache.fetchPartialState(members, subtreeRoot, integrationPoint);
+ }
+
+ public int getNumberOfLocksHeld()
+ {
+ return cache.getNumberOfLocksHeld();
+ }
+
+ public boolean exists(String fqn)
+ {
+ return exists(Fqn.fromString(fqn));
+ }
+
+ public boolean exists(Fqn<?> fqn)
+ {
+ return peek(fqn, false, false) != null;
+ }
+
+ public Notifier getNotifier()
+ {
+ return notifier;
+ }
+
+ public Marshaller getMarshaller()
+ {
+ return marshaller;
+ }
+
+ public GravitateResult gravitateData(Fqn fqn, boolean searchBuddyBackupSubtrees)
+ {
+ return cache.gravitateData(fqn, searchBuddyBackupSubtrees);
+ }
+
+ public NodeSPI<K, V> peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes)
+ {
+ return (NodeSPI<K, V>) cache.peek(fqn, includeDeletedNodes, includeInvalidNodes);
+ }
+
+ public NodeSPI<K, V> peek(Fqn fqn, boolean includeDeletedNodes)
+ {
+ return (NodeSPI<K, V>) cache.peek(fqn, includeDeletedNodes);
+ }
+
+ public void addCacheListener(Object listener)
+ {
+ notifier.addCacheListener(listener);
+ }
+
+ public void removeCacheListener(Object listener)
+ {
+ notifier.removeCacheListener(listener);
+ }
+
+ public Set<Object> getCacheListeners()
+ {
+ return notifier.getCacheListeners();
+ }
+
+ public void create() throws CacheException
+ {
+ cache.create();
+ }
+
+ public void start() throws CacheException
+ {
+ cache.start();
+ }
+
+ public void stop()
+ {
+ cache.stop();
+ }
+
+ public void destroy()
+ {
+ cache.destroy();
+ }
+
+ public CacheStatus getCacheStatus()
+ {
+ return cache.getCacheStatus();
+ }
+
+ public InvocationContext getInvocationContext()
+ {
+ return invocationContextContainer.get();
+ }
+
+ public void setInvocationContext(InvocationContext ctx)
+ {
+ invocationContextContainer.set(ctx);
+ }
+
+ public Address getLocalAddress()
+ {
+ return cache.getLocalAddress();
+ }
+
+ public List<Address> getMembers()
+ {
+ return cache.getMembers();
+ }
+
+ public String getVersion()
+ {
+ return Version.printVersion();
+ }
+
+ public void move(Fqn<?> nodeToMove, Fqn<?> newParent) throws NodeNotExistsException
+ {
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.moveMethodLocal, nodeToMove, newParent);
+ invoke(m);
+ }
+
+ public void move(String nodeToMove, String newParent) throws NodeNotExistsException
+ {
+ move(Fqn.fromString(nodeToMove), Fqn.fromString(newParent));
+ }
+
+ public boolean removeRegion(Fqn fqn)
+ {
+ return regionManager.removeRegion(fqn);
+ }
+
+ public Region getRegion(Fqn fqn, boolean createIfAbsent)
+ {
+ return regionManager.getRegion(fqn, createIfAbsent);
+ }
+
+ public void evict(Fqn<?> fqn, boolean recursive)
+ {
+ //this method should be called by eviction thread only, so no transaction - expected (sec param is false)
+ Node<K, V> node = peek(fqn, false);
+ if (node != null && node.isResident())
+ {
+ return;
+ }
+ if (recursive)
+ {
+ if (node != null)
+ {
+ evictChildren((NodeSPI<K, V>) node);
+ }
+ }
+ else
+ {
+ evict(fqn);
+ }
+ }
+
+ private void evictChildren(NodeSPI<K, V> n)
+ {
+ for (NodeSPI<K, V> child : n.getChildrenDirect())
+ {
+ evictChildren(child);
+ }
+ evict(n.getFqn());
+ }
+
+ public void evict(Fqn<?> fqn)
+ {
+ Node<K, V> node = peek(fqn, false);
+ if (node != null && node.isResident())
+ {
+ return;
+ }
+ if (fqn.isRoot())
+ {
+ // special treatment for root eviction
+ // we need to preserve options
+ InvocationContext ctx = getInvocationContext();
+ Option o = ctx.getOptionOverrides();
+ for (Object childName : cache.peek(fqn, false, false).getChildrenNames())
+ {
+ ctx.setOptionOverrides(o);
+ evict(new Fqn<Object>(fqn, childName));
+ }
+ }
+ else
+ {
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.evictNodeMethodLocal, fqn);
+ invoke(m);
+ }
+ }
+
+ public V get(Fqn<?> fqn, K key)
+ {
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.getKeyValueMethodLocal, fqn, key, true);
+ return (V) invoke(m);
+ }
+
+ public V get(String fqn, K key)
+ {
+ return get(Fqn.fromString(fqn), key);
+ }
+
+ public boolean removeNode(Fqn<?> fqn)
+ {
+ GlobalTransaction tx = cache.getCurrentTransaction();
+ // special case if we are removing the root. Remove all children instead.
+ if (fqn.isRoot())
+ {
+ boolean result = true;
+ // we need to preserve options
+ InvocationContext ctx = getInvocationContext();
+ Option o = ctx.getOptionOverrides();
+ for (Object childName : peek(fqn, false, false).getChildrenNames())
+ {
+ ctx.setOptionOverrides(o);
+ result = removeNode(new Fqn<Object>(fqn, childName)) && result;
+ }
+
+ return result;
+ }
+ else
+ {
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.removeNodeMethodLocal, tx, fqn, true);
+ Object retval = invoke(m);
+ return retval != null && (Boolean) retval;
+ }
+
+ }
+
+ public boolean removeNode(String fqn)
+ {
+ return removeNode(Fqn.fromString(fqn));
+ }
+
+ public Node getNode(Fqn<?> fqn)
+ {
+ GlobalTransaction tx = cache.getCurrentTransaction();
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.getNodeMethodLocal, fqn);
+ return (Node) invoke(m);
+ }
+
+ public Node getNode(String fqn)
+ {
+ return getNode(Fqn.fromString(fqn));
+ }
+
+ public V remove(Fqn<?> fqn, K key) throws CacheException
+ {
+ GlobalTransaction tx = cache.getCurrentTransaction();
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.removeKeyMethodLocal, tx, fqn, key, true);
+ return (V) invoke(m);
+ }
+
+ public V remove(String fqn, K key)
+ {
+ return remove(Fqn.fromString(fqn), key);
+ }
+
+ public void put(Fqn<?> fqn, Map<K, V> data)
+ {
+ GlobalTransaction tx = cache.getCurrentTransaction();
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.putDataMethodLocal, tx, fqn, data, true);
+ invoke(m);
+ }
+
+ public void put(String fqn, Map<K, V> data)
+ {
+ put(Fqn.fromString(fqn), data);
+ }
+
+ public void putForExternalRead(Fqn<?> fqn, K key, V value)
+ {
+ // if the node exists then this should be a no-op.
+ if (peek(fqn, false, false) != null)
+ {
+ getInvocationContext().getOptionOverrides().setFailSilently(true);
+ GlobalTransaction tx = cache.getCurrentTransaction();
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.putForExternalReadMethodLocal, tx, fqn, key, value);
+ invoke(m);
+ }
+ else
+ {
+ if (log.isDebugEnabled())
+ log.debug("putForExternalRead() called with Fqn " + fqn + " and this node already exists. This method is hence a no op.");
+ }
+ }
+
+ public V put(Fqn<?> fqn, K key, V value)
+ {
+ GlobalTransaction tx = cache.getCurrentTransaction();
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal, tx, fqn, key, value, true);
+ return (V) invoke(m);
+ }
+
+ public V put(String fqn, K key, V value)
+ {
+ return put(Fqn.fromString(fqn), key, value);
+ }
+
+ public Set<Object> getCacheListeners(Fqn region)
+ {
+ throw new UnsupportedOperationException("Not implemented in this release");
+ }
+
+ public void removeCacheListener(Fqn region, Object listener)
+ {
+ throw new UnsupportedOperationException("Not implemented in this release");
+ }
+
+ public void addCacheListener(Fqn region, Object listener)
+ {
+ throw new UnsupportedOperationException("Not implemented in this release");
+ }
+
+ /**
+ * Retrieves a defensively copied data map of the underlying node.
+ *
+ * @param fqn
+ * @return map of data, or an empty map
+ * @throws CacheException
+ */
+ public Map<K, V> getData(Fqn<?> fqn)
+ {
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.getDataMapMethodLocal, fqn);
+ return (Map<K, V>) invoke(m);
+ }
+
+ /**
+ * Returns a set of attribute keys for the Fqn.
+ * Returns null if the node is not found, otherwise a Set.
+ * The set is a copy of the actual keys for this node.
+ *
+ * @param fqn name of the node
+ */
+ public Set getKeys(String fqn)
+ {
+ return getKeys(Fqn.fromString(fqn));
+ }
+
+ /**
+ * Returns a set of attribute keys for the Fqn.
+ * Returns null if the node is not found, otherwise a Set.
+ * The set is a copy of the actual keys for this node.
+ *
+ * @param fqn name of the node
+ */
+ public Set<K> getKeys(Fqn<?> fqn)
+ {
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.getKeysMethodLocal, fqn);
+ return (Set<K>) invoke(m);
+ }
+
+ /**
+ * Removes the keys and properties from a node.
+ */
+ public void clearData(String fqn) throws CacheException
+ {
+ clearData(Fqn.fromString(fqn));
+ }
+
+ /**
+ * Removes the keys and properties from a named node.
+ */
+ public void clearData(Fqn fqn)
+ {
+ GlobalTransaction tx = getCurrentTransaction();
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.removeDataMethodLocal, tx, fqn, true);
+ invoke(m);
+ }
+
+ /**
+ * Returns all children of a given node. Returns an empty set if there are no children.
+ * The set is unmodifiable.
+ *
+ * @param fqn The fully qualified name of the node
+ * @return Set an unmodifiable set of children names, Object.
+ */
+ public <E> Set<E> getChildrenNames(Fqn<E> fqn)
+ {
+ MethodCall m = MethodCallFactory.create(MethodDeclarations.getChildrenNamesMethodLocal, fqn);
+ Set<E> retval = null;
+ retval = (Set<E>) invoke(m);
+ if (retval != null)
+ retval = Collections.unmodifiableSet(new HashSet<E>(retval));
+ else
+ retval = Collections.emptySet();
+ return retval;
+ }
+
+ public Set getChildrenNames(String fqn)
+ {
+ return getChildrenNames(Fqn.fromString(fqn));
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,447 @@
+package org.jboss.cache.invocation;
+
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.NodeNotValidException;
+import org.jboss.cache.NodeSPI;
+import org.jboss.cache.UnversionedNode;
+import org.jboss.cache.factories.annotations.Inject;
+import org.jboss.cache.lock.NodeLock;
+import org.jboss.cache.optimistic.DataVersion;
+import org.jboss.cache.transaction.GlobalTransaction;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The delegate that users (and interceptor authors) interact with when they obtain a node from the cache or another node.
+ * This wrapper delegates calls down the interceptor chain.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+ at SuppressWarnings("unchecked")
+public class NodeInvocationDelegate<K, V> extends AbstractInvocationDelegate implements NodeSPI<K, V>
+{
+ private UnversionedNode node;
+ private CacheSPI<K, V> spi;
+
+ public NodeInvocationDelegate(UnversionedNode node)
+ {
+ this.node = node;
+ }
+
+ @Inject
+ private void injectDependencies(CacheSPI spi)
+ {
+ this.spi = spi;
+ }
+
+ public boolean isChildrenLoaded()
+ {
+ return node.isChildrenLoaded();
+ }
+
+ public void setChildrenLoaded(boolean loaded)
+ {
+ node.setChildrenLoaded(true);
+ }
+
+ public boolean isDataLoaded()
+ {
+ return node.isDataLoaded();
+ }
+
+ public void setDataLoaded(boolean dataLoaded)
+ {
+ node.setDataLoaded(dataLoaded);
+ }
+
+ public Map<Object, Node<K, V>> getChildrenMapDirect()
+ {
+ return node.getChildrenMapDirect();
+ }
+
+ public void setChildrenMapDirect(Map<Object, Node<K, V>> children)
+ {
+ node.setChildrenMapDirect(children);
+ }
+
+ public NodeSPI<K, V> getOrCreateChild(Object name, GlobalTransaction tx)
+ {
+ return node.getOrCreateChild(name, tx);
+ }
+
+ public NodeLock getLock()
+ {
+ return node.getLock();
+ }
+
+ public void setFqn(Fqn<?> f)
+ {
+ node.setFqn(f);
+ }
+
+ public boolean isDeleted()
+ {
+ return node.isDeleted();
+ }
+
+ public void markAsDeleted(boolean marker)
+ {
+ node.markAsDeleted(marker);
+ }
+
+ public void markAsDeleted(boolean marker, boolean recursive)
+ {
+ node.markAsDeleted(marker, recursive);
+ }
+
+ public void addChild(Object nodeName, Node<K, V> nodeToAdd)
+ {
+ node.addChild(nodeName, nodeToAdd);
+ }
+
+ public void printDetails(StringBuffer sb, int indent)
+ {
+ node.printDetails(sb, indent);
+ }
+
+ public void print(StringBuffer sb, int indent)
+ {
+ node.print(sb, indent);
+ }
+
+ public void setVersion(DataVersion version)
+ {
+ node.setVersion(version);
+ }
+
+ public DataVersion getVersion()
+ {
+ return node.getVersion();
+ }
+
+ public Set<NodeSPI<K, V>> getChildrenDirect()
+ {
+ return node.getChildrenDirect();
+ }
+
+ public void removeChildrenDirect()
+ {
+ node.removeChildrenDirect();
+ }
+
+ public Set<NodeSPI<K, V>> getChildrenDirect(boolean includeMarkedAsDeleted)
+ {
+ return node.getChildrenDirect(includeMarkedAsDeleted);
+ }
+
+ public NodeSPI<K, V> getChildDirect(Object childName)
+ {
+ return node.getChildDirect(childName);
+ }
+
+ public NodeSPI<K, V> addChildDirect(Fqn childName)
+ {
+ return node.addChildDirect(childName);
+ }
+
+ public void addChildDirect(NodeSPI<K, V> child)
+ {
+ node.addChildDirect(child);
+ }
+
+ public NodeSPI<K, V> getChildDirect(Fqn childName)
+ {
+ return node.getChildDirect(childName);
+ }
+
+ public boolean removeChildDirect(Fqn fqn)
+ {
+ return node.removeChildDirect(fqn);
+ }
+
+ public boolean removeChildDirect(Object childName)
+ {
+ return node.removeChildDirect(childName);
+ }
+
+ public V removeDirect(K key)
+ {
+ return (V) node.removeDirect(key);
+ }
+
+ public V putDirect(K key, V value)
+ {
+ return (V) node.putDirect(key, value);
+ }
+
+ public void putAllDirect(Map<K, V> data)
+ {
+ node.putAllDirect(data);
+ }
+
+ public Map<K, V> getDataDirect()
+ {
+ return node.getDataDirect();
+ }
+
+ public V getDirect(K key)
+ {
+ return (V) node.getDirect(key);
+ }
+
+ public void clearDataDirect()
+ {
+ node.clearDataDirect();
+ }
+
+ public Set<K> getKeysDirect()
+ {
+ return node.getKeysDirect();
+ }
+
+ public Set<Object> getChildrenNamesDirect()
+ {
+ return node.getChildrenNamesDirect();
+ }
+
+ public CacheSPI<K, V> getCache()
+ {
+ return spi;
+ }
+
+ public NodeSPI<K, V> getParent()
+ {
+ return node.getParent();
+ }
+
+ public Set<Node<K, V>> getChildren()
+ {
+ assertValid();
+ if (spi == null) return Collections.emptySet();
+ Set<Node<K, V>> children = new HashSet<Node<K, V>>();
+ for (Object c : spi.getChildrenNames(getFqn()))
+ {
+ Node n = spi.getNode(new Fqn(getFqn(), c));
+ if (n != null) children.add(n);
+ }
+ return Collections.unmodifiableSet(children);
+ }
+
+ public Set<Object> getChildrenNames()
+ {
+ assertValid();
+ return cache.getChildrenNames(getFqn());
+ }
+
+ public Map<K, V> getData()
+ {
+ assertValid();
+ if (spi == null) return Collections.emptyMap();
+ return spi.getData(getFqn());
+ }
+
+ public Set<K> getKeys()
+ {
+ assertValid();
+ Set keys = spi.getKeys(getFqn());
+ return keys == null ? Collections.emptySet() : Collections.unmodifiableSet(keys);
+ }
+
+ public Fqn getFqn()
+ {
+ return node.getFqn();
+ }
+
+ public Node<K, V> addChild(Fqn<?> f)
+ {
+ // TODO: Revisit. Is this really threadsafe? See comment in putIfAbsent() - same solution should be applied here too.
+ assertValid();
+ Fqn nf = new Fqn(getFqn(), f);
+ spi.put(nf, null);
+ return getChild(f);
+ }
+
+ public boolean removeChild(Fqn<?> f)
+ {
+ assertValid();
+ return cache.removeNode(new Fqn(getFqn(), f));
+ }
+
+ public boolean removeChild(Object childName)
+ {
+ assertValid();
+ return cache.removeNode(new Fqn(getFqn(), childName));
+ }
+
+ public Node<K, V> getChild(Fqn<?> f)
+ {
+ assertValid();
+ return spi.getNode(new Fqn(getFqn(), f));
+ }
+
+ public Node<K, V> getChild(Object name)
+ {
+ assertValid();
+ return spi.getNode(new Fqn(getFqn(), name));
+ }
+
+ public V put(K key, V value)
+ {
+ assertValid();
+ return (V) spi.put(getFqn(), key, value);
+ }
+
+ public V putIfAbsent(K k, V v)
+ {
+ assertValid();
+ // TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex.
+ // will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl.
+ synchronized (this)
+ {
+ if (!getKeys().contains(k))
+ return put(k, v);
+ else
+ return get(k);
+ }
+ }
+
+ public V replace(K key, V value)
+ {
+ assertValid();
+ // TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex.
+ // will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl.
+ synchronized (this)
+ {
+ if (getKeys().contains(key))
+ {
+ return put(key, value);
+ }
+ else
+ return null;
+ }
+ }
+
+ public boolean replace(K key, V oldValue, V newValue)
+ {
+ assertValid();
+ // TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex.
+ // will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl.
+ synchronized (this)
+ {
+ if (oldValue.equals(get(key)))
+ {
+ put(key, newValue);
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+
+ public void putAll(Map<K, V> data)
+ {
+ assertValid();
+ cache.put(getFqn(), data);
+ }
+
+ public void replaceAll(Map<K, V> data)
+ {
+ assertValid();
+ cache.put(getFqn(), data, true);
+ }
+
+ public V get(K key)
+ {
+ assertValid();
+ return (V) spi.get(getFqn(), key);
+ }
+
+ public V remove(K key)
+ {
+ assertValid();
+ return (V) spi.remove(getFqn(), key);
+ }
+
+ public void clearData()
+ {
+ assertValid();
+ spi.clearData(getFqn());
+ }
+
+ public int dataSize()
+ {
+ assertValid();
+ return spi.getKeys(getFqn()).size();
+ }
+
+ public boolean hasChild(Fqn<?> f)
+ {
+ // TODO: This could be made more efficient when calls are made directly on the node
+ assertValid();
+ return getChild(f) != null;
+ }
+
+ public boolean hasChild(Object o)
+ {
+ // TODO: This could be made more efficient when calls are made directly on the node
+ assertValid();
+ return getChild(o) != null;
+ }
+
+ public boolean isValid()
+ {
+ return node.isValid();
+ }
+
+ public boolean isResident()
+ {
+ return node.isResident();
+ }
+
+ public void setResident(boolean resident)
+ {
+ node.setResident(resident);
+ }
+
+ public boolean isLockForChildInsertRemove()
+ {
+ return node.isLockForChildInsertRemove();
+ }
+
+ public void setLockForChildInsertRemove(boolean lockForChildInsertRemove)
+ {
+ node.setLockForChildInsertRemove(lockForChildInsertRemove);
+ }
+
+ public boolean hasChildrenDirect()
+ {
+ return node.hasChildrenDirect();
+ }
+
+ public Map getInternalState(boolean onlyInternalState)
+ {
+ return node.getInternalState(onlyInternalState);
+ }
+
+ public void setInternalState(Map state)
+ {
+ node.setInternalState(state);
+ }
+
+ public void setValid(boolean valid, boolean recursive)
+ {
+ node.setValid(valid, recursive);
+ }
+
+ protected void assertValid()
+ {
+ if (!node.isValid())
+ throw new NodeNotValidException("Node " + getFqn() + " is not valid. Perhaps it has been moved or removed.");
+ }
+}
Added: core/trunk/src/main/java/org/jboss/cache/invocation/RemoteCacheInvocationDelegate.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/invocation/RemoteCacheInvocationDelegate.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/invocation/RemoteCacheInvocationDelegate.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,58 @@
+package org.jboss.cache.invocation;
+
+import org.jboss.cache.marshall.MethodCall;
+import org.jboss.cache.marshall.MethodDeclarations;
+
+import java.util.List;
+
+/**
+ * A sublcass of CacheInvocationDelegate, used by remote instances to invoke methods on current cache.
+ *
+ * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @since 2.1.0
+ */
+public class RemoteCacheInvocationDelegate extends CacheInvocationDelegate
+{
+ public RemoteCacheInvocationDelegate()
+ {
+ originLocal = false;
+ }
+
+ public Object _replicate(MethodCall methodCall) throws Throwable
+ {
+ try
+ {
+ Object retVal = invoke(methodCall);
+ // we only need to return values for a set of remote calls; not every call.
+ if (MethodDeclarations.returnValueForRemoteCall(methodCall.getMethodId()))
+ {
+ return retVal;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ catch (Throwable ex)
+ {
+ if (methodCall.getMethodId() != MethodDeclarations.putForExternalReadMethodLocal_id
+ || methodCall.getMethodId() != MethodDeclarations.putForExternalReadVersionedMethodLocal_id)
+ {
+ if (!MethodDeclarations.isBuddyGroupOrganisationMethod(methodCall.getMethodId()) && log.isWarnEnabled())
+ log.warn("replication failure with methodCall " + methodCall + " exception", ex);
+ throw ex;
+ }
+ else return null;
+
+ }
+ }
+
+ /**
+ * Replicates a list of method calls.
+ */
+ public void _replicate(List<MethodCall> methodCalls) throws Throwable
+ {
+ for (MethodCall methodCall : methodCalls) _replicate(methodCall);
+ }
+
+}
More information about the jbosscache-commits
mailing list