Vaadin Server Push Example
Server push is a technology when the server pushes data to the client without the client asking for that data just like the old intranet client/server architecture when the clients get updated by the server. This kind of communication was near to impossible in the web few years before, first the bandwidth, when the Internet begins was not enough for server push, several attempts before the HTML5 websocket like webcasting and comet have not been successfull enough and is with HTML5 that server push become widely used.
1. The tools
- Java JDK 8
- Latest Eclipse Mars
- Vaadin 7.6.4
- Tomcat Server 8
2. Introduction
Vaadin make very easy to use server push, you only need to use the @Push
annotation and Vaadin is using server push. In this example I am going to show you how to handle the server push to send content to the client. I am using here Labels as a containers to the data pushed from the server but you can use any container you want. In the first server push, the server is pushing the time every second and in the second one the server is pushing every 10 seconds.
3. Prerequisites
- JDK installed
- Eclipse Mars installed and working
- Vaadin 7.6.4 plugin installed
- Tomcat 8 installed and running
4. Set up the project
In the file menu choose File -> New -> Other:
Now from the list choose Vaadin 7 project:
Hit next and name your project then hit finish.
5. Coding the example
5.1 The quote generator class
I created a class that generates some random quotes from an array.
Quote array
private String[] quotes = new String[20];
Declared the array.
Generate quotes
public String getQuote(){ Random r = new Random(); return quotes[r.nextInt(20)]; }
Generate a random quote from the array.
5.2 Initial preparations
@Push annotation
@Push @SuppressWarnings("serial") @Theme("vaadinseverpushbeta") public class VaadinseverpushbetaUI extends UI{
To use server push just add the annotation @Push to the UI class annotations. Adding @Push to a UI class configures the UI for automatic push.
Class Variables
private QuoteGenerator qg; private VerticalLayout layout; private Label theTime;
The private QuoteGenerator qg;
is used for the custom POJO Class created before to generate quotes. private VerticalLayout layout;
the layout of our UI class and private Label theTime;
to store the datetime pushed from the server.
5.3 First Thread
I am using Threads to make the server pushes.
First Thread
class MyFirsthThread extends Thread { @Override public void run() { try { while (true) { Thread.sleep(1000); access(new Runnable() { @Override public void run() { theTime.setValue("Its now : " + Instant.now()); } }); } } catch (InterruptedException e) { e.printStackTrace(); } } }
This thread update the private Label theTime;
every second as long as the webpage is running, the time is coming from the server using server push. Thread.sleep(1000);
, sleep the thread for 1000 milliseconds = 1 second. access(new Runnable()
locks the UI and provide exclusive access to the current runnable, All the UI operations must be inside an access block because the thread needs a lock to modify the UI, if you try to modify the UI outside an access block an exception is raised. theTime.setValue("Its now : " + Instant.now());
modify the content of the Label using server push.
5.4 Second Thread
Second Thread
class MySecondThread2 extends Thread { int count = 0; @Override public void run() { try { while (count < 4) { Thread.sleep(10000); access(new Runnable() { @Override public void run() { layout.addComponent(new Label(qg.getQuote())); count++; } }); } access(new Runnable() { @Override public void run() { layout.addComponent(new Label("No more messages for you !")); } }); } catch (InterruptedException e) { e.printStackTrace(); } } }
This thread run four times every 10 seconds and every time it runs, adds a label with a random quote from the server using server push.int count = 0;
its a counter to limit the number of times this thread runs. while (count < 4) {
, the while checks the counter. Thread.sleep(10000);
the thread sleep for 10 seconds, remember the parameter of the sleep is in miliseconds. access(new Runnable() {
using the access block to lock the UI. layout.addComponent(new Label(qg.getQuote()));
adds a new Label with a random quote every time it runs. count++;
Update the counter. After the thread have added 4 labels it exits from the while and add a last label to show to the user that it finish layout.addComponent(new Label("No more messages for you !"));
with no more messages.
5.5 The init method
Init Method
@Override protected void init(VaadinRequest request) { qg = new QuoteGenerator(); layout = new VerticalLayout(); layout.setMargin(true); setContent(layout); theTime = new Label(); theTime.setValue("Its now : " + Instant.now()); layout.addComponent(theTime); new MyFirsthThread().start(); new MySecondThread2().start(); }
In the init method first create the random quote generator instance qg = new QuoteGenerator();
. Then create the layout layout = new VerticalLayout();
. Create the label to hold the time theTime = new Label();
. Start the first thread new MyFirsthThread().start();
and start the second thread new MySecondThread2().start();
.
6. The complete source code
QuoteGenerator.java
package com.example.vaadinserverpush; import java.util.Random; public class QuoteGenerator { private String[] quotes = new String[20]; public QuoteGenerator (){ quotes[0] = "A friend asks only for your time not your money."; quotes[1] = "Your high-minded principles spell success."; quotes[2] = "Enjoy the good luck a companion brings you."; quotes[3] = "Hidden in a valley beside an open stream- This will be the type of place where you will find your dream."; quotes[4] = "What ever you're goal is in life, embrace it visualize it, and for it will be yours."; quotes[5] = "You will become great if you believe in yourself."; quotes[6] = "Never give up. You're not a failure if you don't give up."; quotes[7] = "It is now, and in this world, that we must live."; quotes[8] = "Adversity is the parent of virtue."; quotes[9] = "A stranger, is a friend you have not spoken to yet."; quotes[10] = "A new voyage will fill your life with untold memories."; quotes[11] = "Its amazing how much good you can do if you dont care who gets the credit."; quotes[12] = "Stop wishing. Start doing."; quotes[13] = "Your fortune is as sweet as a cookie."; quotes[14] = "Don't pursue happiness - create it."; quotes[15] = "Everything happens for a reason."; quotes[16] = "Rivers need springs."; quotes[17] = "All progress occurs because people dare to be different."; quotes[18] = "It is not necessary to show others you have change; the change will be obvious."; quotes[19] = "Next full moon brings an enchanting evening."; } public String getQuote(){ Random r = new Random(); return quotes[r.nextInt(20)]; } }
VaadinserverpushUI.java
package com.example.vaadinserverpush; import java.time.Instant; import javax.servlet.annotation.WebServlet; import com.vaadin.annotations.Push; import com.vaadin.annotations.Theme; import com.vaadin.annotations.VaadinServletConfiguration; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinServlet; import com.vaadin.ui.Label; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; @Push @SuppressWarnings("serial") @Theme("vaadinserverpush") public class VaadinserverpushUI extends UI { @WebServlet(value = "/*", asyncSupported = true) @VaadinServletConfiguration(productionMode = false, ui = VaadinserverpushUI.class) public static class Servlet extends VaadinServlet { } private QuoteGenerator qg; private VerticalLayout layout; private Label theTime; @Override protected void init(VaadinRequest request) { qg = new QuoteGenerator(); layout = new VerticalLayout(); layout.setMargin(true); setContent(layout); theTime = new Label(); theTime.setValue("Its now : " + Instant.now()); layout.addComponent(theTime); new MyFirsthThread().start(); new MySecondThread2().start(); } class MyFirsthThread extends Thread { @Override public void run() { try { while (true) { Thread.sleep(1000); access(new Runnable() { @Override public void run() { theTime.setValue("Its now : " + Instant.now()); } }); } } catch (InterruptedException e) { e.printStackTrace(); } } } class MySecondThread2 extends Thread { int count = 0; @Override public void run() { try { while (count < 4) { Thread.sleep(10000); access(new Runnable() { @Override public void run() { layout.addComponent(new Label(qg.getQuote())); count++; } }); } access(new Runnable() { @Override public void run() { layout.addComponent(new Label("No more messages for you !")); } }); } catch (InterruptedException e) { e.printStackTrace(); } } } }
7. Running the example
Right click on the project folder and choose Run as -> Run on server choose Tomcat 8 server and hit finish.
8. Results
Open your application in a browser and fire Up the developer tools CTRL+SHIFT+i
shortcut in most browsers, you should see something like the following image, go to the console tab:
Let’s run the application and examine the console output to see whats Vaadin doing under the hood.
After running the application for a while you get a lot of messages lets filter these messages to get only the server push messages.
First you get a update time message every second:
Time update
Mon Apr 04 15:47:05 GMT-430 2016 com.vaadin.client.communication.AtmospherePushConnection INFO: Received push (websocket) message: for(;;);[{"syncId": 20, "clientId": 0, "changes" : [], "state":{"34":{"text":"Its now : 2016-04-04T20:17:05.428Z"}}, "types":{"34":"1"}, "hierarchy":{}, "rpc" : [], "meta" : {"async":true}, "resources" : {}, "timings":[115, 15]}] VaadinServerPush:1:5094 Mon Apr 04 15:47:05 GMT-430 2016 com.vaadin.client.communication.MessageHandler Mon Apr 04 15:47:06 GMT-430 2016 com.vaadin.client.communication.AtmospherePushConnection INFO: Received push (websocket) message: for(;;);[{"syncId": 21, "clientId": 0, "changes" : [], "state":{"34":{"text":"Its now : 2016-04-04T20:17:06.429Z"}}, "types":{"34":"1"}, "hierarchy":{}, "rpc" : [], "meta" : {"async":true}, "resources" : {}, "timings":[115, 15]}] VaadinServerPush:1:5094 Mon Apr 04 15:47:06 GMT-430 2016 com.vaadin.client.communication.MessageHandler
And every 10 seconds you should get a random message:
Random Message
Mon Apr 04 15:47:06 GMT-430 2016 com.vaadin.client.communication.AtmospherePushConnection INFO: Received push (websocket) message: for(;;);[{"syncId": 22, "clientId": 0, "changes" : [], "state":{"33":{"childData":{"34":{"alignmentBitmask":5,"expandRatio":0},"35":{"alignmentBitmask":5,"expandRatio":0},"36":{"alignmentBitmask":5,"expandRatio":0}}},"36":{"text":"Your fortune is as sweet as a cookie.","width":"100.0%"}}, "types":{"33":"2","36":"1"}, "hierarchy":{"33":["34","35","36"]}, "rpc" : [], "meta" : {"async":true}, "resources" : {}, "timings":[115, 15]}] VaadinServerPush:1:5094 Mon Apr 04 15:47:06 GMT-430 2016 com.vaadin.client.communication.MessageHandler Mon Apr 04 15:47:16 GMT-430 2016 com.vaadin.client.communication.AtmospherePushConnection INFO: Received push (websocket) message: for(;;);[{"syncId": 33, "clientId": 0, "changes" : [], "state":{"33":{"childData":{"34":{"alignmentBitmask":5,"expandRatio":0},"35":{"alignmentBitmask":5,"expandRatio":0},"36":{"alignmentBitmask":5,"expandRatio":0},"37":{"alignmentBitmask":5,"expandRatio":0}}},"37":{"text":"A new voyage will fill your life with untold memories.","width":"100.0%"}}, "types":{"33":"2","37":"1"}, "hierarchy":{"33":["34","35","36","37"]}, "rpc" : [], "meta" : {"async":true}, "resources" : {}, "timings":[115, 15]}] VaadinServerPush:1:5094 Mon Apr 04 15:47:16 GMT-430 2016 com.vaadin.client.communication.MessageHandler Mon Apr 04 15:47:26 GMT-430 2016 com.vaadin.client.communication.AtmospherePushConnection INFO: Received push (websocket) message: for(;;);[{"syncId": 44, "clientId": 0, "changes" : [], "state":{"33":{"childData":{"34":{"alignmentBitmask":5,"expandRatio":0},"35":{"alignmentBitmask":5,"expandRatio":0},"36":{"alignmentBitmask":5,"expandRatio":0},"37":{"alignmentBitmask":5,"expandRatio":0},"38":{"alignmentBitmask":5,"expandRatio":0}}},"38":{"text":"A new voyage will fill your life with untold memories.","width":"100.0%"}}, "types":{"33":"2","38":"1"}, "hierarchy":{"33":["34","35","36","37","38"]}, "rpc" : [], "meta" : {"async":true}, "resources" : {}, "timings":[115, 15]}] VaadinServerPush:1:5094 Mon Apr 04 15:47:26 GMT-430 2016 com.vaadin.client.communication.MessageHandler Mon Apr 04 15:47:26 GMT-430 2016 com.vaadin.client.communication.AtmospherePushConnection INFO: Received push (websocket) message: for(;;);[{"syncId": 45, "clientId": 0, "changes" : [], "state":{"33":{"childData":{"34":{"alignmentBitmask":5,"expandRatio":0},"35":{"alignmentBitmask":5,"expandRatio":0},"36":{"alignmentBitmask":5,"expandRatio":0},"37":{"alignmentBitmask":5,"expandRatio":0},"38":{"alignmentBitmask":5,"expandRatio":0},"39":{"alignmentBitmask":5,"expandRatio":0}}},"39":{"text":"No more messages for you !","width":"100.0%"}}, "types":{"33":"2","39":"1"}, "hierarchy":{"33":["34","35","36","37","38","39"]}, "rpc" : [], "meta" : {"async":true}, "resources" : {}, "timings":[115, 15]}] VaadinServerPush:1:5094 Mon Apr 04 15:47:26 GMT-430 2016 com.vaadin.client.communication.MessageHandler
Let’s see this in detail. For every message you get:
com.vaadin.client.communication.AtmospherePushConnection
Vaadin uses the Atmosphere framework to manage server push, Atmosphere is a framework that that include a mix of WebSocket, Comet and RESTful behavior.
Also you get: INFO: Received push (websocket) message:
that is telling you that the webapplication is receiving server push messages.
Now we are sure that Vaadin is using server push to update our page.
9. Download the Source Code
This was an example of Vaadin Server Push.
You can download the Eclipse project here: VaadinServerPush
your solution will acumulate an “infinite” amount of threads over time. each page refresh/visit will start a new MyFirstThread instance and never stops it.