Repository: wicket
Updated Branches:
  refs/heads/wicket-6.x e84792753 -> 482c595f5


WICKET-5701 WebSocketRequestHandler is not set as a scheduled and thus 
RequestCycle#find(AjaxRequestTarget.class) doesn't work


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

Branch: refs/heads/wicket-6.x
Commit: 482c595f5c1b2ee5032140ed0ede79d9f48af26f
Parents: e847927
Author: Martin Tzvetanov Grigorov <[email protected]>
Authored: Wed Sep 17 15:37:21 2014 +0300
Committer: Martin Tzvetanov Grigorov <[email protected]>
Committed: Wed Sep 17 15:37:21 2014 +0300

----------------------------------------------------------------------
 .../ws/api/AbstractWebSocketProcessor.java      | 104 +++++-----------
 .../api/WebSocketMessageBroadcastHandler.java   | 113 +++++++++++++++++
 .../protocol/ws/api/WebSocketRequestMapper.java |  74 ++++++++++++
 ...WebSocketTesterRequestCycleListenerTest.java | 120 +++++++++++++++++++
 4 files changed, 335 insertions(+), 76 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/482c595f/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
----------------------------------------------------------------------
diff --git 
a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
 
b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
index 34cc816..fea34a9 100644
--- 
a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
+++ 
b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
@@ -25,14 +25,12 @@ import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.Page;
 import org.apache.wicket.Session;
 import org.apache.wicket.ThreadContext;
-import org.apache.wicket.event.Broadcast;
 import org.apache.wicket.markup.IMarkupResourceStreamProvider;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.page.IPageManager;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.protocol.http.WicketFilter;
 import org.apache.wicket.protocol.ws.IWebSocketSettings;
-import org.apache.wicket.protocol.ws.WebSocketSettings;
 import org.apache.wicket.protocol.ws.api.event.WebSocketBinaryPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketClosedPayload;
 import org.apache.wicket.protocol.ws.api.event.WebSocketConnectedPayload;
@@ -49,17 +47,15 @@ import org.apache.wicket.protocol.ws.api.registry.IKey;
 import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
 import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
 import org.apache.wicket.protocol.ws.api.registry.ResourceNameKey;
+import org.apache.wicket.request.IRequestHandler;
 import org.apache.wicket.request.Url;
+import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
 import org.apache.wicket.request.cycle.RequestCycle;
 import org.apache.wicket.request.cycle.RequestCycleContext;
 import org.apache.wicket.request.http.WebRequest;
-import org.apache.wicket.request.resource.IResource;
-import org.apache.wicket.request.resource.ResourceReference;
-import org.apache.wicket.request.resource.SharedResourceReference;
 import org.apache.wicket.session.ISessionStore;
 import org.apache.wicket.util.lang.Args;
 import org.apache.wicket.util.lang.Checks;
-import org.apache.wicket.util.lang.Classes;
 import org.apache.wicket.util.resource.IResourceStream;
 import org.apache.wicket.util.resource.StringResourceStream;
 import org.apache.wicket.util.string.Strings;
@@ -79,7 +75,7 @@ public abstract class AbstractWebSocketProcessor implements 
IWebSocketProcessor
        /**
         * A pageId indicating that the endpoint is WebSocketResource
         */
-       private static final int NO_PAGE_ID = -1;
+       static final int NO_PAGE_ID = -1;
 
        private static final Method GET_FILTER_PATH_METHOD;
        static
@@ -220,20 +216,9 @@ public abstract class AbstractWebSocketProcessor 
implements IWebSocketProcessor
                        WebSocketResponse webResponse = new 
WebSocketResponse(connection);
                        try
                        {
-                               RequestCycle requestCycle;
-                               if (oldRequestCycle == null || message 
instanceof IWebSocketPushMessage)
-                               {
-                                       RequestCycleContext context = new 
RequestCycleContext(webRequest, webResponse,
-                                                       
application.getRootRequestMapper(), 
application.getExceptionMapperProvider().get());
-
-                                       requestCycle = 
application.getRequestCycleProvider().get(context);
-                                       
requestCycle.getUrlRenderer().setBaseUrl(baseUrl);
-                                       
ThreadContext.setRequestCycle(requestCycle);
-                               }
-                               else
-                               {
-                                       requestCycle = oldRequestCycle;
-                               }
+                               WebSocketRequestMapper requestMapper = new 
WebSocketRequestMapper(application.getRootRequestMapper());
+                               RequestCycle requestCycle = 
createRequestCycle(requestMapper, webResponse);
+                               ThreadContext.setRequestCycle(requestCycle);
 
                                ThreadContext.setApplication(application);
 
@@ -250,25 +235,20 @@ public abstract class AbstractWebSocketProcessor 
implements IWebSocketProcessor
                                }
 
                                IPageManager pageManager = 
