create demo page showing how to use push in order to update a component via web-sockets.
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..98a9779 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
@@ -50,6 +50,7 @@
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);
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/WebSocketPushUpdateProgressDemoPage.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.java
new file mode 100644
index 0000000..01d16b2
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/WebSocketPushUpdateProgressDemoPage.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.examples.websocket;
+
+import org.apache.wicket.examples.WicketExamplePage;
+import org.apache.wicket.examples.websocket.progress.ProgressBarTogglePanel;
+import org.apache.wicket.protocol.https.RequireHttps;
+
+@RequireHttps
+public class WebSocketPushUpdateProgressDemoPage extends WicketExamplePage
+{
+ public WebSocketPushUpdateProgressDemoPage()
+ {
+ add(new ProgressBarTogglePanel("progressPanel"));
+ }
+}
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html
new file mode 100644
index 0000000..971f7f2
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.html
@@ -0,0 +1,26 @@
+<?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
+ 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.
+-->
+<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..03e7733
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressBarTogglePanel.java
@@ -0,0 +1,126 @@
+/*
+ * 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.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;
+ private ProgressUpdater.ProgressUpdateTask progressUpdateTask;
+
+ public ProgressBarTogglePanel(String id)
+ {
+ super(id);
+
+ setOutputMarkupId(true);
+
+ add(new WebSocketBehavior()
+ {
+ @Override
+ protected void onConnect(ConnectedMessage message) {
+ progressUpdateTask = ProgressBarTogglePanel.startProgressTask(message);
+ }
+ });
+
+ 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)
+ {
+ if (progressUpdateTask.isRunning() && !progressUpdateTask.isCanceled())
+ {
+ progressUpdateTask.cancel();
+ }
+ else
+ {
+ ScheduledExecutorService service = JSR356Application.get().getScheduledExecutorService();
+ ProgressUpdater.restart(progressUpdateTask, service);
+ }
+ target.add(ProgressBarTogglePanel.this);
+ }
+ }.setBody(new IModel<String>()
+ {
+ @Override
+ public String getObject()
+ {
+ 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";
+ }
+ })
+ {
+ @Override
+ protected void onConfigure()
+ {
+ super.onConfigure();
+ setVisible(showProgress);
+ }
+ });
+ }
+
+ public static ProgressUpdater.ProgressUpdateTask startProgressTask(ConnectedMessage message)
+ {
+ ScheduledExecutorService service = JSR356Application.get().getScheduledExecutorService();
+ return ProgressUpdater.start(message, service);
+ }
+
+ @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);
+ }
+ }
+ }
+}
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..60a3e94
--- /dev/null
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java
@@ -0,0 +1,159 @@
+/*
+ * 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.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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A helper class that uses the web connection to push components updates to the client.
+ */
+public class ProgressUpdater
+{
+ private static final Logger LOGGER = LoggerFactory.getLogger(ProgressUpdater.class);
+
+ public static ProgressUpdateTask start(ConnectedMessage message, ScheduledExecutorService scheduledExecutorService)
+ {
+ // create an asynchronous task that will write the data to the client
+ ProgressUpdateTask progressUpdateTask = new ProgressUpdateTask(message.getApplication(), message.getSessionId(), message.getKey());
+ scheduledExecutorService.schedule(progressUpdateTask, 1, TimeUnit.SECONDS);
+ return progressUpdateTask;
+ }
+
+ public static void restart(ProgressUpdateTask progressUpdateTask, ScheduledExecutorService scheduledExecutorService) {
+ scheduledExecutorService.schedule(progressUpdateTask, 1, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 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 final IKey key;
+
+ private boolean canceled = false;
+ private boolean running = false;
+
+ private ProgressUpdateTask(Application application, String sessionId, IKey key)
+ {
+ this.applicationName = application.getName();
+ this.sessionId = sessionId;
+ this.key = key;
+ }
+
+ @Override
+ public void run()
+ {
+ 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;
+ }
+
+ if (canceled)
+ {
+ canceled = false;
+ running = false;
+ return;
+ }
+
+ WebSocketPushBroadcaster broadcaster =
+ new WebSocketPushBroadcaster(webSocketSettings.getConnectionRegistry());
+ broadcaster.broadcast(new ConnectedMessage(application, sessionId, 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;
+ }
+
+ public boolean isRunning() {
+ return running;
+ }
+
+ public boolean isCanceled() {
+ return canceled;
+ }
+
+ public void cancel() {
+ this.canceled = true;
+ this.running = false;
+ }
+ }
+}