porposed CDI-1.1 enhancements

This page contains proposals of the DeltaSpike community for CDI 1.1. Some parts might be used also for DeltaSpike for CDI 1.0.

Context Management (with community agreement)

public interface ManagedContext extends Context
{
    void start();

    void stop();
}

[TODO]

Context Management of Weld-Core (API)

A set of dependent scoped built in beans are available for context management.

/**
 * <p>
 * Lifecycle management for built in contexts. {@link ManagedContext} only
 * allows a context to be activated, deactivated and destroyed. It does not
 * allow the context to be associated with an underlying data store. These
 * operations are defined on {@link BoundContext}.
 * </p>
 *
 * <p>
 * CDI provides a number of managed contexts: {@link SessionContext},
 * {@link ConversationContext}, {@link RequestContext}. All these managed
 * contexts are scoped to the thread, and propagation of the backing store
 * between threads is the responsibility of the managed context user.
 * </p>
 *
 * @see BoundContext
 *
 */
public interface ManagedContext extends Context {

   /**
    * Activate the Context.
    */
   public void activate();

   /**
    * Deactivate the Context, destroying any instances if the context is invalid.
    */
   public void deactivate();

   /**
    * Mark the context as due for destruction when deactivate is called.
    */
   public void invalidate();

}

b

/**
 * <p>
 * Allows a thread-based context to be bound to some external instance storage
 * (such as an HttpSession).
 * </p>
 *
 * <p>
 * A context may be <em>detachable</em> in which case a call to
 * {@link ManagedContext#invalidate()} will detach the context from it's
 * associated storage. A detached context is still usable (instances may be
 * added or removed) however changes will not be written through to the
 * underlying data store.
 * </p>
 *
 * <p>
 * Normally, a detachable context will have it's underlying bean store attached
 * on a call to {@link ManagedContext#activate()} and detached on a call to
 * {@link ManagedContext#deactivate()} however a subtype of {@link BoundContext}
 * may change this behavior.
 * </p>
 *
 * <p>
 * If you call {@link #associate(Object)} you must ensure that you call
 * {@link #dissociate(Object)} in all cases, otherwise you risk memory leaks.
 * </p>
 *
 * @param <S> the type of the external instance storage
 * @see ManagedContext
 */
public interface BoundContext<S> extends Context {

   /**
    * Associate the context with the storage (for this thread). Once
    * {@link #associate(Object)} has been called, further calls to
    * {@link #associate(Object)} will be ignored, until the context has been
    * subsequently {@link #dissociate(Object)} from the storage.
    *
    * @param storage the external storage
    * @return true if the storage was attached, otherwise false
    */
   public boolean associate(S storage);

   /**
    * Dissociate the context from the storage (for this thread). The context
    * will only dissociate from the same storage it associated with.
    *
    * @param storage the external storage
    * @return true if the storage was dissociated
    */
   public boolean dissociate(S storage);

}


/**
 * <p>
 * The built in dependent context, associated with {@link Dependent}. It is
 * always active.
 * </p>
 *
 * <p>
 * There is one Dependent context which can be injected using:
 * </p>
 *
 * <pre>
 * &#064Inject DependentContext dependentContext;
 * </pre>
 *
 */
public interface DependentContext extends Context { }

b

/**
 * <p>
 * The built in request context is associated with {@link RequestScoped} and is
 * a managed context which can be activated, invalidated and deactivated.
 * </p>
 *
 * <p>
 * CDI comes with one implementation of the request context. The
 * {@link HttpRequestContext}, in which conversations are bound to the
 * {@link ServletRequest}, can be injected:
 * </p>
 *
 * <pre>
 * &#064Inject &#064Http RequestContext requestContext;
 * </pre>
 *
 * @see HttpRequestContext
 * @see RequestScoped
 *
 */
public interface RequestContext extends ManagedContext {}

b

/**
 * <p>
 * The built in session context is associated with {@link SessionScoped}. It can
 * be activated, invalidated and deactivated.
 * </p>
 *
 * <p>
 * CDI comes with one implementation of the session context. The
 * {@link HttpSessionContext}, in which conversations are bound to the
 * {@link HttpSession}, can be injected:
 * </p>
 *
 * <pre>
 * &#064Inject &#064Http SessionContext sessionContext;
 * </pre>
 *
 * @see HttpSessionContext
 * @sees {@link SessionScoped}
 *
 */
public interface SessionContext extends ManagedContext {}

b

