org.infinispan.factories.AbstractComponentRegistry Maven / Gradle / Ivy
package org.infinispan.factories;
import org.infinispan.commons.CacheException;
import org.infinispan.commons.util.ReflectionUtil;
import org.infinispan.commons.util.Util;
import org.infinispan.commons.CacheConfigurationException;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.factories.annotations.DefaultFactoryFor;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.SurvivesRestarts;
import org.infinispan.factories.components.ComponentMetadata;
import org.infinispan.factories.components.ComponentMetadataRepo;
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.TimeService;
import org.infinispan.util.logging.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
/**
* A registry where components which have been created are stored. Components are stored as singletons, registered
* under a specific name.
*
* Components can be retrieved from the registry using {@link #getComponent(Class)}.
*
* 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.
*
* Default factories are treated as components too and will need to be wired before being used.
*
* 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: - INSTANTIATED - when first
* constructed
- CONSTRUCTED - when created using the DefaultCacheFactory
- STARTED - when {@link
* org.infinispan.Cache#start()} is called
- STOPPED - when {@link org.infinispan.Cache#stop()} is called
*
*
* Cache configuration can only be changed and will only be re-injected if the cache is not in the {@link
* org.infinispan.lifecycle.ComponentStatus#RUNNING} state.
*
* Thread Safety: instances of {@link GlobalComponentRegistry} can be concurrently updated so all
* the write operations are serialized through class intrinsic lock.
*
* @author Manik Surtani
* @author Galder Zamarreño
* @since 4.0
*/
@SurvivesRestarts
@Scope(Scopes.NAMED_CACHE)
public abstract class AbstractComponentRegistry implements Lifecycle, Cloneable {
private static final String DEPENDENCIES_ENABLE_JVMOPTION = "infinispan.debugDependencies";
/**
* Set the system property infinispan.debugDependencies to true to enable some extra information to
* errors generated by the component factory.
*/
public static final boolean DEBUG_DEPENDENCIES = Boolean.getBoolean(DEPENDENCIES_ENABLE_JVMOPTION);
private Stack debugStack = DEBUG_DEPENDENCIES ? new Stack() : null;
/**
* Contains class definitions of component factories that can be used to construct certain components
*/
// private Map> defaultFactories = null;
private static final Object NULL_COMPONENT = new Object();
// component and method containers
private final ConcurrentMap componentLookup = new ConcurrentHashMap(1);
protected volatile ComponentStatus state = ComponentStatus.INSTANTIATED;
private static final PrioritizedMethod[] EMPTY_PRIO_METHODS = {};
/**
* Retrieves the state of the registry
*
* @return state of the registry
*/
public ComponentStatus getStatus() {
return state;
}
protected abstract ClassLoader getClassLoader();
protected abstract Log getLog();
public abstract ComponentMetadataRepo getComponentMetadataRepo();
/**
* 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 CacheConfigurationException if there is a problem wiring the instance
*/
public void wireDependencies(Object target) throws CacheConfigurationException {
try {
Class> targetClass = target.getClass();
ComponentMetadata metadata = getComponentMetadataRepo().findComponentMetadata(targetClass);
if (metadata != null && metadata.getInjectMethods() != null && metadata.getInjectMethods().length != 0) {
// search for anything we need to inject
for (ComponentMetadata.InjectMetadata injectMetadata : metadata.getInjectMethods()) {
Class>[] methodParameters = injectMetadata.getParameterClasses();
if (methodParameters == null) {
methodParameters = ReflectionUtil.toClassArray(injectMetadata.getParameters(), getClassLoader());
injectMetadata.setParameterClasses(methodParameters);
}
Method method = injectMetadata.getMethod();
if (method == null) {
method = ReflectionUtil.findMethod(targetClass, injectMetadata.getMethodName(), methodParameters);
injectMetadata.setMethod(method);
}
invokeInjectionMethod(target, injectMetadata);
}
}
} catch (Exception e) {
throw new CacheConfigurationException("Unable to configure component (type: " + target.getClass() + ", instance " + target + ")", e);
}
}
/**
* 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 synchronized final void registerComponent(Object component, Class> type) {
registerComponent(component, type.getName(), type.equals(component.getClass()));
}
public synchronized final void registerComponent(Object component, String name) {
registerComponent(component, name, name.equals(component.getClass().getName()));
}
public synchronized final void registerComponent(Object component, String name, boolean nameIsFQCN) {
registerComponentInternal(component, name, nameIsFQCN);
}
protected synchronized final void registerNonVolatileComponent(Object component, String name) {
registerComponentInternal(component, name, false);
}
protected synchronized final void registerNonVolatileComponent(Object component, Class> type) {
registerComponentInternal(component, type.getName(), true);
}
protected synchronized void registerComponentInternal(Object component, String name, boolean nameIsFQCN) {
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().tracef("Attempting to register a component equal to one that already exists under the same name (%s). Not doing anything.", name);
return;
}
}
Component c;
if (old != null) {
getLog().tracef("Replacing old component %s with new instance %s", old, component);
old.instance = component;
old.methodsScanned = false;
c = old;
} else {
c = new Component();
c.name = name;
c.instance = component;
componentLookup.put(name, c);
}
c.metadata = getComponentMetadataRepo().findComponentMetadata(component.getClass());
try {
c.buildInjectionMethodsList();
} catch (ClassNotFoundException cnfe) {
throw new CacheException("Error injecting dependencies for component " + name, cnfe);
}
// inject dependencies for this component
// we inject dependencies only after the component is already in the map to support cyclical dependencies
c.injectDependencies();
if (old == null) getLog().tracef("Registering component %s under name %s", c, name);
if (state == ComponentStatus.RUNNING) {
populateLifeCycleMethods(c);
try {
invokeStartMethods(Arrays.asList(c.startMethods));
} catch (Throwable t) {
// the component hasn't started properly, remove its registration
componentLookup.remove(name);
// the caller will log the exception
handleLifecycleTransitionFailure(t);
}
}
}
@SuppressWarnings("unchecked")
private void invokeInjectionMethod(Object o, ComponentMetadata.InjectMetadata injectMetadata) {
Class>[] dependencies = injectMetadata.getParameterClasses();
if (dependencies.length > 0) {
Object[] params = new Object[dependencies.length];
if (getLog().isTraceEnabled())
getLog().tracef("Injecting dependencies for method [%s] on an instance of [%s].", injectMetadata.getMethod(), o.getClass().getName());
for (int i = 0; i < dependencies.length; i++) {
String name = injectMetadata.getParameterName(i);
boolean nameIsFQCN = !injectMetadata.isParameterNameSet(i);
params[i] = getOrCreateComponent(dependencies[i], name, nameIsFQCN);
}
ReflectionUtil.invokeAccessibly(o, injectMetadata.getMethod(), params);
}
}
/**
* 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).
*
* 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}.
*
* Summing it up, component retrieval happens in the following order:
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})
*
*
* @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 CacheConfigurationException if there is a problem with constructing or wiring the instance.
*/
protected synchronized T getOrCreateComponent(Class componentClass) {
return getOrCreateComponent(componentClass, componentClass.getName(), true);
}
protected T getOrCreateComponent(Class componentClass, String name) {
return getOrCreateComponent(componentClass, name, false);
}
@SuppressWarnings("unchecked")
protected synchronized T getOrCreateComponent(Class componentClass, String name, boolean nameIsFQCN) {
if (DEBUG_DEPENDENCIES) debugStack.push(name);
Object component;
Component oldWrapper = lookupComponent(componentClass.getName(), name, nameIsFQCN);
if (oldWrapper != null) {
component = unwrapComponent(oldWrapper);
} else {
// create this component and add it to the registry
AbstractComponentFactory factory = getFactory(componentClass);
getLog().tracef("Creating component %s with factory %s", name, factory.getClass());
component = factory instanceof NamedComponentFactory ?
((NamedComponentFactory) factory).construct(componentClass, name)
: factory.construct(componentClass);
if (component != null) {
registerComponent(component, name, nameIsFQCN);
} else {
getLog().tracef("Registering a null for component %s", name);
registerNullComponent(name);
}
}
if (DEBUG_DEPENDENCIES) debugStack.pop();
return (T) 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 synchronized AbstractComponentFactory getFactory(Class> componentClass) {
getLog().tracef("Looking up factory for class %s", componentClass);
String cfClass = getComponentMetadataRepo().findFactoryForComponent(componentClass);
if (cfClass == null) {
throwStackAwareConfigurationException("No registered default factory for component '" + componentClass + "' found!");
}
// 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) {
cf = createComponentFactoryInternal(componentClass, cfClass);
}
// ensure the component factory is in the STARTED state!
Component c = lookupComponent(cfClass, cfClass, true);
if (c.instance != cf)
throwStackAwareConfigurationException("Component factory " + cfClass + " incorrectly registered!");
return cf;
}
protected synchronized AbstractComponentFactory createComponentFactoryInternal(Class> componentClass, String cfClass) {
//first check as it might have been created in between by another thread
AbstractComponentFactory component = getComponent(cfClass);
if (component != null) return component;
//hasn't yet been created. Create and put in registry
getLog().tracef("Creating factory %s for component %s", cfClass, componentClass);
AbstractComponentFactory cf = instantiateFactory(cfClass);
if (cf == null)
throwStackAwareConfigurationException("Unable to locate component factory for component " + componentClass);
// we simply register this factory. Registration will take care of constructing any dependencies.
registerComponent(cf, cfClass);
return cf;
}
protected Component lookupComponent(String componentClassName, String componentName, boolean nameIsFQCN) {
return componentLookup.get(componentName);
}
/**
* No such thing as a meta factory yet. Factories are created using this method which attempts to use an empty
* public constructor.
*
* @param factoryName classname of factory to be created
* @return factory instance
*/
AbstractComponentFactory instantiateFactory(String factoryName) {
Class> factory = Util.loadClass(factoryName, getClass().getClassLoader());
if (AutoInstantiableFactory.class.isAssignableFrom(factory)) {
try {
return (AbstractComponentFactory) factory.newInstance();
} catch (Exception e) {
// unable to get a hold of an instance!!
throw new CacheConfigurationException("Unable to instantiate factory " + factory + " Debug stack: " + debugStack, e);
}
} else {
throw new CacheConfigurationException("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 name name of component to register as a null
*/
protected synchronized final void registerNullComponent(String name) {
registerComponent(NULL_COMPONENT, name, false);
}
/**
* 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
*/
@SuppressWarnings("unchecked")
public T getComponent(Class type) {
String className = type.getName();
return (T) getComponent(className, className, true);
}
@SuppressWarnings("unchecked")
public T getComponent(String componentClassName) {
return (T) getComponent(componentClassName, componentClassName, true);
}
@SuppressWarnings("unchecked")
public T getComponent(String componentClassName, String name) {
return (T) getComponent(componentClassName, name, false);
}
@SuppressWarnings("unchecked")
public T getComponent(Class componentClass, String name) {
return (T) getComponent(componentClass.getName(), name, false);
}
@SuppressWarnings("unchecked")
public T getComponent(String componentClassName, String name, boolean nameIsFQCN) {
Component wrapper = lookupComponent(componentClassName, name, nameIsFQCN);
if (wrapper == null) return null;
return (T) unwrapComponent(wrapper);
}
/**
* Get the component from a wrapper, properly handling null
components.
*/
private Object unwrapComponent(Component wrapper) {
return 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.
*/
protected ClassLoader registerDefaultClassLoader(ClassLoader loader) {
ClassLoader loaderToUse = loader == null ? getClass().getClassLoader() : loader;
registerComponent(loaderToUse, ClassLoader.class);
// make sure the class loader is non-volatile, so it survives restarts.
// componentLookup.get(ClassLoader.class.getName()).survivesRestarts = true;
return loaderToUse;
}
/**
* Rewires components. Used to rewire components in the CR if a cache has been stopped (moved to state TERMINATED),
* which would (almost) empty the registry of components. Rewiring will re-inject all dependencies so that the cache
* can be started again.
*
*/
public void rewire() {
// need to re-inject everything again.
for (Component c : new HashSet(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()) populateLifeCycleMethods(c);
}
private PrioritizedMethod[] processPrioritizedMethods(ComponentMetadata.PrioritizedMethodMetadata[] methodMetadata,
Class> componentClass, Component c) {
PrioritizedMethod[] retval;
int numStartMethods = methodMetadata.length;
if (numStartMethods == 0) {
retval = EMPTY_PRIO_METHODS;
} else {
retval = new PrioritizedMethod[numStartMethods];
for (int i = 0; i < numStartMethods; i++) {
retval[i] = new PrioritizedMethod();
retval[i].component = c;
retval[i].metadata = methodMetadata[i];
if (methodMetadata[i].getMethod() == null) {
Method method = ReflectionUtil.findMethod(componentClass, methodMetadata[i].getMethodName());
methodMetadata[i].setMethod(method);
}
}
if (retval.length > 1) Arrays.sort(retval);
}
return retval;
}
private void populateLifeCycleMethods(Component c) {
if (!c.methodsScanned) {
c.methodsScanned = true;
Class> componentClass = c.instance.getClass();
// START methods first
c.startMethods = processPrioritizedMethods(c.metadata.getStartMethods(), componentClass, c);
// And now the STOP methods
c.stopMethods = processPrioritizedMethods(c.metadata.getStopMethods(), componentClass, c);
}
}
/**
* Removes any components not annotated as @SurvivesRestarts.
*/
public synchronized void resetVolatileComponents() {
// destroy all components to clean up resources
getLog().tracef("Resetting volatile components");
for (Component c : new HashSet(componentLookup.values())) {
// the component is volatile!!
if (!c.metadata.isSurvivesRestarts()) {
getLog().tracef("Removing volatile component %s", c.name);
componentLookup.remove(c.name);
}
}
if (getLog().isTraceEnabled())
getLog().tracef("Reset volatile components. Registry now contains %s", componentLookup.keySet());
}
// ------------------------------ START: Publicly available lifecycle methods -----------------------------
// These methods perform a check for appropriate transition and then delegate to similarly named internal methods.
/**
* 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, it will be initialized first.
*/
@Override
public synchronized void start() {
if (!state.startAllowed()) {
if (state.needToDestroyFailedCache())
destroy(); // this will take us back to TERMINATED
if (state.needToInitializeBeforeStart()) {
rewire();
} else
return;
}
state = ComponentStatus.INITIALIZING;
try {
internalStart();
} 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.
*/
@Override
public synchronized void stop() {
if (!state.stopAllowed()) {
getLog().debugf("Ignoring call to stop() as current state is %s", state);
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().failedToCallStopAfterFailure(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.
*
* 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 {
if (state.stopAllowed())
stop();
} catch (CacheException e) {
getLog().stopBeforeDestroyFailed(e);
}
try {
resetVolatileComponents();
} 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 re-throws 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.getCause() != null && t.getCause() instanceof CacheConfigurationException)
throw (CacheConfigurationException) t.getCause();
else if (t.getCause() != null && t.getCause() instanceof InvocationTargetException && t.getCause().getCause() != null && t.getCause().getCause() instanceof CacheConfigurationException)
throw (CacheConfigurationException) t.getCause().getCause();
else 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);
}
private void internalStart() throws CacheException, IllegalArgumentException {
// start all internal components
// first cache all start, stop and destroy methods.
populateLifecycleMethods();
List startMethods = new ArrayList(componentLookup.size());
for (Component c : componentLookup.values()) {
Collections.addAll(startMethods, c.startMethods);
}
// sort the start methods by priority
Collections.sort(startMethods);
// fire all START methods according to priority
invokeStartMethods(startMethods);
addShutdownHook();
state = ComponentStatus.RUNNING;
}
private void invokeStartMethods(Collection startMethods) {
boolean traceEnabled = getLog().isTraceEnabled();
for (PrioritizedMethod em : startMethods) {
if (traceEnabled)
getLog().tracef("Invoking start method %s on component %s", em.metadata.getMethod(), em.component.getName());
em.invoke();
}
}
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 stopMethods = new ArrayList(componentLookup.size());
for (Component c : componentLookup.values()) {
// if one of the components threw an exception during startup
// the stop methods list may not have been initialized
if (c.stopMethods != null) {
Collections.addAll(stopMethods, c.stopMethods);
}
}
Collections.sort(stopMethods);
// fire all STOP methods according to priority
boolean traceEnabled = getLog().isTraceEnabled();
for (PrioritizedMethod em : stopMethods) {
if (traceEnabled)
getLog().tracef("Invoking stop method %s on component %s", em.metadata.getMethod(), em.component.getName());
try {
em.invoke();
} catch (Throwable t) {
getLog().componentFailedToStop(t);
}
}
destroy();
}
// ------------------------------ END: Actual internal lifecycle methods --------------------------------
/**
* Asserts whether invocations are allowed on the cache or not. Returns true if invocations are to be
* allowed, false 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
* org.infinispan.configuration.cache.StateTransferConfiguration#timeout()} millis, checking for a valid state.
*
* @param originLocal true if the call originates locally (i.e., from the {@link org.infinispan.cache.impl.CacheImpl} 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().cacheNotStarted();
}
return false;
}
/**
* Blocks until the current cache instance is in its {@link org.infinispan.lifecycle.ComponentStatus#RUNNING started}
* phase. Blocks for up to {@link org.infinispan.configuration.cache.StateTransferConfiguration#timeout()} 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 = 20;
TimeService timeService = getTimeService();
final long startupWaitTime = getConfiguration().clustering().stateTransfer().timeout();
final long giveUpTime = timeService.expectedEndTime(startupWaitTime, TimeUnit.MILLISECONDS);
while (!timeService.isTimeExpired(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 " + startupWaitTime + " millis.");
}
/**
* Returns an immutable set containing all the components that exists in the repository at this moment.
*
* @return a set of components
*/
public Set getRegisteredComponents() {
HashSet defensiveCopy = new HashSet(componentLookup.values());
return Collections.unmodifiableSet(defensiveCopy);
}
@Override
public AbstractComponentRegistry clone() throws CloneNotSupportedException {
AbstractComponentRegistry dolly = (AbstractComponentRegistry) super.clone();
dolly.state = ComponentStatus.INSTANTIATED;
return dolly;
}
public abstract TimeService getTimeService();
/**
* 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
*/
ComponentMetadata.InjectMetadata[] injectionMethods;
PrioritizedMethod[] startMethods;
PrioritizedMethod[] stopMethods;
ComponentMetadata metadata;
@Override
public String toString() {
return "Component{" +
"instance=" + instance +
", name=" + name +
'}';
}
/**
* Injects dependencies into this component.
*/
public void injectDependencies() {
if (injectionMethods != null && injectionMethods.length > 0) {
for (ComponentMetadata.InjectMetadata injectMetadata : injectionMethods) invokeInjectionMethod(instance, injectMetadata);
}
}
public Object getInstance() {
return instance;
}
public String getName() {
return name;
}
public ComponentMetadata getMetadata() {
return metadata;
}
public void buildInjectionMethodsList() throws ClassNotFoundException {
injectionMethods = metadata.getInjectMethods();
if (injectionMethods != null && injectionMethods.length > 0) {
Class> clazz = instance.getClass();
for (ComponentMetadata.InjectMetadata meta: injectionMethods) {
Class>[] parameterClasses = meta.getParameterClasses();
if (parameterClasses == null) {
parameterClasses = ReflectionUtil.toClassArray(meta.getParameters(), getClassLoader());
meta.setParameterClasses(parameterClasses);
}
Method m = meta.getMethod();
if (m == null) {
m = ReflectionUtil.findMethod(clazz, meta.getMethodName(), parameterClasses);
meta.setMethod(m);
}
}
}
}
}
/**
* Wrapper to encapsulate a method along with a priority
*/
static class PrioritizedMethod implements Comparable {
ComponentMetadata.PrioritizedMethodMetadata metadata;
Component component;
@Override
public int compareTo(PrioritizedMethod o) {
int thisVal = metadata.getPriority();
int anotherVal = o.metadata.getPriority();
return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PrioritizedMethod)) return false;
PrioritizedMethod that = (PrioritizedMethod) o;
if (component != null ? !component.equals(that.component) : that.component != null) return false;
if (metadata != null ? !metadata.equals(that.metadata) : that.metadata != null) return false;
return true;
}
@Override
public int hashCode() {
int result = metadata != null ? metadata.hashCode() : 0;
result = 31 * result + (component != null ? component.hashCode() : 0);
return result;
}
void invoke() {
ReflectionUtil.invokeAccessibly(component.instance, metadata.getMethod(), null);
}
@Override
public String toString() {
return "PrioritizedMethod{" +
"method=" + metadata.getMethod().getName() +
", priority=" + metadata.getPriority() +
'}';
}
}
protected void throwStackAwareConfigurationException(String message) {
if (debugStack == null) {
throw new CacheConfigurationException(message + ". To get more detail set the system property " + DEPENDENCIES_ENABLE_JVMOPTION + " to true");
} else {
throw new CacheConfigurationException(message + " Debug stack: " + debugStack);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy