Repository: wicket
Updated Branches:
  refs/heads/suspend_request_cycle [created] 64f746fba


experimenting with suspension of requestCycle via servlet 3 async

Notes:
- enabled async-supported in web.xml for ComponentReferenceApplication
- LinkPage shows usage of RequestCycle#suspend()
- pageAccessSynchronizer uses the requestCycle instead of the thread
now, since two threads might process a page (PageAccessSynchronizerTest
still fails)
- ServletWebRequest must not call httpServletRequest#getContextPath(),
since it returns null during async processing


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

Branch: refs/heads/suspend_request_cycle
Commit: 64f746fba8bb0235dd870e2a9d2ba1eaa192ada5
Parents: 05a1741
Author: Sven Meier <svenme...@apache.org>
Authored: Fri Feb 17 18:30:51 2017 +0100
Committer: Sven Meier <svenme...@apache.org>
Committed: Fri Feb 17 19:58:44 2017 +0100

----------------------------------------------------------------------
 .../wicket/page/PageAccessSynchronizer.java     |  45 +++---
 .../http/servlet/ServletWebRequest.java         |   2 +-
 .../wicket/request/cycle/RequestCycle.java      | 153 ++++++++++++++++++-
 .../wicket/examples/compref/LinkPage.java       |  15 ++
 wicket-examples/src/main/webapp/WEB-INF/web.xml |  22 +--
 5 files changed, 191 insertions(+), 46 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/64f746fb/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java 
b/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java
index 86fbb8b..df359e6 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/page/PageAccessSynchronizer.java
@@ -23,6 +23,7 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.function.Supplier;
 
 import org.apache.wicket.Application;
+import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.settings.ExceptionSettings.ThreadDumpStrategy;
 import org.apache.wicket.util.LazyInitializer;
 import org.apache.wicket.util.lang.Threads;
@@ -93,8 +94,8 @@ public class PageAccessSynchronizer implements Serializable
         */
        public void lockPage(int pageId) throws CouldNotLockPageException
        {
-               final Thread thread = Thread.currentThread();
-               final PageLock lock = new PageLock(pageId, thread);
+               final RequestCycle cycle = RequestCycle.get();
+               final PageLock lock = new PageLock(pageId, cycle);
                final Time start = Time.now();
 
                boolean locked = false;
@@ -110,12 +111,12 @@ public class PageAccessSynchronizer implements 
Serializable
                        if (isDebugEnabled)
                        {
                                logger.debug("'{}' attempting to acquire lock 
to page with id '{}'",
-                                       thread.getName(), pageId);
+                                       cycle.getStartTime(), pageId);
                        }
 
                        previous = locks.get().putIfAbsent(pageId, lock);
 
-                       if (previous == null || previous.thread == thread)
+                       if (previous == null || previous.cycle == cycle)
                        {
                                // first thread to acquire lock or lock is 
already owned by this thread
                                locked = true;
@@ -134,7 +135,7 @@ public class PageAccessSynchronizer implements Serializable
                {
                        if (isDebugEnabled)
                        {
-                               logger.debug("{} acquired lock to page {}", 
thread.getName(), pageId);
+                               logger.debug("{} acquired lock to page {}", 
cycle.getStartTime(), pageId);
                        }
                }
                else
@@ -144,8 +145,8 @@ public class PageAccessSynchronizer implements Serializable
                                logger.warn(
                                        "Thread '{}' failed to acquire lock to 
page with id '{}', attempted for {} out of allowed {}." +
                                                        " The thread that holds 
the lock has name '{}'.",
-                                       thread.getName(), pageId, 
start.elapsedSince(), timeout,
-                                                       
previous.thread.getName());
+                                       cycle.getStartTime(), pageId, 
start.elapsedSince(), timeout,
+                                                       
previous.cycle.getStartTime());
                                if (Application.exists())
                                {
                                        ThreadDumpStrategy strategy = 
Application.get()
@@ -157,7 +158,7 @@ public class PageAccessSynchronizer implements Serializable
                                                        
Threads.dumpAllThreads(logger);
                                                        break;
                                                case THREAD_HOLDING_LOCK :
-                                                       
Threads.dumpSingleThread(logger, previous.thread);
+//                                                     
Threads.dumpSingleThread(logger, previous.thread);
                                                        break;
                                                case NO_THREADS :
                                                default :
@@ -165,7 +166,7 @@ public class PageAccessSynchronizer implements Serializable
                                        }
                                }
                        }
