Package org.infinispan.factories

Source Code of org.infinispan.factories.AbstractComponentRegistry$PrioritizedMethod

/*
* JBoss, Home of Professional Open Source.
* Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.factories;

import org.infinispan.CacheException;
import org.infinispan.Version;
import org.infinispan.config.Configuration;
import org.infinispan.config.ConfigurationException;
import org.infinispan.config.RuntimeConfig;
import org.infinispan.factories.annotations.ComponentName;
import org.infinispan.factories.annotations.DefaultFactoryFor;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.NonVolatile;
import org.infinispan.factories.annotations.Start;
import org.infinispan.factories.annotations.Stop;
import org.infinispan.factories.scopes.Scope;
import org.infinispan.factories.scopes.Scopes;
import org.infinispan.lifecycle.ComponentStatus;
import org.infinispan.lifecycle.Lifecycle;
import org.infinispan.util.BeanUtils;
import org.infinispan.util.ReflectionUtil;
import org.infinispan.util.logging.Log;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

/**
* A registry where components which have been created are stored.  Components are stored as singletons, registered
* under a specific name.
* <p/>
* Components can be retrieved from the registry using {@link #getComponent(Class)}.
* <p/>
* Components can be registered using {@link #registerComponent(Object, Class)}, which will cause any dependencies to be
* wired in as well.  Components that need to be created as a result of wiring will be done using {@link
* #getOrCreateComponent(Class)}, which will look up the default factory for the component type (factories annotated
* with the appropriate {@link DefaultFactoryFor} annotation.
* <p/>
* Default factories are treated as components too and will need to be wired before being used.
* <p/>
* The registry can exist in one of several states, as defined by the {@link org.infinispan.lifecycle.ComponentStatus}
* enumeration. In terms of the cache, state changes in the following manner: <ul> <li>INSTANTIATED - when first
* constructed</li> <li>CONSTRUCTED - when created using the DefaultCacheFactory</li> <li>STARTED - when {@link
* org.infinispan.Cache#start()} is called</li> <li>STOPPED - when {@link org.infinispan.Cache#stop()} is called</li>
* </ul>
* <p/>
* Cache configuration can only be changed and will only be reinjected if the cache is not in the {@link
* org.infinispan.lifecycle.ComponentStatus#RUNNING} state.
*
* @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
* @since 4.0
*/
@NonVolatile
@Scope(Scopes.NAMED_CACHE)
public abstract class AbstractComponentRegistry implements Lifecycle {

   // Make sure this is ALWAYS false when being checked in to the code repository!
   public static final boolean DEBUG_DEPENDENCIES = false;
   private Stack<String> debugStack = DEBUG_DEPENDENCIES ? new Stack<String>() : null;

   /**
    * Contains class definitions of component factories that can be used to construct certain components
    */
   private Map<Class, Class<? extends AbstractComponentFactory>> defaultFactories = null;

   protected static final Object NULL_COMPONENT = new Object();

   // component and method containers
   final Map<String, Component> componentLookup = new HashMap<String, Component>();

   volatile ComponentStatus state = ComponentStatus.INSTANTIATED;

   /**
    * Retrieves the state of the registry
    *
    * @return state of the registry
    */
   public ComponentStatus getStatus() {
      return state;
   }

   protected abstract Log getLog();

   /**
    * Wires an object instance with dependencies annotated with the {@link Inject} annotation, creating more components
    * as needed based on the Configuration passed in if these additional components don't exist in the {@link
    * ComponentRegistry}.  Strictly for components that don't otherwise live in the registry and have a lifecycle, such
    * as Commands.
    *
    * @param target object to wire
    * @throws ConfigurationException if there is a problem wiring the instance
    */
   public void wireDependencies(Object target) throws ConfigurationException {
      try {
         // don't use the reflection cache for wireDependencies calls since these are not managed by the ComponentRegistry
         // and may be invoked at any time, even after the cache starts.
         List<Method> methods = ReflectionUtil.getAllMethods(target.getClass(), Inject.class);

         // search for anything we need to inject
         for (Method method : methods) invokeInjectionMethod(target, method);
      }
      catch (Exception e) {
         throw new ConfigurationException("Unable to configure component (type: " + target.getClass() + ", instance " + target + ")", e);
      }
   }