/**
 * <p>
 * The built in application context, associated with {@link ApplicationScoped}.
 * It is always active (not managed) and is backed by an application scoped
 * singleton.
 * </p>
 *
 * <p>
 * CDI comes with one Application context which can be injected using:
 * </p>
 *
 * <pre>
 * &#064Inject ApplicationContext applicationContext;
 * </pre>
 *
 * @see SingletonContext
 * @see ApplicationScoped
 *
 */
public interface ApplicationContext extends Context {

   /**
    * Invalidate the context, causing all bean instances to be destroyed.
    */
   public void invalidate();

}

b

/**
 * <p>
 * The built in singleton context, associated with {@link Singleton}.
 * It is always active (not managed) and is backed by an application scoped
 * singleton.
 * </p>
 *
 * <p>
 * CDI comes with one Singleton context which can be injected using:
 * </p>
 *
 * <pre>
 * &#064Inject SingletonContext singletonContext;
 * </pre>
 *
 * @author Pete Muir
 * @see SingletonContext
 * @see ApplicationScoped
 *
 */
public interface SingletonContext extends Context {}

And these HTTP backed implementations:

/**
 * <p>
 * A request context which can be bound to the {@link ServletRequest}. The
 * context is automatically attached to the map on activation, and detached when
 * {@link #invalidate()} is called.
 * </p>
 *
 * <p>
 * This context is not thread safe, and provides no thread safety for the
 * underlying map.
 * </p>
 *
 */
public interface HttpRequestContext extends BoundContext<ServletRequest>, RequestContext {}

b

/**
 * <p>
 * A session context which can be bound to the {@link HttpServletRequest}. The
 * context is automatically attached to the map on activation, and detached when
 * {@link #invalidate()} is called.
 * </p>
 *
 * <p>
 * This context is not thread safe, and provides no thread safety for the
 * underlying map.
 * </p>
 *
 */
public interface HttpSessionContext extends BoundContext<HttpServletRequest>, SessionContext
{

   /**
    * <p>
    * Mark the Session Context for destruction; the Session Context will be
    * detached from the underling Http Session, and instances marked for
    * destruction when the Http Request is destroyed.
    * </p>
    *
    */
   public void invalidate();

   /**
    * <p>
    * Destroy the session and all conversations stored in the session.
    * </p>
    *
    * <p>
    * If the context is not currently associated with a
    * {@link HttpServletRequest}, then the context will be associated with the
    * specified {@link HttpSession} (for this thread), activated, destroyed, and
    * then deactivated.
    * </p>
    *
    * <p>
    * If the context is already associated with a {@link HttpServletRequest}
    * then this call will detach the context from the underlying Http Session,
    * and mark the context for destruction when the request is destroyed.
    * </p>
    *
    * @param session the {@link HttpSession} in which to store the bean
    *           instances
    * @return true if the context was destroyed immediately
    */
   public boolean destroy(HttpSession session);

}

Map-bound contexts

We may also wish to add map-bound contexts, in which the contexts are backed by a map. Note that the example javadoc above needs expanding if we do.

Add these dependent scoped beans:

/**
 * <p>
 * Allows a thread-based context to be bound to some external instance storage
 * (such as an HttpSession).
 * </p>
 *
 * <p>
 * A context may be <em>detachable</em> in which case a call to
 * {@link ManagedContext#invalidate()} will detach the context from it's
 * associated storage. A detached context is still usable (instances may be
 * added or removed) however changes will not be written through to the
 * underlying data store.
 * </p>
 *
 * <p>
 * Normally, a detachable context will have it's underlying bean store attached
 * on a call to {@link ManagedContext#activate()} and detached on a call to
 * {@link ManagedContext#deactivate()} however a subtype of {@link BoundContext}
 * may change this behavior.
 * </p>
 *
 * <p>
 * If you call {@link #associate(Object)} you must ensure that you call
 * {@link #dissociate(Object)} in all cases, otherwise you risk memory leaks.
 * </p>
 *
 * @param <S> the type of the external instance storage
 * @see ManagedContext
 */
public interface BoundContext<S> extends Context
{

   /**
    * Associate the context with the storage (for this thread). Once
    * {@link #associate(Object)} has been called, further calls to
    * {@link #associate(Object)} will be ignored, until the context has been
    * subsequently {@link #dissociate(Object)} from the storage.
    *
    * @param storage the external storage
    * @return true if the storage was attached, otherwise false
    */
   public boolean associate(S storage);