session.getPageManager();
-                               try
-                               {
-                                       Page page = getPage(pageManager);
-
-                                       WebSocketRequestHandler requestHandler 
= new WebSocketRequestHandler(page, connection);
+                               Page page = getPage(pageManager);
 
-                                       WebSocketPayload payload = 
createEventPayload(message, requestHandler);
+                               WebSocketRequestHandler requestHandler = new 
WebSocketRequestHandler(page, connection);
 
-                                       sendPayload(payload, page);
+                               WebSocketPayload payload = 
createEventPayload(message, requestHandler);
 
-                                       if (!(message instanceof 
ConnectedMessage || message instanceof ClosedMessage))
-                                       {
-                                               
requestHandler.respond(requestCycle);
-                                       }
-                               }
-                               finally
+                               if (!(message instanceof ConnectedMessage || 
message instanceof ClosedMessage))
                                {
-                                       pageManager.commitRequest();
+                                       
requestCycle.scheduleRequestHandlerAfterCurrent(requestHandler);
                                }
+
+                               IRequestHandler broadcastingHandler = new 
WebSocketMessageBroadcastHandler(pageId, resourceName, payload);
+                               requestMapper.setHandler(broadcastingHandler);
+                               requestCycle.processRequestAndDetach();
                        }
                        catch (Exception x)
                        {
@@ -294,54 +274,26 @@ public abstract class AbstractWebSocketProcessor 
implements IWebSocketProcessor
                }
        }
 
