[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 &amp;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/>
+ *          &amp; Inject
+ *          public void setMyMap(&amp;ComponentName("BlahMap") Map blahMap)
+ *          {
+ *             this.blahMap = blahMap;
+ *          }
+ *          &amp; Inject
+ *          public void injectMoreStuff(&amp;ComponentName("AnotherMap") Map anotherMap, &amp;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/>
+ *          &amp;Inject
+ *          public void setTransactionManager(TransactionManager tm)
+ *          {
+ *             this.tm = tm;
+ *          }
+ * <p/>
+ *          &amp;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