   /**
    * Dissociate the context from the storage (for this thread). The context
    * will only dissociate from the same storage it associated with.
    *
    * @param storage the external storage
    * @return true if the storage was dissociated
    */
   public boolean dissociate(S storage);

}

b

/**
 * <p>
 * A request context which can be bound to any Map. The context is automatically
 * attached to the map on activation, and detached when {@link #invalidate()} is
 * called.
 * </p>
 *
 * <p>
 * This context is not thread safe, and provides no thread safety for the
 * underlying map. A thread-safe map can be used to back the context.
 * </p>
 */
public interface BoundRequestContext extends RequestContext, BoundContext<Map<String, Object>> {}

b

/**
 * <p>
 * A session context which can be bound to any Map. The context is automatically
 * attached to the map on activation, and detached when {@link #invalidate()} is
 * called.
 * </p>
 *
 * <p>
 * This context is not thread safe, and provides no thread safety for the
 * underlying map. A thread-safe map can be used to back the context.
 * </p>
 *
 */
public interface BoundSessionContext extends SessionContext, BoundContext<Map<String, Object>> {}

As always, conversations are harder to model. In this case, we need to provide both a map for the "session" (in which a conversation may stored long term) and a map for the current "request".

/**
 * <p>
 * A conversation is used to span multiple requests, however is shorter than a
 * session. The {@link BoundConversationContext} uses one Map to represent a
 * request, and a second to represent the session, which are encapsulated in a
 * {@link BoundRequest}.
 * </p>
 *
 */
public interface BoundRequest {

   /**
    * Get the current request map.
    *
    * @return
    */
   public Map<String, Object> getRequestMap();

   /**
    * <p>
    * Get the current session map.
    * </p>
    *
    * <p>
    * A {@link BoundRequest} may be backed by a data store that only creates
    * sessions on demand. It is recommended that if the session is not created
    * on demand, or that the session has already been created (but is not
    * required by this access) that the session is returned as it allows the
    * conversation context to work more efficiently.
    * </p>
    *
    * @param create if true, then a session must be created
    * @return the session map; null may be returned if create is false
    */
   public Map<String, Object> getSessionMap(boolean create);

}

Context / Conversation Management of Weld-Core (API)

/**
 * <p>
 * The built in conversation context is associated with
 * {@link ConversationScoped}. It can be activated, invalidated and deactivated.
 * and provides various options for configuring conversation defaults.
 * </p>
 *
 * <p>
 * CDI comes with one implementation of the conversation context. The
 * {@link HttpConversationContext}, in which conversations are bound to the
 * {@link HttpSession}, can be injected:
 * </p>
 *
 * <pre>
 * &#064Inject &#064Http ConversationContext conversationContext;
 * </pre>
 *
 * @see BoundConversationContext
 * @see HttpConversationContext
 * @see ConversationScoped
 *
 */
public interface ConversationContext extends ManagedContext
{

   /**
    * Cause any expired conversations to be ended, and therefore marked for
    * destruction when deactivate is called.
    *
    * @throws IllegalStateException if the context is unable to access the
    *            underlying data store
    */
   public void invalidate();

   /**
    * Activate the conversation context, using the id provided to attempt to
    * restore a long-running conversation
    *
    * @param cid if the cid is null, a transient conversation will be created,
    *           otherwise the conversation will be restored
    * @throws IllegalStateException if the context is unable to access the
    *            underlying data store
    */
   public void activate(String cid);

   /**
    * Activate the conversation context, starting a new transient conversation
    *
    * @throws IllegalStateException if the context is unable to access the
    *            underlying data store
    */
   public void activate();

   /**
    * Set the name of the parameter used to propagate the conversation id
    *
    * @param cid the name of the conversation id parameter
    */
   public void setParameterName(String cid);

   /**
    * Get the name of the parameter used to propagate the conversation id
    *
    * @return the name of the conversation id parameter
    */
   public String getParameterName();

   /**
    * Set the concurrent access timeout
    *
    * @param timeout the timeout (in ms) for the concurrent access lock
    */
   public void setConcurrentAccessTimeout(long timeout);

   /**
    * Get the current concurrent access timeout
    *
    * @return the timeout (in ms) for the concurrent access lock
    */
   public long getConcurrentAccessTimeout();

   /**
    * Set the default inactivity timeout. This may be overridden on a per
    * conversation basis using {@link Conversation#setTimeout(long)}
    *
    * @param timeout the default inactivity timeout (in ms)
    */
   public void setDefaultTimeout(long timeout);

