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/07 08:52:11 UTC

[wicket] branch WICKET-6930-websocket-improvements updated (c48a23a -> 45be187)

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

reiern70 pushed a change to branch WICKET-6930-websocket-improvements
in repository https://gitbox.apache.org/repos/asf/wicket.git.


 discard c48a23a  WICKET-6930: pass page class as a kind of context
 discard 7a4e4b2  introduce isEmpty() for partial update
     new 45be187  WICKET-6930: 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 (nort dependent on page ID)

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (c48a23a)
            \
             N -- N -- N   refs/heads/WICKET-6930-websocket-improvements (45be187)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../wicket/examples/websocket/JSR356Session.java   |  7 +++++++
 .../websocket/progress/ProgressBarTogglePanel.java |  5 +++++
 .../websocket/progress/ProgressUpdater.java        | 24 ++++++++++++++--------
 3 files changed, 27 insertions(+), 9 deletions(-)

[wicket] 01/01: WICKET-6930: 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 (nort dependent on page ID)

Posted by re...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

reiern70 pushed a commit to branch WICKET-6930-websocket-improvements
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit 45be1873d49b039a6f7d322767a4d2b1fd51b0ff
Author: ernestosemedy <ba...@semedy.com>
AuthorDate: Sat Nov 6 10:41:57 2021 +0300

    WICKET-6930: 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 (nort dependent on page ID)
---
 .../org/apache/wicket/page/PartialPageUpdate.java  |  8 +++
 .../examples/websocket/JSR356Application.java      | 11 +++-
 .../wicket/examples/websocket/JSR356Session.java   | 58 +++++++++++++++++++
 .../WebSocketPushUpdateProgressDemoPage.java       |  3 +-
 .../websocket/progress/ProgressBarTogglePanel.java | 41 ++++++--------
 .../websocket/progress/ProgressUpdater.java        | 42 +++++++-------
 .../ws/api/AbstractWebSocketConnection.java        |  2 +-
 .../ws/api/AbstractWebSocketProcessor.java         | 31 ++++++----
 .../protocol/ws/api/BaseWebSocketBehavior.java     |  2 +
 .../protocol/ws/api/WebSocketPushBroadcaster.java  | 66 ++++++++++++++++++++++
 .../protocol/ws/api/WebSocketRequestHandler.java   | 14 ++++-
 .../api/registry/IWebSocketConnectionRegistry.java | 25 +++++++-
 .../wicket/protocol/ws/api/registry/PageIdKey.java | 24 ++++++++
 .../SimpleWebSocketConnectionRegistry.java         | 26 +++++++++
 .../ws/api/res/js/wicket-websocket-jquery.js       |  1 +
 .../ws/api/res/js/wicket-websocket-setup.js.tmpl   |  2 +-
 .../ws/util/tester/TestWebSocketProcessor.java     |  1 +
 .../util/tester/WebSocketTesterBehaviorTest.java   |  2 +-
 .../ws/javax/JavaxWebSocketConnection.java         |  2 +-
 .../wicket/protocol/ws/javax/WicketEndpoint.java   |  3 +-
 20 files changed, 300 insertions(+), 64 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 f95c959..f5cef02 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/JSR356Application.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java
index 98a9779..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;
@@ -70,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..075140b
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Session.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+    }
+
+    public synchronized void startTask() {
+        if (progressUpdateTask != null && progressUpdateTask.isRunning()) {
+            return;
+        }
+        ScheduledExecutorService service = JSR356Application.get().getScheduledExecutorService();
+        progressUpdateTask = ProgressUpdater.start(getApplication(), getId(), service);
+    }
+
+    public synchronized void cancel() {
+        if (progressUpdateTask == null || !progressUpdateTask.isRunning()) {
+            return;
+        }
+        progressUpdateTask.cancel();
+    }
+
+    public static JSR356Session getInstance() {
+        return (JSR356Session)get();
+    }
+}
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
index 01d16b2..bbf923d 100644
--- 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
@@ -18,10 +18,11 @@ 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;
 import org.apache.wicket.protocol.https.RequireHttps;
 
 @RequireHttps