-                       throw new CouldNotLockPageException(pageId, 
thread.getName(), timeout);
+                       throw new CouldNotLockPageException(pageId, "" + 
cycle.getStartTime(), timeout);
                }
        }
 
@@ -190,7 +191,7 @@ public class PageAccessSynchronizer implements Serializable
 
        private void internalUnlockPages(final Integer pageId)
        {
-               final Thread thread = Thread.currentThread();
+               final RequestCycle cycle = RequestCycle.get();
                final Iterator<PageLock> locks = 
this.locks.get().values().iterator();
 
                final boolean isDebugEnabled = logger.isDebugEnabled();
@@ -200,12 +201,12 @@ public class PageAccessSynchronizer implements 
Serializable
                        // remove all locks held by this thread if 'pageId' is 
not specified
                        // otherwise just the lock for this 'pageId'
                        final PageLock lock = locks.next();
-                       if ((pageId == null || pageId == lock.pageId) && 
lock.thread == thread)
+                       if ((pageId == null || pageId == lock.pageId) && 
lock.cycle == cycle)
                        {
                                locks.remove();
                                if (isDebugEnabled)
                                {
-                                       logger.debug("'{}' released lock to 
page with id '{}'", thread.getName(),
+                                       logger.debug("'{}' released lock to 
page with id '{}'", cycle.getStartTime(),
                                                lock.pageId);
                                }
                                // if any locks were removed notify threads 
waiting for a lock
@@ -288,8 +289,8 @@ public class PageAccessSynchronizer implements Serializable
                /** page id */
                private final int pageId;
 
-               /** thread that owns the lock */
-               private final Thread thread;
+               /** cycle that owns the lock */
+               private final RequestCycle cycle;
 
                private volatile boolean released = false;
 
@@ -297,12 +298,12 @@ public class PageAccessSynchronizer implements 
Serializable
                 * Constructor
                 * 
                 * @param pageId
-                * @param thread
+                * @param cycle
                 */
-               public PageLock(int pageId, Thread thread)
+               public PageLock(int pageId, RequestCycle cycle)
                {
                        this.pageId = pageId;
-                       this.thread = thread;
+                       this.cycle = cycle;
                }
 
                /**
@@ -316,9 +317,9 @@ public class PageAccessSynchronizer implements Serializable
                /**
                 * @return thread that owns the lock
                 */
-               public Thread getThread()
+               public RequestCycle getCycle()
                {
-                       return thread;
+                       return cycle;
                }
 
                final synchronized void waitForRelease(long remaining, boolean 
isDebugEnabled)
@@ -331,7 +332,7 @@ public class PageAccessSynchronizer implements Serializable
                                {
                                        logger.debug(
                                                "lock for page with id {} no 
longer locked by {}, falling through", pageId,
-                                               thread.getName());
+                                               cycle.getStartTime());
                                }
                                return;
                        }
@@ -339,7 +340,7 @@ public class PageAccessSynchronizer implements Serializable
                        if (isDebugEnabled)
                        {
                                logger.debug("{} waiting for lock to page {} 
for {}",
-                                       thread.getName(), pageId, 
Duration.milliseconds(remaining));
+                                       cycle.getStartTime(), pageId, 
Duration.milliseconds(remaining));
                        }
                        try
                        {
@@ -355,7 +356,7 @@ public class PageAccessSynchronizer implements Serializable
                {
                        if (isDebugEnabled)
                        {
-                               logger.debug("'{}' notifying blocked threads", 
thread.getName());
+                               logger.debug("'{}' notifying blocked threads", 
cycle.getStartTime());
                        }
                        released = true;
                        notifyAll();

http://git-wip-us.apache.org/repos/asf/wicket/blob/64f746fb/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebRequest.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebRequest.java
 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebRequest.java
index c668cdf..91b6ddf 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebRequest.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/protocol/http/servlet/ServletWebRequest.java
@@ -181,7 +181,7 @@ public class ServletWebRequest extends WebRequest
                }
                StringBuilder url = new StringBuilder();
                uri = Strings.stripJSessionId(uri);
-               String contextPath = httpServletRequest.getContextPath();
+               String contextPath = getContextPath();
 
                if (LOG.isDebugEnabled())
                {

http://git-wip-us.apache.org/repos/asf/wicket/blob/64f746fb/wicket-core/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java
----------------------------------------------------------------------
diff --git 
a/wicket-core/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java 
b/wicket-core/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java
index 272fcde..ea513bb 100644
--- 
a/wicket-core/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java
+++ 
b/wicket-core/src/main/java/org/apache/wicket/request/cycle/RequestCycle.java
@@ -18,6 +18,11 @@ package org.apache.wicket.request.cycle;
 
 import java.util.Optional;
 
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.http.HttpServletRequest;
+
 import org.apache.wicket.Application;
 import org.apache.wicket.MetaDataEntry;
 import org.apache.wicket.MetaDataKey;
@@ -45,6 +50,7 @@ import org.apache.wicket.request.UrlRenderer;
 import org.apache.wicket.request.component.IRequestablePage;
 import 
org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
 import org.apache.wicket.request.handler.resource.ResourceRequestHandler;
+import org.apache.wicket.request.http.WebResponse;
 import org.apache.wicket.request.mapper.parameter.PageParameters;
 import org.apache.wicket.request.resource.IResource;
 import org.apache.wicket.request.resource.ResourceReference;
@@ -123,6 +129,8 @@ public class RequestCycle implements IRequestCycle, 
IEventSink
 
        private Response activeResponse;
 
+       private SuspensionImpl suspension;
+
        /**
         * Construct.
         * 
@@ -222,7 +230,9 @@ public class RequestCycle implements IRequestCycle, 
IEventSink
                }
                finally
                {
-                       detach();
+                       if (suspension == null) {
+                               detach();
+                       }
                }
                return result;
        }
@@ -283,7 +293,12 @@ public class RequestCycle implements IRequestCycle, 
IEventSink
                                IRequestHandler next = 
requestHandlerExecutor.execute(handler);
                                listeners.onRequestHandlerExecuted(this, 
handler);
                                
-                               handler = next;
+                               if (suspension == null) {
+                                       handler = next;
+                               } else {
+                                       handler = null;
+                                       suspension.handler = next;
+                               }
                        }
                        catch (RuntimeException e)
                        {
@@ -305,6 +320,22 @@ public class RequestCycle implements IRequestCycle, 
IEventSink
        }
 
        /**
+        * Suspend the request cycle to be resumed on another thread.
+        * 
+        * @param timeout {@literal 0} for no timeout
+        * @return suspension to be resumed
+        */
+       public Suspension suspend(long timeout) {
+               HttpServletRequest httpServletRequest = 
(HttpServletRequest)request.getContainerRequest();
+               
+               AsyncContext context = httpServletRequest.startAsync();
+               context.setTimeout(timeout);
+               suspension = new SuspensionImpl(context);
+                       
+               return suspension; 
+       }
+
+       /**
         * Execute a requestHandler for the given exception.
         * 
         * @param exception
@@ -925,4 +956,122 @@ public class RequestCycle implements IRequestCycle, 
IEventSink
 
        }
 
+       public interface Suspension {
+               void resume();
+       }
+
+       class SuspensionImpl implements Suspension, AsyncListener {
+
+               AsyncContext asyncContext;
+
+               Application application;
+
+               ClassLoader classLoader;
+
+               IRequestHandler handler;
+
+               SuspensionImpl(AsyncContext asyncContext)
+               {
+                       this.asyncContext = asyncContext;
+                       this.asyncContext.addListener(this);
+                       
+                       application = Application.get();
+                       classLoader = 
Thread.currentThread().getContextClassLoader();
+               }
+               
+               /**
+                * Resume the suspension.
+                */
+               public synchronized void resume()
+               {
+                       if (suspension == null) {
+                               throw new WicketRuntimeException("not longer 
suspended");
+                       }
+                       suspension = null;
+                       
+                       final ThreadContext previousThreadContext = 
ThreadContext.detach();
+                       ThreadContext.setApplication(application);
+
+                       final ClassLoader previousClassLoader = 
Thread.currentThread().getContextClassLoader();
+                       if (previousClassLoader != classLoader)
+                       {
+                               
Thread.currentThread().setContextClassLoader(classLoader);
+                       }
+
+                       try
+                       {
+                               set(RequestCycle.this);
+                               
+                               execute(handler);
+
+                               ((WebResponse)getResponse()).flush();
+
+                               detach();
+                       }
+                       finally
+                       {
+                               set(null);
+                               
+                               ThreadContext.restore(previousThreadContext);
+
+                               if (classLoader != previousClassLoader)
+                               {
+                                       
Thread.currentThread().setContextClassLoader(previousClassLoader);
+                               }
+                               
+                               asyncContext.complete();
+                       }
+               }
+
+               @Override
+               public void onComplete(AsyncEvent event)
+               {
+               }
+
+               @Override
+               public synchronized void onTimeout(AsyncEvent event)
+               {
+                       suspension = null;
+                       
+                       final ThreadContext previousThreadContext = 
ThreadContext.detach();
+                       ThreadContext.setApplication(application);
+
+                       final ClassLoader previousClassLoader = 
Thread.currentThread().getContextClassLoader();
+                       if (previousClassLoader != classLoader)
+                       {
+                               
Thread.currentThread().setContextClassLoader(classLoader);
+                       }
+
+                       try
+                       {
+                               set(RequestCycle.this);
+                               
+                               detach();
+                       }
+                       finally
+                       {
+                               set(null);
+                               
+                               ThreadContext.restore(previousThreadContext);
+
+                               if (classLoader != previousClassLoader)
+                               {
+                                       
Thread.currentThread().setContextClassLoader(previousClassLoader);
+                               }
+                               
+                               asyncContext.complete();
+                       }
+               }
+
+               @Override
+               public void onError(AsyncEvent event)
+               {
+               }
+
+
+               @Override
+               public void onStartAsync(AsyncEvent event)
+               {
+               }
+       }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/64f746fb/wicket-examples/src/main/java/org/apache/wicket/examples/compref/LinkPage.java
----------------------------------------------------------------------
diff --git 
a/wicket-examples/src/main/java/org/apache/wicket/examples/compref/LinkPage.java
 
b/wicket-examples/src/main/java/org/apache/wicket/examples/compref/LinkPage.java
index 403838a..b5b8a9b 100644
--- 
a/wicket-examples/src/main/java/org/apache/wicket/examples/compref/LinkPage.java
+++ 
b/wicket-examples/src/main/java/org/apache/wicket/examples/compref/LinkPage.java
@@ -21,6 +21,7 @@ import org.apache.wicket.lambda.Lambdas;
 import org.apache.wicket.markup.html.basic.Label;
 import org.apache.wicket.markup.html.link.Link;
 import org.apache.wicket.model.PropertyModel;
+import org.apache.wicket.request.cycle.RequestCycle.Suspension;
 
 /**
  * Page with examples on {@link org.apache.wicket.markup.html.link.Link}.
@@ -111,6 +112,20 @@ public class LinkPage extends WicketExamplePage
                        public void onClick()
                        {
                                count3.increment();
+                               
+                               Suspension suspension = 
getRequestCycle().suspend(0);
+                               
+                               new Thread(() -> {
+                                       try
+                                       {
+                                               Thread.sleep(5000);
+                                       }
+                                       catch (InterruptedException interrupted)
+                                       {
+                                       }
+                                       
+                                       suspension.resume();
+                               }).start();
                        }
                }
                add(new ButtonLink("link3"));

http://git-wip-us.apache.org/repos/asf/wicket/blob/64f746fb/wicket-examples/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/wicket-examples/src/main/webapp/WEB-INF/web.xml 
b/wicket-examples/src/main/webapp/WEB-INF/web.xml
index e23b1a1..b100851 100644
--- a/wicket-examples/src/main/webapp/WEB-INF/web.xml
+++ b/wicket-examples/src/main/webapp/WEB-INF/web.xml
@@ -251,6 +251,7 @@
        <filter>
                <filter-name>ComponentReferenceApplication</filter-name>
                
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
+               <async-supported>true</async-supported>
                <init-param>
                  <param-name>applicationClassName</param-name>
                  
<param-value>org.apache.wicket.examples.compref.ComponentReferenceApplication</param-value>
@@ -796,27 +797,6 @@
        </filter-mapping>
 
 
-    <!-- CDI EXAMPLE APPLICATION -->
-    <filter>
-        <filter-name>CdiApplication</filter-name>
-        
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
-        <init-param>
-            <param-name>applicationClassName</param-name>
-            
<param-value>org.apache.wicket.examples.cdi.CdiApplication</param-value>
-        </init-param>
-    </filter>
-    <filter-mapping>
-        <filter-name>CdiApplication</filter-name>
-        <url-pattern>/cdi/*</url-pattern>
-    </filter-mapping>
-
-    <listener>
-        <!-- initialize Weld in servlet environment -->
-        
<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
-    </listener>
-    <!-- END CDI EXAMPLE APPLICATION -->
-
-
     <!-- Bean Validation EXAMPLE APPLICATION -->
     <filter>
         <filter-name>BeanValidation</filter-name>

Reply via email to