From 04aaa7f3a29f96d35c7eaf76cd1c3cf126eb437a Mon Sep 17 00:00:00 2001 From: ernestosemedy Date: Thu, 20 May 2021 12:00:23 +0300 Subject: [PATCH] WICKET-6930: This commit adds: 1) allow to skip sending empty WS messages 2) pass page class as a kind of context that can be used to send a web socket to different pages + example of updating a component in a given page (not dependent on page ID) --- .../apache/wicket/page/PartialPageUpdate.java | 8 + .../wicket/examples/websocket/HomePage.html | 1 + .../examples/websocket/JSR356Application.java | 12 +- .../examples/websocket/JSR356Session.java | 66 +++++++ .../websocket/WebSocketBehaviorDemoPage.java | 2 - .../WebSocketMultiTabResourceDemoPage.java | 2 - .../WebSocketPushUpdateProgressDemoPage.html | 8 + .../WebSocketPushUpdateProgressDemoPage.java | 29 +++ .../websocket/WebSocketResourceDemoPage.java | 2 - .../progress/ProgressBarTogglePanel.html | 26 +++ .../progress/ProgressBarTogglePanel.java | 111 +++++++++++ .../websocket/progress/ProgressUpdater.java | 177 ++++++++++++++++++ .../ws/api/AbstractWebSocketConnection.java | 2 +- .../ws/api/AbstractWebSocketProcessor.java | 33 ++-- .../ws/api/BaseWebSocketBehavior.java | 12 ++ .../ws/api/WebSocketPushBroadcaster.java | 68 +++++++ .../ws/api/WebSocketRequestHandler.java | 17 +- .../protocol/ws/api/registry/AbstractKey.java | 33 ++++ .../wicket/protocol/ws/api/registry/IKey.java | 8 +- .../IWebSocketConnectionRegistry.java | 26 ++- .../protocol/ws/api/registry/PageIdKey.java | 13 +- .../ws/api/registry/ResourceNameKey.java | 8 +- .../ws/api/registry/ResourceNameTokenKey.java | 9 +- .../SimpleWebSocketConnectionRegistry.java | 26 +++ .../ws/api/res/js/wicket-websocket-jquery.js | 4 + .../api/res/js/wicket-websocket-setup.js.tmpl | 2 +- .../util/tester/TestWebSocketProcessor.java | 17 +- .../tester/WebSocketTesterBehaviorTest.java | 2 +- .../ws/javax/JavaxWebSocketConnection.java | 2 +- .../protocol/ws/javax/WicketEndpoint.java | 1 - 30 files changed, 692 insertions(+), 35 deletions(-) create mode 100644 wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Session.java create mode 100644 wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.html create mode 100644 wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.java create mode 100644 wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html create mode 100644 wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.java create mode 100644 wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java create mode 100644 wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/AbstractKey.java diff --git a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java index f95c9593008..f5cef0236ba 100644 --- a/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java +++ b/wicket-core/src/main/java/org/apache/wicket/page/PartialPageUpdate.java @@ -149,6 +149,14 @@ public PartialPageUpdate(final Page page) headerBuffer = new ResponseBuffer(response); } + /** + * @return returns true if and only if nothing has being added to partial update. + */ + public boolean isEmpty() + { + return prependJavaScripts.isEmpty() && appendJavaScripts.isEmpty() && domReadyJavaScripts.isEmpty() && markupIdToComponent.isEmpty(); + } + /** * Serializes this object to the response. * diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/HomePage.html b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/HomePage.html index 3821f6769d4..0207e5262fb 100644 --- a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/HomePage.html +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/HomePage.html @@ -29,6 +29,7 @@
  • demo with WebSocketBehavior
  • demo with WebSocketResource
  • demo with WebSocketResource and multiple tabs
  • +
  • Update a component via server-side initiated notifications
  • diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java index bb38205fc6c..e178f7c6847 100644 --- a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java @@ -16,12 +16,15 @@ */ package org.apache.wicket.examples.websocket; +import org.apache.wicket.Session; import org.apache.wicket.examples.WicketExampleApplication; import org.apache.wicket.examples.websocket.charts.ChartWebSocketResource; import org.apache.wicket.protocol.http.WebApplication; import org.apache.wicket.protocol.https.HttpsConfig; import org.apache.wicket.protocol.https.HttpsMapper; import org.apache.wicket.protocol.ws.WebSocketSettings; +import org.apache.wicket.request.Request; +import org.apache.wicket.request.Response; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -50,6 +53,7 @@ public void init() setRootRequestMapper(new HttpsMapper(getRootRequestMapper(), new HttpsConfig(8080, 8443))); mountPage("/behavior", WebSocketBehaviorDemoPage.class); + mountPage("/push", WebSocketPushUpdateProgressDemoPage.class); mountPage("/resource", WebSocketResourceDemoPage.class); mountPage("/resource-multi-tab", WebSocketMultiTabResourceDemoPage.class); @@ -69,7 +73,13 @@ public void init() getCspSettings().blocking().disabled(); } - @Override + @Override + public Session newSession(Request request, Response response) + { + return new JSR356Session(request); + } + + @Override protected void onDestroy() { scheduledExecutorService.shutdownNow(); diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Session.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Session.java new file mode 100644 index 00000000000..71910a260d4 --- /dev/null +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Session.java @@ -0,0 +1,66 @@ +/* + * 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.examples.websocket; + + +import java.util.concurrent.ScheduledExecutorService; + +import org.apache.wicket.examples.websocket.progress.ProgressUpdater; +import org.apache.wicket.protocol.http.WebSession; +import org.apache.wicket.request.Request; + +public class JSR356Session extends WebSession +{ + private ProgressUpdater.ProgressUpdateTask progressUpdateTask; + + public JSR356Session(Request request) + { + super(request); + } + + public ProgressUpdater.ProgressUpdateTask getProgressUpdateTask() + { + return progressUpdateTask; + } + + private synchronized void startTask() { + if (progressUpdateTask != null && progressUpdateTask.isRunning()) + { + return; + } + + JSR356Application application = JSR356Application.get(); + ScheduledExecutorService service = application.getScheduledExecutorService(); + progressUpdateTask = ProgressUpdater.start(application, getId(), service); + } + + public synchronized void startOrCancelTask() { + if (progressUpdateTask != null && progressUpdateTask.isRunning() && !progressUpdateTask.isCanceled()) + { + progressUpdateTask.cancel(); + } + else + { + startTask(); + } + } + + public static JSR356Session get() { + return (JSR356Session)WebSession.get(); + } +} diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketBehaviorDemoPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketBehaviorDemoPage.java index 8d038aa8919..c6bff98458a 100644 --- a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketBehaviorDemoPage.java +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketBehaviorDemoPage.java @@ -25,7 +25,6 @@ import org.apache.wicket.examples.websocket.charts.WebSocketChart; import org.apache.wicket.extensions.ajax.AjaxDownloadBehavior; import org.apache.wicket.markup.html.WebMarkupContainer; -import org.apache.wicket.protocol.https.RequireHttps; import org.apache.wicket.protocol.ws.api.WebSocketBehavior; import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler; import org.apache.wicket.protocol.ws.api.message.ConnectedMessage; @@ -36,7 +35,6 @@ import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.StringResourceStream; -@RequireHttps public class WebSocketBehaviorDemoPage extends WicketExamplePage { private static final long serialVersionUID = 1L; diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketMultiTabResourceDemoPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketMultiTabResourceDemoPage.java index 03ec5d6ced9..534906c0f96 100644 --- a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketMultiTabResourceDemoPage.java +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketMultiTabResourceDemoPage.java @@ -19,12 +19,10 @@ import org.apache.wicket.examples.WicketExamplePage; import org.apache.wicket.examples.websocket.charts.ChartWebSocketResource; import org.apache.wicket.examples.websocket.charts.WebSocketChart; -import org.apache.wicket.protocol.https.RequireHttps; import org.apache.wicket.protocol.ws.api.BaseWebSocketBehavior; import java.util.UUID; -@RequireHttps public class WebSocketMultiTabResourceDemoPage extends WicketExamplePage { public WebSocketMultiTabResourceDemoPage() diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.html b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.html new file mode 100644 index 00000000000..55210d31040 --- /dev/null +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.html @@ -0,0 +1,8 @@ + + + + +
    +
    + + \ No newline at end of file diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.java new file mode 100644 index 00000000000..a61ed6681c5 --- /dev/null +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.java @@ -0,0 +1,29 @@ +/* + * 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.examples.websocket; + +import org.apache.wicket.examples.WicketExamplePage; +import org.apache.wicket.examples.websocket.progress.ProgressBarTogglePanel; +import org.apache.wicket.examples.websocket.progress.ProgressUpdater; + +public class WebSocketPushUpdateProgressDemoPage extends WicketExamplePage implements ProgressUpdater.ITaskProgressListener +{ + public WebSocketPushUpdateProgressDemoPage() + { + add(new ProgressBarTogglePanel("progressPanel")); + } +} diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketResourceDemoPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketResourceDemoPage.java index d8aef4d600c..079bff9c968 100644 --- a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketResourceDemoPage.java +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketResourceDemoPage.java @@ -19,10 +19,8 @@ import org.apache.wicket.examples.WicketExamplePage; import org.apache.wicket.examples.websocket.charts.ChartWebSocketResource; import org.apache.wicket.examples.websocket.charts.WebSocketChart; -import org.apache.wicket.protocol.https.RequireHttps; import org.apache.wicket.protocol.ws.api.BaseWebSocketBehavior; -@RequireHttps public class WebSocketResourceDemoPage extends WicketExamplePage { public WebSocketResourceDemoPage() diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html new file mode 100644 index 00000000000..971f7f299e3 --- /dev/null +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html @@ -0,0 +1,26 @@ + + + + + +
    +
    +
    +
    + + \ No newline at end of file diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.java new file mode 100644 index 00000000000..dc7078e6772 --- /dev/null +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.java @@ -0,0 +1,111 @@ +/* + * 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.examples.websocket.progress; + +import java.util.concurrent.ScheduledExecutorService; + +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.markup.html.AjaxLink; +import org.apache.wicket.event.IEvent; +import org.apache.wicket.examples.websocket.JSR356Application; +import org.apache.wicket.examples.websocket.JSR356Session; +import org.apache.wicket.markup.html.basic.Label; +import org.apache.wicket.markup.html.panel.Panel; +import org.apache.wicket.model.IModel; +import org.apache.wicket.protocol.ws.api.WebSocketBehavior; +import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload; +import org.apache.wicket.protocol.ws.api.message.ConnectedMessage; + +public class ProgressBarTogglePanel extends Panel +{ + + private int progress = 0; + private boolean showProgress = true; + + + public ProgressBarTogglePanel(String id) + { + super(id); + + setOutputMarkupId(true); + + add(new WebSocketBehavior() + { + }); + + add(new AjaxLink("hideShowProgress") + { + @Override + public void onClick(AjaxRequestTarget target) + { + showProgress = !showProgress; + target.add(ProgressBarTogglePanel.this); + } + }.setBody((IModel) () -> showProgress ? "Hide progress" : "Show progress")); + + add(new AjaxLink("cancelRestartTask") + { + @Override + public void onClick(AjaxRequestTarget target) + { + JSR356Session.get().startOrCancelTask(); + target.add(ProgressBarTogglePanel.this); + } + }.setBody((IModel) () -> { + ProgressUpdater.ProgressUpdateTask progressUpdateTask = JSR356Session.get().getProgressUpdateTask(); + return progressUpdateTask != null && progressUpdateTask.isRunning() && !progressUpdateTask.isCanceled() + ? "Cancel task" : + "Restart task"; + })); + + add(new Label("progressBar", (IModel) () -> { + ProgressUpdater.ProgressUpdateTask progressUpdateTask = JSR356Session.get().getProgressUpdateTask(); + return progressUpdateTask != null && progressUpdateTask.isRunning() + ? "Background Task is " + progress + "% completed" + : "No task is running"; + }) + { + @Override + protected void onConfigure() + { + super.onConfigure(); + setVisible(showProgress); + } + }); + } + + + @Override + public void onEvent(IEvent event) + { + if (event.getPayload() instanceof WebSocketPushPayload) + { + WebSocketPushPayload wsEvent = (WebSocketPushPayload) event.getPayload(); + if (wsEvent.getMessage() instanceof ProgressUpdater.ProgressUpdate) + { + ProgressUpdater.ProgressUpdate progressUpdate = (ProgressUpdater.ProgressUpdate)wsEvent.getMessage(); + progress = progressUpdate.getProgress(); + wsEvent.getHandler().add(this); + } + else if (wsEvent.getMessage() instanceof ProgressUpdater.TaskCanceled) + { + // task was canceled + wsEvent.getHandler().add(this); + } + } + } +} diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java new file mode 100644 index 00000000000..48f98488692 --- /dev/null +++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java @@ -0,0 +1,177 @@ +/* + * 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.examples.websocket.progress; + +import java.io.Serializable; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.wicket.Application; +import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.application.IClassResolver; +import org.apache.wicket.page.IManageablePage; +import org.apache.wicket.protocol.ws.WebSocketSettings; +import org.apache.wicket.protocol.ws.api.IWebSocketConnection; +import org.apache.wicket.protocol.ws.api.WebSocketPushBroadcaster; +import org.apache.wicket.protocol.ws.api.message.ConnectedMessage; +import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A helper class that uses the web connection to push components updates to the client. + */ +public class ProgressUpdater +{ + /** + * Marks a page as a listener to task progress. + */ + public interface ITaskProgressListener { + + } + + private static final Logger LOGGER = LoggerFactory.getLogger(ProgressUpdater.class); + + public static ProgressUpdateTask start(Application application, String session, ScheduledExecutorService scheduledExecutorService) + { + // create an asynchronous task that will write the data to the client + ProgressUpdateTask progressUpdateTask = new ProgressUpdateTask(application, session); + scheduledExecutorService.schedule(progressUpdateTask, 1, TimeUnit.SECONDS); + return progressUpdateTask; + } + + /** + * Signal task was canceled. + */ + public static class TaskCanceled implements IWebSocketPushMessage + { + } + + /** + * A push message used to update progress. + */ + public static class ProgressUpdate implements IWebSocketPushMessage + { + + private final int progress; + + public ProgressUpdate(int progress) + { + this.progress = progress; + } + + public int getProgress() + { + return progress; + } + } + + /** + * A task that sends data to the client by pushing it to the web socket connection + */ + public static class ProgressUpdateTask implements Runnable, Serializable + { + /** + * The following fields are needed to be able to lookup the IWebSocketConnection from + * IWebSocketConnectionRegistry + */ + private final String applicationName; + private final String sessionId; + + private volatile boolean canceled = false; + private volatile boolean running = false; + + private ProgressUpdateTask(Application application, String sessionId) + { + this.applicationName = application.getName(); + this.sessionId = sessionId; + } + + @Override + public void run() + { + running = true; + Application application = Application.get(applicationName); + WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(application); + + int progress = 0; + + while (progress <= 100) + { + try + { + WebSocketPushBroadcaster broadcaster = + new WebSocketPushBroadcaster(webSocketSettings.getConnectionRegistry()); + + if (canceled) + { + canceled = false; + running = false; + broadcaster.broadcastAllMatchingFilter(application, (sessionId, key) -> + ProgressUpdateTask.this.sessionId.equals(sessionId) && key instanceof PageIdKey + && ITaskProgressListener.class.isAssignableFrom(getPageClass(application, key)), + new TaskCanceled()); + return; + } + broadcaster.broadcastAllMatchingFilter(application, (sessionId, key) -> + ProgressUpdateTask.this.sessionId.equals(sessionId) && key instanceof PageIdKey && + ITaskProgressListener.class.isAssignableFrom(getPageClass(application, key)), + new ProgressUpdate(progress)); + + // sleep for a while to simulate work + TimeUnit.SECONDS.sleep(1); + progress++; + } + catch (InterruptedException x) + { + Thread.currentThread().interrupt(); + break; + } + catch (Exception e) + { + LOGGER.error("unexpected exception", e); + break; + } + } + running = false; + } + + protected Class getPageClass(Application application, IKey iKey) { + try { + return application.getApplicationSettings().getClassResolver().resolveClass(iKey.getContext()); + } catch (ClassNotFoundException e) { + throw new WicketRuntimeException(e); + } + } + + public boolean isRunning() { + return running; + } + + public boolean isCanceled() { + return canceled; + } + + public void cancel() { + this.canceled = true; + this.running = false; + } + } +} diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketConnection.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketConnection.java index 8726d56a335..3cd0f627d43 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketConnection.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketConnection.java @@ -50,7 +50,7 @@ public AbstractWebSocketConnection(AbstractWebSocketProcessor webSocketProcessor @Override public void sendMessage(IWebSocketPushMessage message) { - webSocketProcessor.broadcastMessage(message); + webSocketProcessor.broadcastMessage(message, this); } @Override 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 f891751635b..bf5a6a812d4 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 @@ -81,9 +81,11 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor * A pageId indicating that the endpoint is WebSocketResource */ static final int NO_PAGE_ID = -1; + static final String NO_PAGE_CLASS = "_NO_PAGE"; private final WebRequest webRequest; private final int pageId; + private final String context; private final String resourceName; private final String connectionToken; private final Url baseUrl; @@ -112,6 +114,7 @@ public AbstractWebSocketProcessor(final HttpServletRequest request, final WebApp } this.sessionId = httpSession.getId(); String pageId = request.getParameter("pageId"); + this.context = request.getParameter("context"); this.resourceName = request.getParameter("resourceName"); this.connectionToken = request.getParameter("connectionToken"); if (Strings.isEmpty(pageId) && Strings.isEmpty(resourceName)) @@ -181,7 +184,7 @@ protected final void onConnect(final IWebSocketConnection connection) { } } - broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key)); + broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key), connection); } @Override @@ -203,6 +206,13 @@ public void onError(Throwable t) } } + public final void broadcastMessage(final IWebSocketMessage message) + { + IKey key = getRegistryKey(); + IWebSocketConnection connection = connectionRegistry.getConnection(application, sessionId, key); + broadcastMessage(message, connection); + } + /** * Exports the Wicket thread locals and broadcasts the received message from the client to all * interested components and behaviors in the page with id {@code #pageId} @@ -215,11 +225,8 @@ public void onError(Throwable t) * @param message * the message to broadcast */ - public final void broadcastMessage(final IWebSocketMessage message) + public final void broadcastMessage(final IWebSocketMessage message, IWebSocketConnection connection) { - IKey key = getRegistryKey(); - IWebSocketConnection connection = connectionRegistry.getConnection(application, sessionId, key); - if (connection != null && (connection.isOpen() || isSpecialMessage(message))) { Application oldApplication = ThreadContext.getApplication(); @@ -249,8 +256,8 @@ public final void broadcastMessage(final IWebSocketMessage message) if (session == null) { - connectionRegistry.removeConnection(application, sessionId, key); - LOG.debug("No Session could be found for session id '{}' and key '{}'!", sessionId, key); + connectionRegistry.removeConnection(application, sessionId, connection.getKey()); + LOG.debug("No Session could be found for session id '{}' and key '{}'!", sessionId, connection.getKey()); return; } @@ -261,7 +268,7 @@ public final void broadcastMessage(final IWebSocketMessage message) { WebSocketRequestHandler requestHandler = webSocketSettings.newWebSocketRequestHandler(page, connection); - WebSocketPayload payload = createEventPayload(message, requestHandler); + WebSocketPayload payload = createEventPayload(message, requestHandler); if (!(message instanceof ConnectedMessage || isSpecialMessage(message))) { requestCycle.scheduleRequestHandlerAfterCurrent(requestHandler); @@ -356,9 +363,9 @@ protected final String getSessionId() return sessionId; } - private WebSocketPayload createEventPayload(IWebSocketMessage message, WebSocketRequestHandler handler) + private WebSocketPayload createEventPayload(IWebSocketMessage message, WebSocketRequestHandler handler) { - final WebSocketPayload payload; + final WebSocketPayload payload; if (message instanceof TextMessage) { payload = new WebSocketTextPayload((TextMessage) message, handler); @@ -399,15 +406,15 @@ protected IKey getRegistryKey() IKey key; if (Strings.isEmpty(resourceName)) { - key = new PageIdKey(pageId); + key = new PageIdKey(pageId, context); } else { if (Strings.isEmpty(connectionToken)) { - key = new ResourceNameKey(resourceName); + key = new ResourceNameKey(resourceName, context); } else { - key = new ResourceNameTokenKey(resourceName, connectionToken); + key = new ResourceNameTokenKey(resourceName, connectionToken, context); } } return key; diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/BaseWebSocketBehavior.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/BaseWebSocketBehavior.java index c0a1f18dd75..6bf3b4af4a0 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/BaseWebSocketBehavior.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/BaseWebSocketBehavior.java @@ -120,9 +120,21 @@ protected String getWebSocketSetupScript(Map parameters) { return webSocketSetupTemplate.asString(parameters); } + /** + * Override to return a context. By default, this is the page class name. + * + * @param component the {@link org.apache.wicket.Component} + * @return the context for this websocket behavior. + */ + protected String getContext(Component component) { + return component.getPage().getClass().getName(); + } + private Map getParameters(Component component) { Map variables = Generics.newHashMap(); + variables.put("context", getContext(component)); + // set falsy JS values for the non-used parameter if (Strings.isEmpty(resourceName)) { diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketPushBroadcaster.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketPushBroadcaster.java index db653d20e7b..c5f6d78e029 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketPushBroadcaster.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketPushBroadcaster.java @@ -109,6 +109,74 @@ public void broadcastAll(Application application, IWebSocketPushMessage message) process(application, wsConnections, message); } + /** + * Processes the given message in all pages in a given session that have active Web Socket connections. + * The message is sent as an event to the Page and components of the session allowing the components + * to be updated. + * + * This method can be invoked from any thread, even a non-wicket thread. By default all processing + * is done in the caller thread. Use + * {@link WebSocketSettings#setWebSocketPushMessageExecutor(org.apache.wicket.protocol.ws.concurrent.Executor)} + * to move processing to background threads. + * + * If some connections are not in valid state they are silently ignored. + * + * @param application + * The wicket application + * + * @param sessionId + * The session ID + * @param message + * The push message event + */ + public void broadcastAllInSession(Application application, String sessionId, IWebSocketPushMessage message) + { + Args.notNull(application, "application"); + Args.notNull(message, "message"); + Args.notNull(sessionId, "sessionId"); + + Collection wsConnections = registry.getConnections(application, sessionId); + if (wsConnections == null || wsConnections.isEmpty()) + { + return; + } + process(application, wsConnections, message); + } + + /** + * Processes the given message in all pages in a given session that have active Web Socket connections and match + * the given filter. The message is sent as an event to the Page and components of the session allowing the components + * to be updated. + * + * This method can be invoked from any thread, even a non-wicket thread. By default all processing + * is done in the caller thread. Use + * {@link WebSocketSettings#setWebSocketPushMessageExecutor(org.apache.wicket.protocol.ws.concurrent.Executor)} + * to move processing to background threads. + * + * If some connections are not in valid state they are silently ignored. + * + * @param application + * The wicket application + * + * @param connectionsFilter + * the {@link org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry.IConnectionsFilter} + * @param message + * The push message event + */ + public void broadcastAllMatchingFilter(Application application, IWebSocketConnectionRegistry.IConnectionsFilter connectionsFilter, IWebSocketPushMessage message) + { + Args.notNull(application, "application"); + Args.notNull(message, "message"); + Args.notNull(connectionsFilter, "connectionsFilter"); + + Collection wsConnections = registry.getConnections(application, connectionsFilter); + if (wsConnections == null || wsConnections.isEmpty()) + { + return; + } + process(application, wsConnections, message); + } + private void process(final Application application, final Collection wsConnections, final IWebSocketPushMessage message) { diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java index 7df3735033f..7ce1f620b5f 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java @@ -97,7 +97,17 @@ public void push(byte[] message, int offset, int length) } } - + /** + * @return if true then EMPTY partial updates will se send. If false then EMPTY + * partial updates will be skipped. A possible use case is: a page receives and a push event but no one is + * listening to it, and nothing is added to {@link org.apache.wicket.protocol.ws.api.WebSocketRequestHandler} + * thus no real push to client is needed. For compatibilities this is set to true. Thus EMPTY updates are sent + * by default. + */ + protected boolean shouldPushWhenEmpty() + { + return true; + } protected PartialPageUpdate getUpdate() { if (update == null) { @@ -129,7 +139,10 @@ public void respond(IRequestCycle requestCycle) { if (update != null) { - update.writeTo(requestCycle.getResponse(), "UTF-8"); + if (shouldPushWhenEmpty() || !update.isEmpty()) + { + update.writeTo(requestCycle.getResponse(), "UTF-8"); + } } } diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/AbstractKey.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/AbstractKey.java new file mode 100644 index 00000000000..afe2eaf1345 --- /dev/null +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/AbstractKey.java @@ -0,0 +1,33 @@ +/* + * 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.registry; + +public class AbstractKey implements IKey +{ + + private final String context; + + public AbstractKey(String context) + { + this.context = context; + } + + @Override + public String getContext() { + return context; + } +} diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IKey.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IKey.java index b0774dca0ad..25a5196dab7 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IKey.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IKey.java @@ -23,4 +23,10 @@ * connection in {@link IWebSocketConnectionRegistry} */ public interface IKey extends IClusterable -{} +{ + /** + * @return return a context for the key. This could be, for instance, a page class name or a resource class name. + * I.e. something that allow to discriminate keys along different pages. + */ + String getContext(); +} diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IWebSocketConnectionRegistry.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IWebSocketConnectionRegistry.java index 2782a93b01c..76656082467 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IWebSocketConnectionRegistry.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IWebSocketConnectionRegistry.java @@ -28,6 +28,18 @@ */ public interface IWebSocketConnectionRegistry { + /** + * Interface allowing to filter web-sockets connections. This could be used for use cases like the + * following: you need to deliver messages to all page instances satisfying certain conditions (e.g. + * they contain some progress reporting component). + */ + interface IConnectionsFilter + { + + boolean accept(String sessionId, IKey key); + + } + /** * @param application * the web application to look in @@ -44,11 +56,23 @@ public interface IWebSocketConnectionRegistry * the web application to look in * @param sessionId * the http session id - * @return collection of web socket connection used by a client with the given session id + * @return collection of web socket connections used by a client with the given session id */ Collection getConnections(Application application, String sessionId); + /** + * + * @param application + * the web application to look in + * @param connectionsFilter + * the {@link org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry.IConnectionsFilter} + * + * @return collection of web socket connections that match certain filter + */ + Collection getConnections(Application application, IConnectionsFilter connectionsFilter); + + /** * @param application * the web application to look in diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/PageIdKey.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/PageIdKey.java index 84eee23ce4f..2aa7de0b7d6 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/PageIdKey.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/PageIdKey.java @@ -16,20 +16,29 @@ */ package org.apache.wicket.protocol.ws.api.registry; +import org.apache.wicket.Session; +import org.apache.wicket.WicketRuntimeException; +import org.apache.wicket.page.IManageablePage; import org.apache.wicket.util.lang.Args; /** * A key based on page's id */ -public class PageIdKey implements IKey +public class PageIdKey extends AbstractKey { private final Integer pageId; public PageIdKey(Integer pageId) { - this.pageId = Args.notNull(pageId, "pageId"); + this(pageId, null); } + public PageIdKey(Integer pageId, String context) + { + super(context); + this.pageId = Args.notNull(pageId, "pageId"); + } + @Override public boolean equals(Object o) { diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/ResourceNameKey.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/ResourceNameKey.java index 4f45aedc3ee..c5f4bc42c93 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/ResourceNameKey.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/ResourceNameKey.java @@ -21,12 +21,18 @@ /** * A key based on shared resource's name */ -public class ResourceNameKey implements IKey +public class ResourceNameKey extends AbstractKey { private final String resourceName; public ResourceNameKey(String resourceName) { + this(resourceName, null); + } + + public ResourceNameKey(String resourceName, String context) + { + super(context); this.resourceName = Args.notNull(resourceName, "resourceName"); } diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/ResourceNameTokenKey.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/ResourceNameTokenKey.java index be76c123157..e4b890a2bea 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/ResourceNameTokenKey.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/ResourceNameTokenKey.java @@ -23,13 +23,20 @@ /** * A key based on shared resource's name and a token */ -public class ResourceNameTokenKey implements IKey +public class ResourceNameTokenKey extends AbstractKey { private final String resourceName; private final String connectionToken; + public ResourceNameTokenKey(String resourceName, String connectionToken) { + this(resourceName, connectionToken, null); + } + + public ResourceNameTokenKey(String resourceName, String connectionToken, String context) + { + super(context); this.resourceName = Args.notNull(resourceName, "resourceName"); this.connectionToken = Args.notNull(connectionToken, "connectionToken"); } diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/SimpleWebSocketConnectionRegistry.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/SimpleWebSocketConnectionRegistry.java index b5005aae81a..1a175905464 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/SimpleWebSocketConnectionRegistry.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/SimpleWebSocketConnectionRegistry.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Map; import java.util.concurrent.ConcurrentMap; import org.apache.wicket.Application; @@ -79,6 +80,31 @@ public Collection getConnections(Application application, return connections; } + @Override + public Collection getConnections(Application application, IConnectionsFilter connectionsFilter) + { + Args.notNull(application, "application"); + Args.notNull(connectionsFilter, "connectionsFilter"); + + Collection connections = new ArrayList<>(); + ConcurrentMap> connectionsBySession = application.getMetaData(KEY); + if (connectionsBySession != null) + { + for (Map.Entry> connectionsByPage : connectionsBySession.entrySet()) + { + for (Map.Entry connectionEntry: connectionsByPage.getValue().entrySet()) + { + if (connectionsFilter.accept(connectionsByPage.getKey(), connectionEntry.getKey())) + { + connections.add(connectionEntry.getValue()); + } + } + + } + } + return connections; + } + /** * Returns a collection of currently active websockets. The connections might close at any time. * diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/res/js/wicket-websocket-jquery.js b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/res/js/wicket-websocket-jquery.js index cd06ad5ee74..93b73867943 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/res/js/wicket-websocket-jquery.js +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/res/js/wicket-websocket-jquery.js @@ -81,6 +81,10 @@ } } + if (WWS.context) { + url += '&context=' + encodeURIComponent(WWS.context); + } + url += '&wicket-ajax-baseurl=' + encodeURIComponent(WWS.baseUrl); url += '&wicket-app-name=' + encodeURIComponent(WWS.appName); diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/res/js/wicket-websocket-setup.js.tmpl b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/res/js/wicket-websocket-setup.js.tmpl index 1533cbf24d8..98e9bc8ebcc 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/res/js/wicket-websocket-setup.js.tmpl +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/res/js/wicket-websocket-setup.js.tmpl @@ -2,7 +2,7 @@ 'use strict'; if (typeof(Wicket.WebSocket.appName) === "undefined") { - jQuery.extend(Wicket.WebSocket, { pageId: ${pageId}, resourceName: '${resourceName}', connectionToken: '${connectionToken}', + jQuery.extend(Wicket.WebSocket, { pageId: ${pageId}, context: '${context}', resourceName: '${resourceName}', connectionToken: '${connectionToken}', baseUrl: '${baseUrl}', contextPath: '${contextPath}', appName: '${applicationName}', port: ${port}, securePort: ${securePort}, filterPrefix: '${filterPrefix}', sessionId: '${sessionId}' }); Wicket.WebSocket.createDefaultConnection(); diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java index e48b9084ffd..cc121e6b7a8 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java @@ -61,6 +61,17 @@ public TestWebSocketProcessor(final WicketTester wicketTester, final Page page) super(createRequest(wicketTester, page), (WebApplication) page.getApplication()); } + /** + * Constructor. + * + * @param resourceName + * the name of the shared resource that will handle the web socket messages + */ + public TestWebSocketProcessor(final WicketTester wicketTester, final String resourceName, Page page) + { + super(createRequest(wicketTester, resourceName, page), wicketTester.getApplication()); + } + /** * Constructor. * @@ -69,7 +80,7 @@ public TestWebSocketProcessor(final WicketTester wicketTester, final Page page) */ public TestWebSocketProcessor(final WicketTester wicketTester, final String resourceName) { - super(createRequest(wicketTester, resourceName), wicketTester.getApplication()); + super(createRequest(wicketTester, resourceName, null), wicketTester.getApplication()); } /** @@ -84,6 +95,7 @@ private static HttpServletRequest createRequest(final WicketTester wicketTester, Args.notNull(page, "page"); MockHttpServletRequest request = createRequest(wicketTester); request.addParameter("pageId", page.getId()); + request.addParameter("context", page.getClass().getName()); return request; } @@ -94,11 +106,12 @@ private static HttpServletRequest createRequest(final WicketTester wicketTester, * the page that may have registered {@link org.apache.wicket.protocol.ws.api.WebSocketBehavior} * @return a mock http request */ - private static HttpServletRequest createRequest(final WicketTester wicketTester, final String resourceName) + private static HttpServletRequest createRequest(final WicketTester wicketTester, final String resourceName, final Page page) { Args.notNull(resourceName, "resourceName"); MockHttpServletRequest request = createRequest(wicketTester); request.addParameter("resourceName", resourceName); + request.addParameter("context", page != null ? page.getClass().getName() : ""); return request; } diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterBehaviorTest.java b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterBehaviorTest.java index 29ec707d6ff..912cec5ff3c 100644 --- a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterBehaviorTest.java +++ b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterBehaviorTest.java @@ -131,7 +131,7 @@ protected void onOutMessage(String message) } }; webSocketTester.broadcast(tester.getApplication(), tester.getHttpSession().getId(), - new PageIdKey(page.getPageId()), broadcastMessage); + new PageIdKey(page.getPageId(), page.getClass().getName()), broadcastMessage); assertTrue(messageReceived.get()); webSocketTester.destroy(); diff --git a/wicket-native-websocket/wicket-native-websocket-javax/src/main/java/org/apache/wicket/protocol/ws/javax/JavaxWebSocketConnection.java b/wicket-native-websocket/wicket-native-websocket-javax/src/main/java/org/apache/wicket/protocol/ws/javax/JavaxWebSocketConnection.java index 41fa3044b4c..f7e4ca440b4 100644 --- a/wicket-native-websocket/wicket-native-websocket-javax/src/main/java/org/apache/wicket/protocol/ws/javax/JavaxWebSocketConnection.java +++ b/wicket-native-websocket/wicket-native-websocket-javax/src/main/java/org/apache/wicket/protocol/ws/javax/JavaxWebSocketConnection.java @@ -49,7 +49,7 @@ public class JavaxWebSocketConnection extends AbstractWebSocketConnection public JavaxWebSocketConnection(Session session, AbstractWebSocketProcessor webSocketProcessor) { super(webSocketProcessor); - this.session = Args.notNull(session, "connection"); + this.session = Args.notNull(session, "session"); } @Override diff --git a/wicket-native-websocket/wicket-native-websocket-javax/src/main/java/org/apache/wicket/protocol/ws/javax/WicketEndpoint.java b/wicket-native-websocket/wicket-native-websocket-javax/src/main/java/org/apache/wicket/protocol/ws/javax/WicketEndpoint.java index 0636831f9da..4808dbd1304 100644 --- a/wicket-native-websocket/wicket-native-websocket-javax/src/main/java/org/apache/wicket/protocol/ws/javax/WicketEndpoint.java +++ b/wicket-native-websocket/wicket-native-websocket-javax/src/main/java/org/apache/wicket/protocol/ws/javax/WicketEndpoint.java @@ -123,7 +123,6 @@ private String getApplicationName(Session session) { String appName = null; - @SuppressWarnings("unchecked") Map> parameters = session.getRequestParameterMap(); if (parameters != null) {