[weld-commits] Weld SVN: r5964 - core/trunk/impl/src/main/java/org/jboss/weld/context and 5 other directories.

weld-commits at lists.jboss.org weld-commits at lists.jboss.org
Sat Feb 27 08:31:36 EST 2010


Author: nickarls
Date: 2010-02-27 08:31:35 -0500 (Sat, 27 Feb 2010)
New Revision: 5964

Added:
   core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationManager.java
   core/trunk/impl/src/main/java/org/jboss/weld/conversation/ManagedConversation.java
Removed:
   api/trunk/weld/src/main/java/org/jboss/weld/conversation/ConversationManager.java
   core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationEntry.java
Modified:
   api/trunk/weld/src/main/java/org/jboss/weld/conversation/ConversationIdGenerator.java
   core/trunk/impl/src/main/java/org/jboss/weld/context/ContextLifecycle.java
   core/trunk/impl/src/main/java/org/jboss/weld/context/ConversationContext.java
   core/trunk/impl/src/main/java/org/jboss/weld/context/beanstore/AbstractAttributeBackedBeanStore.java
   core/trunk/impl/src/main/java/org/jboss/weld/context/beanstore/HashMapBeanStore.java
   core/trunk/impl/src/main/java/org/jboss/weld/conversation/AbstractConversationManager.java
   core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationImpl.java
   core/trunk/impl/src/main/java/org/jboss/weld/conversation/ServletConversationManager.java
   core/trunk/impl/src/main/java/org/jboss/weld/jsf/ConversationAwareViewHandler.java
   core/trunk/impl/src/main/java/org/jboss/weld/jsf/WeldPhaseListener.java
   core/trunk/impl/src/main/java/org/jboss/weld/servlet/ConversationBeanStore.java
   core/trunk/impl/src/main/java/org/jboss/weld/servlet/ConversationPropagationFilter.java
   core/trunk/impl/src/main/java/org/jboss/weld/servlet/HttpPassThruSessionBeanStore.java
   core/trunk/impl/src/main/java/org/jboss/weld/servlet/HttpSessionBeanStore.java
   core/trunk/impl/src/main/java/org/jboss/weld/servlet/ServletLifecycle.java
   core/trunk/tests/src/test/java/org/jboss/weld/tests/serialization/SerializationTest.java
   core/trunk/tests/src/test/java/org/jboss/weld/tests/serialization/TestConversationManager.java
Log:
Conversation Manager API
Integration of lifecycle and JSF to CM API
Session invalidation survival (load + save of BeanStores)
Sync + asynch conversation manager

Modified: api/trunk/weld/src/main/java/org/jboss/weld/conversation/ConversationIdGenerator.java
===================================================================
--- api/trunk/weld/src/main/java/org/jboss/weld/conversation/ConversationIdGenerator.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ api/trunk/weld/src/main/java/org/jboss/weld/conversation/ConversationIdGenerator.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -20,7 +20,7 @@
  * Generates conversation ID:s for the conversation manager
  * 
  * @author Nicklas Karlsson
