JBoss Cache SVN: r4832 - in core/trunk/src/main/java/org/jboss/cache: factories and 2 other directories.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)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@jboss.org">Manik Surtani (manik(a)jboss.org)</a>
+ * @see org.jboss.cache.factories.ComponentFactory
*/
-public class DefaultCacheFactory<K, V> implements CacheFactory<K, V>
+@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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@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@jboss.org">manik(a)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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+public class ComponentRegistry
+{
+ /**
+ * Can contain either unnamed singletons (in which case the key is the class of the instance) or named singletons, keyed by a
+ * String representing the name of the instance.
+ */
+ private Map<Object, Object> registry = new HashMap<Object, Object>();
+ /**
+ * Contains class definitions of component factories that can be used to construct certain components
+ */
+ Map<Class, Class<? extends ComponentFactory>> defaultFactories = null;
+
+ private static Log log = LogFactory.getLog(ComponentRegistry.class);
+
+ public ComponentRegistry(Configuration configuration)
+ {
+ // bootstrap.
+ registerComponent(configuration);
+ }
+
+ /**
+ * Adds a singleton instance to the registry. Note that if an instance of this component already exists in the
+ * registry it is overwritten. The instance is registered under type <tt>component.getClass()</tt>. If the component
+ * implements an interface or extends an abstract class, it may make more sense to use {@link #registerComponent(Class, Object)}
+ *
+ * @param component component to add to the registry.
+ */
+ public void registerComponent(Object component)
+ {
+ registerComponent(component.getClass(), component);
+ }
+
+ /**
+ * Adds a singleton instance to the registry. Note that if an instance of this component already exists in the
+ * registry it is overwritten.
+ *
+ * @param componentType the type to register the component under.
+ * @param component component to add to the registry.
+ */
+ public void registerComponent(Class componentType, Object component)
+ {
+ registry.put(componentType, component);
+ }
+
+ /**
+ * Adds an instance component to the registry, registering the component under the given name. If an instance already
+ * exists in the registry under the given name, it will be overwritten.
+ *
+ * @param name name of the instance
+ * @param component component to add
+ */
+ public void registerComponent(String name, Object component)
+ {
+ registry.put(name, component);
+ }
+
+ /**
+ * Retrieves a singleton component which can be assigned to the type passed in. Will return a null if no such component
+ * is registered
+ *
+ * @param c type to be able to assign component to.
+ * @return component, if found, or a null otherwise.
+ */
+ public <T> T getComponent(Class<T> c)
+ {
+ if (c == null)
+ {
+ log.error("Scanning for component null!! This is a bug!!", new Throwable());
+ return null;
+ }
+
+ if (log.isTraceEnabled()) log.trace("Scanning registry for an instance that can be assigned to class " + c);
+ T component = (T) registry.get(c);
+
+ if (component == null)
+ {
+ // search for subclasses.
+ for (Object object : registry.keySet())
+ {
+ if (object instanceof Class)
+ {
+ if (c.isAssignableFrom((Class) object)) component = (T) registry.get(object);
+ }
+
+ }
+ }
+
+ return component;
+ }
+
+ /**
+ * Retrieves a named component which can be assigned to the type passed in. Will return a null if no such component
+ * is registered
+ *
+ * @param c type to be able to assign component to.
+ * @return component, if found, or a null otherwise.
+ */
+ public <T> T getComponent(String name, Class<T> c)
+ {
+ T component = (T) registry.get(c);
+ if (component == null) return null;
+ if (c.isAssignableFrom(component.getClass())) return component;
+ else
+ throw new ConfigurationException("Component registered under " + name + " is of type " + component.getClass() + " and cannot be assigned to " + c);
+ }
+
+ /**
+ * Retrieves a named component if one exists, and if not, attempts to find a factory capable of constructing the component
+ * (factories annotated with the {@link org.jboss.cache.factories.annotations.DefaultFactoryFor} annotation that is capable
+ * of creating the component class).
+ * <p/>
+ * If an instance needs to be constructed, dependencies are then automatically wired into the instance, based on methods
+ * on the component type annotated with {@link org.jboss.cache.factories.annotations.Inject}.
+ * <p/>
+ * Summing it up, component retrieval happens in the following order:<br />
+ * 1. Look for an appropriate component that exists in the {@link Configuration} that may be injected from an external system.
+ * 2. Look for a class definition passed in to the {@link org.jboss.cache.config.Configuration} - such as an EvictionPolicy implementation
+ * 3. Attempt to create it by looking for an appropriate factory (annotated with {@link org.jboss.cache.factories.annotations.DefaultFactoryFor})
+ * <p/>
+ *
+ * @param componentName name of component to be created. If null, uses the fully qualified class name as component name.
+ * @param componentClass type of component to be retrieved. Should not be null.
+ * @return a fully wired component instance, or null if one cannot be found or constructed.
+ * @throws ConfigurationException if there is a problem with consructing or wiring the instance.
+ */
+ public <T> T getOrCreateComponent(String componentName, Class<T> componentClass)
+ {
+ T component = (componentName == null) ? getComponent(componentClass) : getComponent(componentName, componentClass);
+
+ if (component == null)
+ {
+ // first see if this has been injected externally.
+ component = getFromConfiguration(componentClass);
+
+ if (component == null)
+ {
+ // create this component and add it to the registry
+ ComponentFactory factory = getFactory(componentClass);
+ component = factory.construct(componentName, componentClass);
+ }
+
+ if (component != null)
+ {
+ // wire dependencies
+ wireDependencies(component);
+ if (componentName == null)
+ registerComponent(componentClass, component);
+ else
+ registerComponent(componentName, component);
+ }
+ }
+ return component;
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T getFromConfiguration(Class<T> componentClass)
+ {
+ if (log.isDebugEnabled())
+ log.debug("Looking in configuration for an instance of " + componentClass + " that may have been injected from an external source.");
+ Method getter = BeanUtils.getterMethod(Configuration.class, componentClass);
+ T returnValue = null;
+
+ if (getter != null)
+ {
+ try
+ {
+ returnValue = (T) getter.invoke(getConfiguration());
+ }
+ catch (Exception e)
+ {
+ log.warn("Unable to invoke getter " + getter + " on Configuration.class!", e);
+ }
+ }
+
+ // now try the RuntimeConfig - a legacy "registry" of sorts.
+ if (returnValue == null)
+ {
+ getter = BeanUtils.getterMethod(RuntimeConfig.class, componentClass);
+ if (getter != null)
+ {
+ try
+ {
+ returnValue = (T) getter.invoke(getConfiguration().getRuntimeConfig());
+ }
+ catch (Exception e)
+ {
+ log.warn("Unable to invoke getter " + getter + " on RuntimeConfig.class!", e);
+ }
+ }
+ }
+ return returnValue;
+ }
+
+ Configuration getConfiguration()
+ {
+ // this is assumed to always be present as a part of the bootstrap/construction of a ComponentRegistry.
+ return getComponent(Configuration.class);
+ }
+
+ /**
+ * Updates (re-injects) any dependencies needed by all components already in the registry.
+ */
+ public void updateDependencies()
+ {
+ // make sure we work off a copy of the values set since wireDependencies may add components to the registry
+ // and this may otherwise end up in a CME.
+
+ Set components = new HashSet(registry.values());
+
+ for (Object component : components)
+ {
+ wireDependencies(component);
+ }
+ }
+
+ /**
+ * Removes a component from the registry. If the component has already been injected into other components, you should
+ * call {@link #updateDependencies()} to ensure dependencies are updated.
+ *
+ * @param clazz class of component to remove.
+ */
+ public void unregisterComponent(Class<?> clazz)
+ {
+ registry.remove(clazz);
+ }
+
+ /**
+ * Removes a component from the registry. If the component has already been injected into other components, you should
+ * call {@link #updateDependencies()} to ensure dependencies are updated.
+ *
+ * @param name name of the component to remove.
+ */
+ public void unregisterComponent(String name)
+ {
+ registry.remove(name);
+ }
+
+ /**
+ * Wires an object instance with dependencies annotated with the {@link org.jboss.cache.factories.annotations.Inject} annotation, creating more components
+ * as needed based on the Configuration passed in if these additional components don't exist in the
+ * {@link ComponentRegistry}.
+ *
+ * @param target object to wire
+ * @param configuration Configuration object with knowledge of what components to create and how to create them
+ * @throws ConfigurationException if there is a problem wiring the instance
+ */
+ public void wireDependencies(Object target) throws ConfigurationException
+ {
+ if (log.isTraceEnabled()) log.trace("Inspecting class " + target.getClass());
+ try
+ {
+ List<Method> methods = getAllMethods(target.getClass());
+ if (log.isTraceEnabled())
+ log.trace("Found method set containing " + methods.size() + " methods that need injection: " + methods);
+
+ // search for anything we need to inject
+ for (Method method : methods)
+ {
+ if (log.isTraceEnabled()) log.trace("Method " + method + " needs some other components injected!");
+ performInjection(method, target);
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to configure component (type: " + target.getClass() + ", instance " + target + ")", e);
+ }
+ }
+
+ /**
+ * Looks through the parameter list of the given method, attempts to locate parameters that fit the types that may
+ * exist in the {@link ComponentRegistry}, and then calls the method on the target object with the necessary parameters.
+ *
+ * @param method Method to call
+ * @param target Instance on which to call the method
+ * @param configuration contains details of how to create components if they do not already exist.
+ * @throws IllegalAccessException if the method cannot be called
+ * @throws java.lang.reflect.InvocationTargetException
+ * if the method cannot be called
+ */
+ @SuppressWarnings("unchecked")
+ private <T> void performInjection(Method method, T target) throws IllegalAccessException, InvocationTargetException
+ {
+ Class[] parameterTypes = method.getParameterTypes();
+ Annotation[][] annotationsOnParams = method.getParameterAnnotations();
+
+ Object[] parameters = new Object[parameterTypes.length];
+
+ for (int i = 0; i < parameterTypes.length; i++)
+ {
+ String componentName = extractComponentName(annotationsOnParams[i]);
+ parameters[i] = getOrCreateComponent(componentName, parameterTypes[i]);
+ }
+
+ // make sure we set this method to be accessible, so we can call private, package and protected
+ // methods rather than just public ones.
+ method.setAccessible(true);
+
+ // invoke the method with the parameters we've worked out.
+ method.invoke(target, parameters);
+ }
+
+ private String extractComponentName(Annotation[] annotationsOnParameter)
+ {
+ for (Annotation a : annotationsOnParameter)
+ {
+ if (a instanceof ComponentName)
+ {
+ ComponentName cn = (ComponentName) a;
+ return cn.value();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a set of Methods that require components injected. This includes all public, protected, package and private
+ * methods, as well as those of superclasses. Note that this does *not* include overridden methods.
+ * <p/>
+ * Includes all methods annotated with &Inject as well as methods that are <i>not</i> annotated, but instead
+ * have parameters annotated accordingly.
+ *
+ * @param c class to inspect
+ * @return Set of Method objects that require injection.
+ */
+ private List<Method> getAllMethods(Class c)
+ {
+ List<Method> annotated = new LinkedList<Method>();
+ inspectRecursively(c, annotated);
+ return annotated;
+ }
+
+ private void inspectRecursively(Class c, List<Method> s)
+ {
+ // Superclass first
+ if (!c.equals(Object.class)) inspectRecursively(c.getSuperclass(), s);
+
+ for (Method m : c.getDeclaredMethods())
+ {
+ // don't bother if this method has already been overridden by a subclass
+ if (!alreadyFound(m, s) && m.isAnnotationPresent(Inject.class))
+ {
+ s.add(m);
+ }
+ }
+ }
+
+ private boolean alreadyFound(Method m, Collection<Method> s)
+ {
+ for (Method found : s)
+ {
+ if (m.getName().equals(found.getName()) &&
+ Arrays.equals(m.getParameterTypes(), found.getParameterTypes()))
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Retrieves a component factory instance capable of constructing components of a specified type.
+ *
+ * @param componentClass type of component to construct
+ * @return component factory capable of constructing such components
+ */
+ protected ComponentFactory getFactory(Class componentClass)
+ {
+ if (defaultFactories == null) scanDefaultFactories();
+ Class<? extends ComponentFactory> cfClass = defaultFactories.get(componentClass);
+
+ // a component factory is a component too! See if one has been created and exists in the registry
+ ComponentFactory cf = getComponent(cfClass);
+ if (cf == null)
+ {
+ // hasn't yet been created. Create and put in registry
+ cf = instantiateFactory(cfClass);
+ if (cf != null)
+ {
+ // wire factory deps
+ wireDependencies(cf);
+ // register this factory
+ registerComponent(cf);
+ }
+ }
+
+ if (cf == null)
+ throw new ConfigurationException("Unable to locate component factory for component " + componentClass);
+ return cf;
+ }
+
+ /**
+ * Scans the class path for classes annotated with {@link org.jboss.cache.factories.annotations.DefaultFactoryFor}, and
+ * analyses which components can be created by such factories.
+ */
+ void scanDefaultFactories()
+ {
+ defaultFactories = new HashMap<Class, Class<? extends ComponentFactory>>();
+
+ ClasspathScanner scanner = new ClasspathScanner();
+
+ Set<Class<? extends ComponentFactory>> factories = scanner.scan(DefaultFactoryFor.class, ComponentFactory.class);
+
+ for (Class<? extends ComponentFactory> factory : factories)
+ {
+ DefaultFactoryFor dFFAnnotation = factory.getAnnotation(DefaultFactoryFor.class);
+ for (Class targetClass : dFFAnnotation.classes()) defaultFactories.put(targetClass, factory);
+ }
+ }
+
+ /**
+ * No such thing as a meta factory yet. Factories are created using this method which attempts to use an empty public
+ * constructor, or a "getInstance" method.
+ *
+ * @param factory class of factory to be created
+ * @return factory instance
+ */
+ ComponentFactory instantiateFactory(Class<? extends ComponentFactory> factory)
+ {
+ Constructor ctor = null;
+ Method singletonAccessor = null;
+ try
+ {
+ ctor = factory.getConstructor();
+ }
+ catch (NoSuchMethodException e)
+ {
+ log.trace("No default, public constructor on " + factory);
+ // perhaps this factory is a singleton.
+ try
+ {
+ singletonAccessor = factory.getMethod("getInstance");
+ }
+ catch (NoSuchMethodException e1)
+ {
+ log.trace("No getInstance() method on " + factory);
+ }
+ }
+
+ if (ctor == null && singletonAccessor == null)
+ {
+ // unable to get a hold of an instance!!
+ throw new ConfigurationException("Unable to instantiate factory " + factory + ": no public, default constructor or getInstance() static method!");
+ }
+
+ ComponentFactory instance;
+ try
+ {
+ if (ctor == null)
+ {
+ instance = (ComponentFactory) singletonAccessor.invoke(null);
+ }
+ else
+ {
+ instance = (ComponentFactory) ctor.newInstance();
+ }
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Unable to instantiate factory " + factory, e);
+ }
+
+ return instance;
+ }
+
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,43 @@
+package org.jboss.cache.factories;
+
+import org.jboss.cache.RegionManager;
+import org.jboss.cache.config.ConfigurationException;
+import org.jboss.cache.factories.annotations.DefaultFactoryFor;
+import org.jboss.cache.loader.CacheLoaderManager;
+import org.jboss.cache.notifications.Notifier;
+import org.jboss.cache.remoting.jgroups.CacheMessageListener;
+import org.jboss.cache.statetransfer.StateTransferManager;
+import org.jboss.cache.transaction.TransactionTable;
+
+/**
+ * Simple factory that just uses reflection and an empty constructor of the component type.
+ *
+ * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@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@jboss.org">Manik Surtani (manik(a)jboss.org)</a>
*/
-public class InterceptorChainFactory
+@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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@DefaultFactoryFor(classes = {TransactionManager.class})
+public class TransactionManagerFactory extends ComponentFactory
+{
+ @Override
+ @SuppressWarnings("unchecked")
+ protected <T> T construct(String componentName, Class<T> componentType)
+ {
+ // See if we had a TransactionManager injected into our config
+ TransactionManager transactionManager = configuration.getRuntimeConfig().getTransactionManager();
+ TransactionManagerLookup lookup = null;
+
+ if (transactionManager == null)
+ {
+ // Nope. See if we can look it up from JNDI
+ if (configuration.getTransactionManagerLookupClass() != null)
+ {
+ try
+ {
+ Class clazz = Thread.currentThread().getContextClassLoader().loadClass(configuration.getTransactionManagerLookupClass());
+ lookup = (TransactionManagerLookup) clazz.newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("Problems looking up transaction manager", e);
+ }
+ }
+
+ try
+ {
+ if (lookup != null)
+ {
+ transactionManager = lookup.getTransactionManager();
+ configuration.getRuntimeConfig().setTransactionManager(transactionManager);
+ }
+ else
+ {
+ if (configuration.getNodeLockingScheme() == Configuration.NodeLockingScheme.OPTIMISTIC)
+ {
+ log.fatal("No transaction manager lookup class has been defined. Transactions cannot be used and thus OPTIMISTIC locking cannot be used");
+ }
+ else
+ {
+ log.info("No transaction manager lookup class has been defined. Transactions cannot be used");
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ log.info("failed looking up TransactionManager, will not use transactions", e);
+ }
+ }
+ return (T) transactionManager;
+ }
+}
Modified: core/trunk/src/main/java/org/jboss/cache/factories/XmlConfigurationParser.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/XmlConfigurationParser.java 2007-12-11 17:19:55 UTC (rev 4831)
+++ core/trunk/src/main/java/org/jboss/cache/factories/XmlConfigurationParser.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -20,8 +20,8 @@
import org.jboss.cache.config.EvictionRegionConfig;
import org.jboss.cache.config.MissingPolicyException;
import org.jboss.cache.eviction.EvictionPolicy;
+import org.jboss.cache.util.BeanUtils;
import org.jboss.cache.util.Util;
-import org.jboss.cache.util.BeanUtils;
import org.jboss.cache.xml.XmlHelper;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@@ -89,9 +89,10 @@
/**
* Parses an input stream containing XML text and returns a new configuration.
+ *
* @param stream input stream to parse. SHould not be null.
+ * @return a configured Configuration object representing the configuration in the stream
* @since 2.1.0
- * @return a configured Configuration object representing the configuration in the stream
*/
public Configuration parseStream(InputStream stream)
{
@@ -101,7 +102,7 @@
return parseConfiguration(mbeanElement);
}
-
+
public Configuration parseConfiguration(Element configurationRoot)
{
ParsedAttributes attributes = extractAttributes(configurationRoot);
@@ -117,7 +118,7 @@
// Special handling for XML elements -- we hard code the parsing
setXmlValues(c, attributes.xmlAttribs);
- return c;
+ return c;
}
/**
@@ -139,23 +140,23 @@
if (evictionPolicy != null)
{
throw new ConfigurationException("XmlConfigurationParser does not " +
- "support the JBC 1.x attribute EvictionPolicyClass. Set the default " +
- "eviction policy via the policyClass element in the EvictionConfig section");
+ "support the JBC 1.x attribute EvictionPolicyClass. Set the default " +
+ "eviction policy via the policyClass element in the EvictionConfig section");
}
String multiplexerService = attributes.stringAttribs.remove("MultiplexerService");
if (multiplexerService != null)
{
throw new ConfigurationException("XmlConfigurationParser does not " +
- "support the JBC 1.x attribute MultiplexerService. Inject the " +
- "multiplexer directly using Configuration.getRuntimeConfig().setMuxChannelFactory()");
+ "support the JBC 1.x attribute MultiplexerService. Inject the " +
+ "multiplexer directly using Configuration.getRuntimeConfig().setMuxChannelFactory()");
}
String serviceName = attributes.stringAttribs.remove("ServiceName");
if (serviceName != null)
{
log.warn("XmlConfigurationParser does not support the deprecated " +
- "attribute ServiceName. If JMX registration is needed, " +
- "register a CacheJmxWrapper or PojoCacheJmxWrapper in " +
- "JMX with the desired name");
+ "attribute ServiceName. If JMX registration is needed, " +
+ "register a CacheJmxWrapper or PojoCacheJmxWrapper in " +
+ "JMX with the desired name");
}
}
@@ -176,16 +177,16 @@
if (keepStats != null && attributes.stringAttribs.get("ExposeManagementStatistics") == null)
{
log.warn("Found non-existent JBC 1.x attribute 'UseInterceptorMbeans' and replaced " +
- "with 'ExposeManagementStatistics'. Please update your config " +
- "to use the new attribute name");
+ "with 'ExposeManagementStatistics'. Please update your config " +
+ "to use the new attribute name");
attributes.stringAttribs.put("ExposeManagementStatistics", keepStats);
}
Element clc = attributes.xmlAttribs.remove("CacheLoaderConfiguration");
if (clc != null && attributes.xmlAttribs.get("CacheLoaderConfig") == null)
{
log.warn("Found non-existent JBC 1.x attribute 'CacheLoaderConfiguration' and replaced " +
- "with 'CacheLoaderConfig'. Please update your config " +
- "to use the new attribute name");
+ "with 'CacheLoaderConfig'. Please update your config " +
+ "to use the new attribute name");
attributes.xmlAttribs.put("CacheLoaderConfig", clc);
}
}
@@ -237,13 +238,13 @@
{
if (isXmlAttribs)
{
- method = objectClass.getMethod(setter, new Class[]{Element.class});
- method.invoke(target, new Object[]{entry.getValue()});
+ method = objectClass.getMethod(setter, Element.class);
+ method.invoke(target, entry.getValue());
}
else
{
- method = objectClass.getMethod(setter, new Class[]{String.class});
- method.invoke(target, new Object[]{entry.getValue()});
+ method = objectClass.getMethod(setter, String.class);
+ method.invoke(target, entry.getValue());
}
continue;
@@ -284,7 +285,7 @@
try
{
- m.invoke(target, new Object[]{parameter});
+ m.invoke(target, parameter);
}
catch (Exception e)
{
@@ -301,19 +302,19 @@
{
String propname = entry.getKey();
if ("BuddyReplicationConfiguration".equals(propname)
- || "BuddyReplicationConfig".equals(propname))
+ || "BuddyReplicationConfig".equals(propname))
{
BuddyReplicationConfig brc = parseBuddyReplicationConfig(entry.getValue());
conf.setBuddyReplicationConfig(brc);
}
else if ("CacheLoaderConfiguration".equals(propname)
- || "CacheLoaderConfig".equals(propname))
+ || "CacheLoaderConfig".equals(propname))
{
CacheLoaderConfig clc = parseCacheLoaderConfig(entry.getValue());
conf.setCacheLoaderConfig(clc);
}
else if ("EvictionPolicyConfiguration".equals(propname)
- || "EvictionPolicyConfig".equals(propname))
+ || "EvictionPolicyConfig".equals(propname))
{
EvictionConfig ec = parseEvictionConfig(entry.getValue());
conf.setEvictionConfig(ec);
@@ -433,7 +434,7 @@
/* singletonStore element can only appear once in a cacheloader, so we just take the first one ignoring any
subsequent definitions in cacheloader element*/
Node singletonStoreNode = cacheLoaderelement.getElementsByTagName("singletonStore").item(0);
- if (singletonStoreNode!= null && singletonStoreNode.getNodeType() == Node.ELEMENT_NODE)
+ if (singletonStoreNode != null && singletonStoreNode.getNodeType() == Node.ELEMENT_NODE)
{
Element singletonStoreElement = (Element) singletonStoreNode;
boolean singletonStoreEnabled = XmlHelper.readBooleanContents(singletonStoreElement, "enabled");
@@ -467,14 +468,14 @@
// If they set the default eviction policy in the element, use that
// in preference to the external attribute
String temp = XmlHelper.getTagContents(element,
- EvictionConfig.EVICTION_POLICY_CLASS, ATTR, NAME);
+ EvictionConfig.EVICTION_POLICY_CLASS, ATTR, NAME);
if (temp != null && temp.length() > 0)
{
ec.setDefaultEvictionPolicyClass(temp);
}
temp = XmlHelper.getTagContents(element,
- EvictionConfig.WAKEUP_INTERVAL_SECONDS, ATTR, NAME);
+ EvictionConfig.WAKEUP_INTERVAL_SECONDS, ATTR, NAME);
int wakeupIntervalSeconds = 0;
if (temp != null)
@@ -491,7 +492,7 @@
int eventQueueSize = 0;
temp = XmlHelper.getTagContents(element,
- EvictionConfig.EVENT_QUEUE_SIZE, ATTR, NAME);
+ EvictionConfig.EVENT_QUEUE_SIZE, ATTR, NAME);
if (temp != null)
{
@@ -563,7 +564,7 @@
if (evictionClass == null || evictionClass.length() == 0)
{
throw new MissingPolicyException(
- "There is no Eviction Policy Class specified on the region or for the entire cache!");
+ "There is no Eviction Policy Class specified on the region or for the entire cache!");
}
}
@@ -593,7 +594,7 @@
catch (Exception e)
{
throw new RuntimeException("Failed to instantiate eviction configuration of class " +
- policy.getEvictionConfigurationClass(), e);
+ policy.getEvictionConfigurationClass(), e);
}
parseEvictionPolicyConfig(element, epc);
Added: core/trunk/src/main/java/org/jboss/cache/factories/annotations/ClasspathScanner.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/annotations/ClasspathScanner.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/annotations/ClasspathScanner.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,168 @@
+package org.jboss.cache.factories.annotations;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Class for scanning archives and classpaths in the current JBoss Cache classpath for classes annotated with a given annotation. Inspired by a similar class in
+ * JBoss SEAM.
+ *
+ * @author Manik Surtani
+ */
+public class ClasspathScanner
+{
+ private Log log = LogFactory.getLog(ClasspathScanner.class);
+ private URLClassLoader classLoader;
+
+ /**
+ * Constructor with the type of annotation to scan for.
+ */
+ public ClasspathScanner()
+ {
+ classLoader = (URLClassLoader) ClasspathScanner.class.getClassLoader();
+ }
+
+ /**
+ * Scans the class path element that contains JBoss Cache for all classes that contain the annotation type this class is
+ * initialised with. Note that this only scans CLASSES for the annotation; not methods, etc.
+ *
+ * @param annotationType the type of annotation to scan for.
+ * @param classType the type of class to scan for. Subclasses will be scanned, others will not.
+ * @return a set of Classes that contain the specified annotation on the class.
+ */
+ public <T> Set<Class<? extends T>> scan(Class<? extends Annotation> annotationType, Class<T> classType)
+ {
+ // only scan the current ClassPath location that contains this file. Could be a directory or a JAR file.
+ URL url = getURLPathFromClassLoader();
+ String urlPath = url.getFile();
+ if (urlPath.endsWith("/"))
+ {
+ urlPath = urlPath.substring(0, urlPath.length() - 1);
+ }
+
+ Set<Class<? extends T>> classes = Collections.emptySet();
+ try
+ {
+ if (log.isDebugEnabled()) log.debug("scanning: " + urlPath);
+ File file = new File(urlPath);
+ if (file.isDirectory())
+ {
+ classes = handleDirectory(file, null, classType, annotationType);
+ }
+ else
+ {
+ classes = handleArchive(file, classType, annotationType);
+ }
+ }
+ catch (IOException ioe)
+ {
+ log.warn("could not read entries", ioe);
+ }
+ catch (ClassNotFoundException e)
+ {
+ log.warn("Unable to load class", e);
+ }
+
+ return classes;
+ }
+
+ private URL getURLPathFromClassLoader()
+ {
+ URL u2 = classLoader.findResource("org/jboss/cache/Version.class");
+
+ for (URL u : classLoader.getURLs())
+ {
+ if (u2.toString().startsWith(u.toString()))
+ {
+ return u;
+ }
+ }
+
+ return null;
+ }
+
+
+ private <T> Set<Class<? extends T>> handleArchive(File file, Class<T> classType, Class<? extends Annotation> annotationType) throws IOException, ClassNotFoundException
+ {
+ Set<Class<? extends T>> classesWithAnnotations = new HashSet<Class<? extends T>>();
+ ZipFile zip = new ZipFile(file);
+ Enumeration<? extends ZipEntry> entries = zip.entries();
+ while (entries.hasMoreElements())
+ {
+ ZipEntry entry = entries.nextElement();
+ String name = entry.getName();
+ Class<? extends T> c = handleItem(name, classType, annotationType);
+ if (c != null) classesWithAnnotations.add(c);
+ }
+
+ return classesWithAnnotations;
+ }
+
+ private <T> Set<Class<? extends T>> handleDirectory(File file, String path, Class<T> classType, Class<? extends Annotation> annotationType) throws IOException, ClassNotFoundException
+ {
+ Set<Class<? extends T>> classesWithAnnotations = new HashSet<Class<? extends T>>();
+ for (File child : file.listFiles())
+ {
+ String newPath = path == null ? child.getName() : path + '/' + child.getName();
+ if (child.isDirectory())
+ {
+ classesWithAnnotations.addAll(handleDirectory(child, newPath, classType, annotationType));
+ }
+ else
+ {
+ Class<? extends T> c = handleItem(newPath, classType, annotationType);
+ if (c != null)
+ {
+ classesWithAnnotations.add(c);
+ }
+ }
+ }
+
+ return classesWithAnnotations;
+ }
+
+ private <T> Class<? extends T> handleItem(String name, Class<T> classType, Class<? extends Annotation> annotationType) throws IOException, ClassNotFoundException
+ {
+ if (!name.endsWith(".class")) return null;
+
+ Class<? extends T> c = getClassFile(filenameToClassname(name), classType);
+ if (c != null && hasAnnotation(c, annotationType))
+ {
+ return c;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private <T> Class<? extends T> getClassFile(String name, Class<T> classType) throws IOException, ClassNotFoundException
+ {
+ Class c = classLoader.loadClass(name);
+ if (c != null && classType.isAssignableFrom(c)) return c;
+ else return null;
+ }
+
+ private boolean hasAnnotation(Class clazz, Class<? extends Annotation> annotationType)
+ {
+ return (clazz.isAnnotationPresent(annotationType));
+ }
+
+ private static String filenameToClassname(String filename)
+ {
+ return filename.substring(0, filename.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
+ }
+
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/annotations/ComponentName.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/annotations/ComponentName.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/annotations/ComponentName.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,52 @@
+package org.jboss.cache.factories.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used with the {@link org.jboss.cache.factories.annotations.Inject} annotation to specify the name of a component to inject.
+ * Note that you still need to annotate your method with {@link org.jboss.cache.factories.annotations.Inject} for this
+ * annotation to be of any use. You can also combine injections of named and singleton components, see example below.
+ * <p/>
+ * Usage example:
+ * <p/>
+ * <pre>
+ * public class MyClass
+ * {
+ * private Map blahMap;
+ * private Map anotherMap;
+ * private List someList;
+ * private TransactionManager tm;
+ * <p/>
+ * & Inject
+ * public void setMyMap(&ComponentName("BlahMap") Map blahMap)
+ * {
+ * this.blahMap = blahMap;
+ * }
+ * & Inject
+ * public void injectMoreStuff(&ComponentName("AnotherMap") Map anotherMap, &ComponentName("SomeList") List someList, TransactionManager singletonTM)
+ * {
+ * this.anotherMap = anotherMap;
+ * this.someList = someList;
+ * this.tm = singletonTM;
+ * }
+ * }
+ * <p/>
+ * </pre>
+ *
+ * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik(a)jboss.org</a>)
+ * @see org.jboss.cache.factories.annotations.Inject
+ * @since 2.1.0
+ */
+// ensure this annotation is available at runtime.
+(a)Retention(RetentionPolicy.RUNTIME)
+
+// only applies to fields.
+(a)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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@Target(TYPE)
+(a)Retention(RetentionPolicy.RUNTIME)
+public @interface DefaultFactoryFor
+{
+ /**
+ * Components that may be constructed by a factory annotated with this annotation.
+ */
+ Class[] classes();
+}
Added: core/trunk/src/main/java/org/jboss/cache/factories/annotations/Inject.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/factories/annotations/Inject.java (rev 0)
+++ core/trunk/src/main/java/org/jboss/cache/factories/annotations/Inject.java 2007-12-11 17:25:09 UTC (rev 4832)
@@ -0,0 +1,55 @@
+package org.jboss.cache.factories.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to annotate a method as one that is used to inject a registered component into another component. The component
+ * to be constructed must be built using the {@link org.jboss.cache.factories.ComponentFactory#construct(Class)} method, or if your object that needs
+ * components injected into it already exists, it can be built using the {@link org.jboss.cache.factories.ComponentFactory#wireComponents(Object)}
+ * method.
+ * <p/>
+ * Usage example:
+ * <pre>
+ * public class MyClass
+ * {
+ * private TransactionManager tm;
+ * private BuddyManager bm;
+ * private Notifier n;
+ * <p/>
+ * &Inject
+ * public void setTransactionManager(TransactionManager tm)
+ * {
+ * this.tm = tm;
+ * }
+ * <p/>
+ * &Inject
+ * public void injectMoreStuff(BuddyManager bm, Notifier n)
+ * {
+ * this.bm = bm;
+ * this.n = n;
+ * }
+ * }
+ * <p/>
+ * </pre>
+ * and an instance of this class can be constructed and wired using
+ * <pre>
+ * MyClass myClass = componentFactory.construct(MyClass.class); // instance will have dependencies injected.
+ * </pre>
+ * If you wish to use named components, you can use the optional {@link org.jboss.cache.factories.annotations.ComponentName}
+ * annotation on each parameter.
+ *
+ * @author Manik Surtani
+ * @see ComponentName
+ * @since 2.1.0
+ */
+// ensure this annotation is available at runtime.
+(a)Retention(RetentionPolicy.RUNTIME)
+
+// only applies to fields.
+(a)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@jboss.org">manik(a)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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@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@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@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@jboss.org">manik(a)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);
+ }
+
+}
17 years
JBoss Cache SVN: r4831 - core/trunk.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2007-12-11 12:19:55 -0500 (Tue, 11 Dec 2007)
New Revision: 4831
Modified:
core/trunk/pom.xml
Log:
Updated JGroups
Modified: core/trunk/pom.xml
===================================================================
--- core/trunk/pom.xml 2007-12-11 17:18:58 UTC (rev 4830)
+++ core/trunk/pom.xml 2007-12-11 17:19:55 UTC (rev 4831)
@@ -21,7 +21,7 @@
<dependency>
<groupId>jgroups</groupId>
<artifactId>jgroups</artifactId>
- <version>2.6.1.GA</version>
+ <version>2.6.1</version>
</dependency>
<dependency>
<groupId>jdbm</groupId>
17 years
JBoss Cache SVN: r4830 - core/trunk.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2007-12-11 12:18:58 -0500 (Tue, 11 Dec 2007)
New Revision: 4830
Modified:
core/trunk/pom.xml
Log:
Updated JGroups
Modified: core/trunk/pom.xml
===================================================================
--- core/trunk/pom.xml 2007-12-11 17:13:57 UTC (rev 4829)
+++ core/trunk/pom.xml 2007-12-11 17:18:58 UTC (rev 4830)
@@ -21,14 +21,9 @@
<dependency>
<groupId>jgroups</groupId>
<artifactId>jgroups</artifactId>
- <version>2.6.0.GA</version>
+ <version>2.6.1.GA</version>
</dependency>
<dependency>
- <groupId>org.jboss.microcontainer</groupId>
- <artifactId>jboss-aop-mc-int</artifactId>
- <version>2.0.0.Beta6</version>
- </dependency>
- <dependency>
<groupId>jdbm</groupId>
<artifactId>jdbm</artifactId>
<version>1.0</version>
17 years
JBoss Cache SVN: r4829 - in core/trunk/src/test/java/org/jboss/cache: profiling and 1 other directory.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2007-12-11 12:13:57 -0500 (Tue, 11 Dec 2007)
New Revision: 4829
Added:
core/trunk/src/test/java/org/jboss/cache/profiling/
core/trunk/src/test/java/org/jboss/cache/profiling/AbstractProfileTest.java
core/trunk/src/test/java/org/jboss/cache/profiling/ProfileSlaveTest.java
core/trunk/src/test/java/org/jboss/cache/profiling/ProfileTest.java
Log:
Added tests to run a profiler on
Added: core/trunk/src/test/java/org/jboss/cache/profiling/AbstractProfileTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/profiling/AbstractProfileTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/profiling/AbstractProfileTest.java 2007-12-11 17:13:57 UTC (rev 4829)
@@ -0,0 +1,50 @@
+package org.jboss.cache.profiling;
+
+import org.jboss.cache.Cache;
+import org.jboss.cache.DefaultCacheFactory;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.factories.UnitTestCacheConfigurationFactory;
+import org.testng.annotations.AfterTest;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+/**
+ * // TODO Document this
+ *
+ * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@Test(groups = "profiling")
+public abstract class AbstractProfileTest
+{
+ protected Cache cache;
+
+ @BeforeTest
+ public void setUp()
+ {
+ Configuration cfg = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC);
+ cache = DefaultCacheFactory.getInstance().createCache(cfg, false);
+ }
+
+ @AfterTest
+ public void tearDown()
+ {
+ cache.stop();
+ }
+
+ public abstract void testReplSync() throws Exception;
+
+ public abstract void testReplAsync() throws Exception;
+
+ public abstract void testReplSyncOptimistic() throws Exception;
+
+ public abstract void testReplAsyncOptimistic() throws Exception;
+
+ public abstract void testReplSyncBR() throws Exception;
+
+ public abstract void testReplAsyncBR() throws Exception;
+
+ public abstract void testReplSyncOptBR() throws Exception;
+
+ public abstract void testReplAsyncOptBR() throws Exception;
+}
Added: core/trunk/src/test/java/org/jboss/cache/profiling/ProfileSlaveTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/profiling/ProfileSlaveTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/profiling/ProfileSlaveTest.java 2007-12-11 17:13:57 UTC (rev 4829)
@@ -0,0 +1,86 @@
+package org.jboss.cache.profiling;
+
+import org.jboss.cache.config.BuddyReplicationConfig;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.transaction.DummyTransactionManagerLookup;
+import org.testng.annotations.Test;
+
+/**
+ * Slave to go with ProfileTest. Should be done in a different VM. Can be profiled as well to profile receiving
+ * messages.
+ *
+ * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@Test(groups = "profiling")
+public class ProfileSlaveTest extends AbstractProfileTest
+{
+
+ private void waitForTest() throws Exception
+ {
+ System.out.println("Slave listening for remote connections. Hit Enter when done.");
+ System.in.read();
+ }
+
+ public void testReplSync() throws Exception
+ {
+ cache.start();
+ waitForTest();
+ }
+
+ public void testReplAsync() throws Exception
+ {
+ cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC);
+ cache.start();
+ waitForTest();
+ }
+
+ public void testReplSyncOptimistic() throws Exception
+ {
+ cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC);
+ cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
+ cache.start();
+ waitForTest();
+ }
+
+ public void testReplAsyncOptimistic() throws Exception
+ {
+ cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC);
+ cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC);
+ cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
+ cache.start();
+ waitForTest();
+ }
+
+ public void testReplSyncBR() throws Exception
+ {
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ cache.getConfiguration().setBuddyReplicationConfig(brc);
+ testReplSync();
+ }
+
+ public void testReplAsyncBR() throws Exception
+ {
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ cache.getConfiguration().setBuddyReplicationConfig(brc);
+ testReplAsync();
+ }
+
+ public void testReplSyncOptBR() throws Exception
+ {
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ cache.getConfiguration().setBuddyReplicationConfig(brc);
+ testReplSyncOptimistic();
+ }
+
+ public void testReplAsyncOptBR() throws Exception
+ {
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ cache.getConfiguration().setBuddyReplicationConfig(brc);
+ testReplAsyncOptimistic();
+ }
+}
Added: core/trunk/src/test/java/org/jboss/cache/profiling/ProfileTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/profiling/ProfileTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/profiling/ProfileTest.java 2007-12-11 17:13:57 UTC (rev 4829)
@@ -0,0 +1,173 @@
+package org.jboss.cache.profiling;
+
+import org.jboss.cache.Fqn;
+import org.jboss.cache.config.BuddyReplicationConfig;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.misc.TestingUtil;
+import org.jboss.cache.transaction.DummyTransactionManagerLookup;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test to use with a profiler to profile replication. To be used in conjunction with ProfileSlaveTest.
+ * <p/>
+ * Typical usage pattern:
+ * <p/>
+ * 1. Start a single test method in ProfileSlaveTest. This will block until you kill it.
+ * 2. Start the corresponding test in this class, with the same name, in a different JVM, and attached to a profiler.
+ * 3. Profile away!
+ * <p/>
+ *
+ * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik(a)jboss.org</a>)
+ * @since 2.1.0
+ */
+@Test(groups = "profiling")
+public class ProfileTest extends AbstractProfileTest
+{
+ /*
+ Test configuration options
+ */
+ protected static final long DURATION = 2 * 60000;
+ protected static final int NUM_THREADS = 10;
+ protected static final int MAX_RANDOM_SLEEP_MILLIS = 100;
+ protected static final int MAX_DEPTH = 8;
+ protected static final int MAX_OVERALL_NODES = 10000;
+
+
+ private List<Fqn> fqns = new ArrayList<Fqn>(MAX_OVERALL_NODES);
+ private Random r = new Random();
+
+ @BeforeTest
+ public void initialiseFqns()
+ {
+ fqns.clear();
+ for (int i = 0; i < MAX_OVERALL_NODES; i++)
+ {
+ Fqn fqn = createRandomFqn(r);
+ while (fqns.contains(fqn)) fqn = createRandomFqn(r);
+ if (i % 100 == 0) System.out.println("Generated " + i + " fqns");
+ fqns.add(fqn);
+ }
+ System.gc();
+ System.out.println("Finished initialising. Starting test.");
+ }
+
+ private Fqn createRandomFqn(Random r)
+ {
+ String s = "/";
+ int depth = r.nextInt(MAX_DEPTH);
+ for (int i = 0; i < depth; i++)
+ {
+ s += r.nextInt(Integer.MAX_VALUE) + "/";
+ }
+
+ return Fqn.fromString(s);
+ }
+
+ public void testReplSync() throws Exception
+ {
+ cache.start();
+ doTest();
+ }
+
+ public void testReplAsync() throws Exception
+ {
+ cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC);
+ cache.start();
+ doTest();
+ }
+
+ public void testReplSyncOptimistic() throws Exception
+ {
+ cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC);
+ cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
+ cache.start();
+ doTest();
+ }
+
+ public void testReplAsyncOptimistic() throws Exception
+ {
+ cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC);
+ cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC);
+ cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
+ cache.start();
+ doTest();
+ }
+
+ public void testReplSyncBR() throws Exception
+ {
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ cache.getConfiguration().setBuddyReplicationConfig(brc);
+ testReplSync();
+ }
+
+ public void testReplAsyncBR() throws Exception
+ {
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ cache.getConfiguration().setBuddyReplicationConfig(brc);
+ testReplAsync();
+ }
+
+ public void testReplSyncOptBR() throws Exception
+ {
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ cache.getConfiguration().setBuddyReplicationConfig(brc);
+ testReplSyncOptimistic();
+ }
+
+ public void testReplAsyncOptBR() throws Exception
+ {
+ BuddyReplicationConfig brc = new BuddyReplicationConfig();
+ brc.setEnabled(true);
+ cache.getConfiguration().setBuddyReplicationConfig(brc);
+ testReplAsyncOptimistic();
+ }
+
+ private void doTest() throws Exception
+ {
+ ExecutorService exec = Executors.newFixedThreadPool(NUM_THREADS);
+ long end = System.currentTimeMillis() + DURATION;
+
+ while (System.currentTimeMillis() < end)
+ {
+ exec.execute(new Runnable()
+ {
+ public void run()
+ {
+ String k = getRandomString();
+ String v = getRandomString();
+ cache.put(fqns.get(r.nextInt(MAX_OVERALL_NODES)), k, v);
+ }
+ });
+ TestingUtil.sleepRandom(MAX_RANDOM_SLEEP_MILLIS);
+ }
+
+ // wait for executors to complete!
+ exec.shutdown();
+ exec.awaitTermination(10000, TimeUnit.MILLISECONDS);
+
+ System.out.println("Completed Test!");
+ }
+
+ private String getRandomString()
+ {
+ StringBuilder sb = new StringBuilder();
+ int len = r.nextInt(10);
+
+ for (int i = 0; i < len; i++)
+ {
+ sb.append((char) (63 + r.nextInt(26)));
+ }
+ return sb.toString();
+ }
+}
17 years
Build failed in Hudson: jboss-cache-core-jdk1.5 » JBoss Cache - Core Edition #164
by jboss-qa-internal@redhat.com
See https://hudson.jboss.org/hudson/job/jboss-cache-core-jdk1.5/org.jboss.cac...
Changes:
[manik.surtani(a)jboss.com] Should use CacheSPI where possible in tests
------------------------------------------
started
Building remotely on dev47-linux
$ /qa/tools/opt/jdk1.5.0_12/bin/java -Xmx256m -cp /home/hudson/hudson_workspace/maven-agent.jar:/qa/tools/opt/maven-2.0.6/boot/classworlds-1.1.jar hudson.maven.agent.Main /qa/tools/opt/maven-2.0.6 /qa/services/hudson/hudson_1.149/slave.jar /home/hudson/hudson_workspace/maven-interceptor.jar
channel started
[INFO] Scanning for projects...
WAGON_VERSION: 1.0-beta-2
[INFO] ----------------------------------------------------------------------------
[INFO] Building JBoss Cache - Core Edition
[INFO] task-segment: [package]
[INFO] ----------------------------------------------------------------------------
[INFO] [enforcer:enforce {execution: enforce-java}]
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
[INFO] Compiling 297 source files to /home/hudson/hudson_workspace/workspace/jboss-cache-core-jdk1.5/./target/classes
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
[INFO] Compiling 330 source files to /home/hudson/hudson_workspace/workspace/jboss-cache-core-jdk1.5/./target/test-classes
[HUDSON] Archiving /home/hudson/hudson_workspace/workspace/jboss-cache-core-jdk1.5/./pom.xml
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Compilation failure
/home/hudson/hudson_workspace/workspace/jboss-cache-core-jdk1.5/./src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.java:[35,15] incompatible types
found : java.util.ArrayList<org.jboss.cache.CacheSPI<java.lang.Object,java.lang.Object>>
required: java.util.List<org.jboss.cache.CacheImpl<java.lang.Object,java.lang.Object>>
/home/hudson/hudson_workspace/workspace/jboss-cache-core-jdk1.5/./src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.java:[70,15] incompatible types
found : java.util.ArrayList<org.jboss.cache.CacheSPI<java.lang.Object,java.lang.Object>>
required: java.util.List<org.jboss.cache.CacheImpl<java.lang.Object,java.lang.Object>>
/home/hudson/hudson_workspace/workspace/jboss-cache-core-jdk1.5/./src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.java:[152,15] incompatible types
found : java.util.ArrayList<org.jboss.cache.CacheSPI<java.lang.Object,java.lang.Object>>
required: java.util.List<org.jboss.cache.CacheImpl<java.lang.Object,java.lang.Object>>
[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 minute 54 seconds
[INFO] Finished at: Tue Dec 11 12:07:36 EST 2007
[INFO] Final Memory: 17M/63M
[INFO] ------------------------------------------------------------------------
Sending e-mails to: dpospisi(a)redhat.com manik.surtani(a)jboss.com
Build was marked for publishing on https://hudson.jboss.org/hudson/
finished: FAILURE
17 years
JBoss Cache SVN: r4828 - in core/trunk/src: test/java/org/jboss/cache/optimistic and 1 other directory.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2007-12-11 12:07:21 -0500 (Tue, 11 Dec 2007)
New Revision: 4828
Added:
core/trunk/src/test/java/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java
Removed:
core/trunk/src/test/java/org/jboss/cache/optimistic/OpLockingInterceptorTest.java
Modified:
core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticLockingInterceptor.java
Log:
JBCACHE-1228 - opt locking interceptor always acquires WRITE locks
Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticLockingInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticLockingInterceptor.java 2007-12-11 17:00:59 UTC (rev 4827)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/OptimisticLockingInterceptor.java 2007-12-11 17:07:21 UTC (rev 4828)
@@ -6,8 +6,14 @@
*/
package org.jboss.cache.interceptors;
-import org.jboss.cache.*;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.NodeSPI;
import org.jboss.cache.lock.NodeLock;
+import static org.jboss.cache.lock.NodeLock.LockType.READ;
+import static org.jboss.cache.lock.NodeLock.LockType.WRITE;
import org.jboss.cache.optimistic.TransactionWorkspace;
import org.jboss.cache.optimistic.WorkspaceNode;
import org.jboss.cache.transaction.GlobalTransaction;
@@ -48,16 +54,20 @@
try
{
TransactionWorkspace<?, ?> workspace = getTransactionWorkspace(gtx);
+ TransactionEntry te = cache.getTransactionTable().get(gtx);
if (log.isDebugEnabled()) log.debug("Locking nodes in transaction workspace for GlobalTransaction " + gtx);
for (WorkspaceNode workspaceNode : workspace.getNodes().values())
{
NodeSPI node = workspaceNode.getNode();
- boolean acquired = node.getLock().acquire(gtx, lockAcquisitionTimeout, NodeLock.LockType.WRITE);
+
+ boolean isWriteLockNeeded = workspaceNode.isDirty() || (workspaceNode.isChildrenModified() && (configuration.isLockParentForChildInsertRemove() || node.isLockForChildInsertRemove()));
+
+ boolean acquired = node.getLock().acquire(gtx, lockAcquisitionTimeout, isWriteLockNeeded ? WRITE : READ);
if (acquired)
{
if (trace) log.trace("Acquired lock on node " + node.getFqn());
- cache.getTransactionTable().addLock(gtx, node.getLock());
+ te.addLock(node.getLock());
}
else
{
@@ -102,15 +112,14 @@
throw new CacheException("_lock() passed up the interceptor stack when Optimistic Locking is used. This is NOT supported.");
}
- private Object transactionFinalized(InvocationContext ctx)
- throws Throwable
+ private Object transactionFinalized(InvocationContext ctx) throws Throwable
{
Object retval = null;
// we need to let the stack run its commits or rollbacks first -
// we unlock last - even if an exception occurs
try
- {
- retval = nextInterceptor(ctx);
+ {
+ retval = nextInterceptor(ctx);
}
finally
{
Deleted: core/trunk/src/test/java/org/jboss/cache/optimistic/OpLockingInterceptorTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/optimistic/OpLockingInterceptorTest.java 2007-12-11 17:00:59 UTC (rev 4827)
+++ core/trunk/src/test/java/org/jboss/cache/optimistic/OpLockingInterceptorTest.java 2007-12-11 17:07:21 UTC (rev 4828)
@@ -1,366 +0,0 @@
-/*
- * Created on 17-Feb-2005
- *
- *
- *
- */
-package org.jboss.cache.optimistic;
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertNotNull;
-import static org.testng.AssertJUnit.assertTrue;
-import static org.testng.AssertJUnit.fail;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import javax.transaction.Transaction;
-
-import org.jboss.cache.CacheImpl;
-import org.jboss.cache.Fqn;
-import org.jboss.cache.NodeSPI;
-import org.jboss.cache.interceptors.Interceptor;
-import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor;
-import org.jboss.cache.interceptors.OptimisticLockingInterceptor;
-import org.jboss.cache.interceptors.OptimisticNodeInterceptor;
-import org.jboss.cache.loader.SamplePojo;
-import org.jboss.cache.lock.NodeLock;
-import org.jboss.cache.marshall.MethodCall;
-import org.jboss.cache.marshall.MethodCallFactory;
-import org.jboss.cache.marshall.MethodDeclarations;
-import org.jboss.cache.transaction.DummyTransactionManager;
-import org.jboss.cache.transaction.GlobalTransaction;
-import org.jboss.cache.transaction.OptimisticTransactionEntry;
-import org.jboss.cache.transaction.TransactionTable;
-
-/**
- * @author xenephon
- */
-@SuppressWarnings("unchecked")
-public class OpLockingInterceptorTest extends AbstractOptimisticTestCase
-{
- public void testTransactionPrepareMethod() throws Exception
- {
-
- TestListener listener = new TestListener();
- final CacheImpl cache = createCacheWithListener(listener);
-
- Interceptor lockingInterceptor = new OptimisticLockingInterceptor();
- lockingInterceptor.setCache(cache);
- Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
- interceptor.setCache(cache);
- Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
- nodeInterceptor.setCache(cache);
- MockInterceptor dummy = new MockInterceptor();
- dummy.setCache(cache);
- lockingInterceptor.setNext(interceptor);
- interceptor.setNext(nodeInterceptor);
- nodeInterceptor.setNext(dummy);
-
- cache.setInterceptorChain(lockingInterceptor);
-
- // first set up a node with a pojo
- DummyTransactionManager mgr = DummyTransactionManager.getInstance();
- mgr.begin();
- Transaction tx = mgr.getTransaction();
-
- // inject InvocationContext
- cache.getInvocationContext().setTransaction(tx);
- cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));
-
- SamplePojo pojo = new SamplePojo(21, "test");
- Map temp = new HashMap();
- temp.put("key1", pojo);
- cache.put("/one/two", temp);
-
- assertEquals(null, dummy.getCalled());
- TransactionTable table = cache.getTransactionTable();
-
- GlobalTransaction gtx = table.get(tx);
-
- OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
-
- TransactionWorkspace workspace = entry.getTransactionWorkSpace();
-
- /*GlobalTransaction.class,
- List.class,
- Address.class,
- boolean.class*/
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertTrue(entry.getLocks().isEmpty());
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- assertEquals(null, dummy.getCalled());
-
- //now let us do a prepare
- MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
- try
- {
- cache._replicate(prepareMethod);
- }
- catch (Throwable t)
- {
-
- }
-
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- NodeLock lock = (NodeLock) it.next();
- assertTrue(lock.isWriteLocked());
- assertEquals(gtx, lock.getWriterOwner());
- }
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- //assertEquals(null,dummy.getCalled());
-
-
- mgr.commit();
-
- cache.stop();
-
- }
-
- public void testTransactionCommitMethod() throws Exception
- {
-
- TestListener listener = new TestListener();
- final CacheImpl cache = createCacheWithListener(listener);
-
- Interceptor lockingInterceptor = new OptimisticLockingInterceptor();
- lockingInterceptor.setCache(cache);
- Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
- interceptor.setCache(cache);
- Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
- nodeInterceptor.setCache(cache);
- MockInterceptor dummy = new MockInterceptor();
- dummy.setCache(cache);
- lockingInterceptor.setNext(interceptor);
- interceptor.setNext(nodeInterceptor);
- nodeInterceptor.setNext(dummy);
-
- cache.setInterceptorChain(lockingInterceptor);
-
- // first set up a node with a pojo
- DummyTransactionManager mgr = DummyTransactionManager.getInstance();
- mgr.begin();
- Transaction tx = mgr.getTransaction();
-
- // inject InvocationContext
- cache.getInvocationContext().setTransaction(tx);
- cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));
-
- SamplePojo pojo = new SamplePojo(21, "test");
- Map temp = new HashMap();
- temp.put("key1", pojo);
- cache.put("/one/two", temp);
-
- assertEquals(null, dummy.getCalled());
- TransactionTable table = cache.getTransactionTable();
-
- GlobalTransaction gtx = table.get(tx);
-
- OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
-
- TransactionWorkspace workspace = entry.getTransactionWorkSpace();
-
- /*GlobalTransaction.class,
- List.class,
- Address.class,
- boolean.class*/
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertTrue(entry.getLocks().isEmpty());
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- assertEquals(null, dummy.getCalled());
-
- //now let us do a prepare
- MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
- try
- {
- cache._replicate(prepareMethod);
- }
- catch (Throwable t)
- {
-
- }
-
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- NodeLock lock = (NodeLock) it.next();
- assertTrue(lock.isWriteLocked());
- assertEquals(gtx, lock.getWriterOwner());
- }
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- //assertEquals(null,dummy.getCalled());
- assertEquals(MethodDeclarations.optimisticPrepareMethod, dummy.getCalled());
-
-
- MethodCall commitMethod = MethodCallFactory.create(MethodDeclarations.commitMethod, new Object[]{gtx});
- try
- {
- cache._replicate(commitMethod);
- }
- catch (Throwable t)
- {
- fail();
- }
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- NodeLock lock = (NodeLock) it.next();
- assertEquals(false, lock.isLocked());
-
- }
- //make sure the nodes and locks are the same order
- int i = 0;
- for (Iterator it = workspace.getNodes().values().iterator(); it.hasNext();)
- {
- NodeSPI node = ((WorkspaceNode) it.next()).getNode();
- assertEquals(node.getLock(), entry.getLocks().get(i));
- i++;
- }
- assertEquals(MethodDeclarations.commitMethod, dummy.getCalled());
- mgr.commit();
-
- cache.stop();
-
- }
-
- public void testTransactionRollbackMethod() throws Exception
- {
-
- TestListener listener = new TestListener();
- final CacheImpl cache = createCacheWithListener(listener);
-
- Interceptor lockingInterceptor = new OptimisticLockingInterceptor();
- lockingInterceptor.setCache(cache);
- Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
- interceptor.setCache(cache);
- Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
- nodeInterceptor.setCache(cache);
- MockInterceptor dummy = new MockInterceptor();
- dummy.setCache(cache);
- lockingInterceptor.setNext(interceptor);
- interceptor.setNext(nodeInterceptor);
- nodeInterceptor.setNext(dummy);
-
- cache.setInterceptorChain(lockingInterceptor);
-
- // first set up a node with a pojo
- DummyTransactionManager mgr = DummyTransactionManager.getInstance();
- mgr.begin();
- Transaction tx = mgr.getTransaction();
-
- // inject InvocationContext
- cache.getInvocationContext().setTransaction(tx);
- cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));
-
- SamplePojo pojo = new SamplePojo(21, "test");
- Map temp = new HashMap();
- temp.put("key1", pojo);
- cache.put("/one/two", temp);
-
- assertEquals(null, dummy.getCalled());
- TransactionTable table = cache.getTransactionTable();
-
- GlobalTransaction gtx = table.get(tx);
-
- OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
-
- TransactionWorkspace workspace = entry.getTransactionWorkSpace();
-
- /*GlobalTransaction.class,
- List.class,
- Address.class,
- boolean.class*/
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertTrue(entry.getLocks().isEmpty());
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- assertEquals(null, dummy.getCalled());
-
- //now let us do a prepare
- MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
- try
- {
- cache._replicate(prepareMethod);
- }
- catch (Throwable t)
- {
-
- }
-
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- NodeLock lock = (NodeLock) it.next();
- assertTrue(lock.isWriteLocked());
- assertEquals(gtx, lock.getWriterOwner());
- }
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- assertEquals(MethodDeclarations.optimisticPrepareMethod, dummy.getCalled());
-
-
- MethodCall rollbackMethod = MethodCallFactory.create(MethodDeclarations.rollbackMethod, new Object[]{gtx});
- try
- {
- cache._replicate(rollbackMethod);
- }
- catch (Throwable t)
- {
- fail();
- }
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- NodeLock lock = (NodeLock) it.next();
- assertEquals(false, lock.isLocked());
-
- }
- //make sure the nodes and locks are the same order
- int i = 0;
- for (Iterator it = workspace.getNodes().values().iterator(); it.hasNext();)
- {
- NodeSPI node = ((WorkspaceNode) it.next()).getNode();
- assertEquals(node.getLock(), entry.getLocks().get(i));
- i++;
- }
- assertEquals(MethodDeclarations.rollbackMethod, dummy.getCalled());
- mgr.commit();
-
- cache.stop();
-
- }
-
-}
Added: core/trunk/src/test/java/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java 2007-12-11 17:07:21 UTC (rev 4828)
@@ -0,0 +1,246 @@
+package org.jboss.cache.optimistic;
+
+import junit.framework.Assert;
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.InvocationContext;
+import org.jboss.cache.NodeSPI;
+import org.jboss.cache.interceptors.Interceptor;
+import org.jboss.cache.interceptors.OptimisticInterceptor;
+import org.jboss.cache.interceptors.OptimisticLockingInterceptor;
+import org.jboss.cache.lock.NodeLock;
+import static org.jboss.cache.lock.NodeLock.LockType.READ;
+import static org.jboss.cache.lock.NodeLock.LockType.WRITE;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import javax.transaction.TransactionManager;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * // Test for JBCACHE-1228
+ *
+ * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik(a)jboss.org</a>)
+ */
+@Test(groups = "functional")
+public class OptimisticLockInterceptorTest extends AbstractOptimisticTestCase
+{
+ private CacheSPI<Object, Object> cache;
+ private LockReportInterceptor lri;
+ private Fqn parent = Fqn.fromString("/parent");
+ private Fqn child = Fqn.fromString("/parent/child");
+ private TransactionManager tm;
+
+ @BeforeMethod
+ protected void setUp() throws Exception
+ {
+ cache = createCache();
+ lri = new LockReportInterceptor();
+ lri.setCache(cache);
+
+ Interceptor i = cache.getInterceptorChain().get(0);
+ Interceptor lockInterceptor = i;
+ // find the OptimisticLockInterceptor and insert LockReportInterceptor after that.
+ while (!(lockInterceptor instanceof OptimisticLockingInterceptor))
+ {
+ System.out.println("Inspecting " + lockInterceptor.getClass().getSimpleName());
+ lockInterceptor = lockInterceptor.getNext();
+ }
+ Interceptor next = lockInterceptor.getNext();
+
+ lockInterceptor.setNext(lri);
+ lri.setNext(next);
+
+ cache.put(child, "key", "value");
+
+ tm = cache.getTransactionManager();
+ }
+
+ @AfterMethod
+ public void tearDown()
+ {
+ cache.stop();
+ }
+
+ public void testPut() throws Exception
+ {
+ tm.begin();
+ cache.put(child, "key2", "value2");
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsReadLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testGet() throws Exception
+ {
+ tm.begin();
+ cache.get(child, "key2");
+ lri.reset();
+ // nothing is stale, expecting nothing here.
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testRemove() throws Exception
+ {
+ tm.begin();
+ cache.remove(child, "key2");
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsReadLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testPutLockParentForCIR() throws Exception
+ {
+ cache.getConfiguration().setLockParentForChildInsertRemove(true);
+ cache.removeNode(parent);
+ cache.put(parent, "k", "v");
+
+ tm.begin();
+ cache.put(child, "key2", "value2");
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsWriteLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testGetLockParentForCIR() throws Exception
+ {
+ cache.getConfiguration().setLockParentForChildInsertRemove(true);
+ tm.begin();
+ cache.get(child, "key2");
+ lri.reset();
+ // nothing is stale, expecting nothing here.
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testRemoveLockParentForCIR() throws Exception
+ {
+ cache.getConfiguration().setLockParentForChildInsertRemove(true);
+ tm.begin();
+ cache.removeNode(child);
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsWriteLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+
+ public void testPutNodeNotExists() throws Exception
+ {
+ cache.removeNode(Fqn.ROOT);
+ tm.begin();
+ cache.put(child, "key2", "value2");
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsWriteLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testGetNodeNotExists() throws Exception
+ {
+ cache.removeNode(Fqn.ROOT);
+ tm.begin();
+ cache.get(child, "key2");
+ lri.reset();
+ // nothing is stale, expecting nothing here.
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testRemoveNodeNotExists() throws Exception
+ {
+ cache.removeNode(Fqn.ROOT);
+ tm.begin();
+ cache.remove(child, "key2");
+ lri.reset();
+ // nothing is stale, expecting nothing here.
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ private void assertNoStaleLocks()
+ {
+ assert cache.getNumberOfLocksHeld() == 0;
+ }
+}
+
+class LockReportInterceptor extends OptimisticInterceptor
+{
+ private Map<Fqn, NodeLock.LockType> expected = new HashMap<Fqn, NodeLock.LockType>();
+ private Map<Fqn, NodeLock.LockType> actual = new HashMap<Fqn, NodeLock.LockType>();
+
+ void reset()
+ {
+ expected.clear();
+ actual.clear();
+ }
+
+ void assertReceivedExpectedLocks()
+ {
+ Assert.assertEquals(expected, actual);
+ }
+
+ void expectsReadLock(Fqn f)
+ {
+ expected.put(f, READ);
+ }
+
+ void expectsWriteLock(Fqn f)
+ {
+ expected.put(f, WRITE);
+ }
+
+ @Override
+ public Object invoke(InvocationContext ctx) throws Throwable
+ {
+ TransactionWorkspace w = getTransactionWorkspace(ctx.getGlobalTransaction());
+ Map nodeMap = w.getNodes();
+ for (Iterator i = nodeMap.keySet().iterator(); i.hasNext();)
+ {
+ WorkspaceNode wn = (WorkspaceNode) nodeMap.get(i.next());
+ NodeSPI n = wn.getNode();
+ NodeLock lock = n.getLock();
+ if (lock.isLocked())
+ {
+ actual.put(n.getFqn(), lock.isReadLocked() ? READ : WRITE);
+ }
+ }
+
+ return super.invoke(ctx);
+ }
+}
17 years
JBoss Cache SVN: r4827 - core/trunk/src/test/java/org/jboss/cache/buddyreplication.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2007-12-11 12:00:59 -0500 (Tue, 11 Dec 2007)
New Revision: 4827
Modified:
core/trunk/src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.java
Log:
Should use CacheSPI where possible in tests
Modified: core/trunk/src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.java 2007-12-11 16:41:18 UTC (rev 4826)
+++ core/trunk/src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.java 2007-12-11 17:00:59 UTC (rev 4827)
@@ -6,19 +6,14 @@
*/
package org.jboss.cache.buddyreplication;
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertFalse;
-import static org.testng.AssertJUnit.assertNotNull;
-import static org.testng.AssertJUnit.assertNull;
-
-import java.util.ArrayList;
-
-import org.jboss.cache.Cache;
-import org.jboss.cache.CacheImpl;
+import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.misc.TestingUtil;
+import static org.testng.AssertJUnit.*;
import org.testng.annotations.Test;
+import java.util.ArrayList;
+
/**
* Tests how groups are formed and disbanded
*
@@ -37,7 +32,7 @@
public void testNonRegionBasedStateTransfer() throws Exception
{
- caches = new ArrayList<CacheImpl<Object, Object>>();
+ caches = new ArrayList<CacheSPI<Object, Object>>();
caches.add(createCache(1, "TEST", false, true));
Fqn<String> main = Fqn.fromString("/a/b/c");
@@ -45,18 +40,18 @@
caches.add(createCache(1, "TEST", false, true));
- TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheImpl[0]), VIEW_BLOCK_TIMEOUT);
+ TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), VIEW_BLOCK_TIMEOUT);
TestingUtil.sleepThread(getSleepTimeout());
Fqn<String> test = new Fqn<String>(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN,
- BuddyManager.getGroupNameFromAddress(caches.get(0).getLocalAddress()));
+ BuddyManager.getGroupNameFromAddress(caches.get(0).getLocalAddress()));
test = new Fqn<String>(test, main);
assertEquals("State transferred", "Joe", caches.get(1).get(test, "name"));
caches.add(createCache(1, "TEST", false, true));
- TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheImpl[0]), VIEW_BLOCK_TIMEOUT);
+ TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), VIEW_BLOCK_TIMEOUT);
TestingUtil.sleepThread(getSleepTimeout());
assertNull("State not transferred", caches.get(2).get(test, "name"));
@@ -69,11 +64,11 @@
assertEquals("State transferred", "Joe", caches.get(2).get(test, "name"));
}
-
+
public void testRegionBasedStateTransfer() throws Exception
{
- caches = new ArrayList<CacheImpl<Object, Object>>();
-
+ caches = new ArrayList<CacheSPI<Object, Object>>();
+
caches.add(createCache(1, "TEST", false, false));
caches.add(createCache(1, "TEST", false, false));
caches.add(createCache(1, "TEST", false, false));
@@ -93,14 +88,14 @@
caches.get(1).start();
caches.get(2).start();
caches.get(3).start();
-
- TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheImpl[0]), VIEW_BLOCK_TIMEOUT);
+
+ TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), VIEW_BLOCK_TIMEOUT);
TestingUtil.sleepThread(getSleepTimeout());
-
+
Fqn fqnA = Fqn.fromString("/a");
Fqn fqnD = Fqn.fromString("/d");
-
- // FIXME We have to use a hack to get JBC to recognize that our
+
+ // FIXME We have to use a hack to get JBC to recognize that our
// regions are for marshalling
ClassLoader cl = Fqn.class.getClassLoader();
caches.get(0).getRegion(fqnA, true).registerContextClassLoader(cl);
@@ -111,36 +106,36 @@
caches.get(1).getRegion(fqnD, true).registerContextClassLoader(cl);
caches.get(2).getRegion(fqnD, true).registerContextClassLoader(cl);
caches.get(3).getRegion(fqnD, true).registerContextClassLoader(cl);
-
+
caches.get(0).getRegion(fqnA, true).activate();
caches.get(1).getRegion(fqnA, true).activate();
caches.get(2).getRegion(fqnA, true).activate();
caches.get(3).getRegion(fqnA, true).activate();
-
+
caches.get(0).getRegion(fqnD, true).activate();
caches.get(1).getRegion(fqnD, true).activate();
-
+
Fqn mainA = Fqn.fromString("/a/b/c");
caches.get(0).put(mainA, "name", "Joe");
-
+
Fqn mainD = Fqn.fromString("/d/e/f");
caches.get(0).put(mainD, "name", "Joe");
-
- Fqn group = new Fqn(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN,
- BuddyManager.getGroupNameFromAddress(caches.get(0).getLocalAddress()));
+
+ Fqn group = new Fqn(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN,
+ BuddyManager.getGroupNameFromAddress(caches.get(0).getLocalAddress()));
Fqn testA = new Fqn(group, mainA);
assertEquals("/a replicated", "Joe", caches.get(1).get(testA, "name"));
assertNull("No backup of /a", caches.get(2).get(testA, "name"));
-
+
Fqn testD = new Fqn(group, mainD);
assertEquals("/d replicated", "Joe", caches.get(1).get(testD, "name"));
assertNull("No backup of /d", caches.get(2).get(testD, "name"));
-
+
// Make 2 the buddy of 0 -- this should cause a push from 0 to 2
caches.get(1).stop();
- TestingUtil.sleepThread(getSleepTimeout());
-
+ TestingUtil.sleepThread(getSleepTimeout());
+
assertEquals("/a state transferred", "Joe", caches.get(2).get(testA, "name"));
assertNull("/d state not transferred", caches.get(2).get(testD, "name"));
@@ -154,7 +149,7 @@
public void testPersistentStateTransfer() throws Exception
{
- caches = new ArrayList<CacheImpl<Object, Object>>();
+ caches = new ArrayList<CacheSPI<Object, Object>>();
caches.add(createCacheWithCacheLoader(false, false, false, true, false));
caches.get(0).getConfiguration().setFetchInMemoryState(false);
@@ -169,13 +164,13 @@
caches.get(1).start();
- TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheImpl[0]), VIEW_BLOCK_TIMEOUT);
+ TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), VIEW_BLOCK_TIMEOUT);
TestingUtil.sleepThread(getSleepTimeout());
Fqn test = BuddyManager.getBackupFqn(caches.get(0).getLocalAddress(), main);
assertFalse("/a/b/c shld not be bin memory", caches.get(1).exists(test));
- assertNotNull("/a/b/c shld be in CL", caches.get(1).getCacheLoader().get(test));
+ assertNotNull("/a/b/c shld be in CL", caches.get(1).getCacheLoaderManager().getCacheLoader().get(test));
assertEquals("/a/b/c shld in cache loader", "Joe", caches.get(1).get(test, "name"));
}
}
\ No newline at end of file
17 years
JBoss Cache SVN: r4826 - in core/branches/1.4.X: tests/functional/org/jboss/cache/optimistic and 1 other directory.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2007-12-11 11:41:18 -0500 (Tue, 11 Dec 2007)
New Revision: 4826
Added:
core/branches/1.4.X/tests/functional/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java
Removed:
core/branches/1.4.X/tests/functional/org/jboss/cache/optimistic/OpLockingInterceptorTest.java
Modified:
core/branches/1.4.X/src/org/jboss/cache/interceptors/OptimisticLockingInterceptor.java
core/branches/1.4.X/src/org/jboss/cache/interceptors/OptimisticNodeInterceptor.java
Log:
JBCACHE-1228 - optimistic locking interceptor always acquired write locks
Modified: core/branches/1.4.X/src/org/jboss/cache/interceptors/OptimisticLockingInterceptor.java
===================================================================
--- core/branches/1.4.X/src/org/jboss/cache/interceptors/OptimisticLockingInterceptor.java 2007-12-10 23:47:41 UTC (rev 4825)
+++ core/branches/1.4.X/src/org/jboss/cache/interceptors/OptimisticLockingInterceptor.java 2007-12-11 16:41:18 UTC (rev 4826)
@@ -8,6 +8,7 @@
import org.jboss.cache.CacheException;
import org.jboss.cache.DataNode;
+import org.jboss.cache.Fqn;
import org.jboss.cache.GlobalTransaction;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.TransactionEntry;
@@ -20,7 +21,10 @@
import java.lang.reflect.Method;
import java.util.Collection;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
/**
* Locks nodes during transaction boundaries
@@ -137,9 +141,10 @@
}
- private Object lockNodes(GlobalTransaction gtx) throws Exception
+ private void lockNodes(GlobalTransaction gtx) throws Exception
{
TransactionWorkspace workspace = getTransactionWorkspace(gtx);
+ TransactionEntry te = cache.getTransactionTable().get(gtx);
log.debug("locking nodes");
// should be an ordered list
@@ -150,20 +155,19 @@
WorkspaceNode workspaceNode = (WorkspaceNode) it.next();
DataNode node = workspaceNode.getNode();
- boolean acquired = node.acquire(gtx, lockAcquisitionTimeout, DataNode.LOCK_TYPE_WRITE);
+ boolean writeLock = workspaceNode.isDirty() || workspaceNode.isCreated() || workspaceNode.isDeleted() || (workspaceNode.isChildrenModified() && cache.getLockParentForChildInsertRemove());
+
+ boolean acquired = node.acquire(gtx, lockAcquisitionTimeout, writeLock ? DataNode.LOCK_TYPE_WRITE : DataNode.LOCK_TYPE_READ);
if (acquired)
{
if (log.isTraceEnabled()) log.trace("acquired lock on node " + node.getName());
- cache.getTransactionTable().addLock(gtx, node.getLock());
+ te.addLock(node.getLock());
}
else
{
throw new CacheException("unable to acquire lock on node " + node.getName());
}
-
}
- return null;
-
}
Modified: core/branches/1.4.X/src/org/jboss/cache/interceptors/OptimisticNodeInterceptor.java
===================================================================
--- core/branches/1.4.X/src/org/jboss/cache/interceptors/OptimisticNodeInterceptor.java 2007-12-10 23:47:41 UTC (rev 4825)
+++ core/branches/1.4.X/src/org/jboss/cache/interceptors/OptimisticNodeInterceptor.java 2007-12-11 16:41:18 UTC (rev 4826)
@@ -366,13 +366,16 @@
DataNode node = cache.peek(fqn);
if (node == null)
{
- return null; // seems to happen quite a bit
+ workspaceNode = null; // seems to happen quite a bit
}
- workspaceNode = NodeFactory.getInstance().createWorkspaceNode(node, workspace);
- workspace.addNode(workspaceNode);
+ else
+ {
+ workspaceNode = NodeFactory.getInstance().createWorkspaceNode(node, workspace);
+ workspace.addNode(workspaceNode);
+ }
}
// the node has been deleted dude!
- if (workspaceNode.isDeleted())
+ if (workspaceNode != null && workspaceNode.isDeleted())
{
if (log.isDebugEnabled()) log.debug("Node " + fqn + " has been deleted in the workspace.");
if (undeleteIfNecessary)
@@ -393,6 +396,13 @@
log.trace("Setting versioning to explicit");
workspaceNode.setVersioningImplicit(false);
}
+
+ // now make sure all parents are in the wsp as well
+ if (workspaceNode != null)
+ {
+ if (!fqn.isRoot()) getOrCreateWorkspaceNode(fqn.getParent(), workspace, false);
+ }
+
return workspaceNode;
}
}
Deleted: core/branches/1.4.X/tests/functional/org/jboss/cache/optimistic/OpLockingInterceptorTest.java
===================================================================
--- core/branches/1.4.X/tests/functional/org/jboss/cache/optimistic/OpLockingInterceptorTest.java 2007-12-10 23:47:41 UTC (rev 4825)
+++ core/branches/1.4.X/tests/functional/org/jboss/cache/optimistic/OpLockingInterceptorTest.java 2007-12-11 16:41:18 UTC (rev 4826)
@@ -1,366 +0,0 @@
-/*
- * Created on 17-Feb-2005
- *
- *
- *
- */
-package org.jboss.cache.optimistic;
-
-import org.jboss.cache.*;
-import org.jboss.cache.interceptors.Interceptor;
-import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor;
-import org.jboss.cache.interceptors.OptimisticLockingInterceptor;
-import org.jboss.cache.interceptors.OptimisticNodeInterceptor;
-import org.jboss.cache.loader.SamplePojo;
-import org.jboss.cache.lock.IdentityLock;
-import org.jboss.cache.marshall.MethodCallFactory;
-import org.jboss.cache.marshall.MethodDeclarations;
-import org.jboss.cache.transaction.DummyTransactionManager;
-import org.jgroups.blocks.MethodCall;
-
-import javax.transaction.Transaction;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * @author xenephon
- */
-public class OpLockingInterceptorTest extends AbstractOptimisticTestCase
-{
-
-
- /**
- * @param name
- */
- public OpLockingInterceptorTest(String name)
- {
- super(name);
-
- }
-
- public void testTransactionPrepareMethod() throws Exception
- {
-
- TestListener listener = new TestListener();
- final TreeCache cache = createCacheWithListener(listener);
-
- Interceptor lockingInterceptor = new OptimisticLockingInterceptor();
- lockingInterceptor.setCache(cache);
- Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
- interceptor.setCache(cache);
- Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
- nodeInterceptor.setCache(cache);
- MockInterceptor dummy = new MockInterceptor();
- dummy.setCache(cache);
- lockingInterceptor.setNext(interceptor);
- interceptor.setNext(nodeInterceptor);
- nodeInterceptor.setNext(dummy);
-
- cache.setInterceptorChain(lockingInterceptor);
-
-// first set up a node with a pojo
- DummyTransactionManager mgr = DummyTransactionManager.getInstance();
- mgr.begin();
- Transaction tx = mgr.getTransaction();
-
- // inject InvocationContext
- cache.getInvocationContext().setTransaction(tx);
- cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));
-
- SamplePojo pojo = new SamplePojo(21, "test");
- Map temp = new HashMap();
- temp.put("key1", pojo);
- cache.put("/one/two", temp);
-
- assertEquals(null, dummy.getCalled());
- TransactionTable table = cache.getTransactionTable();
-
- GlobalTransaction gtx = table.get(tx);
-
- OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
-
- TransactionWorkspace workspace = entry.getTransactionWorkSpace();
-
- /*GlobalTransaction.class,
- List.class,
- Address.class,
- boolean.class*/
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertTrue(entry.getLocks().isEmpty());
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- assertEquals(null, dummy.getCalled());
-
- //now let us do a prepare
- MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
- try
- {
- cache._replicate(prepareMethod);
- }
- catch (Throwable t)
- {
-
- }
-
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- IdentityLock lock = (IdentityLock) it.next();
- assertTrue(lock.isWriteLocked());
- assertEquals(gtx, lock.getWriterOwner());
- }
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- //assertEquals(null,dummy.getCalled());
-
-
- mgr.commit();
-
- cache.stopService();
-
- }
-
- public void testTransactionCommitMethod() throws Exception
- {
-
- TestListener listener = new TestListener();
- final TreeCache cache = createCacheWithListener(listener);
-
- Interceptor lockingInterceptor = new OptimisticLockingInterceptor();
- lockingInterceptor.setCache(cache);
- Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
- interceptor.setCache(cache);
- Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
- nodeInterceptor.setCache(cache);
- MockInterceptor dummy = new MockInterceptor();
- dummy.setCache(cache);
- lockingInterceptor.setNext(interceptor);
- interceptor.setNext(nodeInterceptor);
- nodeInterceptor.setNext(dummy);
-
- cache.setInterceptorChain(lockingInterceptor);
-
-// first set up a node with a pojo
- DummyTransactionManager mgr = DummyTransactionManager.getInstance();
- mgr.begin();
- Transaction tx = mgr.getTransaction();
-
- // inject InvocationContext
- cache.getInvocationContext().setTransaction(tx);
- cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));
-
- SamplePojo pojo = new SamplePojo(21, "test");
- Map temp = new HashMap();
- temp.put("key1", pojo);
- cache.put("/one/two", temp);
-
- assertEquals(null, dummy.getCalled());
- TransactionTable table = cache.getTransactionTable();
-
- GlobalTransaction gtx = table.get(tx);
-
- OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
-
- TransactionWorkspace workspace = entry.getTransactionWorkSpace();
-
- /*GlobalTransaction.class,
- List.class,
- Address.class,
- boolean.class*/
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertTrue(entry.getLocks().isEmpty());
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- assertEquals(null, dummy.getCalled());
-
- //now let us do a prepare
- MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
- try
- {
- cache._replicate(prepareMethod);
- }
- catch (Throwable t)
- {
-
- }
-
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- IdentityLock lock = (IdentityLock) it.next();
- assertTrue(lock.isWriteLocked());
- assertEquals(gtx, lock.getWriterOwner());
- }
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- //assertEquals(null,dummy.getCalled());
- assertEquals(MethodDeclarations.optimisticPrepareMethod, dummy.getCalled());
-
-
- MethodCall commitMethod = MethodCallFactory.create(MethodDeclarations.commitMethod, new Object[]{gtx});
- try
- {
- cache._replicate(commitMethod);
- }
- catch (Throwable t)
- {
- fail();
- }
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- IdentityLock lock = (IdentityLock) it.next();
- assertEquals(false, lock.isLocked());
-
- }
- //make sure the nodes and locks are the same order
- int i = 0;
- for (Iterator it = workspace.getNodes().values().iterator(); it.hasNext();)
- {
- DataNode node = ((WorkspaceNode) it.next()).getNode();
- assertEquals(node.getLock(), entry.getLocks().get(i));
- i++;
- }
- assertEquals(MethodDeclarations.commitMethod, dummy.getCalled());
- mgr.commit();
-
- cache.stopService();
-
- }
-
- public void testTransactionRollbackMethod() throws Exception
- {
-
- TestListener listener = new TestListener();
- final TreeCache cache = createCacheWithListener(listener);
-
- Interceptor lockingInterceptor = new OptimisticLockingInterceptor();
- lockingInterceptor.setCache(cache);
- Interceptor interceptor = new OptimisticCreateIfNotExistsInterceptor();
- interceptor.setCache(cache);
- Interceptor nodeInterceptor = new OptimisticNodeInterceptor();
- nodeInterceptor.setCache(cache);
- MockInterceptor dummy = new MockInterceptor();
- dummy.setCache(cache);
- lockingInterceptor.setNext(interceptor);
- interceptor.setNext(nodeInterceptor);
- nodeInterceptor.setNext(dummy);
-
- cache.setInterceptorChain(lockingInterceptor);
-
-// first set up a node with a pojo
- DummyTransactionManager mgr = DummyTransactionManager.getInstance();
- mgr.begin();
- Transaction tx = mgr.getTransaction();
-
- // inject InvocationContext
- cache.getInvocationContext().setTransaction(tx);
- cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx));
-
- SamplePojo pojo = new SamplePojo(21, "test");
- Map temp = new HashMap();
- temp.put("key1", pojo);
- cache.put("/one/two", temp);
-
- assertEquals(null, dummy.getCalled());
- TransactionTable table = cache.getTransactionTable();
-
- GlobalTransaction gtx = table.get(tx);
-
- OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx);
-
- TransactionWorkspace workspace = entry.getTransactionWorkSpace();
-
- /*GlobalTransaction.class,
- List.class,
- Address.class,
- boolean.class*/
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertTrue(entry.getLocks().isEmpty());
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- assertEquals(null, dummy.getCalled());
-
- //now let us do a prepare
- MethodCall prepareMethod = MethodCallFactory.create(MethodDeclarations.optimisticPrepareMethod, new Object[]{gtx, entry.getModifications(), gtx.getAddress(), Boolean.FALSE});
- try
- {
- cache._replicate(prepareMethod);
- }
- catch (Throwable t)
- {
-
- }
-
-
- assertEquals(3, workspace.getNodes().size());
- assertNotNull(workspace.getNode(Fqn.fromString("/one/two")));
- assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1"));
- assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size());
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- IdentityLock lock = (IdentityLock) it.next();
- assertTrue(lock.isWriteLocked());
- assertEquals(gtx, lock.getWriterOwner());
- }
- assertEquals(1, entry.getModifications().size());
- assertTrue(!cache.exists("/one/two"));
- assertEquals(MethodDeclarations.optimisticPrepareMethod, dummy.getCalled());
-
-
- MethodCall rollbackMethod = MethodCallFactory.create(MethodDeclarations.rollbackMethod, new Object[]{gtx});
- try
- {
- cache._replicate(rollbackMethod);
- }
- catch (Throwable t)
- {
- fail();
- }
- assertEquals(3, entry.getLocks().size());
- for (Iterator it = entry.getLocks().iterator(); it.hasNext();)
- {
- IdentityLock lock = (IdentityLock) it.next();
- assertEquals(false, lock.isLocked());
-
- }
- //make sure the nodes and locks are the same order
- int i = 0;
- for (Iterator it = workspace.getNodes().values().iterator(); it.hasNext();)
- {
- DataNode node = ((WorkspaceNode) it.next()).getNode();
- assertEquals(node.getLock(), entry.getLocks().get(i));
- i++;
- }
- assertEquals(MethodDeclarations.rollbackMethod, dummy.getCalled());
- mgr.commit();
-
- cache.stopService();
-
- }
-
-}
Added: core/branches/1.4.X/tests/functional/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java
===================================================================
--- core/branches/1.4.X/tests/functional/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java (rev 0)
+++ core/branches/1.4.X/tests/functional/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java 2007-12-11 16:41:18 UTC (rev 4826)
@@ -0,0 +1,245 @@
+package org.jboss.cache.optimistic;
+
+import junit.framework.Assert;
+import org.jboss.cache.DataNode;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.TreeCache;
+import org.jboss.cache.interceptors.Interceptor;
+import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor;
+import org.jboss.cache.interceptors.OptimisticInterceptor;
+import org.jboss.cache.interceptors.OptimisticLockingInterceptor;
+import org.jboss.cache.lock.IdentityLock;
+import org.jgroups.blocks.MethodCall;
+
+import javax.transaction.TransactionManager;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * // Test for JBCACHE-1228
+ *
+ * @author Manik Surtani (<a href="mailto:manik@jboss.org">manik(a)jboss.org</a>)
+ */
+public class OptimisticLockInterceptorTest extends AbstractOptimisticTestCase
+{
+ private TreeCache cache;
+ private LockReportInterceptor lri;
+ private Fqn parent = Fqn.fromString("/parent");
+ private Fqn child = Fqn.fromString("/parent/child");
+ private TransactionManager tm;
+
+ public OptimisticLockInterceptorTest(String name)
+ {
+ super(name);
+ }
+
+ protected void setUp() throws Exception
+ {
+ cache = createCache();
+ lri = new LockReportInterceptor();
+ lri.setCache(cache);
+
+ Interceptor i = (Interceptor) cache.getInterceptors().get(0);
+ Interceptor lockInterceptor = i;
+ // find the OptimisticLockInterceptor and insert LockReportInterceptor after that.
+ while (!(lockInterceptor instanceof OptimisticLockingInterceptor)) lockInterceptor = lockInterceptor.getNext();
+ Interceptor next = lockInterceptor.getNext();
+
+ lockInterceptor.setNext(lri);
+ lri.setNext(next);
+
+ cache.setInterceptorChain(i);
+
+ cache.put(child, "key", "value");
+
+ tm = cache.getTransactionManager();
+ }
+
+ protected void tearDown()
+ {
+ cache.stop();
+ }
+
+ public void testPut() throws Exception
+ {
+ tm.begin();
+ cache.put(child, "key2", "value2");
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsReadLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testGet() throws Exception
+ {
+ tm.begin();
+ cache.get(child, "key2");
+ lri.reset();
+ // nothing is stale, expecting nothing here.
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testRemove() throws Exception
+ {
+ tm.begin();
+ cache.remove(child, "key2");
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsReadLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testPutLockParentForCIR() throws Exception
+ {
+ cache.setLockParentForChildInsertRemove(true);
+ cache.remove(parent);
+ cache.put(parent, "k", "v");
+
+ tm.begin();
+ cache.put(child, "key2", "value2");
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsWriteLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testGetLockParentForCIR() throws Exception
+ {
+ cache.setLockParentForChildInsertRemove(true);
+ tm.begin();
+ cache.get(child, "key2");
+ lri.reset();
+ // nothing is stale, expecting nothing here.
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testRemoveLockParentForCIR() throws Exception
+ {
+ cache.setLockParentForChildInsertRemove(true);
+ tm.begin();
+ cache.remove(child);
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsWriteLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+
+ public void testPutNodeNotExists() throws Exception
+ {
+ cache.remove(Fqn.ROOT);
+ tm.begin();
+ cache.put(child, "key2", "value2");
+ lri.reset();
+ lri.expectsReadLock(Fqn.ROOT);
+ lri.expectsWriteLock(parent);
+ lri.expectsWriteLock(child);
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testGetNodeNotExists() throws Exception
+ {
+ cache.remove(Fqn.ROOT);
+ tm.begin();
+ cache.get(child, "key2");
+ lri.reset();
+ // nothing is stale, expecting nothing here.
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ public void testRemoveNodeNotExists() throws Exception
+ {
+ cache.remove(Fqn.ROOT);
+ tm.begin();
+ cache.remove(child, "key2");
+ lri.reset();
+ // nothing is stale, expecting nothing here.
+ tm.commit();
+ lri.assertReceivedExpectedLocks();
+
+ assertNoStaleLocks();
+ }
+
+ private void assertNoStaleLocks()
+ {
+ assertEquals(cache.getNumberOfLocksHeld(), 0);
+ }
+}
+
+class LockReportInterceptor extends OptimisticInterceptor
+{
+ private Object READ = "READ";
+ private Object WRITE = "WRITE";
+ private Map expected = new HashMap();
+ private Map actual = new HashMap();
+
+ void reset()
+ {
+ expected.clear();
+ actual.clear();
+ }
+
+ void assertReceivedExpectedLocks()
+ {
+ Assert.assertEquals(expected, actual);
+ }
+
+ void expectsReadLock(Fqn f)
+ {
+ expected.put(f, READ);
+ }
+
+ void expectsWriteLock(Fqn f)
+ {
+ expected.put(f, WRITE);
+ }
+
+ public Object invoke(MethodCall call) throws Throwable
+ {
+ TransactionWorkspace w = getTransactionWorkspace(getInvocationContext().getGlobalTransaction());
+ Map nodeMap = w.getNodes();
+ for (Iterator i = nodeMap.keySet().iterator(); i.hasNext();)
+ {
+ WorkspaceNode wn = (WorkspaceNode) nodeMap.get(i.next());
+ DataNode n = wn.getNode();
+ IdentityLock lock = n.getLock();
+ if (lock.isLocked())
+ {
+ actual.put(n.getFqn(), lock.isReadLocked() ? READ : WRITE);
+ }
+ }
+
+ return super.invoke(call);
+ }
+
+
+}
17 years
JBoss Cache SVN: r4825 - in core/trunk/src/main/java/org/jboss/cache: interceptors and 1 other directories.
by jbosscache-commits@lists.jboss.org
Author: mircea.markus
Date: 2007-12-10 18:47:41 -0500 (Mon, 10 Dec 2007)
New Revision: 4825
Modified:
core/trunk/src/main/java/org/jboss/cache/InvocationContext.java
core/trunk/src/main/java/org/jboss/cache/interceptors/MethodDispacherInterceptor.java
core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
core/trunk/src/main/java/org/jboss/cache/transaction/TransactionTable.java
Log:
code cleanups in PesimisticInterceptor
Modified: core/trunk/src/main/java/org/jboss/cache/InvocationContext.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/InvocationContext.java 2007-12-10 21:57:40 UTC (rev 4824)
+++ core/trunk/src/main/java/org/jboss/cache/InvocationContext.java 2007-12-10 23:47:41 UTC (rev 4825)
@@ -250,4 +250,18 @@
ctx.methodCall = methodCall;
return ctx;
}
+
+ /**
+ * If the acq timeout if overwritten for current call, then return that one.
+ * If not overwritten return default value.
+ */
+ public long getContextLockAcquisitionTimeout(long defaultFalue) {
+ long timeout = defaultFalue;
+ if (getOptionOverrides() != null
+ && getOptionOverrides().getLockAcquisitionTimeout() >= 0)
+ {
+ timeout = getOptionOverrides().getLockAcquisitionTimeout();
+ }
+ return timeout;
+ }
}
Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/MethodDispacherInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/MethodDispacherInterceptor.java 2007-12-10 21:57:40 UTC (rev 4824)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/MethodDispacherInterceptor.java 2007-12-10 23:47:41 UTC (rev 4825)
@@ -32,9 +32,6 @@
*
* @author Mircea.Markus(a)jboss.com
* @version 2.2
- * todo - gtx is contained in InvocationContext. Check wheter passing method calls is or isn't redundant
- * todo - check wheter is possible to group methods, e.g. MethodDeclarations.putMethods, treansationableMethods?
- * todo - Refactor stuff in pessimistic lock interceptor
* todo - Refactor stuff in txint
* todo - revisit backward compatibility
*/
@@ -50,67 +47,7 @@
processOverwritternMethods();
}
- /**
- * Builds the list of methods that are overwiritten.
- */
- private void processOverwritternMethods()
- {
- checkIfOverwritten(MethodDeclarations.putDataEraseMethodLocal_id, "handlePutDataEraseMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Map.class, boolean.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.putDataMethodLocal_id, "handlePutDataMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Map.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.putForExternalReadMethodLocal_id, "handlePutForExternalReadMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Object.class);
- checkIfOverwritten(MethodDeclarations.putKeyValMethodLocal_id, "handlePutKeyValueMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Object.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.moveMethodLocal_id, "handleMoveMethod",InvocationContext.class, Fqn.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.addChildMethodLocal_id, "handleAddChildMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Node.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.getKeyValueMethodLocal_id, "handleGetKeyValueMethod", InvocationContext.class, Fqn.class, Object.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.getNodeMethodLocal_id, "handleGetNodeMethod", InvocationContext.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.getChildrenNamesMethodLocal_id, "handleGetChildrenNamesMethod",InvocationContext.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.releaseAllLocksMethodLocal_id, "handleReleaseAllLocksMethod",InvocationContext.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.printMethodLocal_id, "handlePrintMethod",InvocationContext.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.getKeysMethodLocal_id, "handleGetKeysMethod", InvocationContext.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.getDataMapMethodLocal_id, "handleGetDataMapMethod", InvocationContext.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.rollbackMethod_id, "handleRollbackMethod", InvocationContext.class, GlobalTransaction.class);
- checkIfOverwritten(MethodDeclarations.removeNodeMethodLocal_id, "handleRemoveNodeMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.removeKeyMethodLocal_id, "handleRemoveKeyMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.removeDataMethodLocal_id, "handleRemoveDataMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.commitMethod_id, "handleCommitMethod",InvocationContext.class, GlobalTransaction.class);
- checkIfOverwritten(MethodDeclarations.optimisticPrepareMethod_id, "handleOptimisticPrepareMethod", InvocationContext.class, GlobalTransaction.class, List.class, Map.class, Address.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.prepareMethod_id, "handlePrepareMethod", InvocationContext.class, GlobalTransaction.class, List.class, Address.class, boolean.class);
- checkIfOverwritten(MethodDeclarations.evictNodeMethodLocal_id, "handleEvictMethod", InvocationContext.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.evictVersionedNodeMethodLocal_id, "handleEvictVersionedNodeMethod", InvocationContext.class, Fqn.class, DataVersion.class);
- checkIfOverwritten(MethodDeclarations.existsMethod_id, "handleExistsMethod", InvocationContext.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.putDataEraseVersionedMethodLocal_id, "handlePutDataEraseVersionedMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Map.class, boolean.class, boolean.class, DataVersion.class);
- checkIfOverwritten(MethodDeclarations.putDataVersionedMethodLocal_id, "handlePutDataVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Map.class, Boolean.class, DataVersion.class);
- checkIfOverwritten(MethodDeclarations.putKeyValVersionedMethodLocal_id, "handlePutKeyValueVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Object.class, boolean.class, DataVersion.class);
- checkIfOverwritten(MethodDeclarations.putForExternalReadVersionedMethodLocal_id, "handlePutForExternalReadVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Object.class, DataVersion.class);
- checkIfOverwritten(MethodDeclarations.dataGravitationCleanupMethod_id, "handleDataGravitationCleanupMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Fqn.class);
- checkIfOverwritten(MethodDeclarations.removeNodeVersionedMethodLocal_id, "handleRemoveNodeVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, boolean.class, DataVersion.class);
- checkIfOverwritten(MethodDeclarations.removeKeyVersionedMethodLocal_id, "handleRemoveKeyVersionedMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, boolean.class, DataVersion.class);
- checkIfOverwritten(MethodDeclarations.removeDataVersionedMethodLocal_id, "handleRemoveDataVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, boolean.class, DataVersion.class);
- checkIfOverwritten(MethodDeclarations.blockChannelMethodLocal_id, "handleBlockChannelMethod",InvocationContext.class);
- checkIfOverwritten(MethodDeclarations.unblockChannelMethodLocal_id, "handleUnblockChannelMethod", InvocationContext.class);
- checkIfOverwritten(MethodDeclarations.lockMethodLocal_id, "handleLockMethod", InvocationContext.class, Fqn.class, NodeLock.LockType.class, boolean.class);
-
- }
-
- private void checkIfOverwritten(int putDataEraseMethodLocal_id, String methodName, Class... args)
- {
- Class currentClass = getClass();
- //if this is a > 1 inheritace deepth and the method was overwritten in the parent. We also have to look into parents
- while (currentClass != MethodDispacherInterceptor.class)
- {
- try
- {
- currentClass.getDeclaredMethod(methodName, args);
- this.overwrittenMethods.add(putDataEraseMethodLocal_id);
- } catch (NoSuchMethodException e)
- {
- //ignore
- }
- currentClass = (Class) currentClass.getGenericSuperclass();
- }
- }
-
- /**
+ /**
* Acts like a 'switch case' that delegates the call to the appropriate method.
*/
public Object invoke(InvocationContext ctx) throws Throwable
@@ -240,7 +177,7 @@
return result;
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_lock(org.jboss.cache.Fqn, org.jboss.cache.lock.NodeLock.LockType, boolean)}
*/
protected Object handleLockMethod(InvocationContext ctx, Fqn fqn, NodeLock.LockType lockType, boolean recursive) throws Throwable
@@ -248,7 +185,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_unblock()}
*/
protected Object handleUnblockChannelMethod(InvocationContext ctx) throws Throwable
@@ -256,7 +193,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_block()}
*/
protected Object handleBlockChannelMethod(InvocationContext ctx) throws Throwable
@@ -264,7 +201,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_removeData(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, boolean, org.jboss.cache.optimistic.DataVersion)}
*/
protected Object handleRemoveDataVersionedMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, boolean createUndoOps, DataVersion dv) throws Throwable
@@ -272,7 +209,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_remove(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, Object, boolean, org.jboss.cache.optimistic.DataVersion)}
*/
protected Object handleRemoveKeyVersionedMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, Object key, boolean createUndoOps, DataVersion dv) throws Throwable
@@ -280,7 +217,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_remove(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, boolean, org.jboss.cache.optimistic.DataVersion)}
*/
protected Object handleRemoveNodeVersionedMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, boolean createUndoOps, DataVersion dv) throws Throwable
@@ -288,7 +225,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_dataGravitationCleanup(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, org.jboss.cache.Fqn)}
*/
protected Object handleDataGravitationCleanupMethod(InvocationContext ctx, GlobalTransaction globalTransaction, Fqn primary, Fqn backup) throws Throwable
@@ -296,7 +233,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_putForExternalRead(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, Object, Object, org.jboss.cache.optimistic.DataVersion)}
*/
protected Object handlePutForExternalReadVersionedMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, Object key, Object value, DataVersion dv) throws Throwable
@@ -304,7 +241,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_put(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, Object, Object, boolean, org.jboss.cache.optimistic.DataVersion)}
*/
protected Object handlePutKeyValueVersionedMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, Object key, Object value, boolean createUndoOps, DataVersion dv) throws Throwable
@@ -312,7 +249,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_put(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, java.util.Map, boolean, org.jboss.cache.optimistic.DataVersion)}
*/
protected Object handlePutDataVersionedMethod(InvocationContext ctx, GlobalTransaction globalTransaction, Fqn fqn, Map map, Boolean createUndoOps, DataVersion dataVersion) throws Throwable
@@ -320,7 +257,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_put(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, java.util.Map, boolean, boolean, org.jboss.cache.optimistic.DataVersion)}
*/
protected Object handlePutDataEraseVersionedMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, Map data, boolean createUndoOps, boolean eraseContent, DataVersion dv) throws Throwable
@@ -328,7 +265,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#exists(String)}
*/
protected Object handleExistsMethod(InvocationContext ctx, Fqn fqn) throws Throwable
@@ -336,22 +273,22 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* used for logging various steps. if null is returned than nothing is logged.
*/
protected abstract Log getLog();
- /**
+ /**
* Each interceptor should extend this if it does not need any processing for current call.
* An sample usage would be: this interceptor is only interested if thre is one transaction going on. If so all
- * handleXYZ would know that we have a transaction going and would not check its state.
+ * handleXYZ would know that we have a transaction going and would not check its state.
*/
protected boolean skipMethodCall(InvocationContext ctx)
{
return false;
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_evict(org.jboss.cache.Fqn, org.jboss.cache.optimistic.DataVersion)}
*/
protected Object handleEvictVersionedNodeMethod(InvocationContext ctx, Fqn fqn, DataVersion dataVersion) throws Throwable
@@ -359,7 +296,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#evict(org.jboss.cache.Fqn)}
*/
protected Object handleEvictMethod(InvocationContext ctx, Fqn fqn) throws Throwable
@@ -367,7 +304,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#prepare(org.jboss.cache.transaction.GlobalTransaction, java.util.List, org.jgroups.Address, boolean)}
*/
protected Object handlePrepareMethod(InvocationContext ctx, GlobalTransaction gtx, List modification, Address coordinator, boolean onePhaseCommit) throws Throwable
@@ -375,7 +312,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#optimisticPrepare(org.jboss.cache.transaction.GlobalTransaction, java.util.List, java.util.Map, org.jgroups.Address, boolean)}
*/
protected Object handleOptimisticPrepareMethod(InvocationContext ctx, GlobalTransaction gtx, List modifications, Map data, Address address, boolean onePhaseCommit) throws Throwable
@@ -383,7 +320,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#commit(org.jboss.cache.transaction.GlobalTransaction)}
*/
protected Object handleCommitMethod(InvocationContext ctx, GlobalTransaction globalTransaction) throws Throwable
@@ -391,7 +328,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_removeData(org.jboss.cache.transaction.GlobalTransaction, Fqn, boolean)}
*/
protected Object handleRemoveDataMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, boolean createUndoOps) throws Throwable
@@ -399,7 +336,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_remove(org.jboss.cache.transaction.GlobalTransaction, String, Object, boolean)}
*/
protected Object handleRemoveKeyMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Object key, boolean createUndoOps) throws Throwable
@@ -407,8 +344,7 @@
return defaultHandlersBehavior();
}
-
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_remove(org.jboss.cache.transaction.GlobalTransaction, String, boolean)}
*/
protected Object handleRemoveNodeMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, boolean createUndoOps) throws Throwable
@@ -416,7 +352,8 @@
return defaultHandlersBehavior();
}
- /**
+
+ /**
* Handles {@link org.jboss.cache.CacheImpl#rollback(org.jboss.cache.transaction.GlobalTransaction)}
*/
protected Object handleRollbackMethod(InvocationContext ctx, GlobalTransaction globalTransaction) throws Throwable
@@ -424,7 +361,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_getData(org.jboss.cache.Fqn)}
*/
protected Object handleGetDataMapMethod(InvocationContext ctx, Fqn fqn) throws Throwable
@@ -432,7 +369,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#getKeys(Fqn)}
*/
protected Object handleGetKeysMethod(InvocationContext ctx, Fqn fqn) throws Throwable
@@ -440,7 +377,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_print(org.jboss.cache.Fqn)}
*/
protected Object handlePrintMethod(InvocationContext ctx, Fqn fqn) throws Throwable
@@ -448,7 +385,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_releaseAllLocks(org.jboss.cache.Fqn)}
*/
protected Object handleReleaseAllLocksMethod(InvocationContext ctx, Fqn fqn) throws Throwable
@@ -456,7 +393,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_getChildrenNames(org.jboss.cache.Fqn)}
*/
protected Object handleGetChildrenNamesMethod(InvocationContext ctx, Fqn fqn) throws Throwable
@@ -464,7 +401,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_get(org.jboss.cache.Fqn)}
*/
protected Object handleGetNodeMethod(InvocationContext ctx, Fqn fqn) throws Throwable
@@ -472,7 +409,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_get(org.jboss.cache.Fqn, Object, boolean)}
*/
protected Object handleGetKeyValueMethod(InvocationContext ctx, Fqn fqn, Object key, boolean sendNodeEvent) throws Throwable
@@ -480,8 +417,7 @@
return defaultHandlersBehavior();
}
-
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_addChild(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, Object, org.jboss.cache.Node, boolean)}
*/
protected Object handleAddChildMethod(InvocationContext ctx, GlobalTransaction tx, Fqn parentFqn, Object childName, Node cn, boolean createUndoOps) throws Throwable
@@ -489,7 +425,8 @@
return defaultHandlersBehavior();
}
- /**
+
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_move(org.jboss.cache.Fqn, org.jboss.cache.Fqn)}
*/
protected Object handleMoveMethod(InvocationContext ctx, Fqn from, Fqn to) throws Throwable
@@ -497,7 +434,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_put(org.jboss.cache.transaction.GlobalTransaction, String, Object, Object, boolean)}
*/
protected Object handlePutKeyValueMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, Object key, Object value, boolean createUndoOps) throws Throwable
@@ -505,7 +442,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_putForExternalRead(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, Object, Object)}
*/
protected Object handlePutForExternalReadMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Object key, Object value) throws Throwable
@@ -513,7 +450,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_put(org.jboss.cache.transaction.GlobalTransaction, String, java.util.Map, boolean)}
*/
protected Object handlePutDataMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Map data, boolean createUndoOps) throws Throwable
@@ -521,7 +458,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handles {@link org.jboss.cache.CacheImpl#_put(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, java.util.Map, boolean, boolean)}
*/
protected Object handlePutDataEraseMethod(InvocationContext ctx, GlobalTransaction gt, Fqn fqn, Map newData, boolean createUndoOps, boolean eraseContents) throws Throwable
@@ -529,7 +466,7 @@
return defaultHandlersBehavior();
}
- /**
+ /**
* Handlers defined here should not be called directlly. There are two scenarios in which a handler might be called:
* 1 - DerivedInterceptor.super - pointless call
* 2 - if the logic that determines that an handler is overwritten fails. Throwing an exception by default is for
@@ -538,5 +475,65 @@
private Object defaultHandlersBehavior()
{
throw new IllegalStateException("this is either called from a derived class or nt overwritten and accidentally called. Either way, is not correct.");
- }
+ }
+
+ /**
+ * Builds the list of methods that are overwiritten.
+ */
+ private void processOverwritternMethods()
+ {
+ checkIfOverwritten(MethodDeclarations.putDataEraseMethodLocal_id, "handlePutDataEraseMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Map.class, boolean.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.putDataMethodLocal_id, "handlePutDataMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Map.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.putForExternalReadMethodLocal_id, "handlePutForExternalReadMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Object.class);
+ checkIfOverwritten(MethodDeclarations.putKeyValMethodLocal_id, "handlePutKeyValueMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Object.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.moveMethodLocal_id, "handleMoveMethod",InvocationContext.class, Fqn.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.addChildMethodLocal_id, "handleAddChildMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Node.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.getKeyValueMethodLocal_id, "handleGetKeyValueMethod", InvocationContext.class, Fqn.class, Object.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.getNodeMethodLocal_id, "handleGetNodeMethod", InvocationContext.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.getChildrenNamesMethodLocal_id, "handleGetChildrenNamesMethod",InvocationContext.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.releaseAllLocksMethodLocal_id, "handleReleaseAllLocksMethod",InvocationContext.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.printMethodLocal_id, "handlePrintMethod",InvocationContext.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.getKeysMethodLocal_id, "handleGetKeysMethod", InvocationContext.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.getDataMapMethodLocal_id, "handleGetDataMapMethod", InvocationContext.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.rollbackMethod_id, "handleRollbackMethod", InvocationContext.class, GlobalTransaction.class);
+ checkIfOverwritten(MethodDeclarations.removeNodeMethodLocal_id, "handleRemoveNodeMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.removeKeyMethodLocal_id, "handleRemoveKeyMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.removeDataMethodLocal_id, "handleRemoveDataMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.commitMethod_id, "handleCommitMethod",InvocationContext.class, GlobalTransaction.class);
+ checkIfOverwritten(MethodDeclarations.optimisticPrepareMethod_id, "handleOptimisticPrepareMethod", InvocationContext.class, GlobalTransaction.class, List.class, Map.class, Address.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.prepareMethod_id, "handlePrepareMethod", InvocationContext.class, GlobalTransaction.class, List.class, Address.class, boolean.class);
+ checkIfOverwritten(MethodDeclarations.evictNodeMethodLocal_id, "handleEvictMethod", InvocationContext.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.evictVersionedNodeMethodLocal_id, "handleEvictVersionedNodeMethod", InvocationContext.class, Fqn.class, DataVersion.class);
+ checkIfOverwritten(MethodDeclarations.existsMethod_id, "handleExistsMethod", InvocationContext.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.putDataEraseVersionedMethodLocal_id, "handlePutDataEraseVersionedMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Map.class, boolean.class, boolean.class, DataVersion.class);
+ checkIfOverwritten(MethodDeclarations.putDataVersionedMethodLocal_id, "handlePutDataVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Map.class, Boolean.class, DataVersion.class);
+ checkIfOverwritten(MethodDeclarations.putKeyValVersionedMethodLocal_id, "handlePutKeyValueVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Object.class, boolean.class, DataVersion.class);
+ checkIfOverwritten(MethodDeclarations.putForExternalReadVersionedMethodLocal_id, "handlePutForExternalReadVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, Object.class, DataVersion.class);
+ checkIfOverwritten(MethodDeclarations.dataGravitationCleanupMethod_id, "handleDataGravitationCleanupMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, Fqn.class);
+ checkIfOverwritten(MethodDeclarations.removeNodeVersionedMethodLocal_id, "handleRemoveNodeVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, boolean.class, DataVersion.class);
+ checkIfOverwritten(MethodDeclarations.removeKeyVersionedMethodLocal_id, "handleRemoveKeyVersionedMethod",InvocationContext.class, GlobalTransaction.class, Fqn.class, Object.class, boolean.class, DataVersion.class);
+ checkIfOverwritten(MethodDeclarations.removeDataVersionedMethodLocal_id, "handleRemoveDataVersionedMethod", InvocationContext.class, GlobalTransaction.class, Fqn.class, boolean.class, DataVersion.class);
+ checkIfOverwritten(MethodDeclarations.blockChannelMethodLocal_id, "handleBlockChannelMethod",InvocationContext.class);
+ checkIfOverwritten(MethodDeclarations.unblockChannelMethodLocal_id, "handleUnblockChannelMethod", InvocationContext.class);
+ checkIfOverwritten(MethodDeclarations.lockMethodLocal_id, "handleLockMethod", InvocationContext.class, Fqn.class, NodeLock.LockType.class, boolean.class);
+
+ }
+
+ private void checkIfOverwritten(int putDataEraseMethodLocal_id, String methodName, Class... args)
+ {
+ Class currentClass = getClass();
+ //if this is a > 1 inheritace deepth and the method was overwritten in the parent. We also have to look into parents
+ while (currentClass != MethodDispacherInterceptor.class)
+ {
+ try
+ {
+ currentClass.getDeclaredMethod(methodName, args);
+ this.overwrittenMethods.add(putDataEraseMethodLocal_id);
+ } catch (NoSuchMethodException e)
+ {
+ //ignore
+ }
+ currentClass = (Class) currentClass.getGenericSuperclass();
+ }
+ }
}
Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java 2007-12-10 21:57:40 UTC (rev 4824)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java 2007-12-10 23:47:41 UTC (rev 4825)
@@ -19,6 +19,14 @@
import java.util.*;
+/*
+* todo refactorings ideas
+* - thre are many places in code that handles that coputes the lock owners: either GTX or Thread.local. The
+* lockOwner can be abstractised as a LockOwner that can be extended by CurrentThreadLock owner and
+ GlobalTransaction owner. This would make the code nicer.
+*/
+
+
/**
* An interceptor that handles locking. When a TX is associated, we register
* for TX completion and unlock the locks acquired within the scope of the TX.
@@ -27,7 +35,6 @@
*
* @author Bela Ban
* @version $Id$
- * //todo = try to see how acquireLuckWithTimeout works inline
*/
public class PessimisticLockInterceptor extends MethodDispacherInterceptor
{
@@ -38,9 +45,7 @@
*/
private Map<Thread, List<NodeLock>> lock_table;
private long lock_acquisition_timeout;
- private LockManager lockManager = new LockManager();
-
public void setCache(CacheSPI cache)
{
super.setCache(cache);
@@ -76,17 +81,31 @@
if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().isSuppressLocking())
{
log.trace("Creating nodes if necessary");
- createNodes(fqn, ctx.getGlobalTransaction());
+ int treeNodeSize = fqn.size();
+ NodeSPI n = cache.getRoot();
+ for (int i = 0; i < treeNodeSize; i++)
+ {
+ Object childName = fqn.get(i);
+ Fqn childFqn = new Fqn(childName);
+ NodeSPI child_node = n.getChildDirect(childFqn);
+ if (child_node == null) child_node = n.addChildDirect(childFqn);
+ manageReverseRemove(ctx.getGlobalTransaction(), child_node, true);
+ n = child_node;
+ }
} else
{
- acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.WRITE, false, true, false, false, false, false);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, true, false, false, true);
}
return nextInterceptor(ctx);
}
protected Object handleLockMethod(InvocationContext ctx, Fqn fqn, NodeLock.LockType lockType, boolean recursive) throws Throwable
{
- acquireLocksWithTimeout(ctx, fqn, lockType, recursive, false, false, false, false, false);
+ acquireLocksWithTimeout(ctx, fqn, lockType, false, false, false, false);
+ if (recursive)
+ {
+ acquireLocksOnChildren(cache.peek(fqn, false), lockType, ctx);
+ }
return null;
}
@@ -95,7 +114,7 @@
// commit propagated up from the tx interceptor
commit(ctx.getGlobalTransaction());
Object retVal = nextInterceptor(ctx);
- cleanup(ctx.getGlobalTransaction());
+ tx_table.cleanup(ctx.getGlobalTransaction());
return retVal;
}
@@ -112,44 +131,83 @@
log.trace("bypassed locking as method commit() doesn't require locking");
}
Object retVal = nextInterceptor(ctx);
- cleanup(globalTransaction);
+ tx_table.cleanup(globalTransaction);
return retVal;
}
protected Object handleRollbackMethod(InvocationContext ctx, GlobalTransaction globalTransaction) throws Throwable
{
- rollback(globalTransaction);
+ TransactionEntry entry = tx_table.get(globalTransaction);
if (log.isTraceEnabled())
{
+ log.trace("called to rollback cache with GlobalTransaction=" + globalTransaction);
+ }
+
+ if (entry == null)
+ {
+ log.error("entry for transaction " + globalTransaction + " not found (transaction has possibly already been rolled back)");
+ }
+ else
+ {
+ Iterator removedNodes = entry.getRemovedNodes().iterator();
+ CacheImpl cacheImpl = (CacheImpl) cache;
+ while (removedNodes.hasNext())
+ {
+ Fqn f = (Fqn) removedNodes.next();
+ cacheImpl.realRemove(f, false);
+
+ }
+ // 1. Revert the modifications by running the undo-op list in reverse. This *cannot* throw any exceptions !
+ entry.undoOperations(cache);
+ }
+ if (log.isTraceEnabled())
+ {
log.trace("bypassed locking as method rollback() doesn't require locking");
}
Object retVal = nextInterceptor(ctx);
- cleanup(globalTransaction);
+ tx_table.cleanup(globalTransaction);
return retVal;
}
protected Object handleMoveMethod(InvocationContext ctx, Fqn from, Fqn to) throws Throwable
{
- long timeout = getLockAcquisitionTimeout(ctx);
+ long timeout = ctx.getContextLockAcquisitionTimeout(lock_acquisition_timeout);
// this call will ensure the node gets a WL and it's current parent gets RL.
if (log.isTraceEnabled()) log.trace("Attempting to get WL on node to be moved [" + from + "]");
- lock(ctx, from, NodeLock.LockType.WRITE, true, false, timeout, true, false, false);
- //now for an RL for the new parent.
- if (log.isTraceEnabled()) log.trace("Attempting to get RL on new parent [" + to + "]");
- lock(ctx, to, NodeLock.LockType.READ, true, false, timeout, false, false, false);
+ if (from != null && ! (configuration.getIsolationLevel() == IsolationLevel.NONE))
+ {
+ lock(ctx, from, NodeLock.LockType.WRITE, false, timeout, true, false);
+ if (ctx.getGlobalTransaction() != null)
+ {
+ cache.getTransactionTable().get(ctx.getGlobalTransaction()).addRemovedNode(from);
+ }
+ acquireLocksOnChildren(cache.peek(from, true), NodeLock.LockType.WRITE, ctx);
+ }
+ if (to != null && !(configuration.getIsolationLevel() == IsolationLevel.NONE))
+ {
+ //now for an RL for the new parent.
+ if (log.isTraceEnabled()) log.trace("Attempting to get RL on new parent [" + to + "]");
+ lock(ctx, to, NodeLock.LockType.READ, false, timeout, false, false);
+ acquireLocksOnChildren(cache.peek(to, true), NodeLock.LockType.READ, ctx);
+ }
Object retValue = nextInterceptor(ctx);
// do a REAL remove here.
NodeSPI n = cache.peek(from, true);
if (n != null)
{
- lockManager.getLock(n).releaseAll(Thread.currentThread());
+ n.getLock().releaseAll(Thread.currentThread());
}
return retValue;
}
protected Object handleRemoveNodeMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, boolean createUndoOps) throws Throwable
{
- boolean created = acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.WRITE, true, true, false, true, false, false);
+ boolean created = acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.WRITE, true, false, true, false);
+ if (ctx.getGlobalTransaction() != null)
+ {
+ cache.getTransactionTable().get(ctx.getGlobalTransaction()).addRemovedNode(fqn);
+ }
+ acquireLocksOnChildren(cache.getRoot().getChildDirect(fqn), NodeLock.LockType.WRITE, ctx);
Object retVal = nextInterceptor(ctx);
if (ctx.getGlobalTransaction() == null)
{
@@ -157,7 +215,7 @@
NodeSPI n = cache.peek(fqn, true);
if (n != null)
{
- lockManager.getLock(n).releaseAll(Thread.currentThread());
+ n.getLock().releaseAll(Thread.currentThread());
}
}
// if this is a delete op and we had to create the node, return a FALSE as nothing *really* was deleted!
@@ -166,7 +224,7 @@
protected Object handlePutForExternalReadMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Object key, Object value) throws Throwable
{
- acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.WRITE, false, true, true, false, false, false);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, true, true, false, true);
return nextInterceptor(ctx);
}
@@ -177,104 +235,99 @@
protected Object handleRemoveDataMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, boolean createUndoOps) throws Throwable
{
- acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.WRITE, false, false, false, false, false, true);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.WRITE, false, false, false, false);
return nextInterceptor(ctx);
}
protected Object handleAddChildMethod(InvocationContext ctx, GlobalTransaction tx, Fqn parentFqn, Object childName, Node cn, boolean createUndoOps) throws Throwable
{
- acquireLocksWithTimeout(ctx, parentFqn, NodeLock.LockType.WRITE, false, false, false, false, false, false);
+ acquireLocksWithTimeout(ctx, parentFqn, NodeLock.LockType.READ, false, false, false, false);
return nextInterceptor(ctx);
}
protected Object handleEvictMethod(InvocationContext ctx, Fqn fqn) throws Throwable
{
- acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.WRITE, false, false, true, false, true, false);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.WRITE, false, true, false, false);
return nextInterceptor(ctx);
}
protected Object handleGetKeyValueMethod(InvocationContext ctx, Fqn fqn, Object key, boolean sendNodeEvent) throws Throwable
{
- return acquireNonRecursiveReadLock(ctx, fqn);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, false, false, false, false);
+ return nextInterceptor(ctx);
}
- private Object acquireNonRecursiveReadLock(InvocationContext ctx, Fqn fqn)
- throws Throwable
+ protected Object handleGetNodeMethod(InvocationContext ctx, Fqn fqn) throws Throwable
{
- acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, false, false, false, false, false, false);
- return nextInterceptor(ctx);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, false, false, false, false);
+ return nextInterceptor(ctx);
}
- protected Object handleGetNodeMethod(InvocationContext ctx, Fqn fqn) throws Throwable
- {
- return acquireNonRecursiveReadLock(ctx, fqn);
- }
-
protected Object handleGetKeysMethod(InvocationContext ctx, Fqn fqn) throws Throwable
{
- return acquireNonRecursiveReadLock(ctx, fqn);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, false, false, false, false);
+ return nextInterceptor(ctx);
}
protected Object handleGetChildrenNamesMethod(InvocationContext ctx, Fqn fqn) throws Throwable
{
- return acquireNonRecursiveReadLock(ctx, fqn);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, false, false, false, false);
+ return nextInterceptor(ctx);
}
protected Object handlePrintMethod(InvocationContext ctx, Fqn fqn) throws Throwable
{
- return acquireNonRecursiveReadLock(ctx, fqn);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, false, false, false, false);
+ return nextInterceptor(ctx);
}
protected Object handleReleaseAllLocksMethod(InvocationContext ctx, Fqn fqn) throws Throwable
{
- return acquireNonRecursiveReadLock(ctx, fqn);
+ acquireLocksWithTimeout(ctx, fqn, NodeLock.LockType.READ, false, false, false, false);
+ return nextInterceptor(ctx);
}
- private boolean acquireLocksWithTimeout(InvocationContext ctx, Fqn fqn, NodeLock.LockType lock_type, boolean recursive, boolean createIfNotExists, boolean zeroLockTimeout, boolean deleteOperation, boolean evictOperation, boolean removeDataOperation)
+ private boolean acquireLocksWithTimeout(InvocationContext ctx, Fqn fqn, NodeLock.LockType lockType,
+ boolean createIfNotExists, boolean zeroLockTimeout,
+ boolean acquireLockOnParent, boolean reverseRemoveCheck)
throws InterruptedException
{
+ if (fqn == null || configuration.getIsolationLevel() == IsolationLevel.NONE)
+ {
+ return false;
+ }
boolean created;
- long timeout = zeroLockTimeout ? 0 : getLockAcquisitionTimeout(ctx);
+ long timeout = zeroLockTimeout ? 0 : ctx.getContextLockAcquisitionTimeout(lock_acquisition_timeout);
// make sure we can bail out of this loop
long cutoffTime = System.currentTimeMillis() + timeout;
boolean firstTry = true;
do
- {
- // this is an additional check to make sure we don't try for too long.
- if (!firstTry && System.currentTimeMillis() > cutoffTime) throw new TimeoutException("Unable to acquire lock on Fqn " + fqn + " after " + timeout + " millis");
- created = lock(ctx, fqn, lock_type, recursive, createIfNotExists, timeout, deleteOperation, evictOperation, removeDataOperation);
+ {
+ // this is an additional check to make sure we don't try for too long.
+ if (!firstTry && System.currentTimeMillis() > cutoffTime)
+ {
+ throw new TimeoutException("Unable to acquire lock on Fqn " + fqn + " after " + timeout + " millis");
+ }
+ created = lock(ctx, fqn, lockType, createIfNotExists, timeout, acquireLockOnParent, reverseRemoveCheck);
firstTry = false;
}
while (createIfNotExists && cache.peek(fqn, true) == null);// keep trying until we have the lock (fixes concurrent remove())
return created;
}
- //todo move to InvocationContext as it is information expert
- private long getLockAcquisitionTimeout(InvocationContext ctx)
- {
- long timeout = lock_acquisition_timeout;
- if (ctx.getOptionOverrides() != null
- && ctx.getOptionOverrides().getLockAcquisitionTimeout() >= 0)
- {
- timeout = ctx.getOptionOverrides().getLockAcquisitionTimeout();
- }
- return timeout;
- }
-
-
/**
- * Locks a given node.
- *
- * @param lock_type DataNode.LOCK_TYPE_READ, DataNode.LOCK_TYPE_WRITE or DataNode.LOCK_TYPE_NONE
- * @param recursive Lock children recursively
- * @return true if the node had to be created
+ * Acquires locks on the node and on its parrents. Read locks are acquired for exsiting ancestors, with two exceptions:
+ * 1) createIfNotExists is true. If an ancestor is created on the fly, then an WL is acquired by default
+ * 2) acquireWriteLockOnParent is true. If so AND {@link org.jboss.cache.Node#isLockForChildInsertRemove()} then a read
+ * lock will be aquired for the parent of the node.
+ * @param createIfNotExists if true, then missing nodes will be cretaed on the fly. If false, method returns if we
+ * reach a node that does not exists
+ * @param reverseRemoveCheck see {@link #manageReverseRemove(org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.NodeSPI, boolean)}
*/
- private boolean lock(InvocationContext ctx, Fqn fqn, NodeLock.LockType lock_type, boolean recursive, boolean createIfNotExists, long timeout, boolean isDeleteOperation, boolean isEvictionOperation, boolean isRemoveDataOperation)
+ private boolean lock(InvocationContext ctx, Fqn fqn, NodeLock.LockType lockType, boolean createIfNotExists, long timeout,
+ boolean acquireWriteLockOnParent, boolean reverseRemoveCheck)
throws TimeoutException, LockingException, InterruptedException
{
- NodeSPI n;
- NodeSPI child_node;
- Object child_name;
Thread currentThread = Thread.currentThread();
GlobalTransaction gtx = ctx.getGlobalTransaction();
boolean created = false;
@@ -283,240 +336,164 @@
{
assertTransactionValid(ctx);
}
-
Object owner = (gtx != null) ? gtx : currentThread;
- int treeNodeSize;
-
+ NodeSPI currentNode;
if (log.isTraceEnabled()) log.trace("Attempting to lock node " + fqn + " for owner " + owner);
-
- if (fqn == null)
- {
- log.error("fqn is null - this should not be the case");
- return false;
- }
-
- if (configuration.getIsolationLevel() == IsolationLevel.NONE)
- {
- lock_type = NodeLock.LockType.NONE;
- }
-
- // we need to make sure this loop doesn't take forever (under a lot of concurrency) either as this can seem like a deadlock.
- // apply a similar timeout check as is done in the loop that calls this lock() method.
long expiryTime = System.currentTimeMillis() + timeout;
- boolean reAcquisitionOnSameNode = false, rerunLoop = true;
-
- while (rerunLoop)
+ currentNode = cache.getRoot();
+ NodeSPI parent = null;
+ String childName = null;
+ int currentIndex = -1;
+ do
{
- n = cache.getRoot();
- treeNodeSize = fqn.size();
-
- for (int i = -1; i < treeNodeSize; i++)
+ if (currentNode == null)
{
- if (rerunLoop) rerunLoop = false;
- created = false;
- if (i == -1)
+ if (createIfNotExists)
{
- // this is the root node
- child_name = Fqn.ROOT.getLastElement();
- child_node = n;
+ currentNode = parent.addChildDirect(new Fqn(childName));
+ created = true;
+ if (log.isTraceEnabled()) log.trace("Child node was null, so created child node " + childName);
}
else
{
- child_name = fqn.get(i);
- child_node = n.getChildDirect(child_name);
- }
-
- // timeout check
- if (reAcquisitionOnSameNode && System.currentTimeMillis() > expiryTime) throw new TimeoutException("Unable to acquire lock on child node " + new Fqn(n.getFqn(), child_name) + " after " + timeout + " millis.");
-
- if (log.isTraceEnabled()) log.trace("Directly got child node " + child_name);
- if (child_node == null && createIfNotExists)
- {
- child_node = n.addChildDirect(new Fqn(child_name));
- created = true;
- if (log.isTraceEnabled()) log.trace("Child node was null, so created child node " + child_name);
- }
-
- if (child_node == null)
- {
- if (log.isTraceEnabled())
- {
- log.trace("failed to find or create child " + child_name + " of node " + n);
- }
+ if (log.isTraceEnabled()) log.trace("failed to find or create child " + childName + " of node " + currentNode);
return false;
}
+ }
+ NodeLock.LockType lockTypeRequired = NodeLock.LockType.READ;
+ if (created || writeLockNeeded(ctx, lockType, currentIndex, acquireWriteLockOnParent, createIfNotExists, fqn, currentNode))
+ {
+ lockTypeRequired = NodeLock.LockType.WRITE;
+ }
- NodeLock.LockType lockTypeRequired;
- if (lock_type == NodeLock.LockType.NONE)
- {
- n = child_node;
- continue;
- }
- else
- {
- if (created || writeLockNeeded(ctx, lock_type, i, treeNodeSize, isEvictionOperation, isDeleteOperation, createIfNotExists, isRemoveDataOperation, fqn, child_node))
- {
- lockTypeRequired = NodeLock.LockType.WRITE;
+ manageReverseRemove(gtx, currentNode, reverseRemoveCheck);
- }
- else
- {
- lockTypeRequired = NodeLock.LockType.READ;
- }
- }
+ // actually acquire the lock we need. This method blocks.
+ acquireNodeLock(currentNode, owner, gtx, lockTypeRequired, timeout);
- // reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
- if (gtx != null && needToReverseRemove(child_node, tx_table.get(gtx), lock_type, isDeleteOperation, createIfNotExists))
- {
- reverseRemove(child_node);
- }
-
- // actually acquire the lock we need. This method blocks.
- acquireNodeLock(child_node, owner, gtx, lockTypeRequired, timeout);
-
- // make sure the lock we acquired isn't on a deleted node/is an orphan!!
+ // make sure the lock we acquired isn't on a deleted node/is an orphan!!
+ // look into invalidated nodes as well
+ NodeSPI repeek = cache.peek(currentNode.getFqn(), true, true);
+ if (currentNode != repeek)
+ {
+ if (log.isTraceEnabled()) log.trace("Was waiting for and obtained a lock on a node that doesn't exist anymore! Attempting lock acquisition again.");
+ // we have an orphan!! Lose the unnecessary lock and re-acquire the lock (and potentially recreate the node).
+ // check if the parent exists!!
// look into invalidated nodes as well
- NodeSPI repeek = cache.peek(child_node.getFqn(), true, true);
- if (child_node != repeek)//repeek != null && child_node != repeek)// || repeek == null && created)
+ currentNode.getLock().releaseAll(owner);
+ if (cache.peek(parent.getFqn(), true, true) == null)
{
- log.trace("Was waiting for and obtained a lock on a node that doesn't exist anymore! Attempting lock acquisition again.");
- // we have an orphan!! Lose the unnecessary lock and re-acquire the lock (and potentially recreate the node).
- // check if the parent exists!!
- // look into invalidated nodes as well
- if (cache.peek(n.getFqn(), true, true) == null)
- {
- // crap!
- log.trace("Parent has been deleted again. Go through the lock method all over again.");
- child_node.getLock().releaseAll(owner);
- rerunLoop = true;
- i = treeNodeSize;
- }
- else
- {
- child_node.getLock().releaseAll(owner);
-
- // do the loop again, but don't assign child_node to n so that child_node is processed again.
- i--;
- reAcquisitionOnSameNode = true;
- }
-
- continue;
+ // crap!
+ if (log.isTraceEnabled()) log.trace("Parent has been deleted again. Go through the lock method all over again.");
+ currentNode = cache.getRoot();
+ parent = null;
}
else
{
- reAcquisitionOnSameNode = false;
- }
-
- if (recursive && isTargetNode(i, treeNodeSize))
- {
- Set<NodeLock> acquired_locks = lockManager.acquireAll(child_node, owner, lock_type, timeout);
- if (acquired_locks.size() > 0)
+ // do the loop again, but don't assign child_node to currentNode so that child_node is processed again.
+ if (System.currentTimeMillis() > expiryTime)
{
- if (gtx != null)
- {
- cache.getTransactionTable().addLocks(gtx, acquired_locks);
- }
- else
- {
- List<NodeLock> locks = getLocks(currentThread);
- locks.addAll(acquired_locks);
- }
+ throw new TimeoutException("Unable to acquire lock on child node " + new Fqn(currentNode.getFqn(), childName) + " after " + timeout + " millis.");
}
}
- n = child_node;
}
- }
-
- // Add the Fqn to be removed to the transaction entry so we can clean up after ourselves during commit/rollback
- if (isDeleteOperation && gtx != null) cache.getTransactionTable().get(gtx).addRemovedNode(fqn);
-
+ else
+ {
+ if (currentNode.getFqn().equals(fqn))//we've just processed the last child
+ {
+ break;
+ }
+ parent = currentNode;
+ currentIndex = currentNode.getFqn().size();
+ currentNode = currentNode.getChildDirect(fqn.get(currentIndex));
+ childName = (String) fqn.get(currentIndex);
+ }
+ } while (true);
return created;
}
- private boolean needToReverseRemove(NodeSPI n, TransactionEntry te, NodeLock.LockType lockTypeRequested, boolean isRemoveOperation, boolean createIfNotExists)
- {
- return !isRemoveOperation && createIfNotExists && lockTypeRequested == NodeLock.LockType.WRITE && n.isDeleted() && te.getRemovedNodes().contains(n.getFqn());
- }
- private void reverseRemove(NodeSPI n)
+ /**
+ * Acquires nodes on the children of this node. nodes on the node itself are not aquired.
+ * If the supplied parent node is null the method returns(no op).
+ */
+ private void acquireLocksOnChildren(NodeSPI parentNode, NodeLock.LockType lockType, InvocationContext ctx)
+ throws InterruptedException
{
- n.markAsDeleted(false);
+ if (parentNode == null)
+ {
+ return;
+ }
+ long timeout = ctx.getContextLockAcquisitionTimeout(lock_acquisition_timeout);
+ GlobalTransaction gtx = ctx.getGlobalTransaction();
+ Object owner = (gtx != null) ? gtx : Thread.currentThread();
+
+ Set<NodeLock> acquiredLocks = parentNode.getLock().acquireAll(owner, timeout, lockType);
+ if (acquiredLocks.size() > 0)
+ {
+ if (gtx != null)
+ {
+ cache.getTransactionTable().addLocks(gtx, acquiredLocks);
+ }
+ else
+ {
+ List<NodeLock> locks = getLocks(Thread.currentThread());
+ locks.addAll(acquiredLocks);
+ }
+ }
}
- private boolean writeLockNeeded(InvocationContext ctx, NodeLock.LockType lock_type, int currentNodeIndex, int treeNodeSize, boolean isEvictOperation, boolean isRemoveOperation, boolean isPutOperation, boolean isRemoveDataOperation, Fqn targetFqn, NodeSPI currentNode)
+ /**
+ * Used by {@link #lock(org.jboss.cache.InvocationContext, org.jboss.cache.Fqn, org.jboss.cache.lock.NodeLock.LockType, boolean, long, boolean, boolean)}.
+ * Determins whter an arbitrary node from the supplied fqn needs an write lock.
+ */
+ private boolean writeLockNeeded(InvocationContext ctx, NodeLock.LockType lockType, int currentNodeIndex, boolean acquireWriteLockOnParent, boolean createIfNotExists, Fqn targetFqn, NodeSPI currentNode)
{
+ int treeNodeSize = targetFqn.size();
// write lock forced!!
- boolean isTargetNode = isTargetNode(currentNodeIndex, treeNodeSize);
-
- if (ctx.getOptionOverrides().isForceWriteLock() && isTargetNode) return true;
-
+ boolean isTargetNode = currentNodeIndex == (treeNodeSize - 1);
+ if (ctx.getOptionOverrides().isForceWriteLock() && isTargetNode) return true;
+ //this can be injected, from the caller as a param named wlParent
if (currentNode.isLockForChildInsertRemove())
{
- if (isRemoveOperation && currentNodeIndex == treeNodeSize - 2)
+ if (acquireWriteLockOnParent && currentNodeIndex == treeNodeSize - 2)
{
return true;// we're doing a remove and we've reached the PARENT node of the target to be removed.
}
-
if (!isTargetNode && cache.peek(new Fqn(currentNode.getFqn(), targetFqn.get(currentNodeIndex + 1)), false) == null)
{
- return isPutOperation;// we're at a node in the tree, not yet at the target node, and we need to create the nextInterceptor node. So we need a WL here.
+ return createIfNotExists;// we're at a node in the tree, not yet at the target node, and we need to create the next node. So we need a WL here.
}
}
- return lock_type == NodeLock.LockType.WRITE && isTargetNode && (isPutOperation || isRemoveOperation || isEvictOperation || isRemoveDataOperation);//normal operation, write lock explicitly requested and this is the target to be written to.
+ return lockType == NodeLock.LockType.WRITE && isTargetNode;//write lock explicitly requested and this is the target to be written to.
}
- private boolean isTargetNode(int nodePosition, int treeNodeSize)
+ private void acquireNodeLock(NodeSPI node, Object owner, GlobalTransaction gtx, NodeLock.LockType lockType, long lockTimeout) throws LockingException, TimeoutException, InterruptedException
{
- return nodePosition == (treeNodeSize - 1);
- }
-
- private void acquireNodeLock(NodeSPI node, Object owner, GlobalTransaction gtx, NodeLock.LockType lock_type, long lock_timeout) throws LockingException, TimeoutException, InterruptedException
- {
- boolean acquired = lockManager.acquire(node, owner, lock_type, lock_timeout);
+ boolean acquired = node.getLock().acquire(owner, lockTimeout, lockType);
if (acquired)
{
// Record the lock for release on method return or tx commit/rollback
- recordNodeLock(gtx, lockManager.getLock(node));
+ NodeLock lock = node.getLock();
+ if (gtx != null)
+ {
+ cache.getTransactionTable().recordNodeLock(gtx, lock);
+ }
+ else
+ {
+ Thread currentThread = Thread.currentThread();
+ List<NodeLock> locks = getLocks(currentThread);
+ if (!locks.contains(lock))
+ {
+ locks.add(lock);
+ lock_table.put(currentThread, locks);
+ }
+ }
}
- else
- {
- //if (log.isDebugEnabled()) log.debug("Unable to acquire lock on node " + node.getFqn());
- //throw new LockingException("Could not acquite lock for " + node.getFqn());
- }
}
- private void recordNodeLock(GlobalTransaction gtx, NodeLock lock)
+ private List<NodeLock> getLocks(Thread currentThread)
{
- if (gtx != null)
- {
- // add the lock to the list of locks maintained for this transaction
- // (needed for release of locks on commit or rollback)
- try
- {
- cache.getTransactionTable().addLock(gtx, lock);
- }
- catch (CacheException e)
- {
- // may happen, if the transaction entry does not exist
- lock.release(gtx);
- throw e;
- }
- }
- else
- {
- Thread currentThread = Thread.currentThread();
- List<NodeLock> locks = getLocks(currentThread);
- if (!locks.contains(lock))
- {
- locks.add(lock);
- lock_table.put(currentThread, locks);
- }
- }
- }
-
- //todo move to lock tabe as it is information xprt
- private List<NodeLock> getLocks(Thread currentThread)
- {
// This sort of looks like a get/put race condition, but
// since we key off the Thread, it's not
List<NodeLock> locks = lock_table.get(currentThread);
@@ -529,50 +506,25 @@
}
- private void createNodes(Fqn fqn, GlobalTransaction gtx)
+ /**
+ * Test if this node needs to be 'undeleted'
+ * reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
+ */
+ private void manageReverseRemove(GlobalTransaction gtx, NodeSPI childNode, boolean reverseRemoveCheck)
{
- int treeNodeSize;
- if ((treeNodeSize = fqn.size()) == 0) return;
- NodeSPI n = cache.getRoot();
- for (int i = 0; i < treeNodeSize; i++)
+ boolean needToReverseRemove = reverseRemoveCheck && childNode.isDeleted() && tx_table.get(gtx).getRemovedNodes().contains(childNode.getFqn());
+ if (gtx != null && needToReverseRemove)
{
- Object child_name = fqn.get(i);
- Fqn childFqn = new Fqn(child_name);
-
- NodeSPI child_node = n.getChildDirect(childFqn);
- if (child_node == null) child_node = n.addChildDirect(childFqn);
- // test if this node needs to be 'undeleted'
- // reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put()
- if (gtx != null && needToReverseRemove(child_node, tx_table.get(gtx), NodeLock.LockType.WRITE, false, true))
- {
- reverseRemove(child_node);
- }
-
- if (child_node == null)
- {
- if (log.isTraceEnabled())
- {
- log.trace("failed to find or create child " + child_name + " of node " + n.getFqn());
- }
- return;
- }
- n = child_node;
+ childNode.markAsDeleted(false);
}
}
/**
* Remove all locks held by <tt>tx</tt>, remove the transaction from the transaction table
- *
- * @param gtx
- * todo move this logic in txTable as it is information expert for this class (this looks procedural)
*/
private void commit(GlobalTransaction gtx)
{
- if (log.isTraceEnabled())
- {
- log.trace("committing cache with gtx " + gtx);
- }
-
+ if (log.isTraceEnabled()) log.trace("committing cache with gtx " + gtx);
TransactionEntry entry = tx_table.get(gtx);
if (entry == null)
{
@@ -590,104 +542,4 @@
}
}
- /**
- * todo move this logic in txTable as it is information expert for this class (this looks procedural)
- */
- private void cleanup(GlobalTransaction gtx)
- {
- if (log.isTraceEnabled()) log.trace("Cleaning up locks for gtx " + gtx);
- TransactionEntry entry = tx_table.get(gtx);
- // Let's do it in stack style, LIFO
- if (entry != null)
- entry.releaseAllLocksLIFO(gtx);
- else
- log.error("No transaction entry present!!", new Throwable());
-
-/*
- Transaction ltx = entry.getTransaction();
- if (log.isTraceEnabled())
- {
- log.trace("removing local transaction " + ltx + " and global transaction " + gtx);
- }
- tx_table.remove(ltx);
- tx_table.remove(gtx);
-*/
- }
-
- /**
- * Revert all changes made inside this TX: invoke all method calls of the undo-ops
- * list. Then release all locks and remove the TX from the transaction table.
- * <ol>
- * <li>Revert all modifications done in the current TX<li/>
- * <li>Release all locks held by the current TX</li>
- * <li>Remove all temporary nodes created by the current TX</li>
- * </ol>
- *
- * todo move this logic in txTable as it is information expert for this class (this looks procedural)
- */
- private void rollback(GlobalTransaction tx)
- {
- TransactionEntry entry = tx_table.get(tx);
-
- if (log.isTraceEnabled())
- {
- log.trace("called to rollback cache with GlobalTransaction=" + tx);
- }
-
- if (entry == null)
- {
- log.error("entry for transaction " + tx + " not found (transaction has possibly already been rolled back)");
- return;
- }
-
- Iterator removedNodes = entry.getRemovedNodes().iterator();
- CacheImpl cacheImpl = (CacheImpl) cache;
- while (removedNodes.hasNext())
- {
- Fqn f = (Fqn) removedNodes.next();
- cacheImpl.realRemove(f, false);
-
- }
-
- // 1. Revert the modifications by running the undo-op list in reverse. This *cannot* throw any exceptions !
- entry.undoOperations(cache);
-
- // This was removed as we don't use temporary nodes anymore; we now create undo-operations on put(), e.g.
- // put(/a/b/c) on /a, create b and c, plus undo operations _remove(a/b/c) and _remove(/a/b)
-
- // 2. Remove all temporary nodes. Need to do it backwards since node is LIFO.
- // for(ListIterator it=new LinkedList(entry.getNodes()).listIterator(entry.getNodes().size());
- // it.hasPrevious();) {
- // node_name=(Fqn)it.previous();
- // try {
- // cache._remove(tx, node_name, false);
- // }
- // catch(Throwable t) {
- // log.error("failed removing node \"" + node_name + "\"", t);
- // }
- // }
-
- // 3. Finally, release all locks held by this TX
- // Let's do it in stack style, LIFO
- // Note that the lock could have been released already so don't panic.
- }
-
- private static class LockManager
- {
- boolean acquire(NodeSPI node, Object owner, NodeLock.LockType lockType, long timeout) throws InterruptedException
- {
- return getLock(node).acquire(owner, timeout, lockType);
- }
-
- NodeLock getLock(NodeSPI node)
- {
- return node.getLock();
- }
-
- public Set<NodeLock> acquireAll(NodeSPI node, Object owner, NodeLock.LockType lockType, long timeout) throws InterruptedException
- {
- return getLock(node).acquireAll(owner, timeout, lockType);
- }
- }
-
}
Modified: core/trunk/src/main/java/org/jboss/cache/transaction/TransactionTable.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/transaction/TransactionTable.java 2007-12-10 21:57:40 UTC (rev 4824)
+++ core/trunk/src/main/java/org/jboss/cache/transaction/TransactionTable.java 2007-12-10 23:47:41 UTC (rev 4825)
@@ -230,6 +230,18 @@
entry.addLocks(locks);
}
+ public void cleanup(GlobalTransaction gtx)
+ {
+ if (log.isTraceEnabled()) log.trace("Cleaning up locks for gtx " + gtx);
+ TransactionEntry entry = this.get(gtx);
+ // Let's do it in stack style, LIFO
+ if (entry != null)
+ entry.releaseAllLocksLIFO(gtx);
+ else
+ log.error("No transaction entry present!!", new Throwable());
+ }
+
+
/**
* Returns summary debug information.
*/
@@ -264,4 +276,21 @@
return sb.toString();
}
+ /**
+ * Add the lock to the list of locks maintained for this transaction
+ * (needed for release of locks on commit or rollback)
+ */
+ public void recordNodeLock(GlobalTransaction gtx, NodeLock lock)
+ {
+ try
+ {
+ addLock(gtx, lock);
+ }
+ catch (CacheException e)
+ {
+ // may happen, if the transaction entry does not exist
+ lock.release(gtx);
+ throw e;
+ }
+ }
}
17 years