   /**
    * This is hard coded for now, since scanning the classpath for factories annotated with {@link DefaultFactoryFor}
    * does not work with all class loaders.  This is a temporary solution until a more elegant one can be designed.
    * <p/>
    * BE SURE TO ADD ANY NEW FACTORY TYPES ANNOTATED WITH DefaultFactoryFor TO THIS SET!!
    * <p/>
    *
    * @return set of known factory types.
    */
   private Set<Class<? extends AbstractComponentFactory>> getHardcodedFactories() {
      Set<Class<? extends AbstractComponentFactory>> s = new HashSet<Class<? extends AbstractComponentFactory>>();
      s.add(BootstrapFactory.class);
      s.add(EmptyConstructorNamedCacheFactory.class);
      s.add(EmptyConstructorFactory.class);
      s.add(InterceptorChainFactory.class);
      s.add(RpcManagerFactory.class);
      s.add(TransactionManagerFactory.class);
      s.add(ReplicationQueueFactory.class);
      s.add(StateTransferManagerFactory.class);
      s.add(LockManagerFactory.class);
      s.add(DataContainerFactory.class);
      s.add(NamedExecutorsFactory.class);
      s.add(TransportFactory.class);
      s.add(MarshallerFactory.class);
      s.add(ResponseGeneratorFactory.class);
      s.add(DistributionManagerFactory.class);
      return s;
   }

   /**
    * Registers a component in the registry under the given type, and injects any dependencies needed.  If a component
    * of this type already exists, it is overwritten.
    *
    * @param component component to register
    * @param type      type of component
    */
   public void registerComponent(Object component, Class type) {
      registerComponent(component, type.getName());
   }

   public void registerComponent(Object component, String name) {
      if (component == null)
         throw new NullPointerException("Cannot register a null component under name [" + name + "]");
      Component old = componentLookup.get(name);

      if (old != null) {
         // if they are equal don't bother
         if (old.instance.equals(component)) {
            getLog().trace("Attempting to register a component equal to one that already exists under the same name ({0}).  Not doing anything.", name);
            return;
         }
      }

      Component c;
      if (old != null) {
         getLog().trace("Replacing old component {0} with new instance {1}", old, component);
         old.instance = component;
         old.methodsScanned = false;
         c = old;

         if (state == ComponentStatus.RUNNING) populateLifecycleMethods();
      } else {
         c = new Component();
         c.name = name;
         c.instance = component;
         componentLookup.put(name, c);
      }
      c.nonVolatile = ReflectionUtil.isAnnotationPresent(component.getClass(), NonVolatile.class);
      addComponentDependencies(c);
      // inject dependencies for this component
      c.injectDependencies();

      if (old == null) getLog().trace("Registering component {0} under name {1}", c, name);
   }

   /**
    * Adds component dependencies for a given component, by populating {@link Component#injectionMethods}.
    *
    * @param c component to add dependencies to
    */
   protected void addComponentDependencies(Component c) {
      Class type = c.instance.getClass();
      List<Method> methods = ReflectionUtil.getAllMethods(type, Inject.class);
      c.injectionMethods.clear();
      c.injectionMethods.addAll(methods);
   }

   @SuppressWarnings("unchecked")
   protected void invokeInjectionMethod(Object o, Method m) {
      Class[] dependencies = m.getParameterTypes();
      Annotation[][] parameterAnnotations = m.getParameterAnnotations();
      Object[] params = new Object[dependencies.length];
      if (getLog().isTraceEnabled()) getLog().trace("Injecting dependencies for method {0}.", m);
      for (int i = 0; i < dependencies.length; i++) {
         params[i] = getOrCreateComponent(dependencies[i], getComponentName(dependencies[i], parameterAnnotations, i));
      }

      ReflectionUtil.invokeAccessibly(o, m, params);
   }