- * @see org.jboss.weld.conversation.ConversationManager#beginOrRestoreConversation(String)
+ * @see org.jboss.weld.conversation.ConversationManager#setupConversation(String)
  */
 public interface ConversationIdGenerator
 {

Deleted: api/trunk/weld/src/main/java/org/jboss/weld/conversation/ConversationManager.java
===================================================================
--- api/trunk/weld/src/main/java/org/jboss/weld/conversation/ConversationManager.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ api/trunk/weld/src/main/java/org/jboss/weld/conversation/ConversationManager.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -1,56 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2010, Red Hat, Inc., and individual contributors
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jboss.weld.conversation;
-
-import java.util.Set;
-
-import javax.enterprise.context.Conversation;
-
-/**
- * A conversation manager responsible for starting, resuming and ending conversations
- * 
- * @author Nicklas Karlsson
- * @see org.jboss.weld.conversation.ConversationManager
- */
-public interface ConversationManager
-{
-   /**
-    * Begins or restores a conversation
-    * 
-    * @param cid The incoming conversation ID. Can be null in cases of transient conversations
-    */
-   public abstract void beginOrRestoreConversation(String cid);
-   
-   /**
-    * Cleans up the current conversation, destroying transient conversation and handling 
-    * long-running conversations
-    */
-   public abstract void cleanupConversation();
-   
-   /**
-    * Destroys all long-running conversations
-    */
-   public abstract void destroyAllConversations();
-   
-   /**
-    * Gets the currently managed long-running conversations
-    * 
-    * @return the conversations
-    */
-   public abstract Set<Conversation> getLongRunningConversations();
-   
-}

Modified: core/trunk/impl/src/main/java/org/jboss/weld/context/ContextLifecycle.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/context/ContextLifecycle.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/context/ContextLifecycle.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -35,6 +35,7 @@
 import org.jboss.weld.bootstrap.api.Service;
 import org.jboss.weld.context.api.BeanStore;
 import org.jboss.weld.context.api.helpers.ConcurrentHashMapBeanStore;
+import org.jboss.weld.context.beanstore.HashMapBeanStore;
 import org.slf4j.cal10n.LocLogger;
 
 /**
@@ -88,6 +89,7 @@
       requestContext.setBeanStore(requestBeanStore);
       requestContext.setActive(true);
       dependentContext.setActive(true);
+      activateConversationContext();
    }
 
    public void endRequest(String id, BeanStore requestBeanStore)
@@ -98,6 +100,7 @@
       requestContext.destroy();
       requestContext.setActive(false);
       requestContext.setBeanStore(null);
+      deactivateConversationContext();
    }
    
    public boolean isRequestActive()
@@ -181,4 +184,16 @@
       return dependentContext;
    }
 
+   public void activateConversationContext()
+   {
+      conversationContext.setActive(true);
+      conversationContext.setBeanStore(new HashMapBeanStore());
+   }
+
+   public void deactivateConversationContext()
+   {
+      conversationContext.setActive(false);
+      conversationContext.setBeanStore(null);
+   }
+
 }

Modified: core/trunk/impl/src/main/java/org/jboss/weld/context/ConversationContext.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/context/ConversationContext.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/context/ConversationContext.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -24,6 +24,8 @@
 
 import javax.enterprise.context.ConversationScoped;
 
+import org.jboss.weld.context.api.BeanStore;
+
 /**
  * The conversation context
  * 
@@ -31,15 +33,29 @@
  */
 public class ConversationContext extends AbstractThreadLocalMapContext
 {
-
-   /**
-    * Constructor
-    */
    public ConversationContext()
    {
       super(ConversationScoped.class);
    }
 
+   private void mergeBeanStores(BeanStore from, BeanStore to)
+   {
+      for (String id : from.getContextualIds())
+      {
+         to.put(id, from.get(id));
+      }
+   }
+
+   public void loadTransientBeanStore(BeanStore sourceBeanStore)
+   {
+      mergeBeanStores(sourceBeanStore, getBeanStore());
+   }
+
+   public void saveTransientBeanStore(BeanStore targetBeanStore)
+   {
+      mergeBeanStores(getBeanStore(), targetBeanStore);
+   }   
+
    @Override
    public String toString()
    {

Modified: core/trunk/impl/src/main/java/org/jboss/weld/context/beanstore/AbstractAttributeBackedBeanStore.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/context/beanstore/AbstractAttributeBackedBeanStore.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/context/beanstore/AbstractAttributeBackedBeanStore.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -29,7 +29,6 @@
 
 import org.jboss.weld.context.api.BeanStore;
 import org.jboss.weld.context.api.ContextualInstance;
-import org.jboss.weld.util.Names;
 import org.jboss.weld.util.collections.EnumerationList;
 import org.slf4j.cal10n.LocLogger;
 

Modified: core/trunk/impl/src/main/java/org/jboss/weld/context/beanstore/HashMapBeanStore.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/context/beanstore/HashMapBeanStore.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/context/beanstore/HashMapBeanStore.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -21,8 +21,6 @@
 import java.util.HashMap;
 import java.util.Map;
 
-import javax.enterprise.context.spi.Contextual;
-
 import org.jboss.weld.context.api.ContextualInstance;
 import org.jboss.weld.context.api.helpers.AbstractMapBackedBeanStore;
 

Modified: core/trunk/impl/src/main/java/org/jboss/weld/conversation/AbstractConversationManager.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/conversation/AbstractConversationManager.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/conversation/AbstractConversationManager.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -1,10 +1,4 @@
 /*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- *
- * Use is subject to license terms.
- *
  * JBoss, Home of Professional Open Source
  * Copyright 2008, Red Hat, Inc., and individual contributors
  * by the @authors tag. See the copyright.txt in the distribution for a
@@ -15,7 +9,7 @@
  * You may obtain a copy of the License at
  * http://www.apache.org/licenses/LICENSE-2.0
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an "AS IS" BASIS,  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
@@ -30,14 +24,15 @@
 import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_TERMINATION_SCHEDULED;
 import static org.jboss.weld.logging.messages.ConversationMessage.DESTROY_ALL_LRC;
 import static org.jboss.weld.logging.messages.ConversationMessage.DESTROY_LRC;
-import static org.jboss.weld.logging.messages.ConversationMessage.DESTROY_TRANSIENT_COVERSATION;
 import static org.jboss.weld.logging.messages.ConversationMessage.LRC_COUNT;
 import static org.jboss.weld.logging.messages.ConversationMessage.NO_CONVERSATION_TO_RESTORE;
 import static org.jboss.weld.logging.messages.ConversationMessage.UNABLE_TO_RESTORE_CONVERSATION;
 
 import java.io.Serializable;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -45,7 +40,6 @@
 import java.util.concurrent.TimeUnit;
 
 import javax.enterprise.context.Conversation;
-import javax.enterprise.inject.Instance;
 import javax.inject.Inject;
 
 import org.jboss.weld.Container;
@@ -65,53 +59,103 @@
  */
 public abstract class AbstractConversationManager implements ConversationManager, Serializable
 {
-   
-   private static final long serialVersionUID = 8375026855239413267L;
+   private static final long serialVersionUID = 1L;
 
    private static final LocLogger log = loggerFactory().getLogger(CONVERSATION);
 
-   // The current conversation
+   private boolean asynchronous = false;
+
    @Inject
-   private Instance<ConversationImpl> currentConversation;
+   protected ConversationImpl conversation;
 
-   // The conversation timeout in milliseconds waiting for access to a blocked
-   // conversation
+   @Inject
+   private ConversationIdGenerator conversationIdGenerator;
+
    @ConversationConcurrentAccessTimeout
    private long concurrentAccessTimeout;
 
-   // A map of current active long-running conversation entries
-   private Map<String, ConversationEntry> longRunningConversations;
+   private Map<String, ManagedConversation> managedConversations;
 
-   /**
-    * Creates a new conversation manager
-    */
    public AbstractConversationManager()
    {
-      longRunningConversations = new ConcurrentHashMap<String, ConversationEntry>();
+      managedConversations = new ConcurrentHashMap<String, ManagedConversation>();
    }
 
-   public void beginOrRestoreConversation(String cid)
+   public ConversationManager setAsynchronous(boolean asynchronous)
    {
+      if (this.asynchronous == asynchronous)
+      {
+         return this;
+      }
+      if (!managedConversations.isEmpty())
+      {
+         log.warn("Switching modes with non-transient conversations present resets the timeouts");
+      }
+      if (asynchronous)
+      {
+         switchToAsynchronous();
+      }
+      else
+      {
+         switchToNonAsynchronous();
+      }
+      return this;
+   }
+
+   private void switchToNonAsynchronous()
+   {
+      for (ManagedConversation managedConversation : managedConversations.values())
+      {
+         managedConversation.cancelTermination();
+         managedConversation.setTerminationHandle(null);
+         managedConversation.touch();
+      }
+   }
+
+   private void switchToAsynchronous()
+   {
+      for (ManagedConversation managedConversation : managedConversations.values())
+      {
+         managedConversation.setTerminationHandle(scheduleForTermination(managedConversation.getConversation()));
+      }
+   }
+
+   private void destroyExpiredConversations()
+   {
+      for (Iterator<ManagedConversation> i = managedConversations.values().iterator(); i.hasNext();)
+      {
+         ManagedConversation managedConversation = i.next();
+         if (managedConversation.isExpired())
+         {
+            managedConversation.destroy();
+            i.remove();
+         }
+      }
+   }
+
+   public ConversationManager setupConversation(String cid)
+   {
+      if (!asynchronous)
+      {
+         destroyExpiredConversations();
+      }
       if (cid == null)
       {
-         // No incoming conversation ID, nothing to do here, continue with
-         // a transient conversation
          log.trace(NO_CONVERSATION_TO_RESTORE);
-         return;
+         return this;
       }
-      if (!longRunningConversations.containsKey(cid))
+      ManagedConversation resumedManagedConversation = managedConversations.get(cid);
+      if (resumedManagedConversation == null)
       {
-         // We got an incoming conversation ID but it was not in the map of
-         // known ones, nothing to do. Log and return to continue with a
-         // transient conversation
          throw new NonexistentConversationException(UNABLE_TO_RESTORE_CONVERSATION, cid, "id not known");
       }
-      ConversationEntry resumedConversationEntry = longRunningConversations.get(cid);
-      // Try to get a lock to the requested conversation, log and return to
-      // continue with a transient conversation if we fail
+      if (asynchronous && !resumedManagedConversation.cancelTermination())
+      {
+         throw new BusyConversationException(CONVERSATION_LOCK_UNAVAILABLE);
+      }
       try
       {
-         if (!resumedConversationEntry.lock(concurrentAccessTimeout))
+         if (!resumedManagedConversation.lock(concurrentAccessTimeout))
          {
             throw new BusyConversationException(CONVERSATION_LOCK_UNAVAILABLE);
          }
@@ -121,79 +165,82 @@
          Thread.currentThread().interrupt();
          throw new BusyConversationException(CONVERSATION_LOCK_UNAVAILABLE);
       }
-      // If we can't cancel the termination, release the lock, return and
-      // continue
-      // with a transient conversation
-      if (!resumedConversationEntry.cancelTermination())
+      String oldConversation = conversation.toString();
+      conversation.switchTo(resumedManagedConversation.getConversation());
+      getConversationContext().loadTransientBeanStore(getBeanStore(cid));
+      log.trace(CONVERSATION_SWITCHED, oldConversation, conversation);
+      return this;
+   }
+
+   private ConversationContext getConversationContext()
+   {
+      return Container.instance().services().get(ContextLifecycle.class).getConversationContext();
+   }
+
+   public ConversationManager teardownConversation()
+   {
+      log.trace(CLEANING_UP_CONVERSATION, conversation);
+      if (conversation.isTransient())
       {
-         resumedConversationEntry.unlock();
-         throw new BusyConversationException(CONVERSATION_LOCK_UNAVAILABLE);
+         endTransientConversation();
+         if (conversation.getResumedId() != null)
+         {
+            handleResumedConversation();
+         }
       }
       else
       {
-         // If all goes well, set the identity of the current conversation to
-         // match the fetched long-running one
-         String oldConversation = currentConversation.toString();
-         currentConversation.get().switchTo(resumedConversationEntry.getConversation());
-         log.trace(CONVERSATION_SWITCHED, oldConversation, currentConversation);
+         endNonTransientConversation();
       }
+      return this;
    }
 
-   // TODO: check that stuff gets terminated when you flip between several
-   // long-running conversations
-   public void cleanupConversation()
+   private void handleResumedConversation()
    {
-      log.trace(CLEANING_UP_CONVERSATION, currentConversation);
-      String cid = currentConversation.get().getUnderlyingId();
-      if (!currentConversation.get().isTransient())
+      ManagedConversation resumedConversation = managedConversations.remove(conversation.getResumedId());
+      if (resumedConversation == null)
       {
-         Future<?> terminationHandle = scheduleForTermination(cid, currentConversation.get().getTimeout());
-         // When the conversation ends, a long-running conversation needs to
-         // start its self-destruct. We can have the case where the conversation
-         // is a previously known conversation (that had it's termination
-         // canceled in the
-         // beginConversation) or the case where we have a completely new
-         // long-running conversation.
-         ConversationEntry longRunningConversation = longRunningConversations.get(cid);
-         if (longRunningConversation != null)
+         return;
+      }
+      resumedConversation.unlock();
+      resumedConversation.destroy();
+   }
+
+   private void endNonTransientConversation()
+   {
+      getConversationContext().saveTransientBeanStore(getBeanStore(conversation.getId()));
+      ManagedConversation oldManagedConversation = managedConversations.get(conversation.getId());
+      if (oldManagedConversation != null)
+      {
+         oldManagedConversation.unlock();
+         if (asynchronous)
          {
-            longRunningConversation.unlock();
-            longRunningConversation.reScheduleTermination(terminationHandle);
+            oldManagedConversation.setTerminationHandle(scheduleForTermination(conversation));
          }
          else
          {
-            ConversationEntry conversationEntry = ConversationEntry.of(getBeanStore(cid), currentConversation.get(), terminationHandle);
-            longRunningConversations.put(cid, conversationEntry);
+            oldManagedConversation.touch();
          }
-         log.trace(CONVERSATION_TERMINATION_SCHEDULED, currentConversation);
-         log.trace(LRC_COUNT, longRunningConversations.size());
       }
       else
       {
-         // If the conversation is not long-running it can be a transient
-         // conversation that has been so from the start or it can be a
-         // long-running conversation that has been demoted during the request
-         log.trace(DESTROY_TRANSIENT_COVERSATION, currentConversation);
-         ConversationEntry longRunningConversation = longRunningConversations.remove(cid);
-         if (longRunningConversation != null)
+         ManagedConversation newManagedConversation = ManagedConversation.of(conversation.unProxy(this), getBeanStore(conversation.getId()));
+         if (asynchronous)
          {
-            longRunningConversation.cancelTermination();
-            longRunningConversation.unlock();
+            log.trace(CONVERSATION_TERMINATION_SCHEDULED, conversation);
+            newManagedConversation.setTerminationHandle(scheduleForTermination(conversation));
          }
-         ConversationContext conversationContext = Container.instance().services().get(ContextLifecycle.class).getConversationContext();
-         conversationContext.destroy();
+         managedConversations.put(conversation.getId(), newManagedConversation);
       }
-      // If the conversation has been switched from one long
-      // running-conversation to another with
-      // Conversation.begin(String), we need to unlock the original conversation
-      // and re-schedule
-      // it for termination
-      String originalCid = currentConversation.get().getOriginalId();
-      ConversationEntry longRunningConversation = originalCid == null ? null : longRunningConversations.get(originalCid);
-      if (longRunningConversation != null)
+      log.trace(LRC_COUNT, managedConversations.size());
+   }
+
+   private void endTransientConversation()
+   {
+      getConversationContext().destroy();
+      if (conversation.getResumedId() != null)
       {
-         longRunningConversation.unlock();
-         longRunningConversation.reScheduleTermination(scheduleForTermination(originalCid, currentConversation.get().getTimeout()));
+         getBeanStore(conversation.getResumedId()).clear();
       }
    }
 
@@ -203,66 +250,88 @@
     * @param cid The id of the conversation to terminate
     * @return The asynchronous job handle
     */
-   private Future<?> scheduleForTermination(String cid, long timeout)
+   private Future<?> scheduleForTermination(Conversation conversation)
    {
-      Runnable terminationTask = new TerminationTask(cid);
-      return Container.instance().services().get(ScheduledExecutorServiceFactory.class).get().schedule(terminationTask, timeout, TimeUnit.MILLISECONDS);
+      Runnable terminationTask = new TerminationTask(conversation.getId());
+      return Container.instance().services().get(ScheduledExecutorServiceFactory.class).get().schedule(terminationTask, conversation.getTimeout(), TimeUnit.MILLISECONDS);
    }
 
-   /**
-    * A termination task that destroys the conversation entry
-    * 
-    * @author Nicklas Karlsson
-    * 
-    */
    private class TerminationTask implements Runnable
    {
-      // The conversation ID to terminate
       private String cid;
 
-      /**
-       * Creates a new termination task
-       * 
-       * @param cid The conversation ID
-       */
       public TerminationTask(String cid)
       {
          this.cid = cid;
       }
 
-      /**
-       * Executes the termination
-       */
       public void run()
       {
          log.debug(DESTROY_LRC, cid, "conversation timed out");
-         longRunningConversations.remove(cid).destroy();
-         log.trace(LRC_COUNT, longRunningConversations.size());
+         ManagedConversation managedConversation = managedConversations.remove(cid);
+         if (managedConversation != null)
+         {
+            managedConversation.destroy();
+         }
+         log.trace(LRC_COUNT, managedConversations.size());
       }
    }
 
-   public void destroyAllConversations()
+   public ConversationManager destroyAllConversations()
    {
       log.debug(DESTROY_ALL_LRC, "session ended");
-      log.trace(LRC_COUNT, longRunningConversations.size());
-      for (ConversationEntry conversationEntry : longRunningConversations.values())
+      log.trace(LRC_COUNT, managedConversations.size());
+      for (ManagedConversation managedConversation : managedConversations.values())
       {
-         log.debug(DESTROY_LRC, conversationEntry, "session ended");
-         conversationEntry.destroy();
+         log.debug(DESTROY_LRC, managedConversation, "session ended");
+         managedConversation.destroy();
       }
-      longRunningConversations.clear();
+      managedConversations.clear();
+      return this;
    }
 
    public Set<Conversation> getLongRunningConversations()
    {
       Set<Conversation> conversations = new HashSet<Conversation>();
-      for (ConversationEntry conversationEntry : longRunningConversations.values())
+      for (ManagedConversation managedConversation : managedConversations.values())
       {
-         conversations.add(conversationEntry.getConversation());
+         conversations.add(managedConversation.getConversation());
       }
       return Collections.unmodifiableSet(conversations);
    }
-   
-   public abstract BeanStore getBeanStore(String cid);
 
+   public ConversationManager activateContext()
+   {
+      Container.instance().services().get(ContextLifecycle.class).activateConversationContext();
+      return this;
+   }
+
+   public ConversationManager deactivateContext()
+   {
+      Container.instance().services().get(ContextLifecycle.class).deactivateConversationContext();
+      return this;
+   }
+
+   public String generateConversationId()
+   {
+      return conversationIdGenerator.nextId();
+   }
+
+   public Map<String, Conversation> getConversations()
+   {
+      Map<String, Conversation> conversations = new HashMap<String, Conversation>();
+      for (ManagedConversation entry : managedConversations.values())
+      {
+         conversations.put(entry.getConversation().getId(), entry.getConversation());
+      }
+      return Collections.unmodifiableMap(conversations);
+   }
+
+   public boolean isContextActive()
+   {
+      return getConversationContext().isActive();
+   }
+
+   protected abstract BeanStore getBeanStore(String cid);
+
 }

Deleted: core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationEntry.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationEntry.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationEntry.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -1,176 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2008, Red Hat, Inc., and individual contributors
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,  
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jboss.weld.conversation;
-
-import static org.jboss.weld.logging.Category.CONVERSATION;
-import static org.jboss.weld.logging.LoggerFactory.loggerFactory;
-import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_LOCKED;
-import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_TERMINATION_CANCELLATION_FAILED;
-import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_TERMINATION_CANCELLED;
-import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_UNAVAILBLE;
-import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_UNLOCKED;
-import static org.jboss.weld.logging.messages.ConversationMessage.ILLEGAL_CONVERSATION_UNLOCK_ATTEMPT;
-
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.jboss.weld.context.ConversationContext;
-import org.jboss.weld.context.api.BeanStore;
-import org.slf4j.cal10n.LocLogger;
-
-/**
- * Represents a long-running conversation entry
- * 
- * @author Nicklas Karlsson
- */
-public class ConversationEntry
-{
-   private static final LocLogger log = loggerFactory().getLogger(CONVERSATION);
-
-   // The conversation
-   private ConversationImpl conversation;
-   // The handle to the asynchronous timeout task
-   private Future<?> terminationHandle;
-   // The lock for concurrent access prevention
-   private ReentrantLock concurrencyLock;
-   // The Bean Store of the conversations
-   private BeanStore beanStore;
-
-   /**
-    * Creates a new conversation entry
-    * 
-    * @param cid The conversation ID
-    * @param terminationHandle The timeout termination handle
-    */
-   protected ConversationEntry(BeanStore beanStore, ConversationImpl conversation, Future<?> terminationHandle)
-   {
-      this.beanStore = beanStore;
-      // conversation is a proxy so we need to make a "real" instance
-      this.conversation = new ConversationImpl(conversation);
-      this.terminationHandle = terminationHandle;
-      this.concurrencyLock = new ReentrantLock();
-   }
-
-   /**
-    * Factory method
-    * 
-    * @param cid The conversation ID
-    * @param terminationHandle The timeout termination handle
-    * @return A new conversation entry
-    */
-   public static ConversationEntry of(BeanStore beanStore, ConversationImpl conversation, Future<?> terminationHandle)
-   {
-      return new ConversationEntry(beanStore, conversation, terminationHandle);
-   }
-
-   /**
-    * Cancels the timeout termination
-    * 
-    * @return True if successful, false otherwise
-    */
-   public boolean cancelTermination()
-   {
-      if (terminationHandle.isCancelled())
-      {
-         return true;
-      }
-      boolean success = terminationHandle.cancel(false);
-      if (success)
-      {
-         log.trace(CONVERSATION_TERMINATION_CANCELLED, conversation);
-      }
-      else
-      {
-         log.warn(CONVERSATION_TERMINATION_CANCELLATION_FAILED, conversation);
-      }
-      return success;
-   }
-
-   /**
-    * Destroys the conversation and it's associated conversational context
-    */
-   public void destroy()
-   {
-      if (!terminationHandle.isCancelled())
-      {
-         cancelTermination();
-      }
-      ConversationContext terminationContext = new ConversationContext();
-      terminationContext.setBeanStore(beanStore);
-      terminationContext.destroy();
-   }
-
-   /**
-    * 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
-    */
-   public boolean lock(long timeout) throws InterruptedException
-   {
-      boolean success = concurrencyLock.tryLock(timeout, TimeUnit.MILLISECONDS);
-      if (success)
-      {
-         log.trace(CONVERSATION_LOCKED, conversation);
-      }
-      else
-      {
-         log.warn(CONVERSATION_UNAVAILBLE, timeout, conversation);
-      }
-      return success;
-   }
-
-   /**
-    * Attempts to unlock the conversation
-    * 
-    * @return true if the unlock was successful, false otherwise
-    */
-   public boolean unlock()
-   {
-      if (!concurrencyLock.isLocked())
-      {
-         return true;
-      }
-      if (concurrencyLock.isHeldByCurrentThread())
-      {
-         concurrencyLock.unlock();
-         log.trace(CONVERSATION_UNLOCKED, conversation);
-      }
-      else
-      {
-         log.warn(ILLEGAL_CONVERSATION_UNLOCK_ATTEMPT, conversation, "not owner");
-      }
-      return !concurrencyLock.isLocked();
-   }
-
-   /**
-    * Re-schedules timeout termination
-    * 
-    * @param terminationHandle The fresh timeout termination handle
-    */
-   public void reScheduleTermination(Future<?> terminationHandle)
-   {
-      this.terminationHandle = terminationHandle;
-   }
-
-   public ConversationImpl getConversation()
-   {
-      return conversation;
-   }
-}

Modified: core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationImpl.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationImpl.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationImpl.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -18,7 +18,6 @@
 
 import static org.jboss.weld.logging.Category.CONVERSATION;
 import static org.jboss.weld.logging.LoggerFactory.loggerFactory;
-import static org.jboss.weld.logging.messages.BeanManagerMessage.CONTEXT_NOT_ACTIVE;
 import static org.jboss.weld.logging.messages.ConversationMessage.BEGIN_CALLED_ON_LONG_RUNNING_CONVERSATION;
 import static org.jboss.weld.logging.messages.ConversationMessage.DEMOTED_LRC;
 import static org.jboss.weld.logging.messages.ConversationMessage.END_CALLED_ON_TRANSIENT_CONVERSATION;
@@ -27,15 +26,13 @@
 
 import java.io.Serializable;
 
+import javax.enterprise.context.ContextNotActiveException;
 import javax.enterprise.context.Conversation;
 import javax.enterprise.context.RequestScoped;
 import javax.enterprise.inject.Default;
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.jboss.weld.Container;
-import org.jboss.weld.context.ContextLifecycle;
-import org.jboss.weld.context.ContextNotActiveException;
 import org.jboss.weld.exceptions.ForbiddenStateException;
 import org.slf4j.cal10n.LocLogger;
 
@@ -50,33 +47,30 @@
 @Default
 public class ConversationImpl implements Conversation, Serializable
 {
+   private static final long serialVersionUID = 1L;
 
-   /**
-    * Eclipse generated UID.
-    */
-   private static final long serialVersionUID = 5262382965141841363L;
-
    private static final LocLogger log = loggerFactory().getLogger(CONVERSATION);
 
-   // The conversation ID
+   @Inject
+   private ConversationManager conversationManager;
+
    private String id;
-   // The original conversation ID (if any)
-   private String originalId;
-   // Is the conversation long-running?
    private boolean _transient = true;
-   // The timeout in milliseconds
    private long timeout;
+   private String resumedId;
 
    /**
     * Creates a new conversation
     */
-   public ConversationImpl() {}
-   
-   protected void checkConversationActive()
+   public ConversationImpl()
    {
-      if (!Container.instance().services().get(ContextLifecycle.class).getConversationContext().isActive())
+   }
+
+   private void checkForActiveConversationContext(String when)
+   {
+      if (!conversationManager.isContextActive())
       {
-         throw new ContextNotActiveException(CONTEXT_NOT_ACTIVE, "@ConversationScoped");
+         throw new ContextNotActiveException("Conversation context not active when calling " + when + " on " + this);
       }
    }
 
@@ -85,12 +79,19 @@
     * 
     * @param conversation The old conversation
     */
-   public ConversationImpl(ConversationImpl conversation)
+   protected ConversationImpl(Conversation conversation, ConversationManager conversationManager)
    {
-      this.id = conversation.getUnderlyingId();
-      this._transient = conversation.isTransient();
-      this.timeout = conversation.getTimeout();
+      id = conversation.getId();
+      _transient = conversation.isTransient();
+      timeout = conversation.getTimeout();
+      // manual injection because of new() usage for unProxy();
+      this.conversationManager = conversationManager;
    }
+   
+   public static ConversationImpl of(Conversation conversation, ConversationManager conversationManager)
+   {
+      return new ConversationImpl(conversation, conversationManager);
+   }
 
    /**
     * Initializes a new conversation
@@ -99,83 +100,66 @@
     * @param timeout The conversation inactivity timeout
     */
    @Inject
-   public void init(ConversationIdGenerator conversationIdGenerator, @ConversationInactivityTimeout long timeout)
+   public void init(@ConversationInactivityTimeout long timeout)
    {
-      this.id = conversationIdGenerator.nextId();
       this.timeout = timeout;
-      this._transient = true;
+      _transient = true;
    }
 
    public void begin()
    {
-      checkConversationActive();
-      if (!isTransient())
+      checkForActiveConversationContext("Conversation.begin()");
+      if (!_transient)
       {
          throw new ForbiddenStateException(BEGIN_CALLED_ON_LONG_RUNNING_CONVERSATION);
       }
       log.debug(PROMOTED_TRANSIENT, id);
-      this._transient = false;
+      _transient = false;
+      id = conversationManager.generateConversationId();
    }
 
    public void begin(String id)
    {
-      checkConversationActive();
-      // Store away the (first) change to the conversation ID. If the original
-      // conversation was long-running,
-      // we might have to place it back for termination once the request is
-      // over.
-      if (originalId == null)
+      checkForActiveConversationContext("Conversation.begin(String)");
+      if (!_transient)
       {
-         originalId = id;
+         throw new ForbiddenStateException(BEGIN_CALLED_ON_LONG_RUNNING_CONVERSATION);
       }
+      if (conversationManager.getConversations().containsKey(id))
+      {
+         throw new IllegalStateException("Conversation ID " + id + " is already in use");
+      }
+      _transient = false;
       this.id = id;
-      begin();
    }
 
    public void end()
    {
-      checkConversationActive();
-      if (isTransient())
+      checkForActiveConversationContext("Conversation.end()");
+      if (_transient)
       {
          throw new ForbiddenStateException(END_CALLED_ON_TRANSIENT_CONVERSATION);
       }
       log.debug(DEMOTED_LRC, id);
-      this._transient = true;
+      _transient = true;
+      id = null;
    }
 
    public String getId()
    {
-      checkConversationActive();
-      if (!isTransient())
-      {
-         return id;
-      }
-      else
-      {
-         return null;
-      }
-   }
-
-   /**
-    * Get the Conversation Id, regardless of whether the conversation is long
-    * running or transient, needed for internal operations
-    * 
-    * @return the id
-    */
-   public String getUnderlyingId()
-   {
+      checkForActiveConversationContext("Conversation.getId()");
       return id;
    }
 
    public long getTimeout()
    {
-      checkConversationActive();
+      checkForActiveConversationContext("Conversation.getTimeout()");
       return timeout;
    }
 
    public void setTimeout(long timeout)
    {
-      checkConversationActive();
+      checkForActiveConversationContext("Conversation.setTimeout()");
       this.timeout = timeout;
    }
 
@@ -185,37 +169,28 @@
     * @param conversation The new conversation
     * 
     */
-   public void switchTo(ConversationImpl conversation)
+   public void switchTo(Conversation conversation)
    {
       log.debug(SWITCHED_CONVERSATION, this, conversation);
-      id = conversation.id;
-      this._transient = conversation._transient;
-      timeout = conversation.timeout;
+      id = conversation.getId();
+      _transient = conversation.isTransient();
+      timeout = conversation.getTimeout();
+      this.resumedId = id;
    }
 
    @Override
    public String toString()
    {
-      return "ID: " + id + ", transient: " + isTransient() + ", timeout: " + timeout + "ms";
+      return "ID: " + id + ", transient: " + _transient + ", timeout: " + timeout + "ms";
    }
 
-   /**
-    * Gets the original ID of the conversation
-    * 
-    * @return The id
-    */
-   public String getOriginalId()
-   {
-      return originalId;
-   }
-
    @Override
    public boolean equals(Object obj)
    {
       if (obj instanceof ConversationImpl)
       {
          ConversationImpl that = (ConversationImpl) obj;
-         return (id == null || that.getUnderlyingId() == null) ? false : id.equals(that.getUnderlyingId());
+         return (id == null || that.getId() == null) ? false : id.equals(that.getId());
       }
       else
       {
@@ -231,9 +206,22 @@
 
    public boolean isTransient()
    {
-      checkConversationActive();
+      checkForActiveConversationContext("Conversation.isTransient()");
       return _transient;
    }
-   
-   
+
+   public void setId(String id)
+   {
+      this.id = id;
+   }
+
+   public String getResumedId()
+   {
+      return resumedId;
+   }
+
+   public ConversationImpl unProxy(ConversationManager conversationManager)
+   {
+      return new ConversationImpl(this, conversationManager);
+   }
 }

Added: core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationManager.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationManager.java	                        (rev 0)
+++ core/trunk/impl/src/main/java/org/jboss/weld/conversation/ConversationManager.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -0,0 +1,96 @@
+package org.jboss.weld.conversation;
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2010, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Map;
+
+import javax.enterprise.context.Conversation;
+
+public interface ConversationManager
+{
+   /**
+    * Checks the state of the conversation context
+    * 
+    * @return true if the conversation context is active, false otherwise
+    */
+   public abstract boolean isContextActive();
+   
+   /**
+    * Activates the conversation context
+    * 
+    * @return The conversation manager
+    * 
+    * @throws IllegalStateException if the context is already active
+    */   
+   public abstract ConversationManager activateContext();
+   
+   /**
+    * Deactivates the conversation context
+    * 
+    * @return The conversation manager
+    * 
+    * @throws IllegalStateException if the context is already deactive
+    */   
+   public abstract ConversationManager deactivateContext();
+
+   /**
+    * Resumes a long running conversation. If the cid is null, nothing is done and the current
+    * transient conversation is resumed
+    * 
+    * 
+    * @param cid The conversation id to restore
+    * @return The conversation manager
+    * @throws NonexistentConversationException If the non-transient conversation is not known
+    * @throws BusyConversationException If the conversation is locked and not released while waiting 
+    * @throws IllegalStateException if the conversation context is not active
+    */
+   
+   public abstract ConversationManager setupConversation(String cid);
+   
+   /**
+    * Destroys the current conversation if it's transient. Stores it for conversation 
+    * propagation if it's non-transient
+    * 
+    * @return The conversation manager
+    * @throws IllegalStateException if the conversation context is not active
+    */
+   public abstract ConversationManager teardownConversation();
+   
+   /**
+    * Gets the current non-transient conversations
+    * 
+    * @return The conversations, mapped by id
+    * @throws IllegalStateException if the conversation context is not active
+    */
+   public abstract Map<String, Conversation> getConversations();
+
+   /**
+    * Returns a new, session-unique conversation ID
+    * 
+    * @return The conversation id
+    * @throws IllegalStateException if the conversation context is not active
+    */   
+   public abstract String generateConversationId();
+ 
+   /**
+    * Destroys all non-transient conversations
+    * 
+    * @return The conversation manager
+    */
+   public abstract ConversationManager destroyAllConversations();
+   
+}

Added: core/trunk/impl/src/main/java/org/jboss/weld/conversation/ManagedConversation.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/conversation/ManagedConversation.java	                        (rev 0)
+++ core/trunk/impl/src/main/java/org/jboss/weld/conversation/ManagedConversation.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -0,0 +1,217 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2008, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,  
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.conversation;
+
+import static org.jboss.weld.logging.Category.CONVERSATION;
+import static org.jboss.weld.logging.LoggerFactory.loggerFactory;
+import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_LOCKED;
+import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_TERMINATION_CANCELLATION_FAILED;
+import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_TERMINATION_CANCELLED;
+import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_UNAVAILBLE;
+import static org.jboss.weld.logging.messages.ConversationMessage.CONVERSATION_UNLOCKED;
+import static org.jboss.weld.logging.messages.ConversationMessage.ILLEGAL_CONVERSATION_UNLOCK_ATTEMPT;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.jboss.weld.context.ConversationContext;
+import org.jboss.weld.context.api.BeanStore;
+import org.slf4j.cal10n.LocLogger;
+
+/**
+ * Represents a long-running conversation entry
+ * 
+ * @author Nicklas Karlsson
+ */
+public class ManagedConversation
+{
+   private static final LocLogger log = loggerFactory().getLogger(CONVERSATION);
+
+   private ConversationImpl conversation;
+   private Future<?> terminationHandle;
+   private ReentrantLock concurrencyLock;
+   private BeanStore beanStore;
+   private long touched;
+
+   /**
+    * Creates a new conversation entry
+    * 
+    * @param cid The conversation ID
+    * @param terminationHandle The timeout termination handle
+    */
+   protected ManagedConversation(ConversationImpl conversation, BeanStore beanStore, Future<?> terminationHandle)
+   {
+      this.conversation = conversation;
+      this.beanStore = beanStore;
+      this.terminationHandle = terminationHandle;
+      this.concurrencyLock = new ReentrantLock();
+      touch();
+   }
+
+   /**
+    * Static factory method
+    * 
+    * @param conversation The conversation to manager
+    * @param beanStore The beanstore of the conversation for termination
+    * @param terminationHandle The asynchronous termination handle
+    * 
+    * @return A manager conversation
+    */
+   public static ManagedConversation of(ConversationImpl conversation, BeanStore beanStore, Future<?> terminationHandle)
+   {
+      return new ManagedConversation(conversation, beanStore, terminationHandle);
+   }
+
+   /**
+    * Static factory method
+    * 
+    * @param conversation The conversation to manager
+    * @param beanStore The beanstore of the conversation for termination
+    * 
+    * @return A manager conversation
+    */
+   public static ManagedConversation of(ConversationImpl conversation, BeanStore beanStore)
+   {
+      return of(conversation, beanStore, null);
+   }      
+
+   /**
+    * Cancels the asynchronous termination
+    * 
+    * @return True if successful, false otherwise
+    */
+   public boolean cancelTermination()
+   {
+      if (terminationHandle == null)
+      {
+         return false;
+      }
+      if (terminationHandle.isCancelled())
+      {
+         return true;
+      }
+      boolean success = terminationHandle.cancel(false);
+      if (success)
+      {
+         log.trace(CONVERSATION_TERMINATION_CANCELLED, conversation);
+      }
+      else
+      {
+         log.warn(CONVERSATION_TERMINATION_CANCELLATION_FAILED, conversation);
+      }
+      return success;
+   }
+
+   /**
+    * Destroys the conversation and it's associated conversational context
+    */
+   public void destroy()
+   {
+      // FIXME: Not really needed but there is a small window for overlapping if it fires(?)
+      if (terminationHandle != null && !terminationHandle.isCancelled())
+      {
+         cancelTermination();
+      }
+      ConversationContext terminationContext = new ConversationContext();
+      terminationContext.setBeanStore(beanStore);
+      terminationContext.destroy();
+   }
+
+   /**
+    * 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
+    */
+   public boolean lock(long timeout) throws InterruptedException
+   {
+      boolean success = concurrencyLock.tryLock(timeout, TimeUnit.MILLISECONDS);
+      if (success)
+      {
+         log.trace(CONVERSATION_LOCKED, conversation);
+      }
+      else
+      {
+         log.warn(CONVERSATION_UNAVAILBLE, timeout, conversation);
+      }
+      return success;
+   }
+
+   /**
+    * Attempts to unlock the conversation
+    * 
+    * @return true if the unlock was successful, false otherwise
+    */
+   public boolean unlock()
+   {
+      if (!concurrencyLock.isLocked())
+      {
+         return true;
+      }
+      if (concurrencyLock.isHeldByCurrentThread())
+      {
+         concurrencyLock.unlock();
+         log.trace(CONVERSATION_UNLOCKED, conversation);
+      }
+      else
+      {
+         log.warn(ILLEGAL_CONVERSATION_UNLOCK_ATTEMPT, conversation, "not owner");
+      }
+      return !concurrencyLock.isLocked();
+   }
+
+   /**
+    * Sets the asynchronous termination handle
+    * 
+    * @param terminationHandle The termination handle
+    */
+   public void setTerminationHandle(Future<?> terminationHandle)
+   {
+      this.terminationHandle = terminationHandle;
+   }
+
+   /**
+    * Gets the wrapped conversation
+    * 
+    * @return The conversation
+    */
+   public ConversationImpl getConversation()
+   {
+      return conversation;
+   }
+   
+   /**
+    * Checks if the conversation has expired
+    * 
+    * @return true if expired, false otherwise
+    */
+   public boolean isExpired()
+   {
+      return System.currentTimeMillis() > (touched + conversation.getTimeout());
+   }
+   
+   /**
+    * Touches the managed conversation, updating the "last used" timestamp
+    */
+   public void touch() 
+   {
+      touched = System.currentTimeMillis();
+   }
+
+}

Modified: core/trunk/impl/src/main/java/org/jboss/weld/conversation/ServletConversationManager.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/conversation/ServletConversationManager.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/conversation/ServletConversationManager.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -1,10 +1,4 @@
 /*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- *
- * Use is subject to license terms.
- *
  * JBoss, Home of Professional Open Source
  * Copyright 2008, Red Hat, Inc., and individual contributors
  * by the @authors tag. See the copyright.txt in the distribution for a
@@ -15,15 +9,16 @@
  * You may obtain a copy of the License at
  * http://www.apache.org/licenses/LICENSE-2.0
  * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
+ * distributed under the License is distributed on an "AS IS" BASIS,  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 package org.jboss.weld.conversation;
 
+import java.io.Serializable;
+
 import javax.enterprise.context.SessionScoped;
-import javax.enterprise.inject.Instance;
 import javax.enterprise.inject.Produces;
 import javax.inject.Inject;
 import javax.servlet.http.HttpSession;
@@ -31,30 +26,19 @@
 import org.jboss.weld.context.api.BeanStore;
 import org.jboss.weld.servlet.ConversationBeanStore;
 
-/**
- * The HTTP session based conversation manager
- * 
- * @author Nicklas Karlsson
- * 
- */
 @SessionScoped
-public class ServletConversationManager extends AbstractConversationManager
+public class ServletConversationManager extends AbstractConversationManager implements Serializable
 {
-   private static final long serialVersionUID = 1647848566880659085L;
+   private static final long serialVersionUID = 1L;
 
    private static final long CONVERSATION_TIMEOUT_IN_MS = 10 * 60 * 1000;
    private static final long CONVERSATION_CONCURRENT_ACCESS_TIMEOUT_IN_MS = 1 * 1000;
    private static final String CONVERSATION_ID_NAME = "cid";
    
-   @Inject 
-   private Instance<HttpSession> httpSession;
-   private boolean               sessionInvalidated = false;
+   private boolean sessionInvalidated;
 
-   @Override
-   public BeanStore getBeanStore(String cid)
-   {
-      return new ConversationBeanStore(httpSession.get(), sessionInvalidated, cid);
-   }
+   @Inject
+   private HttpSession httpSession;
    
    @Produces
    @ConversationInactivityTimeout
@@ -75,10 +59,17 @@
    public static String getConversationIdName()
    {
       return CONVERSATION_ID_NAME;
-   }
+   }   
    
+   @Override
+   protected BeanStore getBeanStore(String cid)
+   {
+      return ConversationBeanStore.of(httpSession, sessionInvalidated, cid);
+   }
+
    public void invalidateSession()
    {
       sessionInvalidated = true;
    }
+
 }

Modified: core/trunk/impl/src/main/java/org/jboss/weld/jsf/ConversationAwareViewHandler.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/jsf/ConversationAwareViewHandler.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/jsf/ConversationAwareViewHandler.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -71,7 +71,7 @@
       ConversationImpl conversation = conversation(getServletContext(context));  
       if (!conversation.isTransient())
       {
-         return new FacesUrlTransformer(actionUrl, context).appendConversationIdIfNecessary(conversation.getUnderlyingId()).getUrl();
+         return new FacesUrlTransformer(actionUrl, context).appendConversationIdIfNecessary(conversation.getId()).getUrl();
       }
       else
       {

Modified: core/trunk/impl/src/main/java/org/jboss/weld/jsf/WeldPhaseListener.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/jsf/WeldPhaseListener.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/jsf/WeldPhaseListener.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -23,42 +23,32 @@
 package org.jboss.weld.jsf;
 
 import static org.jboss.weld.jsf.JsfHelper.getConversationId;
-import static org.jboss.weld.jsf.JsfHelper.getHttpSession;
 import static org.jboss.weld.jsf.JsfHelper.getServletContext;
 import static org.jboss.weld.logging.Category.JSF;
 import static org.jboss.weld.logging.LoggerFactory.loggerFactory;
 import static org.jboss.weld.logging.messages.JsfMessage.CLEANING_UP_CONVERSATION;
 import static org.jboss.weld.logging.messages.JsfMessage.INITIATING_CONVERSATION;
-import static org.jboss.weld.logging.messages.JsfMessage.SKIPPING_CLEANING_UP_CONVERSATION;
-import static org.jboss.weld.servlet.BeanProvider.conversation;
 import static org.jboss.weld.servlet.BeanProvider.conversationManager;
-import static org.jboss.weld.servlet.BeanProvider.httpSessionManager;
 
 import javax.faces.context.FacesContext;
 import javax.faces.event.PhaseEvent;
 import javax.faces.event.PhaseId;
 import javax.faces.event.PhaseListener;
-import javax.servlet.ServletContext;
-import javax.servlet.http.HttpSession;
 
-import org.jboss.weld.Container;
-import org.jboss.weld.context.ContextLifecycle;
-import org.jboss.weld.context.ConversationContext;
-import org.jboss.weld.context.SessionContext;
-import org.jboss.weld.conversation.AbstractConversationManager;
+import org.jboss.weld.conversation.ConversationManager;
 import org.slf4j.cal10n.LocLogger;
 
 /**
  * <p>
- * A JSF phase listener that initializes aspects of Weld in a more
- * fine-grained, integrated manner than what is possible with a servlet filter.
- * This phase listener works in conjunction with other hooks and callbacks
- * registered with the JSF runtime to help manage the Weld lifecycle.
+ * A JSF phase listener that initializes aspects of Weld in a more fine-grained,
+ * integrated manner than what is possible with a servlet filter. This phase
+ * listener works in conjunction with other hooks and callbacks registered with
+ * the JSF runtime to help manage the Weld lifecycle.
  * </p>
  * 
  * <p>
  * It's expected that over time, this phase listener may take on more work, but
- * for now the work is focused soley on conversation management. The phase
+ * for now the work is focused solely on conversation management. The phase
  * listener restores the long-running conversation if the conversation id token
  * is detected in the request, activates the conversation context in either case
  * (long-running or transient), and finally passivates the conversation after
@@ -70,13 +60,26 @@
  */
 public class WeldPhaseListener implements PhaseListener
 {
+   private static final long serialVersionUID = 1L;
+
    private static final LocLogger log = loggerFactory().getLogger(JSF);
 
+   private ConversationManager conversationManager;
+
+   private ConversationManager getConversationManager()
+   {
+      if (conversationManager == null)
+      {
+         conversationManager = conversationManager(getServletContext(FacesContext.getCurrentInstance()));
+      }
+      return conversationManager;
+   }
+
    /**
-    * Execute before every phase in the JSF life cycle. The order this
-    * phase listener executes in relation to other phase listeners is
-    * determined by the ordering of the faces-config.xml descriptors.
-    * This phase listener should take precedence over extensions.
+    * Execute before every phase in the JSF life cycle. The order this phase
+    * listener executes in relation to other phase listeners is determined by
+    * the ordering of the faces-config.xml descriptors. This phase listener
+    * should take precedence over extensions.
     * 
     * @param phaseEvent The phase event
     */
@@ -89,10 +92,10 @@
    }
 
    /**
-    * Execute after every phase in the JSF life cycle. The order this
-    * phase listener executes in relation to other phase listeners is
-    * determined by the ordering of the faces-config.xml descriptors.
-    * This phase listener should take precedence over extensions.
+    * Execute after every phase in the JSF life cycle. The order this phase
+    * listener executes in relation to other phase listeners is determined by
+    * the ordering of the faces-config.xml descriptors. This phase listener
+    * should take precedence over extensions.
     * 
     * @param phaseEvent The phase event
     */
@@ -117,24 +120,14 @@
       log.trace(INITIATING_CONVERSATION, "Restore View");
       initiateSessionAndConversation(facesContext);
    }
-   
+
    /**
     * Execute after the Render Response phase.
     */
    private void afterRenderResponse(FacesContext facesContext)
    {
-      SessionContext sessionContext = Container.instance().services().get(ContextLifecycle.class).getSessionContext();
-      ConversationContext conversationContext = Container.instance().services().get(ContextLifecycle.class).getConversationContext();
-      if (sessionContext.isActive())
-      {
-         log.trace(CLEANING_UP_CONVERSATION, "Render Response", "response complete");
-         conversationManager(getServletContext(facesContext)).cleanupConversation();
-         conversationContext.setActive(false);
-      }
-      else
-      {
-         log.trace(SKIPPING_CLEANING_UP_CONVERSATION, "Render Response", "session has been terminated");
-      }
+      log.trace(CLEANING_UP_CONVERSATION, "Render Response", "response complete");
+      getConversationManager().teardownConversation();
    }
 
    /**
@@ -142,16 +135,8 @@
     */
    private void afterResponseComplete(FacesContext facesContext, PhaseId phaseId)
    {
-      SessionContext sessionContext = Container.instance().services().get(ContextLifecycle.class).getSessionContext();
-      if (sessionContext.isActive())
-      {
-         log.trace(CLEANING_UP_CONVERSATION, phaseId, "the response has been marked complete");
-         conversationManager(getServletContext(facesContext)).cleanupConversation();
-      }
-      else
-      {
-         log.trace(SKIPPING_CLEANING_UP_CONVERSATION, phaseId, "session has been terminated");
-      }
+      log.trace(CLEANING_UP_CONVERSATION, phaseId, "the response has been marked complete");
+      getConversationManager().teardownConversation();
    }
 
    /**
@@ -162,22 +147,7 @@
     */
    private void initiateSessionAndConversation(FacesContext facesContext)
    {
-      ServletContext servletContext = getServletContext(facesContext);
-      AbstractConversationManager conversationManager = (AbstractConversationManager) conversationManager(servletContext);
-      HttpSession session = getHttpSession(facesContext);
-      httpSessionManager(servletContext).setSession(session);
-      try
-      {
-         conversationManager.beginOrRestoreConversation(getConversationId(facesContext));
-      }
-      finally
-      {
-         String cid = conversation(servletContext).getUnderlyingId();
-         
-         ConversationContext conversationContext = Container.instance().services().get(ContextLifecycle.class).getConversationContext();
-         conversationContext.setBeanStore(conversationManager.getBeanStore(cid));
-         conversationContext.setActive(true);
-      }
+      getConversationManager().setupConversation(getConversationId(facesContext));
    }
 
    /**

Modified: core/trunk/impl/src/main/java/org/jboss/weld/servlet/ConversationBeanStore.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/servlet/ConversationBeanStore.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/servlet/ConversationBeanStore.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -19,6 +19,7 @@
 import javax.servlet.http.HttpSession;
 
 import org.jboss.weld.context.ConversationContext;
+import org.jboss.weld.context.api.BeanStore;
 import org.jboss.weld.context.beanstore.NamingScheme;
 
 /**
@@ -28,10 +29,9 @@
  */
 public class ConversationBeanStore extends HttpPassThruSessionBeanStore
 {
-   
    private final NamingScheme namingScheme;
 
-   public ConversationBeanStore(HttpSession session, boolean sessionInvalidated, String cid)
+   protected ConversationBeanStore(HttpSession session, boolean sessionInvalidated, String cid)
    {
       this.namingScheme = new NamingScheme(ConversationContext.class.getName() + "[" + cid + "]", "#");
       if (sessionInvalidated)
@@ -47,4 +47,14 @@
       return namingScheme;
    }
 
+   public static BeanStore of(HttpSession httpSession, boolean sessionInvalidated, String cid)
+   {
+      return new ConversationBeanStore(httpSession, sessionInvalidated, cid);
+   }
+
+   public static BeanStore of(HttpSession httpSession, String cid)
+   {
+      return new ConversationBeanStore(httpSession, false, cid);
+   }
+
 }

Modified: core/trunk/impl/src/main/java/org/jboss/weld/servlet/ConversationPropagationFilter.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/servlet/ConversationPropagationFilter.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/servlet/ConversationPropagationFilter.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -84,8 +84,8 @@
                ConversationImpl conversation = conversation(ctx);
                if (!conversation.isTransient())
                {
-                  path = new FacesUrlTransformer(path, FacesContext.getCurrentInstance()).toRedirectViewId().toActionUrl().appendConversationIdIfNecessary(conversation.getUnderlyingId()).encode();
-                  conversationManager(ctx).cleanupConversation();
+                  path = new FacesUrlTransformer(path, FacesContext.getCurrentInstance()).toRedirectViewId().toActionUrl().appendConversationIdIfNecessary(conversation.getId()).encode();
+                  conversationManager(ctx).teardownConversation();
                }
             }
             super.sendRedirect(path);

Modified: core/trunk/impl/src/main/java/org/jboss/weld/servlet/HttpPassThruSessionBeanStore.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/servlet/HttpPassThruSessionBeanStore.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/servlet/HttpPassThruSessionBeanStore.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -44,12 +44,12 @@
 public class HttpPassThruSessionBeanStore extends HttpSessionBeanStore
 {
 
-   private static final long      serialVersionUID  = 8923580660774253915L;
-   private static final LocLogger log               = loggerFactory().getLogger(CONTEXT);
+   private static final long serialVersionUID = 8923580660774253915L;
+   private static final LocLogger log = loggerFactory().getLogger(CONTEXT);
 
-   private HashMapBeanStore       delegateBeanStore = new HashMapBeanStore();
-   private boolean                attachedToSession = false;
-   private boolean                invalidated       = false;
+   private HashMapBeanStore delegateBeanStore = new HashMapBeanStore();
+   private boolean attachedToSession = false;
+   private boolean invalidated = false;
 
    public HttpPassThruSessionBeanStore()
    {
@@ -129,7 +129,7 @@
       catch (IllegalStateException e)
       {
          // There's not a lot to do here if the session is invalidated while
-         // loading this map.  These beans will not be destroyed since the
+         // loading this map. These beans will not be destroyed since the
          // references are lost.
          delegateBeanStore.clear();
       }

Modified: core/trunk/impl/src/main/java/org/jboss/weld/servlet/HttpSessionBeanStore.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/servlet/HttpSessionBeanStore.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/servlet/HttpSessionBeanStore.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -37,7 +37,7 @@
    private static final NamingScheme NAMING_SCHEME = new NamingScheme(SessionContext.class.getName(), "#");
 
    // The HTTP session context to use as backing map
-   private HttpSession               session;
+   private HttpSession session;
 
    /**
     * Attaches this bean store to a session dynamically. This allows the session

Modified: core/trunk/impl/src/main/java/org/jboss/weld/servlet/ServletLifecycle.java
===================================================================
--- core/trunk/impl/src/main/java/org/jboss/weld/servlet/ServletLifecycle.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/impl/src/main/java/org/jboss/weld/servlet/ServletLifecycle.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -48,7 +48,7 @@
 
    private final ContextLifecycle lifecycle;
 
-   public static final String     REQUEST_ATTRIBUTE_NAME = ServletLifecycle.class.getName() + ".requestBeanStore";
+   public static final String REQUEST_ATTRIBUTE_NAME = ServletLifecycle.class.getName() + ".requestBeanStore";
 
    /**
     * 

Modified: core/trunk/tests/src/test/java/org/jboss/weld/tests/serialization/SerializationTest.java
===================================================================
--- core/trunk/tests/src/test/java/org/jboss/weld/tests/serialization/SerializationTest.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/tests/src/test/java/org/jboss/weld/tests/serialization/SerializationTest.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -36,14 +36,12 @@
       TestConversationManager cMgr = getReference(TestConversationManager.class);
       
       assert cMgr.getConversationInstance() != null;
-      assert cMgr.getConversationInstance().get() != null;
       
       Object deserialized = Utils.deserialize(Utils.serialize(cMgr));
       
       assert deserialized instanceof TestConversationManager;
       TestConversationManager deserializedCMgr = (TestConversationManager) deserialized;
       assert deserializedCMgr.getConversationInstance() != null;
-      assert deserializedCMgr.getConversationInstance().get() != null;
    }
    
    @Test(description="http://lists.jboss.org/pipermail/weld-dev/2010-February/002265.html")

Modified: core/trunk/tests/src/test/java/org/jboss/weld/tests/serialization/TestConversationManager.java
===================================================================
--- core/trunk/tests/src/test/java/org/jboss/weld/tests/serialization/TestConversationManager.java	2010-02-26 08:08:17 UTC (rev 5963)
+++ core/trunk/tests/src/test/java/org/jboss/weld/tests/serialization/TestConversationManager.java	2010-02-27 13:31:35 UTC (rev 5964)
@@ -19,7 +19,6 @@
 import java.lang.reflect.Field;
 
 import javax.enterprise.context.Dependent;
-import javax.enterprise.inject.Instance;
 
 import org.jboss.weld.conversation.AbstractConversationManager;
 import org.jboss.weld.conversation.ConversationImpl;
@@ -28,17 +27,13 @@
 @Dependent
 public class TestConversationManager extends ServletConversationManager
 {
-
-   /**
-    * 
-    */
-   private static final long serialVersionUID = 9081670661490776033L;
+   private static final long serialVersionUID = 2;
    
-   public Instance<ConversationImpl> getConversationInstance() throws Exception
+   public ConversationImpl getConversationInstance() throws Exception
    {
-      Field ccField = AbstractConversationManager.class.getDeclaredField("currentConversation");
+      Field ccField = AbstractConversationManager.class.getDeclaredField("conversation");
       ccField.setAccessible(true);
-      return (Instance<ConversationImpl>) ccField.get(this);
+      return (ConversationImpl) ccField.get(this);
    }
 
 }



More information about the weld-commits mailing list