-public class WebSocketPushUpdateProgressDemoPage extends WicketExamplePage
+public class WebSocketPushUpdateProgressDemoPage extends WicketExamplePage implements ProgressUpdater.ITaskProgressListener
 {
 	public WebSocketPushUpdateProgressDemoPage()
 	{
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
index 03e7733..0dd674c 100644
--- 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
@@ -22,6 +22,7 @@ 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;
@@ -34,7 +35,7 @@ public class ProgressBarTogglePanel extends Panel
 
     private int progress = 0;
     private boolean showProgress = true;
-    private ProgressUpdater.ProgressUpdateTask progressUpdateTask;
+
 
     public ProgressBarTogglePanel(String id)
     {
@@ -46,7 +47,7 @@ public class ProgressBarTogglePanel extends Panel
         {
             @Override
             protected void onConnect(ConnectedMessage message) {
-                progressUpdateTask = ProgressBarTogglePanel.startProgressTask(message);
+
             }
         });
 
@@ -65,33 +66,25 @@ public class ProgressBarTogglePanel extends Panel
             @Override
             public void onClick(AjaxRequestTarget target)
             {
-                if (progressUpdateTask.isRunning() && !progressUpdateTask.isCanceled())
+                ProgressUpdater.ProgressUpdateTask progressUpdateTask = JSR356Session.getInstance().getProgressUpdateTask();
+                if (progressUpdateTask != null && progressUpdateTask.isRunning() && !progressUpdateTask.isCanceled())
                 {
                     progressUpdateTask.cancel();
                 }
                 else
                 {
-                    ScheduledExecutorService service = JSR356Application.get().getScheduledExecutorService();
-                    ProgressUpdater.restart(progressUpdateTask, service);
+                    JSR356Session.getInstance().startTask();
                 }
                 target.add(ProgressBarTogglePanel.this);
             }
-        }.setBody(new IModel<String>()
-        {
-            @Override
-            public String getObject()
-            {
-                return progressUpdateTask != null && progressUpdateTask.isRunning() && !progressUpdateTask.isCanceled() ? "Cancel task" : "Restart task";
-            }
+        }.setBody((IModel<String>) () -> {
+            ProgressUpdater.ProgressUpdateTask progressUpdateTask = JSR356Session.getInstance().getProgressUpdateTask();
+            return progressUpdateTask != null && progressUpdateTask.isRunning() && !progressUpdateTask.isCanceled() ? "Cancel task" : "Restart task";
         }));
 
-        add(new Label("progressBar", new IModel<String>()
-        {
-            @Override
-            public String getObject()
-            {
-                return progressUpdateTask != null && progressUpdateTask.isRunning() ? "Background Task is " + progress + "% completed" : "No task is running";
-            }
+        add(new Label("progressBar", (IModel<String>) () -> {
+            ProgressUpdater.ProgressUpdateTask progressUpdateTask = JSR356Session.getInstance().getProgressUpdateTask();
+            return progressUpdateTask != null && progressUpdateTask.isRunning() ? "Background Task is " + progress + "% completed" : "No task is running";
         })
         {
             @Override
@@ -103,11 +96,6 @@ public class ProgressBarTogglePanel extends Panel
         });
     }
 
-    public static ProgressUpdater.ProgressUpdateTask startProgressTask(ConnectedMessage message)
-    {
-        ScheduledExecutorService service = JSR356Application.get().getScheduledExecutorService();
-        return ProgressUpdater.start(message, service);
-    }
 
     @Override
     public void onEvent(IEvent<?> event)
@@ -121,6 +109,11 @@ public class ProgressBarTogglePanel extends Panel
                 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
index 60a3e94..7584a5d 100644
--- 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
@@ -28,6 +28,7 @@ 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;
 
@@ -36,18 +37,29 @@ import org.slf4j.LoggerFactory;
  */
 public class ProgressUpdater
 {
+	/**
+	 * Marks a page as listening to task progress.
+	 */
+	public interface ITaskProgressListener {
+
+	}
+
 	private static final Logger LOGGER = LoggerFactory.getLogger(ProgressUpdater.class);
 
-	public static ProgressUpdateTask start(ConnectedMessage message, ScheduledExecutorService scheduledExecutorService)
+	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(message.getApplication(), message.getSessionId(), message.getKey());
+		ProgressUpdateTask progressUpdateTask = new ProgressUpdateTask(application, session);
 		scheduledExecutorService.schedule(progressUpdateTask, 1, TimeUnit.SECONDS);
 		return progressUpdateTask;
 	}
 
-	public static void restart(ProgressUpdateTask progressUpdateTask, ScheduledExecutorService scheduledExecutorService) {
-		scheduledExecutorService.schedule(progressUpdateTask, 1, TimeUnit.SECONDS);
+	public static class TaskCanceled implements IWebSocketPushMessage
+	{
+
+		public TaskCanceled()
+		{
+		}
 	}
 
 	/**
@@ -80,16 +92,14 @@ public class ProgressUpdater
 		 */
 		private final String applicationName;
 		private final String sessionId;
-		private final IKey key;
 
 		private boolean canceled = false;
 		private boolean running = false;
 
-		private ProgressUpdateTask(Application application, String sessionId, IKey key)
+		private ProgressUpdateTask(Application application, String sessionId)
 		{
 			this.applicationName = application.getName();
 			this.sessionId = sessionId;
-			this.key = key;
 		}
 
 		@Override
@@ -98,32 +108,26 @@ public class ProgressUpdater
 			running = true;
 			Application application = Application.get(applicationName);
 			WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(application);
-			IWebSocketConnectionRegistry webSocketConnectionRegistry = webSocketSettings.getConnectionRegistry();
 
 			int progress = 0;
 
 			while (progress <= 100)
 			{
-				IWebSocketConnection connection = webSocketConnectionRegistry.getConnection(application, sessionId, key);
 				try
 				{
-					if (connection == null || !connection.isOpen())
-					{
-						running = false;
-						// stop if the web socket connection is closed
-						return;
-					}
+					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(((PageIdKey) key).getPageClass()), new TaskCanceled());
 						return;
 					}
-
-					WebSocketPushBroadcaster broadcaster =
-							new WebSocketPushBroadcaster(webSocketSettings.getConnectionRegistry());
-					broadcaster.broadcast(new ConnectedMessage(application, sessionId, key), new ProgressUpdate(progress));
+					broadcaster.broadcastAllMatchingFilter(application, (sessionId, key) ->
+							ProgressUpdateTask.this.sessionId.equals(sessionId) && key instanceof PageIdKey && ITaskProgressListener.class.isAssignableFrom(((PageIdKey) key).getPageClass()), new ProgressUpdate(progress));
 
 					// sleep for a while to simulate work
 					TimeUnit.SECONDS.sleep(1);
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 f891751..d6d35b0 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 pageClass;
 	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");
+		String pageClass = request.getParameter("pageClass");
 		this.resourceName = request.getParameter("resourceName");
 		this.connectionToken = request.getParameter("connectionToken");
 		if (Strings.isEmpty(pageId) && Strings.isEmpty(resourceName))
@@ -121,10 +124,12 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		if (Strings.isEmpty(pageId) == false)
 		{
 			this.pageId = Integer.parseInt(pageId, 10);
+			this.pageClass = pageClass;
 		}
 		else
 		{
 			this.pageId = NO_PAGE_ID;
+			this.pageClass = NO_PAGE_CLASS;
 		}
 
 		String baseUrl = request.getParameter(WebRequest.PARAM_AJAX_BASE_URL);
@@ -181,7 +186,7 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 			}
 		}
 
-		broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key));
+		broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key), connection);
 	}
 
 	@Override