   private String getComponentName(Class component, Annotation[][] annotations, int paramNumber) {
      String name;
      if (annotations == null ||
            annotations.length <= paramNumber ||
            (name = findComponentName(annotations[paramNumber])) == null) return component.getName();

      return name;
   }

   private String findComponentName(Annotation[] anns) {
      if (anns != null && anns.length > 0) {
         for (Annotation a : anns) {
            if (a instanceof ComponentName) {
               return ((ComponentName) a).value();
            }
         }
      }
      return null;
   }

   /**
    * Retrieves a component if one exists, and if not, attempts to find a factory capable of constructing the component
    * (factories annotated with the {@link 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 Inject}.
    * <p/>
    * Summing it up, component retrieval happens in the following order:<br /> 1.  Look for a component that has already
    * been created and registered. 2.  Look for an appropriate component that exists in the {@link Configuration} that
    * may be injected from an external system. 3.  Look for a class definition passed in to the {@link Configuration} -
    * such as an EvictionPolicy implementation 4.  Attempt to create it by looking for an appropriate factory (annotated
    * with {@link DefaultFactoryFor})
    * <p/>
    *
    * @param componentClass type of component to be retrieved.  Should not be null.
    * @return a fully wired component instance, or null if one cannot be found or constructed.
    * @throws ConfigurationException if there is a problem with consructing or wiring the instance.
    */
   protected <T> T getOrCreateComponent(Class<T> componentClass) {
      return getOrCreateComponent(componentClass, componentClass.getName());
   }

   protected <T> T getOrCreateComponent(Class<T> componentClass, String name) {
      if (DEBUG_DEPENDENCIES) debugStack.push(name);

      T component = getComponent(componentClass, name);

      if (component == null) {
         // first see if this has been injected externally.
         component = getFromConfiguration(componentClass);
         boolean attemptedFactoryConstruction = false;

         if (component == null) {
            // create this component and add it to the registry
            AbstractComponentFactory factory = getFactory(componentClass);
            component = factory instanceof NamedComponentFactory ?
                  ((NamedComponentFactory) factory).construct(componentClass, name)
                  : factory.construct(componentClass);
            attemptedFactoryConstruction = true;

         }

         if (component != null) {
            registerComponent(component, componentClass);
         } else if (attemptedFactoryConstruction) {
            if (getLog().isTraceEnabled())
               getLog().trace("Registering a null for component {0}", componentClass.getSimpleName());
            registerNullComponent(componentClass);
         }
      }

      if (DEBUG_DEPENDENCIES) debugStack.pop();
      return component;
   }

   /**
    * Retrieves a component factory instance capable of constructing components of a specified type.  If the factory
    * doesn't exist in the registry, one is created.
    *
    * @param componentClass type of component to construct
    * @return component factory capable of constructing such components
    */
   protected AbstractComponentFactory getFactory(Class componentClass) {
      Map<Class, Class<? extends AbstractComponentFactory>> defaultFactoryMap = getDefaultFactoryMap();
      Class<? extends AbstractComponentFactory> cfClass = defaultFactoryMap.get(componentClass);
      if (cfClass == null)
         throw new ConfigurationException("No registered default factory for component " + componentClass + " found! Debug stack: " + debugStack);
      // a component factory is a component too!  See if one has been created and exists in the registry
      AbstractComponentFactory cf = getComponent(cfClass);
      if (cf == null) {
         // hasn't yet been created.  Create and put in registry
         cf = instantiateFactory(cfClass);
         if (cf == null)
            throw new ConfigurationException("Unable to locate component factory for component " + componentClass + "  Debug stack: " + debugStack);
         // we simply register this factory.  Registration will take care of constructing any dependencies.
         registerComponent(cf, cfClass);
      }

      // ensure the component factory is in the STARTED state!
      Component c = lookupComponent(cfClass, cfClass.getName());
      if (c.instance != cf)
         throw new ConfigurationException("Component factory " + cfClass + " incorrectly registered! Debug stack: " + debugStack);
      return cf;
   }

   protected Component lookupComponent(Class type, String componentName) {
      return componentLookup.get(componentName);
   }

   protected Map<Class, Class<? extends AbstractComponentFactory>> getDefaultFactoryMap() {
      if (defaultFactories == null) scanDefaultFactories();
      return defaultFactories;
   }

   /**
    * Scans the class path for classes annotated with {@link DefaultFactoryFor}, and analyses which components can be
    * created by such factories.
    */
   void scanDefaultFactories() {
      Map<Class, Class<? extends AbstractComponentFactory>> temp = new HashMap<Class, Class<? extends AbstractComponentFactory>>();
      Set<Class<? extends AbstractComponentFactory>> factories = getHardcodedFactories();

      for (Class<? extends AbstractComponentFactory> factory : factories) {
         // check if this implements auto-instantiable.  If it doesn't have a no-arg constructor throw an exception
         boolean factoryValid = true;
         try {
            if (AutoInstantiableFactory.class.isAssignableFrom(factory) && factory.getConstructor() == null) {
               factoryValid = false;
            }
         } catch (Exception e) {
            factoryValid = false;
         }

         if (!factoryValid)
            throw new RuntimeException("Factory class " + factory + " implements AutoInstantiableFactory but does not expose a public, no-arg constructor!  Debug stack: " + debugStack);

         DefaultFactoryFor dFFAnnotation = factory.getAnnotation(DefaultFactoryFor.class);
         if (dFFAnnotation != null) {
            for (Class targetClass : dFFAnnotation.classes()) temp.put(targetClass, factory);
         }
      }

      defaultFactories = temp;
   }

   /**
    * No such thing as a meta factory yet.  Factories are created using this method which attempts to use an empty
    * public constructor.
    *
    * @param factory class of factory to be created
    * @return factory instance
    */
   AbstractComponentFactory instantiateFactory(Class<? extends AbstractComponentFactory> factory) {
      if (AutoInstantiableFactory.class.isAssignableFrom(factory)) {
         try {
            return factory.newInstance();
         }
         catch (Exception e) {
            // unable to get a hold of an instance!!
            throw new ConfigurationException("Unable to instantiate factory " + factory + "  Debug stack: " + debugStack, e);
         }
      } else {
         throw new ConfigurationException("Cannot auto-instantiate factory " + factory + " as it doesn't implement " + AutoInstantiableFactory.class.getSimpleName() + "!  Debug stack: " + debugStack);
      }
   }

   /**
    * registers a special "null" component that has no dependencies.
    *
    * @param type type of component to register as a null
    */
   void registerNullComponent(Class type) {
      registerComponent(NULL_COMPONENT, type);
   }

   /**
    * Retrieves a component from the {@link Configuration} or {@link RuntimeConfig}.
    *
    * @param componentClass component type
    * @return component, or null if it cannot be found
    */
   @SuppressWarnings("unchecked")
   protected <T> T getFromConfiguration(Class<T> componentClass) {
      getLog().debug("Looking in configuration for an instance of {0} that may have been injected from an external source.", componentClass);
      Method getter = BeanUtils.getterMethod(Configuration.class, componentClass);
      T returnValue = null;

      if (getter != null) {
         try {
            returnValue = (T) getter.invoke(getConfiguration());
         }
         catch (Exception e) {
            getLog().warn("Unable to invoke getter {0} on Configuration.class!", e, getter);
         }
      }

      // 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) {
//               getLog().warn("Unable to invoke getter {0} on RuntimeConfig.class!", e, getter);
//            }
//         }
//      }
      return returnValue;
   }

   /**
    * Retrieves the configuration component.
    *
    * @return a Configuration object
    */
   protected Configuration getConfiguration() {
      // this is assumed to always be present as a part of the bootstrap/construction of a ComponentRegistry.
      return getComponent(Configuration.class);
   }

   /**
    * Retrieves a component of a specified type from the registry, or null if it cannot be found.
    *
    * @param type type to find
    * @return component, or null
    */
   public <T> T getComponent(Class<T> type) {
      return getComponent(type, type.getName());
   }

   @SuppressWarnings("unchecked")
   public <T> T getComponent(Class<T> type, String name) {
      Component wrapper = lookupComponent(type, name);
      if (wrapper == null) return null;

      return (T) (wrapper.instance == NULL_COMPONENT ? null : wrapper.instance);
   }

   /**
    * Registers the default class loader.  This method *must* be called before any other components are registered,
    * typically called by bootstrap code.  Defensively, it is called in the constructor of ComponentRegistry with a null
    * parameter.
    *
    * @param loader a class loader to use by default.  If this is null, the class loader used to load this instance of
    *               ComponentRegistry is used.
    */
   public void registerDefaultClassLoader(ClassLoader loader) {
      registerComponent(loader == null ? getClass().getClassLoader() : loader, ClassLoader.class);
      // make sure the class loaderold is non-volatile, so it survives restarts.
      componentLookup.get(ClassLoader.class.getName()).nonVolatile = true;
   }

   /**
    * Rewires components.  Can only be called if the current state is WIRED or STARTED.
    */
   public void rewire() {
      // need to re-inject everything again.
      for (Component c : new HashSet<Component>(componentLookup.values())) {
         // inject dependencies for this component
         c.injectDependencies();
      }
   }

   /**
    * Scans each registered component for lifecycle methods, and adds them to the appropriate lists, and then sorts them
    * by priority.
    */
   private void populateLifecycleMethods() {
      for (Component c : componentLookup.values()) {
         if (!c.methodsScanned) {
            c.methodsScanned = true;
            c.startMethods.clear();
            c.stopMethods.clear();

            List<Method> methods = ReflectionUtil.getAllMethods(c.instance.getClass(), Start.class);
            for (Method m : methods) {
               PrioritizedMethod em = new PrioritizedMethod();
               em.component = c;
               em.method = m;
               em.priority = m.getAnnotation(Start.class).priority();
               c.startMethods.add(em);
            }

            methods = ReflectionUtil.getAllMethods(c.instance.getClass(), Stop.class);
            for (Method m : methods) {
               PrioritizedMethod em = new PrioritizedMethod();
               em.component = c;
               em.method = m;
               em.priority = m.getAnnotation(Stop.class).priority();
               c.stopMethods.add(em);
            }
         }
      }
   }

   /**
    * Removes any components not annotated as @NonVolatile.
    */
   public void resetNonVolatile() {
      // destroy all components to clean up resources
      for (Component c : new HashSet<Component>(componentLookup.values())) {
         // the component is volatile!!
         if (!c.nonVolatile) {
            componentLookup.remove(c.name);
         }
      }

      if (getLog().isTraceEnabled())
         getLog().trace("Reset volatile components.  Registry now contains {0}", componentLookup.keySet());
   }

   // ------------------------------ START: Publicly available lifecycle methods -----------------------------
   //   These methods perform a check for appropriate transition and then delegate to similarly named internal methods.

   /**
    * Creates the components needed by a cache instance and sets the cache status to {@link
    * org.infinispan.lifecycle.ComponentStatus#INITIALIZING} when it is done.
    */
   private void init() {
      if (state.needToDestroyFailedCache())
         destroy();

      try {
         internalCreate();
      }
      catch (Throwable t) {
         handleLifecycleTransitionFailure(t);
      }
   }

   /**
    * This starts the components in the cache, connecting to channels, starting service threads, etc.  If the cache is
    * not in the {@link org.infinispan.lifecycle.ComponentStatus#INITIALIZING} state, {@link #init()} will be invoked
    * first.
    */
   public void start() {
      boolean createdInStart = false;
      if (!state.startAllowed()) {
         if (state.needToDestroyFailedCache())
            destroy(); // this will take us back to TERMINATED

         if (state.needToInitializeBeforeStart()) {
            init();
            createdInStart = true;
         } else
            return;
      }

      try {
         internalStart(createdInStart);
      }
      catch (Throwable t) {
         handleLifecycleTransitionFailure(t);
      }
   }