-       /**
-        * Sends the payload either to the page (and its WebSocketBehavior)
-        * or to the WebSocketResource with name {@linkplain #resourceName}
-        *
-        * @param payload
-        *          The payload with the web socket message
-        * @param page
-        *          The page that owns the WebSocketBehavior, in case of 
behavior usage
-        */
-       private void sendPayload(final WebSocketPayload payload, final Page 
page)
+       private RequestCycle createRequestCycle(WebSocketRequestMapper 
requestMapper, WebSocketResponse webResponse)
        {
-               final Runnable action = new Runnable()
+               RequestCycleContext context = new 
RequestCycleContext(webRequest, webResponse,
+                               requestMapper, 
application.getExceptionMapperProvider().get());
+
+               RequestCycle requestCycle = 
application.getRequestCycleProvider().get(context);
+               
requestCycle.getListeners().add(application.getRequestCycleListeners());
+               requestCycle.getListeners().add(new 
AbstractRequestCycleListener()
                {
                        @Override
-                       public void run()
+                       public void onDetach(final RequestCycle requestCycle)
                        {
-                               if (pageId != NO_PAGE_ID)
-                               {
-                                       page.send(application, 
Broadcast.BREADTH, payload);
-                               } else
+                               if (Session.exists())
                                {
-                                       ResourceReference reference = new 
SharedResourceReference(resourceName);
-                                       IResource resource = 
reference.getResource();
-                                       if (resource instanceof 
WebSocketResource)
-                                       {
-                                               WebSocketResource wsResource = 
(WebSocketResource) resource;
-                                               wsResource.onPayload(payload);
-                                       } else
-                                       {
-                                               throw new IllegalStateException(
-                                                               
String.format("Shared resource with name '%s' is not a %s but %s",
-                                                                               
resourceName, WebSocketResource.class.getSimpleName(),
-                                                                               
Classes.name(resource.getClass())));
-                                       }
+                                       
Session.get().getPageManager().commitRequest();
                                }
                        }
-               };
-
-               IWebSocketSettings webSocketSettings = 
IWebSocketSettings.Holder.get(application);
-               if (webSocketSettings instanceof WebSocketSettings)
-               {
-                       WebSocketSettings wss = (WebSocketSettings) 
webSocketSettings;
-                       wss.getSendPayloadExecutor().run(action);
-               }
-               else
-               {
-                       action.run();
-               }
+               });
+               requestCycle.getUrlRenderer().setBaseUrl(baseUrl);
+               return requestCycle;
        }
 
        /**

http://git-wip-us.apache.org/repos/asf/wicket/blob/482c595f/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketMessageBroadcastHandler.java
----------------------------------------------------------------------
diff --git 
a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketMessageBroadcastHandler.java
 
b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketMessageBroadcastHandler.java
new file mode 100644
index 0000000..eb1b58e
--- /dev/null
+++ 
b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketMessageBroadcastHandler.java
@@ -0,0 +1,113 @@
+/*
+ * 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.protocol.ws.api;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.Page;
+import org.apache.wicket.Session;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.protocol.ws.IWebSocketSettings;
+import org.apache.wicket.protocol.ws.WebSocketSettings;
+import org.apache.wicket.protocol.ws.api.event.WebSocketPayload;
+import org.apache.wicket.request.IRequestCycle;
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.resource.IResource;
+import org.apache.wicket.request.resource.ResourceReference;
+import org.apache.wicket.request.resource.SharedResourceReference;
+import org.apache.wicket.util.lang.Args;
+import org.apache.wicket.util.lang.Classes;
+
+/**
+ * An {@link org.apache.wicket.request.IRequestHandler} that broadcasts the 
payload to the
+ * page/resource
+ */
+public class WebSocketMessageBroadcastHandler implements IRequestHandler
+{
+       private final int pageId;
+       private final String resourceName;
+       private final WebSocketPayload<?> payload;
+
+       /**
+        * Constructor.
+        *
+        * @param pageId
+        *          The id of the page if {@link 
org.apache.wicket.protocol.ws.api.WebSocketBehavior}
+        *          or {@value 
org.apache.wicket.protocol.ws.api.AbstractWebSocketProcessor#NO_PAGE_ID} if 
using a resource
+        * @param resourceName
+        *          The name of the shared {@link 
org.apache.wicket.protocol.ws.api.WebSocketResource}
+        * @param payload
+        *          The payload to broadcast
+        */
+       WebSocketMessageBroadcastHandler(int pageId, String resourceName, 
WebSocketPayload<?> payload)
+       {
+               this.pageId = pageId;
+               this.resourceName = resourceName;
+               this.payload = Args.notNull(payload, "payload");
+       }
+
+       @Override
+       public void respond(IRequestCycle requestCycle)
+       {
+               final Application application = Application.get();
+
+               final Runnable action = new Runnable()
+               {
+                       @Override
+                       public void run()
+                       {
+                               if (pageId != 
AbstractWebSocketProcessor.NO_PAGE_ID)
+                               {
+                                       Page page = (Page) 
Session.get().getPageManager().getPage(pageId);
+                                       page.send(application, 
Broadcast.BREADTH, payload);
+                               }
+                               else
+                               {
+                                       ResourceReference reference = new 
SharedResourceReference(resourceName);
+                                       IResource resource = 
reference.getResource();
+                                       if (resource instanceof 
WebSocketResource)
+                                       {
+                                               WebSocketResource wsResource = 
(WebSocketResource) resource;
+                                               wsResource.onPayload(payload);
+                                       }
+                                       else
+                                       {
+                                               throw new IllegalStateException(
+                                                               
String.format("Shared resource with name '%s' is not a %s but %s",
+                                                                               
resourceName, WebSocketResource.class.getSimpleName(),
+                                                                               
Classes.name(resource.getClass())));
+                                       }
+                               }
+                       }
+               };
+
+               IWebSocketSettings webSocketSettings = 
IWebSocketSettings.Holder.get(application);
+               if (webSocketSettings instanceof WebSocketSettings)
+               {
+                       WebSocketSettings wss = (WebSocketSettings) 
webSocketSettings;
+                       wss.getSendPayloadExecutor().run(action);
+               }
+               else
+               {
+                       action.run();
+               }
+       }
+
+       @Override
+       public void detach(IRequestCycle requestCycle)
+       {
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/482c595f/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestMapper.java
----------------------------------------------------------------------
diff --git 
a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestMapper.java
 
b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestMapper.java
new file mode 100644
index 0000000..9899b04
--- /dev/null
+++ 
b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestMapper.java
@@ -0,0 +1,74 @@
+/*
+ * 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.protocol.ws.api;
+
+import org.apache.wicket.request.IRequestHandler;
+import org.apache.wicket.request.IRequestMapper;
+import org.apache.wicket.request.Request;
+import org.apache.wicket.request.Url;
+
+/**
+ * An {@link org.apache.wicket.request.IRequestMapper} that is used to set a 
custom
+ * {@link org.apache.wicket.request.IRequestHandler} that broadcasts the
+ * {@link org.apache.wicket.protocol.ws.api.event.WebSocketPayload}
+ */
+class WebSocketRequestMapper implements IRequestMapper
+{
+       private final IRequestMapper delegate;
+
+       private IRequestHandler handler;
+
+       /**
+        * Constructor.
+        *
+        * @param delegate
+        *          The application root request mapper to delegate Url 
creation etc.
+        */
+       public WebSocketRequestMapper(IRequestMapper delegate)
+       {
+               this.delegate = delegate;
+       }
+
+       @Override
+       public IRequestHandler mapRequest(Request request)
+       {
+               return handler;
+       }
+
+       @Override
+       public int getCompatibilityScore(Request request)
+       {
+               return delegate.getCompatibilityScore(request);
+       }
+
+       @Override
+       public Url mapHandler(IRequestHandler requestHandler)
+       {
+               return delegate.mapHandler(requestHandler);
+       }
+
+       /**
+        * Sets the custom request handler
+        *
+        * @param handler
+        *          The request handler that broadcasts the web socket payload
+        */
+       public void setHandler(IRequestHandler handler)
+       {
+               this.handler = handler;
+       }
+}

http://git-wip-us.apache.org/repos/asf/wicket/blob/482c595f/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterRequestCycleListenerTest.java
----------------------------------------------------------------------
diff --git 
a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterRequestCycleListenerTest.java
 
b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterRequestCycleListenerTest.java
new file mode 100644
index 0000000..0b5b95d
--- /dev/null
+++ 
b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterRequestCycleListenerTest.java
@@ -0,0 +1,120 @@
+/*
+ * 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.protocol.ws.util.tester;
+
+import static org.hamcrest.CoreMatchers.is;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.util.string.Strings;
+import org.apache.wicket.util.tester.WicketTester;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for WebSocketTester.
+ * Uses WebSocketBehavior.
+ *
+ * @since 6.18.0
+ */
+public class WebSocketTesterRequestCycleListenerTest extends Assert
+{
+       private final AtomicBoolean beginRequestCalled = new 
AtomicBoolean(false);
+       private final AtomicBoolean endRequestCalled = new AtomicBoolean(false);
+       private final AtomicBoolean detachCalled = new AtomicBoolean(false);
+
+       private WicketTester tester;
+
+       @Before
+       public void before()
+       {
+               tester = new WicketTester();
+               tester.getApplication().getRequestCycleListeners().add(new 
AbstractRequestCycleListener()
+               {
+                       @Override
+                       public void onBeginRequest(RequestCycle cycle)
+                       {
+                               beginRequestCalled.set(true);
+                       }
+
+                       @Override
+                       public void onEndRequest(RequestCycle cycle)
+                       {
+                               endRequestCalled.set(true);
+                       }
+
+                       @Override
+                       public void onDetach(RequestCycle cycle)
+                       {
+                               detachCalled.set(true);
+                       }
+               });
+       }
+
+       @After
+       public void after()
+       {
+               tester.destroy();
+       }
+
+       /**
+        * A simple test that sends and receives a text message.
+        * The page asserts that it received the correct message and then
+        * pushed back the same message but capitalized.
+        */
+       @Test
+       public void verifyRequestCycleListeners()
+       {
+               final String expectedMessage = "some message";
+
+               WebSocketBehaviorTestPage page = new 
WebSocketBehaviorTestPage(expectedMessage);
+               tester.startPage(page);
+
+               // reset the variables after starting the page (no WebSocket 
related request)
+               beginRequestCalled.set(false);
+               endRequestCalled.set(false);
+               detachCalled.set(false);
+
+               // broadcasts WebSocket.ConnectedMessage and notifies the 
listeners
+               WebSocketTester webSocketTester = new WebSocketTester(tester, 
page) {
+                       @Override
+                       protected void onOutMessage(String message)
+                       {
+                               
assertEquals(Strings.capitalize(expectedMessage), message);
+                       }
+               };
+
+               // assert and reset
+               assertThat(beginRequestCalled.compareAndSet(true, false), 
is(true));
+               assertThat(endRequestCalled.compareAndSet(true, false), 
is(true));
+               assertThat(detachCalled.compareAndSet(true, false), is(true));
+
+               // broadcasts WebSocket.TextMessage and notifies the listeners
+               webSocketTester.sendMessage(expectedMessage);
+
+               assertThat(beginRequestCalled.get(), is(true));
+               assertThat(endRequestCalled.get(), is(true));
+               assertThat(detachCalled.get(), is(true));
+
+               webSocketTester.destroy();
+       }
+
+}

Reply via email to