@@ -203,6 +208,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 +227,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 +258,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 +270,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 +365,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,7 +408,7 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 		IKey key;
 		if (Strings.isEmpty(resourceName))
 		{
-			key = new PageIdKey(pageId);
+			key = new PageIdKey(pageId, pageClass);
 		}
 		else
 		{
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 c0a1f18..7ff8d80 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
@@ -128,6 +128,7 @@ public class BaseWebSocketBehavior extends Behavior
 		{
 			int pageId = component.getPage().getPageId();
 			variables.put("pageId", pageId);
+			variables.put("pageClass", component.getPage().getClass().getName());
 			variables.put("resourceName", "");
 			variables.put("connectionToken", "");
 		}
@@ -136,6 +137,7 @@ public class BaseWebSocketBehavior extends Behavior
 			variables.put("resourceName", resourceName);
 			variables.put("connectionToken", connectionToken);
 			variables.put("pageId", false);
+			variables.put("pageClass", "");
 		}
 
 		WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(component.getApplication());
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..fc37a7f 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,72 @@ 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");
+
+		Collection<IWebSocketConnection> wsConnections = registry.getConnections(application, sessionId);
+		if (wsConnections == null)
+		{
+			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");
+
+		Collection<IWebSocketConnection> wsConnections = registry.getConnections(application, connectionsFilter);
+		if (wsConnections == null)
+		{
+			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..70a2603 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,13 @@ public class WebSocketRequestHandler extends AbstractPartialPageRequestHandler i
 		}
 	}
 
