/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2007 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution.context;
import java.lang.IllegalStateException;
import java.lang.ThreadLocal;
import java.lang.UnsupportedOperationException;
import javolution.xml.XMLSerializable;
/**
* <p> This class represents an execution context; they can be associated to
* particular threads or objects.</p>
*
* <p> Context-aware applications may extend the context base class or any
* predefined contexts in order to facilitate <a
* href="package-summary.html#package_description">
* separation of concerns</a>.</p>
*
* <p> The scope of a {@link Context} should be surrounded by a <code>try,
* finally</code> block statement to ensure correct behavior in case
* of exceptions being raised. For example:[code]
* LocalContext.enter(); // Current thread enter a local context.
* try
* ModuloInteger.setModulus(m); // No impact on other threads!
* z = x.times(y); // Multiplication modulo m.
* } finally {
* LocalContext.exit();
* }[/code]</p>
*
* <p> Context objects can be inherited by multiple threads (see
* {@link ConcurrentContext}}, but only one thread may
* {@link #enter(javolution.context.Context) enter} a particular context
* instance at any given time (and becomes its {@link #getOwner() owner}.
* When the owner thread exits its context, the
* context is automatically {@link ObjectFactory#recycle(java.lang.Object)
* recycled}. Consequently, whether or not context objects are reused
* between multiple threads depends upon the {@link AllocatorContext} policy
* with regards to recycling. Threads executing in a {@link PoolContext}
* for example, will reuse the same pool of context objects.</p>
*
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
* @version 5.0, April 15, 2007
*/
public abstract class Context implements XMLSerializable {
/**
* Holds the root context. It is the current context for all newly created
* threads.
*/
public static final Context ROOT = new Root();
/**
* Holds the current context (thread-local).
*/
private static final ThreadLocal CURRENT = new ThreadLocal() {
protected Object initialValue() {
return ROOT;
}
};
/**
* Holds the current owner of this context or <code>null</code> if global
* context.
*/
private Thread _owner;
/**
* Holds the outer context or <code>null</code> if none (root context).
*/
private Context _outer;
/**
* Holds the factory having produced this context if any (for recycling
* purpose upon exit).
*/
private ObjectFactory _factory;
/**
* Holds the inherited allocator context or <code>null</code>
*/
private AllocatorContext _allocator;
/**
* Default constructor.
*/
protected Context() {
}
/**
* Returns the current context for the current thread.
*
* @return the current context.
*/
public static Context getCurrentContext() {
return (Context) Context.CURRENT.get();
}
/**
* Returns the current owner of this context. The owner of a
* context is the thread which {@link #enter(Context) entered}
* the context and has not yet {@link #exit(Context) exited}.
* A context can only have one owner at any given time, although
* contexts can be shared by {@link ConcurrentContext concurrent} threads.
*
* @return the thread owner of this context or <code>null</code>.
*/
public final Thread getOwner() {
return _owner;
}
/**
* Returns the outer context of this context or <code>null</code>
* if {@link #ROOT} or a default context (not entered).
*
* @return the outer context or <code>null</code>.
*/
public final Context getOuter() {
return _outer;
}
/**
* Returns the string representation of this context (default
* <code>"Instance of " + this.getClass().getName()</code>).
*
* @return the string representation of this context.
*/
public String toString() {
return "Instance of " + this.getClass().getName();
}
/**
* The action to be performed after this context becomes the current
* context.
*/
protected abstract void enterAction();
/**
* The action to be performed before this context is no more the current
* context.
*/
protected abstract void exitAction();
/**
* Enters the specified context.
*
* @param context the context being entered.
* @throws IllegalStateException if this context is currently in use.
*/
public static final void enter(Context context) {
if (context._owner != null)
throw new IllegalStateException("Context is currently in use");
Context current = Context.getCurrentContext();
context._outer = current;
context._owner = Thread.currentThread();
context._allocator = context instanceof AllocatorContext ? (AllocatorContext) context : current._allocator;
Context.CURRENT.set(context);
context.enterAction();
}
/**
* Exits the specified context.
*
* @param context the context being exited.
* @throws IllegalStateException if the specified context is not the current context.
*/
public static final void exit(Context context) {
if (Context.getCurrentContext() != context)
throw new IllegalArgumentException("The specified context is not the current context");
Context.exit(context.getClass());
}
/**
* Enters a context of specified type, this context is
* {@link ObjectFactory factory} produced and automatically recycled
* upon {@link #exit exit}. If the specified contextType has no public
* no-arg constructor accessible, then the object factory for the class
* should be {@link ObjectFactory#setInstance explicitely set} (typically
* in a static initializer).
*
* @param contextType the type of context being entered.
* @see ObjectFactory#getInstance(Class)
*/
public static final void enter(Class <? extends Context> contextType) {
ObjectFactory factory = ObjectFactory.getInstance(contextType);
Context context = (Context) factory.object();
context._factory = factory;
Context.enter(context);
}
/**
* Exits the current context (the {@link #getOuter outer} context
* becomes the current context).
*
* @param contextType the type of context being entered.
* @throws IllegalStateException if this context is the {@link #ROOT}
* context or the current thread is not the context owner.
*/
public static void exit(Class <? extends Context> contextType) {
Context context = Context.getCurrentContext();
Context outer = context._outer;
if (outer == null)
throw new IllegalStateException("Cannot exit root context");
if (context._owner != Thread.currentThread())
throw new IllegalStateException("The current thread is not the context owner");
if (!contextType.isInstance(context))
throw new ClassCastException("Current context is an instance of " + context.getClass().getName());
try {
context.exitAction();
} finally {
Context.CURRENT.set(outer);
context._outer = null;
context._owner = null;
context._allocator = null;
if (context._factory != null) { // Factory produced.
context._factory.recycle(context);
context._factory = null;
}
}
}
/**
* Sets the current context, used by {@link ConcurrentContext}
* exclusively.
*
* @param context the concurrent context.
*/
protected static void setConcurrentContext(ConcurrentContext context) {
Context.CURRENT.set(context);
}
/**
* Returns the allocator context used while in this context (shortcut).
*
* @return the allocator context for this context.
*/
final AllocatorContext getAllocatorContext() {
return (_allocator == null) ? AllocatorContext.getDefault() : _allocator;
}
// Holds the root context definition.
private static final class Root extends Context {
protected void enterAction() {
throw new UnsupportedOperationException(
"Cannot enter the root context");
}
protected void exitAction() {
throw new UnsupportedOperationException(
"Cannot enter the root context");
}
};
}