package org.etk.kernel.container.component;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.etk.kernel.container.KernelContainer;
/**
* <p>
* The request life cycle object allows a client to demarcate the life cycle of
* the various components associated with containers. It allows container
* stacking and guarantees that the life cycle of the components will never be
* called twice in the same stack.
* </p>
*/
public class RequestLifeCycle {
/** The current stack. */
private static ThreadLocal<RequestLifeCycleStack> current = new ThreadLocal<RequestLifeCycleStack>();
/** The components of this life cycle. */
private List<ComponentRequestLifecycle> components;
/** The container of this life cycle. */
private final KernelContainer container;
public RequestLifeCycle(KernelContainer container, List<ComponentRequestLifecycle> components) {
this.container = container;
this.components = components;
}
void doBegin() {
for (ComponentRequestLifecycle component : components) {
component.startRequest(container);
}
}
IdentityHashMap<Object, Throwable> doEnd() {
IdentityHashMap<Object, Throwable> result = new IdentityHashMap<Object, Throwable>();
//
for (ComponentRequestLifecycle componentRLF : components) {
Throwable t = null;
try {
componentRLF.endRequest(container);
} catch (Throwable throwable) {
t = throwable;
} finally {
result.put(componentRLF, t);
}
}
//
return result;
}
/**
* Starts the life cycle of the provided container and add it to the life
* cycle stack. Only the components of the container that have not been
* previously enrolled in a life cycle are begun.
*
* @param container the container to use
* @param local will only trigger life cycle for the container and not its
* ancestors
*/
public static void begin(KernelContainer container, boolean local) {
if (container == null) {
throw new NullPointerException();
}
RequestLifeCycleStack lf = current.get();
if (lf == null) {
lf = new RequestLifeCycleStack();
current.set(lf);
}
lf.begin(container, local);
}
/**
* Starts the life cycle of the provided life cycle and add it to the life
* cycle stack. If the life cycle has already been triggered before then no
* operation will be really performed. When the life cycle is called, the
* argument container will be null.
*
* @param lifeCycle the life cycle
*/
public static void begin(ComponentRequestLifecycle lifeCycle) {
if (lifeCycle == null) {
throw new NullPointerException();
}
RequestLifeCycleStack lf = current.get();
if (lf == null) {
lf = new RequestLifeCycleStack();
current.set(lf);
}
lf.begin(lifeCycle);
}
/**
* Starts the life cycle of the provided container and add it to the life
* cycle stack. Only the components of the container that have not been
* previously enrolled in a life cycle are begun.
*
* @param container the container to use
*/
public static void begin(KernelContainer container) {
begin(container, false);
}
/**
* <p>
* Ends the life cycle of the most recent container started. Only the
* components of the container that have not been previously enrolled in a
* life cycle are ended.
* </p>
* <p>
* The result map returned has for keys the components whose the life cycle
* ended during this method call and the associated value are the potential
* throwable that were thrown by those components. It is usefull when writing
* unit test to be aware of the throwable of the various components involved
* in a request life cycle.
* </p>
*
* @throws IllegalStateException if no container life cycle is associated with
* this thread
* @return the result map
*/
public static Map<Object, Throwable> end() throws IllegalStateException {
RequestLifeCycleStack lf = current.get();
if (lf == null) {
throw new IllegalStateException();
}
Map<Object, Throwable> result = lf.end();
if (lf.isEmpty()) {
current.set(null);
}
return result;
}
}