   /**
    * Stops the cache and sets the cache status to {@link org.infinispan.lifecycle.ComponentStatus#TERMINATED} once it
    * is done.  If the cache is not in the {@link org.infinispan.lifecycle.ComponentStatus#RUNNING} state, this is a
    * no-op.
    */
   public void stop() {
      if (!state.stopAllowed()) {
         return;
      }

      // Trying to stop() from FAILED is valid, but may not work
      boolean failed = state == ComponentStatus.FAILED;

      try {
         internalStop();
      }
      catch (Throwable t) {
         if (failed) {
            getLog().warn("Attempted to stop() from FAILED state, but caught exception; try calling destroy()", t);
         }
         failed = true;
         handleLifecycleTransitionFailure(t);
      }
      finally {
         if (!failed) state = ComponentStatus.TERMINATED;
      }
   }

   /**
    * Destroys the cache and frees up any resources.  Sets the cache status to {@link
    * org.infinispan.lifecycle.ComponentStatus#TERMINATED} when it is done.
    * <p/>
    * If the cache is in {@link org.infinispan.lifecycle.ComponentStatus#RUNNING} when this method is called, it will
    * first call {@link #stop()} to stop the cache.
    */
   private void destroy() {
      try {
         stop();
      }
      catch (CacheException e) {
         getLog().warn("Needed to call stop() before destroying but stop() threw exception. Proceeding to destroy", e);
      }

      try {
         resetNonVolatile();
      }
      finally {
         // We always progress to destroyed
         state = ComponentStatus.TERMINATED;
      }
   }
   // ------------------------------ END: Publicly available lifecycle methods -----------------------------

   // ------------------------------ START: Actual internal lifecycle methods --------------------------------

   /**
    * Sets the cacheStatus to FAILED and rethrows the problem as one of the declared types. Converts any
    * non-RuntimeException Exception to CacheException.
    *
    * @param t throwable thrown during failure
    */
   private void handleLifecycleTransitionFailure(Throwable t) {
      state = ComponentStatus.FAILED;
      if (t instanceof CacheException)
         throw (CacheException) t;
      else if (t instanceof RuntimeException)
         throw (RuntimeException) t;
      else if (t instanceof Error)
         throw (Error) t;
      else
         throw new CacheException(t);
   }

   /**
    * The actual create implementation.
    */
   private void internalCreate() {
      state = ComponentStatus.INITIALIZING;
      resetNonVolatile();
      rewire();
   }

   private void internalStart(boolean createdInStart) throws CacheException, IllegalArgumentException {
      if (!createdInStart) {
         // re-wire all dependencies in case stuff has changed since the cache was created
         // remove any components whose construction may have depended upon a configuration that may have changed.
         resetNonVolatile();
         rewire();
      }

      // start all internal components
      // first cache all start, stop and destroy methods.
      populateLifecycleMethods();

      List<PrioritizedMethod> startMethods = new ArrayList<PrioritizedMethod>(componentLookup.size());
      for (Component c : componentLookup.values()) startMethods.addAll(c.startMethods);

      // sort the start methods by priority
      Collections.sort(startMethods);

      // fire all START methods according to priority


      for (PrioritizedMethod em : startMethods) em.invoke();

      addShutdownHook();

      getLog().info("Infinispan version: " + Version.printVersion());
      state = ComponentStatus.RUNNING;
   }

   protected void addShutdownHook() {
      // no op.  Override if needed.
   }

   protected void removeShutdownHook() {
      // no op.  Override if needed.
   }

   /**
    * Actual stop
    */
   private void internalStop() {
      state = ComponentStatus.STOPPING;
      removeShutdownHook();

      List<PrioritizedMethod> stopMethods = new ArrayList<PrioritizedMethod>(componentLookup.size());
      for (Component c : componentLookup.values()) stopMethods.addAll(c.stopMethods);

      Collections.sort(stopMethods);

      // fire all STOP methods according to priority
      for (PrioritizedMethod em : stopMethods) em.invoke();

      destroy();
   }

   // ------------------------------ END: Actual internal lifecycle methods --------------------------------

