summaryrefslogtreecommitdiffstats
path: root/documentation/advanced/advanced-push.asciidoc
diff options
context:
space:
mode:
Diffstat (limited to 'documentation/advanced/advanced-push.asciidoc')
-rw-r--r--documentation/advanced/advanced-push.asciidoc417
1 files changed, 417 insertions, 0 deletions
diff --git a/documentation/advanced/advanced-push.asciidoc b/documentation/advanced/advanced-push.asciidoc
new file mode 100644
index 0000000000..df9279c6bb
--- /dev/null
+++ b/documentation/advanced/advanced-push.asciidoc
@@ -0,0 +1,417 @@
+---
+title: Server Push
+order: 16
+layout: page
+---
+
+[[advanced.push]]
+= Server Push
+
+When you need to update a UI from another UI, possibly of another user, or from
+a background thread running in the server, you usually want to have the update
+show immediately, not when the browser happens to make the next server request.
+For this purpose, you can use __server push__ that sends the data to the browser
+immediately. Push is based on a client-server connection, usually a WebSocket
+connection, that the client establishes and the server can then use to send
+updates to the client.
+
+The server-client communication is done by default with a WebSocket connection
+if the browser and the server support it. If not, Vaadin will fall back to a
+method supported by the browser. Vaadin Push uses a custom build of the
+link:https://github.com/Atmosphere/atmosphere[Atmosphere framework] for
+client-server communication.
+
+[[advanced.push.installation]]
+== Installing the Push Support
+
+The server push support in Vaadin requires the separate Vaadin Push library. It
+is included in the installation package as [filename]#vaadin-push.jar#.
+
+[[advanced.push.installation.ivy]]
+=== Retrieving with Ivy
+
+With Ivy, you can get it with the following declaration in the
+[filename]#ivy.xml#:
+
+
+[source, xml]
+----
+<dependency org="com.vaadin" name="vaadin-push"
+ rev="&vaadin.version;" conf="default->default"/>
+----
+
+In some servers, you may need to exlude a [literal]#++sl4j++# dependency as
+follows:
+
+
+[source, xml]
+----
+<dependency org="com.vaadin" name="vaadin-push"
+ rev="&vaadin.version;" conf="default->default">
+ <exclude org="org.slf4j" name="slf4j-api"/>
+</dependency>
+----
+
+Pay note that the Atmosphere library is a bundle, so if you retrieve the
+libraries with Ant, for example, you need to retrieve
+[literal]#++type="jar,bundle"++#.
+
+
+[[advanced.push.installation.maven]]
+=== Retrieving with Maven
+
+In Maven, you can get the push library with the following dependency in the POM:
+
+
+[source, xml]
+----
+<dependency>
+ <groupId>com.vaadin</groupId>
+ <artifactId>vaadin-push</artifactId>
+ <version>${vaadin.version}</version>
+</dependency>
+----
+
+
+
+[[advanced.push.enabling]]
+== Enabling Push for a UI
+
+To enable server push, you need to define the push mode either in the deployment
+descriptor or with the [classname]#@Push# annotation for the UI.
+
+[[advanced.push.enabling.pushmode]]
+=== Push Modes
+
+You can use server push in two modes: [literal]#++automatic++# and
+[literal]#++manual++#. The automatic mode pushes changes to the browser
+automatically after access() finishes. With the manual mode, you can do the push
+explicitly with [methodname]#push()#, which allows more flexibility.
+
+
+[[advanced.push.enabling.pushmode]]
+=== The [classname]#@Push# annotation
+
+You can enable server push for a UI with the [classname]#@Push# annotation as
+follows. It defaults to automatic mode ( [parameter]#PushMode.AUTOMATIC#).
+
+
+[source, java]
+----
+@Push
+public class PushyUI extends UI {
+----
+
+To enable manual mode, you need to give the [parameter]#PushMode.MANUAL#
+parameter as follows:
+
+
+[source, java]
+----
+@Push(PushMode.MANUAL)
+public class PushyUI extends UI {
+----
+
+
+[[advanced.push.enabling.servlet]]
+=== Servlet Configuration
+
+You can enable the server push and define the push mode also in the servlet
+configuration with the [parameter]#pushmode# parameter for the servlet in the
+[filename]#web.xml# deployment descriptor. If you use a Servlet 3.0 compatible
+server, you also want to enable asynchronous processing with the
+[literal]#++async-supported++# parameter. Note the use of Servlet 3.0 schema in
+the deployment descriptor.
+
+
+[subs="normal"]
+----
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;web-app
+ id="WebApp_ID" version="**3.0**"
+ xmlns="**http://java.sun.com/xml/ns/javaee**"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="**http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd**"&gt;
+ &lt;servlet&gt;
+ &lt;servlet-name&gt;Pushy UI&lt;/servlet-name&gt;
+ &lt;servlet-class&gt;
+ com.vaadin.server.VaadinServlet&lt;/servlet-class&gt;
+
+ &lt;init-param&gt;
+ &lt;param-name&gt;UI&lt;/param-name&gt;
+ &lt;param-value&gt;**com.example.my.PushyUI**&lt;/param-value&gt;
+ &lt;/init-param&gt;
+
+ &lt;!-- Enable server push --&gt;
+ &lt;init-param&gt;
+ &lt;param-name&gt;pushmode&lt;/param-name&gt;
+ &lt;param-value&gt;**automatic**&lt;/param-value&gt;
+ &lt;/init-param&gt;
+ &lt;async-supported&gt;**true**&lt;/async-supported&gt;
+ &lt;/servlet&gt;
+&lt;/web-app&gt;
+----
+
+[[advanced.push.running]]
+== Accessing UI from Another Thread
+
+Making changes to a [classname]#UI# object from another thread and pushing them
+to the browser requires locking the user session when accessing the UI.
+Otherwise, the UI update done from another thread could conflict with a regular
+event-driven update and cause either data corruption or deadlocks. Because of
+this, you may only access an UI using the [methodname]#access()# method, which
+locks the session to prevent conflicts. It takes a [interfacename]#Runnable#
+which it executes as its parameter.
+
+For example:
+
+
+[source, java]
+----
+ui.access(new Runnable() {
+ @Override
+ public void run() {
+ series.add(new DataSeriesItem(x, y));
+ }
+});
+----
+
+In Java 8, where a parameterless lambda expression creates a runnable, you could
+simply write:
+
+
+[source, java]
+----
+ui.access(() ->
+ series.add(new DataSeriesItem(x, y)));
+----
+
+If the push mode is [literal]#++manual++#, you need to push the pending UI
+changes to the browser explicitly with the [methodname]#push()# method.
+
+
+[source, java]
+----
+ui.access(new Runnable() {
+ @Override
+ public void run() {
+ series.add(new DataSeriesItem(x, y));
+ ui.push();
+ }
+});
+----
+
+Below is a complete example of a case where we make UI changes from another
+thread.
+
+
+[source, java]
+----
+public class PushyUI extends UI {
+ Chart chart = new Chart(ChartType.AREASPLINE);
+ DataSeries series = new DataSeries();
+
+ @Override
+ protected void init(VaadinRequest request) {
+ chart.setSizeFull();
+ setContent(chart);
+
+ // Prepare the data display
+ Configuration conf = chart.getConfiguration();
+ conf.setTitle("Hot New Data");
+ conf.setSeries(series);
+
+ // Start the data feed thread
+ new FeederThread().start();
+ }
+
+ class FeederThread extends Thread {
+ int count = 0;
+
+ @Override
+ public void run() {
+ try {
+ // Update the data for a while
+ while (count < 100) {
+ Thread.sleep(1000);
+
+ access(new Runnable() {
+ @Override
+ public void run() {
+ double y = Math.random();
+ series.add(
+ new DataSeriesItem(count++, y),
+ true, count > 10);
+ }
+ });
+ }
+
+ // Inform that we have stopped running
+ access(new Runnable() {
+ @Override
+ public void run() {
+ setContent(new Label("Done!"));
+ }
+ });
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
+----
+See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.simple[on-line example, window="_blank"].
+
+When sharing data between UIs or user sessions, you need to consider the
+message-passing mechanism more carefully, as explained next.
+
+
+[[advanced.push.pusharound]]
+== Broadcasting to Other Users
+
+Broadcasting messages to be pushed to UIs in other user sessions requires having
+some sort of message-passing mechanism that sends the messages to all UIs that
+register as recipients. As processing server requests for different UIs is done
+concurrently in different threads of the application server, locking the threads
+properly is very important to avoid deadlock situations.
+
+[[advanced.push.pusharound.broadcaster]]
+=== The Broadcaster
+
+The standard pattern for sending messages to other users is to use a
+__broadcaster__ singleton that registers the UIs and broadcasts messages to them
+safely. To avoid deadlocks, it is recommended that the messages should be sent
+through a message queue in a separate thread. Using a Java
+[classname]#ExecutorService# running in a single thread is usually the easiest
+and safest way.
+
+
+[source, java]
+----
+public class Broadcaster implements Serializable {
+ static ExecutorService executorService =
+ Executors.newSingleThreadExecutor();
+
+ public interface BroadcastListener {
+ void receiveBroadcast(String message);
+ }
+
+ private static LinkedList<BroadcastListener> listeners =
+ new LinkedList<BroadcastListener>();
+
+ public static synchronized void register(
+ BroadcastListener listener) {
+ listeners.add(listener);
+ }
+
+ public static synchronized void unregister(
+ BroadcastListener listener) {
+ listeners.remove(listener);
+ }
+
+ public static synchronized void broadcast(
+ final String message) {
+ for (final BroadcastListener listener: listeners)
+ executorService.execute(new Runnable() {
+ @Override
+ public void run() {
+ listener.receiveBroadcast(message);
+ }
+ });
+ }
+}
+----
+See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.pusharound[on-line example, window="_blank"].
+
+In Java 8, you could use lambda expressions for the listeners instead of the
+interface, and a parameterless expression to create the runnable:
+
+
+[source, java]
+----
+for (final Consumer<String> listener: listeners)
+ executorService.execute(() ->
+ listener.accept(message));
+----
+See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.pusharound[on-line example, window="_blank"].
+
+
+[[advanced.push.pusharound.receiving]]
+=== Receiving Broadcasts
+
+The receivers need to implement the receiver interface and register to the
+broadcaster to receive the broadcasts. A listener should be unregistered when
+the UI expires. When updating the UI in a receiver, it should be done safely as
+described earlier, by executing the update through the [methodname]#access()#
+method of the UI.
+
+
+[source, java]
+----
+@Push
+public class PushAroundUI extends UI
+ implements Broadcaster.BroadcastListener {
+
+ VerticalLayout messages = new VerticalLayout();
+
+ @Override
+ protected void init(VaadinRequest request) {
+ ... build the UI ...
+
+ // Register to receive broadcasts
+ Broadcaster.register(this);
+ }
+
+ // Must also unregister when the UI expires
+ @Override
+ public void detach() {
+ Broadcaster.unregister(this);
+ super.detach();
+ }
+
+ @Override
+ public void receiveBroadcast(final String message) {
+ // Must lock the session to execute logic safely
+ access(new Runnable() {
+ @Override
+ public void run() {
+ // Show it somehow
+ messages.addComponent(new Label(message));
+ }
+ });
+ }
+}
+----
+See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.pusharound[on-line example, window="_blank"].
+
+
+[[advanced.push.pusharound.sending]]
+=== Sending Broadcasts
+
+To send broadcasts with a broadcaster singleton, such as the one described
+above, you would only need to call the [methodname]#broadcast()# method as
+follows.
+
+
+[source, java]
+----
+final TextField input = new TextField();
+sendBar.addComponent(input);
+
+Button send = new Button("Send");
+send.addClickListener(new ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ // Broadcast the message
+ Broadcaster.broadcast(input.getValue());
+
+ input.setValue("");
+ }
+});
+----
+See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.pusharound[on-line example, window="_blank"].
+
+
+
+
+