-
+	/**
+	 * @return if true then EMPTY partial updates will be ignored.
+	 */
+	protected boolean pushOnEmptyUpdate()
+	{
+		return true;
+	}
 
 	protected PartialPageUpdate getUpdate() {
 		if (update == null) {
@@ -129,7 +135,11 @@ public class WebSocketRequestHandler extends AbstractPartialPageRequestHandler i
 	{
 		if (update != null)
 		{
-			update.writeTo(requestCycle.getResponse(), "UTF-8");
+			// if the update is not empty or empty updates will be pushed anyway then write to response.
+			if (pushOnEmptyUpdate() || !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/IWebSocketConnectionRegistry.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/registry/IWebSocketConnectionRegistry.java
index 2782a93..c4de9a5 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,17 @@ import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
 public interface IWebSocketConnectionRegistry
 {
 	/**
+	 * Interface allowing to filter web-sockets connection. This could be used for use cases like the
+	 * following: you
+	 */
+	interface IConnectionsFilter
+	{
+
+		boolean acceptConnection(String sessionId, IKey key);
+
+	}
+
+	/**
 	 * @param application
 	 *      the web application to look in
 	 * @param sessionId
@@ -44,12 +55,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..ba50ed2 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,6 +16,9 @@
  */
 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;
 
 /**
@@ -25,9 +28,30 @@ public class PageIdKey implements IKey
 {
 	private final Integer pageId;
 
+	private Class<? extends IManageablePage> pageClass;
+
 	public PageIdKey(Integer pageId)
 	{
+		this(pageId, null);
+	}
+
+	public PageIdKey(Integer pageId, String pageClass)
+	{
 		this.pageId = Args.notNull(pageId, "pageId");
+		Args.notNull(pageClass, "pageClass");
+		try {
+			this.pageClass = (Class<IManageablePage>)Thread.currentThread().getContextClassLoader().loadClass(pageClass);
+		} catch (ClassNotFoundException e) {
+			//throw new WicketRuntimeException(e);
+		}
+	}
+
+	/**
+	 * @return returns the page class.
+	 */
+	public Class<? extends IManageablePage> getPageClass()
+	{
+		return pageClass;
 	}
 
 	@Override
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..e176a15 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.acceptConnection(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..e398207 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
@@ -74,6 +74,7 @@
 
 				if (WWS.pageId !== false) {
 					url += '?pageId=' + encodeURIComponent(WWS.pageId);
+					url += '&pageClass=' + encodeURIComponent(WWS.pageClass);
 				} else if (WWS.resourceName) {
 					url += '?resourceName=' + encodeURIComponent(WWS.resourceName);
 					if (WWS.connectionToken) {
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..b73eb01 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},  pageClass: '${pageClass}', 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 e48b908..6d6736d 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
@@ -84,6 +84,7 @@ abstract class TestWebSocketProcessor extends AbstractWebSocketProcessor
 		Args.notNull(page, "page");
 		MockHttpServletRequest request = createRequest(wicketTester);
 		request.addParameter("pageId", page.getId());
+		request.addParameter("pageClass", 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 41fa304..f7e4ca4 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 0636831..a73324b 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
@@ -47,6 +47,7 @@ public class WicketEndpoint extends Endpoint
 	 * The name of the request parameter that holds the application name
 	 */
 	private static final String WICKET_APP_PARAM_NAME = "wicket-app-name";
+	private static final String WICKET_SESSION_ID = "jsessionid";
 
 	private final AtomicBoolean applicationDestroyed = new AtomicBoolean(false);
 
@@ -56,6 +57,7 @@ public class WicketEndpoint extends Endpoint
 	public void onOpen(Session session, EndpointConfig endpointConfig)
 	{
 		String appName = getApplicationName(session);
+		session.getId();
 
 		WebApplication app = (WebApplication) WebApplication.get(appName);
 		app.getApplicationListeners().add(new ApplicationListener(applicationDestroyed));
@@ -123,7 +125,6 @@ public class WicketEndpoint extends Endpoint
 	{
 		String appName = null;
 
-		@SuppressWarnings("unchecked")
 		Map<String, List<String>> parameters = session.getRequestParameterMap();
 		if (parameters != null)
 		{