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; + } + } +}