Updated Branches:
  refs/heads/master b29bb9052 -> 101f0d915

new experimental feature in wicket-cdi - automatic conversation lifecycle 
management


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/101f0d91
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/101f0d91
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/101f0d91

Branch: refs/heads/master
Commit: 101f0d9154e0e263375e9715bae4d0b57136c4a3
Parents: b29bb90
Author: Igor Vaynberg <igor.vaynb...@gmail.com>
Authored: Sat Mar 30 00:07:16 2013 -0700
Committer: Igor Vaynberg <igor.vaynb...@gmail.com>
Committed: Sat Mar 30 00:07:16 2013 -0700

----------------------------------------------------------------------
 .../org/apache/wicket/cdi/AutoConversation.java    |   49 +++++++
 .../org/apache/wicket/cdi/CdiConfiguration.java    |   40 +++++-
 .../apache/wicket/cdi/ConversationPropagator.java  |  111 +++++++++++++--
 .../apache/wicket/cdi/ConversationalComponent.java |   30 ++++
 .../apache/wicket/cdi/ApacheLicenceHeaderTest.java |    1 -
 5 files changed, 216 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/101f0d91/wicket-cdi/src/main/java/org/apache/wicket/cdi/AutoConversation.java
----------------------------------------------------------------------
diff --git 
a/wicket-cdi/src/main/java/org/apache/wicket/cdi/AutoConversation.java 
b/wicket-cdi/src/main/java/org/apache/wicket/cdi/AutoConversation.java
new file mode 100644
index 0000000..62d078f
--- /dev/null
+++ b/wicket-cdi/src/main/java/org/apache/wicket/cdi/AutoConversation.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.wicket.cdi;
+
+import java.io.Serializable;
+
+import javax.enterprise.context.ConversationScoped;
+
+/**
+ * A bean that can be used to override whether the lifecycle of the 
conversation should be managed
+ * automatically or not. See {@link 
CdiConfiguration#setAutoConversationManagement(boolean)} for
+ * details.
+ * 
+ * @author igor
+ */
+@ConversationScoped
+public class AutoConversation implements Serializable
+{
+       private Boolean automatic;
+
+       public AutoConversation()
+       {
+               automatic = false;
+       }
+
+       public void setAutomatic(boolean automatic)
+       {
+               this.automatic = automatic;
+       }
+
+       public boolean isAutomatic()
+       {
+               return automatic;
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/101f0d91/wicket-cdi/src/main/java/org/apache/wicket/cdi/CdiConfiguration.java
----------------------------------------------------------------------
diff --git 
a/wicket-cdi/src/main/java/org/apache/wicket/cdi/CdiConfiguration.java 
b/wicket-cdi/src/main/java/org/apache/wicket/cdi/CdiConfiguration.java
index 6a2bce6..32f3f56 100644
--- a/wicket-cdi/src/main/java/org/apache/wicket/cdi/CdiConfiguration.java
+++ b/wicket-cdi/src/main/java/org/apache/wicket/cdi/CdiConfiguration.java
@@ -24,7 +24,7 @@ import org.apache.wicket.util.lang.Args;
 import org.jboss.seam.conversation.spi.SeamConversationContextFactory;
 
 /**
- * Configures Weld integration
+ * Configures CDI integration
  * 
  * @author igor
  * 
@@ -39,7 +39,7 @@ public class CdiConfiguration
        private boolean injectApplication = true;
        private boolean injectSession = true;
        private boolean injectBehaviors = true;
-
+       private boolean autoConversationManagement = false;
 
        /**
         * Constructor
@@ -69,6 +69,38 @@ public class CdiConfiguration
                return propagation;
        }
 
+       /**
+        * Checks if auto conversation management is enabled. See
+        * {@link #setAutoConversationManagement(boolean)} for details.
+        */
+       public boolean isAutoConversationManagement()
+       {
+               return autoConversationManagement;
+       }
+
+       /**
+        * Toggles automatic conversation management feature.
+        * 
+        * Automatic conversation management controls the lifecycle of the 
conversation based on
+        * presense of components implementing the {@link 
ConversationalComponent} interface. If such
+        * components are found in the page a conversation is makred 
persistent, and if they are not the
+        * conversation is marked transient. This greatly simplifies the 
management of conversation
+        * lifecycle.
+        * 
+        * Sometimes it is necessary to manually control the application. For 
these cases, once a
+        * conversation is started {@link AutoConversation} bean can be used to 
mark the conversation as
+        * manually-managed.
+        * 
+        * @param enabled
+        * 
+        * @return {@code this} for easy chaining
+        */
+       public CdiConfiguration setAutoConversationManagement(boolean enabled)
+       {
+               autoConversationManagement = enabled;
+               return this;
+       }
+
        public CdiConfiguration setPropagation(IConversationPropagation 
propagation)
        {
                this.propagation = propagation;
@@ -153,7 +185,8 @@ public class CdiConfiguration
                // enable conversation propagation
                if (getPropagation() != ConversationPropagation.NONE)
                {
-                       listeners.add(new ConversationPropagator(application, 
container, getPropagation()));
+                       listeners.add(new ConversationPropagator(application, 
container, getPropagation(),
+                               autoConversationManagement));
                        
application.getComponentPreOnBeforeRenderListeners().add(
                                new ConversationExpiryChecker(container));
                        
SeamConversationContextFactory.setDisableNoopInstance(true);
@@ -162,6 +195,7 @@ public class CdiConfiguration
                // enable detach event
                listeners.add(new DetachEventEmitter(container));
 
+
                // inject application instance
                if (isInjectApplication())
                {

http://git-wip-us.apache.org/repos/asf/wicket/blob/101f0d91/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationPropagator.java
----------------------------------------------------------------------
diff --git 
a/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationPropagator.java 
b/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationPropagator.java
index 1d8410d..b8037d0 100644
--- a/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationPropagator.java
+++ b/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationPropagator.java
@@ -22,6 +22,7 @@ import 
javax.enterprise.context.NonexistentConversationException;
 import javax.inject.Inject;
 
 import org.apache.wicket.Application;
+import org.apache.wicket.Component;
 import org.apache.wicket.MetaDataKey;
 import org.apache.wicket.Page;
 import org.apache.wicket.core.request.handler.BufferedResponseRequestHandler;
@@ -40,6 +41,9 @@ import 
org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.PackageResourceReference;
 import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.lang.Objects;
+import org.apache.wicket.util.visit.IVisit;
+import org.apache.wicket.util.visit.IVisitor;
+import org.apache.wicket.util.visit.Visits;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -70,9 +74,14 @@ public class ConversationPropagator extends 
AbstractRequestCycleListener
 
        private final Application application;
 
+       private final boolean auto;
+
        @Inject
        Conversation conversation_;
 
+       @Inject
+       AutoConversation autoConversation;
+
        /**
         * Constructor
         * 
@@ -80,7 +89,7 @@ public class ConversationPropagator extends 
AbstractRequestCycleListener
         * @param propagation
         */
        public ConversationPropagator(Application application, CdiContainer 
container,
-               IConversationPropagation propagation)
+               IConversationPropagation propagation, boolean auto)
        {
                Args.notNull(application, "application");
                Args.notNull(container, "container");
@@ -95,6 +104,7 @@ public class ConversationPropagator extends 
AbstractRequestCycleListener
                this.application = application;
                this.container = container;
                this.propagation = propagation;
+               this.auto = auto;
 
                container.getNonContextualManager().postConstruct(this);
        }
@@ -201,14 +211,24 @@ public class ConversationPropagator extends 
AbstractRequestCycleListener
        {
                Conversation conversation = getConversation(cycle);
 
-               if (conversation == null || conversation.isTransient())
+               if (conversation == null)
                {
                        return;
                }
 
-
                Page page = getPage(handler);
-               if (page != null && propagation.propagatesViaPage(page, 
handler))
+
+               if (page == null)
+               {
+                       return;
+               }
+
+               // apply auto semantics
+
+               autoEndIfNecessary(page, handler, conversation);
+               autoBeginIfNecessary(page, handler, conversation);
+
+               if (propagation.propagatesViaPage(page, handler))
                {
                        // propagate a conversation across non-bookmarkable 
page instances
                        setConversationOnPage(conversation, page);
@@ -218,6 +238,8 @@ public class ConversationPropagator extends 
AbstractRequestCycleListener
        @Override
        public void onRequestHandlerScheduled(RequestCycle cycle, 
IRequestHandler handler)
        {
+               // propagate current non-transient conversation to the newly 
scheduled page
+
                Conversation conversation = getConversation(cycle);
 
                if (conversation == null || conversation.isTransient())
@@ -226,10 +248,13 @@ public class ConversationPropagator extends 
AbstractRequestCycleListener
                }
 
                Page page = getPage(handler);
-               if (page != null && propagation.propagatesViaPage(page, 
handler))
+               if (page != null)
                {
-                       // propagate a conversation across non-bookmarkable 
page instances
-                       setConversationOnPage(conversation, page);
+                       if (propagation.propagatesViaPage(page, handler))
+                       {
+                               // propagate a conversation across 
non-bookmarkable page instances
+                               setConversationOnPage(conversation, page);
+                       }
                }
 
                if (propagation.propagatesViaParameters(handler))
@@ -250,12 +275,22 @@ public class ConversationPropagator extends 
AbstractRequestCycleListener
 
        protected void setConversationOnPage(Conversation conversation, Page 
page)
        {
-               logger.debug("Propagating non-transient conversation {} via 
meta of page instance {}",
-                       conversation.getId(), page);
+               if (conversation == null || conversation.isTransient())
+               {
+                       logger.debug("Detaching transient conversation {} via 
meta of page instance {}",
+                               (conversation == null ? "null" : 
conversation.getId()), page);
 
-               page.setMetaData(CID_KEY, conversation.getId());
-       }
+                       page.setMetaData(CID_KEY, null);
+               }
+               else
+               {
+
+                       logger.debug("Propagating non-transient conversation {} 
via meta of page instance {}",
+                               conversation.getId(), page);
 
+                       page.setMetaData(CID_KEY, conversation.getId());
+               }
+       }
 
        @Override
        public void onUrlMapped(RequestCycle cycle, IRequestHandler handler, 
Url url)
@@ -328,6 +363,60 @@ public class ConversationPropagator extends 
AbstractRequestCycleListener
                return true;
        }
 
+       protected void autoBeginIfNecessary(Page page, IRequestHandler handler,
+               Conversation conversation)
+       {
+               if (!auto || conversation == null || 
!conversation.isTransient() || page == null ||
+                       !propagation.propagatesViaPage(page, handler) || 
!hasConversationalComponent(page))
+               {
+                       return;
+               }
+
+               // auto activate conversation
+
+               conversation.begin();
+               autoConversation.setAutomatic(true);
+
+               logger.debug("Auto-began conversation {} for page {}", 
conversation.getId(), page);
+       }
+
+       protected void autoEndIfNecessary(Page page, IRequestHandler handler, 
Conversation conversation)
+       {
+               if (!auto || conversation == null || conversation.isTransient() 
|| page == null ||
+                       !propagation.propagatesViaPage(page, handler) || 
hasConversationalComponent(page) ||
+                       autoConversation.isAutomatic() == false)
+               {
+                       return;
+               }
+
+               // auto de-activate conversation
+
+               String cid = conversation.getId();
+
+               autoConversation.setAutomatic(false);
+               conversation.end();
+
+               logger.debug("Auto-ended conversation {} for page {}", cid, 
page);
+       }
+
+
+       protected boolean hasConversationalComponent(Page page)
+       {
+               Boolean hasConversational = Visits.visit(page, new 
IVisitor<Component, Boolean>()
+               {
+                       @Override
+                       public void component(Component object, IVisit<Boolean> 
visit)
+                       {
+                               if (object instanceof ConversationalComponent)
+                               {
+                                       visit.stop(true);
+                               }
+                       }
+               });
+
+               return hasConversational == null ? false : hasConversational;
+       }
+
        /**
         * Resolves a page instance from the request handler iff the page 
instance is already created
         * 

http://git-wip-us.apache.org/repos/asf/wicket/blob/101f0d91/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationalComponent.java
----------------------------------------------------------------------
diff --git 
a/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationalComponent.java 
b/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationalComponent.java
new file mode 100644
index 0000000..abe3871
--- /dev/null
+++ 
b/wicket-cdi/src/main/java/org/apache/wicket/cdi/ConversationalComponent.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.wicket.cdi;
+
+/**
+ * Marks a component that requires a conversation. This marker is used by the 
automatic conversation
+ * management feature ({@link 
CdiConfiguration#setAutoConversationManagement(boolean)}) to
+ * automatically begin and end conversations based on the presense of these 
components in the
+ * component hierarchy of pages (can be applied to the page itself).
+ * 
+ * @author igor
+ */
+public interface ConversationalComponent
+{
+
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/101f0d91/wicket-cdi/src/test/java/org/apache/wicket/cdi/ApacheLicenceHeaderTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-cdi/src/test/java/org/apache/wicket/cdi/ApacheLicenceHeaderTest.java 
b/wicket-cdi/src/test/java/org/apache/wicket/cdi/ApacheLicenceHeaderTest.java
index 0a33677..eeb2c21 100644
--- 
a/wicket-cdi/src/test/java/org/apache/wicket/cdi/ApacheLicenceHeaderTest.java
+++ 
b/wicket-cdi/src/test/java/org/apache/wicket/cdi/ApacheLicenceHeaderTest.java
@@ -28,7 +28,6 @@ import 
org.apache.wicket.util.license.ApacheLicenseHeaderTestCase;
  */
 public class ApacheLicenceHeaderTest extends ApacheLicenseHeaderTestCase
 {
-
        /**
         * Construct.
         */

Reply via email to