   /**
    * Get the default inactivity timeout. This may have been overridden on a per
    * conversation basis.
    *
    * @return the default inactivity timeout (in ms)
    */
   public long getDefaultTimeout();

   /**
    * Get conversations currently known to the context. This will include any
    * non transient conversations, as well as any conversations which were
    * previously long running and have been made transient during this request.
    *
    * @return a collection containing the conversations
    *
    * @throws IllegalStateException if the context is unable to access the
    *            underlying data store
    */
   public Collection<ManagedConversation> getConversations();

   /**
    * Get the conversation with the given id.
    *
    * @param id the id of the conversation to get
    * @return the conversation, or null if no conversation is known
    *
    * @throws IllegalStateException if the context is unable to access the
    *            underlying data store
    */
   public ManagedConversation getConversation(String id);

   /**
    * Generate a new, unique, conversation id
    *
    * @return a new, unique conversation id
    *
    * @throws IllegalStateException if the context is unable to access the
    *            underlying data store
    */
   public String generateConversationId();

   /**
    * Get a handle the current conversation (transient or otherwise).
    *
    * @return the current conversation
    * @throws IllegalStateException if the context is unable to access the
    *            underlying data store
    */
   public ManagedConversation getCurrentConversation();

}

b

package org.jboss.weld.context;

import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.Conversation;

/**
 * <p>
 * Provides management operations for conversations, including locking, and
 * expiration management.
 * </p>
 *
 * @see ConversationContext
 *
 */
public interface ManagedConversation extends Conversation {

   /**
    * Attempts to unlock the conversation
    *
    * @throws ContextNotActiveException if the conversation context is not
    *            active
    * @return true if the unlock was successful, false otherwise
    */
   public boolean unlock();

   /**
    * Attempts to lock the conversation for exclusive usage
    *
    * @param timeout The time in milliseconds to wait on the lock
    * @return True if lock was successful, false otherwise
    * @throws InterruptedException if the lock operation was unsuccessful * @throws
    * @throws ContextNotActiveException if the conversation context is not
    *            active
    */
   public boolean lock(long timeout);

   /**
    * Gets the last time the conversation was used (for data access)
    *
    * @return time (in ms) since the conversation was last used
    * @throws ContextNotActiveException if the conversation context is not
    *            active
    */
   public long getLastUsed();

   /**
    * Touches the managed conversation, updating the "last used" timestamp
    *
    * @throws ContextNotActiveException if the conversation context is not
    *            active
    */
   public void touch();

}

b

/**
 * <p>
 * A conversation context which can be bound to a pair of Maps encapsulated by
 * {@link BoundRequest}. The context is automatically attached to the bound
 * request on activation, and detached when {@link #invalidate()} is called.
 * </p>
 *
 * <p>
 * The {@link BoundConversationContext} is detachable, and transient
 * conversations are only attached at the end of a request.
 * </p>
 *
 * <p>
 * This context is not thread safe, and provides no thread safety for the
 * underlying map. A thread-safe map can be used to back the context - in this
 * case the map can be used as an underlying store in multiple threads safely.
 * </p>
 *
 */
public interface BoundConversationContext extends ConversationContext, BoundContext<BoundRequest>
{

   /**
    * Destroy all conversations in the session.
    *
    * @param session the session for which to destroy all conversations
    * @return
    */
   public boolean destroy(Map<String, Object> session);

}

b

/**
 * An Http Session backed conversation context. A transient conversation will be
 * detached from the underlying session. If the conversation is promoted to long
 * running, context will be attached to the underlying Http Session at the end
 * of the request.
 *
 */
public interface HttpConversationContext extends BoundContext<HttpServletRequest>, ConversationContext
{

   /**
    * <p>
    * If the context is not currently associated with a
    * {@link HttpServletRequest}, then the context will be associated with the
    * specified {@link HttpSession} (for this thread), activated, destroyed, and
    * then deactivated. Any conversations associated with the context will also
    * be destroyed.
    * </p>
    *
    * <p>
    * If the context is already associated with a {@link HttpServletRequest}
    * then this call will detach the context from the underlying Http Session,
    * and mark the context for destruction when the request is destroyed.
    * </p>
    *
    * <p>
    * This will cause any transient conversations, and any long running
    * conversations associated with the session, to be destroyed.
    * </p>
    *
    * @param session the {@link HttpSession} in which to store the bean
    *           instances
    * @return true if the context was destroyed immediately
    */
   public boolean destroy(HttpSession session);

}