   /**
    * Asserts whether invocations are allowed on the cache or not.  Returns <tt>true</tt> if invocations are to be
    * allowed, <tt>false</tt> otherwise.  If the origin of the call is remote and the cache status is {@link
    * org.infinispan.lifecycle.ComponentStatus#INITIALIZING}, this method will block for up to {@link
    * Configuration#getStateRetrievalTimeout()} millis, checking for a valid state.
    *
    * @param originLocal true if the call originates locally (i.e., from the {@link org.infinispan.CacheDelegate} or
    *                    false if it originates remotely, i.e., from the {@link org.infinispan.remoting.InboundInvocationHandler}.
    * @return true if invocations are allowed, false otherwise.
    */
   public boolean invocationsAllowed(boolean originLocal) {
      getLog().trace("Testing if invocations are allowed.");
      if (state.allowInvocations()) return true;

      // if this is a locally originating call and the cache is not in a valid state, return false.
      if (originLocal) return false;

      getLog().trace("Is remotely originating.");

      // else if this is a remote call and the status is STARTING, wait until the cache starts.
      if (state == ComponentStatus.INITIALIZING) {
         getLog().trace("Cache is initializing; block.");
         try {
            blockUntilCacheStarts();
            return true;
         }
         catch (InterruptedException e) {
            Thread.currentThread().interrupt();
         }
      } else {
         getLog().warn("Received a remote call but the cache is not in STARTED state - ignoring call.");
      }
      return false;
   }

   /**
    * Blocks until the current cache instance is in its {@link org.infinispan.lifecycle.ComponentStatus#RUNNING started}
    * phase. Blocks for up to {@link 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 = getConfiguration().getStateRetrievalTimeout();
      long giveUpTime = System.currentTimeMillis() + startupWaitTime;

      while (System.currentTimeMillis() < giveUpTime) {
         if (state.allowInvocations()) break;
         Thread.sleep(pollFrequencyMS);
      }

      // check if we have started.
      if (!state.allowInvocations())
         throw new IllegalStateException("Cache not in STARTED state, even after waiting " + getConfiguration().getStateRetrievalTimeout() + " millis.");
   }

   /**
    * A wrapper representing a component in the registry
    */
   public class Component {
      /**
       * A reference to the object instance for this component.
       */
      Object instance;
      /**
       * The name of the component
       */
      String name;
      boolean methodsScanned;
      /**
       * List of injection methods used to inject dependencies into the component
       */
      List<Method> injectionMethods = new ArrayList<Method>(2);
      List<PrioritizedMethod> startMethods = new ArrayList<PrioritizedMethod>(2);
      List<PrioritizedMethod> stopMethods = new ArrayList<PrioritizedMethod>(2);
      /**
       * If true, then this component is not flushed before starting the ComponentRegistry.
       */
      boolean nonVolatile;

      @Override
      public String toString() {
         return "Component{" +
               "instance=" + instance +
               ", name=" + name +
               ", nonVolatile=" + nonVolatile +
               '}';
      }

      /**
       * Injects dependencies into this component.
       */
      public void injectDependencies() {
         for (Method m : injectionMethods) invokeInjectionMethod(instance, m);
      }

      public Object getInstance() {
         return instance;
      }

      public String getName() {
         return name;
      }
   }


   /**
    * Wrapper to encapsulate a method along with a priority
    */
   static class PrioritizedMethod implements Comparable<PrioritizedMethod> {
      Method method;
      Component component;
      int priority;

      public int compareTo(PrioritizedMethod o) {
         return (priority < o.priority ? -1 : (priority == o.priority ? 0 : 1));
      }

      void invoke() {
         ReflectionUtil.invokeAccessibly(component.instance, method, null);
      }

      @Override
      public String toString() {
         return "PrioritizedMethod{" +
               "method=" + method +
               ", priority=" + priority +
               '}';
      }
   }

   /**
    * Returns an immutable set contating all the components that exists in the reporsitory at this moment.
    *
    * @return a set of components
    */
   public Set<Component> getRegisteredComponents() {
      HashSet<Component> defensiveCopy = new HashSet<Component>(componentLookup.values());
      return Collections.unmodifiableSet(defensiveCopy);
   }
}
TOP

Related Classes of org.infinispan.factories.AbstractComponentRegistry$PrioritizedMethod

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
Before(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');