You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

advanced-push.asciidoc 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. ---
  2. title: Server Push
  3. order: 16
  4. layout: page
  5. ---
  6. [[advanced.push]]
  7. = Server Push
  8. When you need to update a UI from another UI, possibly of another user, or from
  9. a background thread running in the server, you usually want to have the update
  10. show immediately, not when the browser happens to make the next server request.
  11. For this purpose, you can use __server push__ that sends the data to the browser
  12. immediately. Push is based on a client-server connection, usually a WebSocket
  13. connection, that the client establishes and the server can then use to send
  14. updates to the client.
  15. The server-client communication is done by default with a WebSocket connection
  16. if the browser and the server support it. If not, Vaadin will fall back to a
  17. method supported by the browser. Vaadin Push uses a custom build of the
  18. link:https://github.com/Atmosphere/atmosphere[Atmosphere framework] for
  19. client-server communication.
  20. [[advanced.push.installation]]
  21. == Installing the Push Support
  22. The server push support in Vaadin requires the separate Vaadin Push library. It
  23. is included in the installation package as [filename]#vaadin-push.jar#.
  24. [[advanced.push.installation.ivy]]
  25. === Retrieving with Ivy
  26. With Ivy, you can get it with the following declaration in the
  27. [filename]#ivy.xml#:
  28. [source, xml]
  29. ----
  30. <dependency org="com.vaadin" name="vaadin-push"
  31. rev="&vaadin.version;" conf="default->default"/>
  32. ----
  33. In some servers, you may need to exlude a [literal]#++sl4j++# dependency as
  34. follows:
  35. [source, xml]
  36. ----
  37. <dependency org="com.vaadin" name="vaadin-push"
  38. rev="&vaadin.version;" conf="default->default">
  39. <exclude org="org.slf4j" name="slf4j-api"/>
  40. </dependency>
  41. ----
  42. Pay note that the Atmosphere library is a bundle, so if you retrieve the
  43. libraries with Ant, for example, you need to retrieve
  44. [literal]#++type="jar,bundle"++#.
  45. [[advanced.push.installation.maven]]
  46. === Retrieving with Maven
  47. In Maven, you can get the push library with the following dependency in the POM:
  48. [source, xml]
  49. ----
  50. <dependency>
  51. <groupId>com.vaadin</groupId>
  52. <artifactId>vaadin-push</artifactId>
  53. <version>${vaadin.version}</version>
  54. </dependency>
  55. ----
  56. [[advanced.push.enabling]]
  57. == Enabling Push for a UI
  58. To enable server push, you need to define the push mode either in the deployment
  59. descriptor or with the [classname]#@Push# annotation for the UI.
  60. [[advanced.push.enabling.pushmode]]
  61. === Push Modes
  62. You can use server push in two modes: [literal]#++automatic++# and
  63. [literal]#++manual++#. The automatic mode pushes changes to the browser
  64. automatically after access() finishes. With the manual mode, you can do the push
  65. explicitly with [methodname]#push()#, which allows more flexibility.
  66. [[advanced.push.enabling.pushmode]]
  67. === The [classname]#@Push# annotation
  68. You can enable server push for a UI with the [classname]#@Push# annotation as
  69. follows. It defaults to automatic mode ( [parameter]#PushMode.AUTOMATIC#).
  70. [source, java]
  71. ----
  72. @Push
  73. public class PushyUI extends UI {
  74. ----
  75. To enable manual mode, you need to give the [parameter]#PushMode.MANUAL#
  76. parameter as follows:
  77. [source, java]
  78. ----
  79. @Push(PushMode.MANUAL)
  80. public class PushyUI extends UI {
  81. ----
  82. [[advanced.push.enabling.servlet]]
  83. === Servlet Configuration
  84. You can enable the server push and define the push mode also in the servlet
  85. configuration with the [parameter]#pushmode# parameter for the servlet in the
  86. [filename]#web.xml# deployment descriptor. If you use a Servlet 3.0 compatible
  87. server, you also want to enable asynchronous processing with the
  88. [literal]#++async-supported++# parameter. Note the use of Servlet 3.0 schema in
  89. the deployment descriptor.
  90. [subs="normal"]
  91. ----
  92. &lt;?xml version="1.0" encoding="UTF-8"?&gt;
  93. &lt;web-app
  94. id="WebApp_ID" version="**3.0**"
  95. xmlns="**http://java.sun.com/xml/ns/javaee**"
  96. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  97. xsi:schemaLocation="**http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd**"&gt;
  98. &lt;servlet&gt;
  99. &lt;servlet-name&gt;Pushy UI&lt;/servlet-name&gt;
  100. &lt;servlet-class&gt;
  101. com.vaadin.server.VaadinServlet&lt;/servlet-class&gt;
  102. &lt;init-param&gt;
  103. &lt;param-name&gt;UI&lt;/param-name&gt;
  104. &lt;param-value&gt;**com.example.my.PushyUI**&lt;/param-value&gt;
  105. &lt;/init-param&gt;
  106. &lt;!-- Enable server push --&gt;
  107. &lt;init-param&gt;
  108. &lt;param-name&gt;pushmode&lt;/param-name&gt;
  109. &lt;param-value&gt;**automatic**&lt;/param-value&gt;
  110. &lt;/init-param&gt;
  111. &lt;async-supported&gt;**true**&lt;/async-supported&gt;
  112. &lt;/servlet&gt;
  113. &lt;/web-app&gt;
  114. ----
  115. [[advanced.push.running]]
  116. == Accessing UI from Another Thread
  117. Making changes to a [classname]#UI# object from another thread and pushing them
  118. to the browser requires locking the user session when accessing the UI.
  119. Otherwise, the UI update done from another thread could conflict with a regular
  120. event-driven update and cause either data corruption or deadlocks. Because of
  121. this, you may only access an UI using the [methodname]#access()# method, which
  122. locks the session to prevent conflicts. It takes a [interfacename]#Runnable#
  123. which it executes as its parameter.
  124. For example:
  125. [source, java]
  126. ----
  127. ui.access(new Runnable() {
  128. @Override
  129. public void run() {
  130. series.add(new DataSeriesItem(x, y));
  131. }
  132. });
  133. ----
  134. In Java 8, where a parameterless lambda expression creates a runnable, you could
  135. simply write:
  136. [source, java]
  137. ----
  138. ui.access(() ->
  139. series.add(new DataSeriesItem(x, y)));
  140. ----
  141. If the push mode is [literal]#++manual++#, you need to push the pending UI
  142. changes to the browser explicitly with the [methodname]#push()# method.
  143. [source, java]
  144. ----
  145. ui.access(new Runnable() {
  146. @Override
  147. public void run() {
  148. series.add(new DataSeriesItem(x, y));
  149. ui.push();
  150. }
  151. });
  152. ----
  153. Below is a complete example of a case where we make UI changes from another
  154. thread.
  155. [source, java]
  156. ----
  157. public class PushyUI extends UI {
  158. Chart chart = new Chart(ChartType.AREASPLINE);
  159. DataSeries series = new DataSeries();
  160. @Override
  161. protected void init(VaadinRequest request) {
  162. chart.setSizeFull();
  163. setContent(chart);
  164. // Prepare the data display
  165. Configuration conf = chart.getConfiguration();
  166. conf.setTitle("Hot New Data");
  167. conf.setSeries(series);
  168. // Start the data feed thread
  169. new FeederThread().start();
  170. }
  171. class FeederThread extends Thread {
  172. int count = 0;
  173. @Override
  174. public void run() {
  175. try {
  176. // Update the data for a while
  177. while (count < 100) {
  178. Thread.sleep(1000);
  179. access(new Runnable() {
  180. @Override
  181. public void run() {
  182. double y = Math.random();
  183. series.add(
  184. new DataSeriesItem(count++, y),
  185. true, count > 10);
  186. }
  187. });
  188. }
  189. // Inform that we have stopped running
  190. access(new Runnable() {
  191. @Override
  192. public void run() {
  193. setContent(new Label("Done!"));
  194. }
  195. });
  196. } catch (InterruptedException e) {
  197. e.printStackTrace();
  198. }
  199. }
  200. }
  201. }
  202. ----
  203. See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.simple[on-line example, window="_blank"].
  204. When sharing data between UIs or user sessions, you need to consider the
  205. message-passing mechanism more carefully, as explained next.
  206. [[advanced.push.pusharound]]
  207. == Broadcasting to Other Users
  208. Broadcasting messages to be pushed to UIs in other user sessions requires having
  209. some sort of message-passing mechanism that sends the messages to all UIs that
  210. register as recipients. As processing server requests for different UIs is done
  211. concurrently in different threads of the application server, locking the threads
  212. properly is very important to avoid deadlock situations.
  213. [[advanced.push.pusharound.broadcaster]]
  214. === The Broadcaster
  215. The standard pattern for sending messages to other users is to use a
  216. __broadcaster__ singleton that registers the UIs and broadcasts messages to them
  217. safely. To avoid deadlocks, it is recommended that the messages should be sent
  218. through a message queue in a separate thread. Using a Java
  219. [classname]#ExecutorService# running in a single thread is usually the easiest
  220. and safest way.
  221. [source, java]
  222. ----
  223. public class Broadcaster implements Serializable {
  224. static ExecutorService executorService =
  225. Executors.newSingleThreadExecutor();
  226. public interface BroadcastListener {
  227. void receiveBroadcast(String message);
  228. }
  229. private static LinkedList<BroadcastListener> listeners =
  230. new LinkedList<BroadcastListener>();
  231. public static synchronized void register(
  232. BroadcastListener listener) {
  233. listeners.add(listener);
  234. }
  235. public static synchronized void unregister(
  236. BroadcastListener listener) {
  237. listeners.remove(listener);
  238. }
  239. public static synchronized void broadcast(
  240. final String message) {
  241. for (final BroadcastListener listener: listeners)
  242. executorService.execute(new Runnable() {
  243. @Override
  244. public void run() {
  245. listener.receiveBroadcast(message);
  246. }
  247. });
  248. }
  249. }
  250. ----
  251. See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.pusharound[on-line example, window="_blank"].
  252. In Java 8, you could use lambda expressions for the listeners instead of the
  253. interface, and a parameterless expression to create the runnable:
  254. [source, java]
  255. ----
  256. for (final Consumer<String> listener: listeners)
  257. executorService.execute(() ->
  258. listener.accept(message));
  259. ----
  260. See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.pusharound[on-line example, window="_blank"].
  261. [[advanced.push.pusharound.receiving]]
  262. === Receiving Broadcasts
  263. The receivers need to implement the receiver interface and register to the
  264. broadcaster to receive the broadcasts. A listener should be unregistered when
  265. the UI expires. When updating the UI in a receiver, it should be done safely as
  266. described earlier, by executing the update through the [methodname]#access()#
  267. method of the UI.
  268. [source, java]
  269. ----
  270. @Push
  271. public class PushAroundUI extends UI
  272. implements Broadcaster.BroadcastListener {
  273. VerticalLayout messages = new VerticalLayout();
  274. @Override
  275. protected void init(VaadinRequest request) {
  276. ... build the UI ...
  277. // Register to receive broadcasts
  278. Broadcaster.register(this);
  279. }
  280. // Must also unregister when the UI expires
  281. @Override
  282. public void detach() {
  283. Broadcaster.unregister(this);
  284. super.detach();
  285. }
  286. @Override
  287. public void receiveBroadcast(final String message) {
  288. // Must lock the session to execute logic safely
  289. access(new Runnable() {
  290. @Override
  291. public void run() {
  292. // Show it somehow
  293. messages.addComponent(new Label(message));
  294. }
  295. });
  296. }
  297. }
  298. ----
  299. See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.pusharound[on-line example, window="_blank"].
  300. [[advanced.push.pusharound.sending]]
  301. === Sending Broadcasts
  302. To send broadcasts with a broadcaster singleton, such as the one described
  303. above, you would only need to call the [methodname]#broadcast()# method as
  304. follows.
  305. [source, java]
  306. ----
  307. final TextField input = new TextField();
  308. sendBar.addComponent(input);
  309. Button send = new Button("Send");
  310. send.addClickListener(new ClickListener() {
  311. @Override
  312. public void buttonClick(ClickEvent event) {
  313. // Broadcast the message
  314. Broadcaster.broadcast(input.getValue());
  315. input.setValue("");
  316. }
  317. });
  318. ----
  319. See the http://demo.vaadin.com/book-examples-vaadin7/book#advanced.push.pusharound[on-line example, window="_blank"].