You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by re...@apache.org on 2021/11/11 10:04:44 UTC

[wicket] branch master updated: 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)

This is an automated email from the ASF dual-hosted git repository.

reiern70 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git


The following commit(s) were added to refs/heads/master by this push:
     new f707376  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)
f707376 is described below

commit f7073760881dbac26e7c94a6f9a67d09bb599cf6
Author: ernestosemedy <ba...@semedy.com>
AuthorDate: Thu May 20 12:00:23 2021 +0300

    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)
---
 .../org/apache/wicket/page/PartialPageUpdate.java  |   8 +
 .../apache/wicket/examples/websocket/HomePage.html |   1 +
 .../examples/websocket/JSR356Application.java      |  12 +-
 .../wicket/examples/websocket/JSR356Session.java   |  66 ++++++++
 .../websocket/WebSocketBehaviorDemoPage.java       |   2 -
 .../WebSocketMultiTabResourceDemoPage.java         |   2 -
 .../WebSocketPushUpdateProgressDemoPage.html       |   8 +
 ...va => WebSocketPushUpdateProgressDemoPage.java} |  15 +-
 .../websocket/WebSocketResourceDemoPage.java       |   2 -
 .../ProgressBarTogglePanel.html}                   |  30 ++--
 .../websocket/progress/ProgressBarTogglePanel.java | 111 +++++++++++++
 .../websocket/progress/ProgressUpdater.java        | 177 +++++++++++++++++++++
 .../ws/api/AbstractWebSocketConnection.java        |   2 +-
 .../ws/api/AbstractWebSocketProcessor.java         |  33 ++--
 .../protocol/ws/api/BaseWebSocketBehavior.java     |  12 ++
 .../protocol/ws/api/WebSocketPushBroadcaster.java  |  68 ++++++++
 .../protocol/ws/api/WebSocketRequestHandler.java   |  17 +-
 .../api/registry/{IKey.java => AbstractKey.java}   |  21 ++-
 .../wicket/protocol/ws/api/registry/IKey.java      |   8 +-
 .../api/registry/IWebSocketConnectionRegistry.java |  26 ++-
 .../wicket/protocol/ws/api/registry/PageIdKey.java |  13 +-
 .../protocol/ws/api/registry/ResourceNameKey.java  |   8 +-
 .../ws/api/registry/ResourceNameTokenKey.java      |   9 +-
 .../SimpleWebSocketConnectionRegistry.java         |  26 +++
 .../ws/api/res/js/wicket-websocket-jquery.js       |   4 +
 .../ws/api/res/js/wicket-websocket-setup.js.tmpl   |   2 +-
 .../ws/util/tester/TestWebSocketProcessor.java     |  17 +-
 .../util/tester/WebSocketTesterBehaviorTest.java   |   2 +-
 .../ws/javax/JavaxWebSocketConnection.java         |   2 +-
 .../wicket/protocol/ws/javax/WicketEndpoint.java   |   1 -
 30 files changed, 633 insertions(+), 72 deletions(-)

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 d9c3ca3..2c8e282 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
@@ -150,6 +150,14 @@ public abstract class PartialPageUpdate
 	}
 
 	/**
+	 * @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.
 	 *
 	 * @param 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 3821f67..0207e52 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 @@
 					<li><a href="WebSocketBehaviorDemoPage.html">demo with WebSocketBehavior</a></li>
 					<li><a href="WebSocketResourceDemoPage.html">demo with WebSocketResource</a></li>
 					<li><a href="WebSocketMultiTabResourceDemoPage.html">demo with WebSocketResource and multiple tabs</a></li>
+					<li><a href="WebSocketPushUpdateProgressDemoPage.html">Update a component via server-side initiated notifications</a></li>
 				</ul>
 			</wicket:link>
 		</wicket:extend>
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 bb38205..e178f7c 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 class JSR356Application extends WicketExampleApplication
 		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 class JSR356Application extends WicketExampleApplication
 		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 0000000..71910a2
--- /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 8d038aa..c6bff98 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.ChartUpdater;
 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.request.resource.ResourceStreamResource;
 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 03ec5d6..534906c 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 @@ package org.apache.wicket.examples.websocket;
 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 0000000..55210d3
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.html
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<body>
+<wicket:extend>
+    <div wicket:id="progressPanel"></div>
+</wicket:extend>
+</body>
+</html>
\ No newline at end of file
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/WebSocketPushUpdateProgressDemoPage.java
similarity index 63%
copy from wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketResourceDemoPage.java
copy to wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.java
index d8aef4d..a61ed66 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/WebSocketPushUpdateProgressDemoPage.java
@@ -17,18 +17,13 @@
 package org.apache.wicket.examples.websocket;
 
 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 org.apache.wicket.examples.websocket.progress.ProgressBarTogglePanel;
+import org.apache.wicket.examples.websocket.progress.ProgressUpdater;
 
-@RequireHttps
-public class WebSocketResourceDemoPage extends WicketExamplePage
+public class WebSocketPushUpdateProgressDemoPage extends WicketExamplePage implements ProgressUpdater.ITaskProgressListener
 {
-	public WebSocketResourceDemoPage()
+	public WebSocketPushUpdateProgressDemoPage()
 	{
-		WebSocketChart chartPanel = new WebSocketChart("chartPanel");
-		chartPanel.add(new BaseWebSocketBehavior(ChartWebSocketResource.NAME));
-		add(chartPanel);
+		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 d8aef4d..079bff9 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 @@ package org.apache.wicket.examples.websocket;
 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/HomePage.html b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html
similarity index 56%
copy from wicket-examples/src/main/java/org/apache/wicket/examples/websocket/HomePage.html
copy to wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html
index 3821f67..971f7f2 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/progress/ProgressBarTogglePanel.html
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="UTF-8"?>
 <!--
    Licensed to the Apache Software Foundation (ASF) under one or more
    contributor license agreements.  See the NOTICE file distributed with
@@ -15,22 +15,12 @@
    See the License for the specific language governing permissions and
    limitations under the License.
 -->
-<!DOCTYPE html>
-<html xmlns:wicket="http://wicket.apache.org">
-	<head>
-		<meta charset="utf-8" />
-        <title>Apache Wicket Native WebSocket demos</title>
-	</head>
-	<body>
-		<wicket:extend>
-
-			<wicket:link>
-				<ul>
-					<li><a href="WebSocketBehaviorDemoPage.html">demo with WebSocketBehavior</a></li>
-					<li><a href="WebSocketResourceDemoPage.html">demo with WebSocketResource</a></li>
-					<li><a href="WebSocketMultiTabResourceDemoPage.html">demo with WebSocketResource and multiple tabs</a></li>
-				</ul>
-			</wicket:link>
-		</wicket:extend>
-	</body>
-</html>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
+<body>
+<wicket:panel>
+    <div><a wicket:id="hideShowProgress"></a></div>
+    <div><a wicket:id="cancelRestartTask"></a></div>
+    <div wicket:id="progressBar"></div>
+</wicket:panel>
+</body>
+</html>
\ 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 0000000..dc7078e
--- /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<Void>("hideShowProgress")
+        {
+            @Override
+            public void onClick(AjaxRequestTarget target)
+            {
+                showProgress = !showProgress;
+                target.add(ProgressBarTogglePanel.this);
+            }
+        }.setBody((IModel<String>) () -> showProgress ? "Hide progress" : "Show progress"));
+
+        add(new AjaxLink<Void>("cancelRestartTask")
+        {
+            @Override
+            public void onClick(AjaxRequestTarget target)
+            {
+                JSR356Session.get().startOrCancelTask();
+                target.add(ProgressBarTogglePanel.this);
+            }
+        }.setBody((IModel<String>) () -> {
+            ProgressUpdater.ProgressUpdateTask progressUpdateTask = JSR356Session.get().getProgressUpdateTask();
+            return progressUpdateTask != null && progressUpdateTask.isRunning() && !progressUpdateTask.isCanceled()
+                    ? "Cancel task" :
+                    "Restart task";
+        }));
+
+        add(new Label("progressBar", (IModel<String>) () -> {
+            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 0000000..48f9848
--- /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 8726d56..3cd0f62 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 abstract class AbstractWebSocketConnection implements IWebSocketConnectio
 	@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 19982fa..f2a3a93 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 abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		}
 		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 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 			}
 		}
 
-		broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key));
+		broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key), connection);
 	}
 
 	@Override
@@ -203,6 +206,13 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		}
 	}
 
+	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 abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 	 * @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 abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 
 				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 abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 				{
 					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 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		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 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		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 b773ef3..420393e 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
@@ -121,9 +121,21 @@ public class BaseWebSocketBehavior extends Behavior
 		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<String, Object> getParameters(Component component) {
 		Map<String, Object> 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 db653d2..c5f6d78 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 class WebSocketPushBroadcaster
 		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<IWebSocketConnection> 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<IWebSocketConnection> wsConnections = registry.getConnections(application, connectionsFilter);
+		if (wsConnections == null  || wsConnections.isEmpty())
+		{
+			return;
+		}
+		process(application, wsConnections, message);
+	}
+
 	private void process(final Application application, final Collection<IWebSocketConnection> 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 7df3735..7ce1f62 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 class WebSocketRequestHandler extends AbstractPartialPageRequestHandler i
 		}
 	}
 
-
+	/**
+	 * @return if <code>true</code> then EMPTY partial updates will se send. If <code>false</code> 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 class WebSocketRequestHandler extends AbstractPartialPageRequestHandler i
 	{
 		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/IKey.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/AbstractKey.java
similarity index 78%
copy from wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IKey.java
copy to wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/AbstractKey.java
index b0774dc..afe2eaf 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/AbstractKey.java
@@ -16,11 +16,18 @@
  */
 package org.apache.wicket.protocol.ws.api.registry;
 
-import org.apache.wicket.util.io.IClusterable;
+public class AbstractKey implements IKey
+{
 
-/**
- * A marker interface for keys that are used to find a web socket
- * connection in {@link IWebSocketConnectionRegistry}
- */
-public interface IKey extends IClusterable
-{}
+    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 b0774dc..25a5196 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 @@ import org.apache.wicket.util.io.IClusterable;
  * 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 2782a93..7665608 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
@@ -29,6 +29,18 @@ import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
 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
 	 * @param sessionId
@@ -44,12 +56,24 @@ 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<IWebSocketConnection> 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<IWebSocketConnection> getConnections(Application application, IConnectionsFilter connectionsFilter);
+
+
+	/**
 	 * @param application
 	 *            the web application to look in
 	 * @return collection of web socket connection used by any client connected to specified application
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 84eee23..2aa7de0 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 4f45aed..c5f4bc4 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 @@ import org.apache.wicket.util.lang.Args;
 /**
  * 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 be76c12..e4b890a 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 @@ import org.apache.wicket.util.lang.Args;
 /**
  * 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 b5005aa..1a17590 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 @@ package org.apache.wicket.protocol.ws.api.registry;
 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 class SimpleWebSocketConnectionRegistry implements IWebSocketConnectionRe
 		return connections;
 	}
 
+	@Override
+	public Collection<IWebSocketConnection> getConnections(Application application, IConnectionsFilter connectionsFilter)
+	{
+		Args.notNull(application, "application");
+		Args.notNull(connectionsFilter, "connectionsFilter");
+
+		Collection<IWebSocketConnection> connections = new ArrayList<>();
+		ConcurrentMap<String, ConcurrentMap<IKey, IWebSocketConnection>> connectionsBySession = application.getMetaData(KEY);
+		if (connectionsBySession != null)
+		{
+			for (Map.Entry<String, ConcurrentMap<IKey, IWebSocketConnection>> connectionsByPage : connectionsBySession.entrySet())
+			{
+				for (Map.Entry<IKey, IWebSocketConnection> 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 cd06ad5..93b7386 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 1533cbf..98e9bc8 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 cf88c30..6e70466 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
@@ -67,9 +67,20 @@ abstract class TestWebSocketProcessor extends AbstractWebSocketProcessor
 	 * @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.
+	 *
+	 * @param resourceName
+	 *      the name of the shared resource that will handle the web socket messages
+	 */
 	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 @@ abstract class TestWebSocketProcessor extends AbstractWebSocketProcessor
 		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 @@ abstract class TestWebSocketProcessor extends AbstractWebSocketProcessor
 	 *      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 29ec707..912cec5 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 @@ public class WebSocketTesterBehaviorTest
 			}
 		};
 		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 236c64c..b9f6419 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 98c64d0..b5b0b2b 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 @@ public class WicketEndpoint extends Endpoint
 	{
 		String appName = null;
 
-		@SuppressWarnings("unchecked")
 		Map<String, List<String>> parameters = session.getRequestParameterMap();
 		if (parameters != null)
 		{