Websocket Channel"
(add page header, footer, raise up 1 level) |
|||
(11 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
{{ZKDevelopersReferencePageHeader}} | {{ZKDevelopersReferencePageHeader}} | ||
+ | =Websockets for default ZK client-server communications= | ||
+ | {{ZK EE}} | ||
+ | In addition to manual websocket use, ZK can be configured to use a websocket channel as a replacement for the default /zkau request and response cycles used to send events and updates between the client and the server. | ||
− | = | + | In WEB-INF/zk.xml, add following lines to enable WebSocket connection: |
+ | <syntaxhighlight lang="xml"> | ||
+ | <listener> | ||
+ | <listener-class>org.zkoss.zkmax.au.websocket.WebSocketWebAppInit</listener-class> | ||
+ | </listener> | ||
+ | </syntaxhighlight> | ||
− | + | To change the update URL, you could also add the following lines into WEB-INF/zk.xml. | |
− | Here we demonstrate how to use the <javadoc>org.zkoss.zk.ui.sys.Storage</javadoc> in a desktop scope to share the application data through the websocket channel. | + | Optional: If not specified, "/zkwm" will be used by default. |
+ | <syntaxhighlight lang="xml"> | ||
+ | <library-property> | ||
+ | <name>org.zkoss.zkmax.au.websocket.WebSocketEndPoint.urlPattern</name> | ||
+ | <value>/yourApp</value> | ||
+ | </library-property> | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | When WebSocket connection is enabled, we'll use WebSocketServerPush by default when server-push started. Note that we cannot guarantee the accessing of the information provided by http requests when WebSocket connection is enabled. | ||
+ | |||
+ | =Manual use of Websockets: Employment/Purpose= | ||
+ | {{versionSince|ZK 8}} | ||
+ | ZK has supported a way to share the application data between a ZK application and a websocket application within the same session. Here we demonstrate how to use the <javadoc>org.zkoss.zk.ui.sys.Storage</javadoc> in a desktop scope to share the application data through the websocket channel. | ||
= Example = | = Example = | ||
== Websocket Server == | == Websocket Server == | ||
− | < | + | <syntaxhighlight line lang="java" highlight="2,12"> |
@ServerEndpoint(value ="/echo/", | @ServerEndpoint(value ="/echo/", | ||
configurator = ZKWebSocket.class) | configurator = ZKWebSocket.class) | ||
public class EchoServer { | public class EchoServer { | ||
@OnOpen | @OnOpen | ||
− | public void onOpen(Session session) { | + | public void onOpen(Session session, EndpointConfig config) { |
+ | //since zk 8.6.4 | ||
+ | ZKWebSocket.initZkDesktop(session, config); | ||
} | } | ||
Line 43: | Line 65: | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
As you can see above, in line 2, we have to register a <javadoc>org.zkoss.zk.ui.http.ZKWebSocket</javadoc> class into the configurator of the ''ServerEndpoint'' annotation. | As you can see above, in line 2, we have to register a <javadoc>org.zkoss.zk.ui.http.ZKWebSocket</javadoc> class into the configurator of the ''ServerEndpoint'' annotation. | ||
− | And in line | + | And in line 12 we can use the method of <javadoc method="getDesktopStorage(javax.websocket.Session)">org.zkoss.zk.ui.http.ZKWebSocket</javadoc> to receive the data storage from a websocket session (the storage is a thread-safe implementation). Note that the websocket session must have a '''dtid''' value which is sent from client as follows. |
− | < | + | <syntaxhighlight line lang="js"> |
// Create a new instance of the websocket | // Create a new instance of the websocket | ||
var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid); | var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid); | ||
− | </ | + | </syntaxhighlight> |
== ZK Application == | == ZK Application == | ||
=== MVVM Example === | === MVVM Example === | ||
− | < | + | <syntaxhighlight line lang="xml"> |
<window id="win" apply="org.zkoss.bind.BindComposer" | <window id="win" apply="org.zkoss.bind.BindComposer" | ||
viewModel="@id('vm') @init('org.zkoss.foo.ZKWebSocketViewModel')"> | viewModel="@id('vm') @init('org.zkoss.foo.ZKWebSocketViewModel')"> | ||
Line 63: | Line 85: | ||
</groupbox> | </groupbox> | ||
</window> | </window> | ||
− | </ | + | </syntaxhighlight> |
− | < | + | <syntaxhighlight line lang="java" highlight="1,20,22,26"> |
@ToServerCommand("update") | @ToServerCommand("update") | ||
public class ZKWebSocketViewModel { | public class ZKWebSocketViewModel { | ||
Line 97: | Line 119: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
As you can see above, in line 22 and 26, we can receive the data storage from the desktop object to share or update the application data into it, so that the websocket echo server can use or get the latest data from it or vice versa. | As you can see above, in line 22 and 26, we can receive the data storage from the desktop object to share or update the application data into it, so that the websocket echo server can use or get the latest data from it or vice versa. | ||
=== MVC Example === | === MVC Example === | ||
− | < | + | <syntaxhighlight line lang="xml"> |
<window id="win" apply="org.zkoss.foo.ZKWebSocketComposer"> | <window id="win" apply="org.zkoss.foo.ZKWebSocketComposer"> | ||
<groupbox title="ZK"> | <groupbox title="ZK"> | ||
Line 108: | Line 130: | ||
</groupbox> | </groupbox> | ||
</window> | </window> | ||
− | </ | + | </syntaxhighlight> |
− | < | + | <syntaxhighlight line lang="java" highlight="21,24,26"> |
public class ZKWebSocketComposer extends SelectorComposer<Window> { | public class ZKWebSocketComposer extends SelectorComposer<Window> { | ||
@Wire Label label; | @Wire Label label; | ||
Line 140: | Line 162: | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
As you can see above, in line 21 and 26, we can receive the data storage from the desktop object to share or update the application data into it, so that the websocket echo server can use or get the latest data from it or vice versa. | As you can see above, in line 21 and 26, we can receive the data storage from the desktop object to share or update the application data into it, so that the websocket echo server can use or get the latest data from it or vice versa. | ||
Line 149: | Line 171: | ||
==== MVVM Example ==== | ==== MVVM Example ==== | ||
Here is the MVVM way to send a command from client to server. | Here is the MVVM way to send a command from client to server. | ||
− | < | + | <syntaxhighlight line lang="js"> |
// Create a new instance of the websocket | // Create a new instance of the websocket | ||
var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid); | var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid); | ||
Line 157: | Line 179: | ||
zkbind.$('$win').command('update'); // the update command has already declared in ZKWebSocketViewModel.java | zkbind.$('$win').command('update'); // the update command has already declared in ZKWebSocketViewModel.java | ||
}; | }; | ||
− | </ | + | </syntaxhighlight> |
==== MVC Example ==== | ==== MVC Example ==== | ||
Here is the MVC way to send a command from client to server. | Here is the MVC way to send a command from client to server. | ||
− | < | + | <syntaxhighlight line lang="js"> |
// Create a new instance of the websocket | // Create a new instance of the websocket | ||
var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid); | var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid); | ||
Line 168: | Line 190: | ||
zkservice.$('$win').command('update'); // the update command has already declared in ZKWebSocketComposer.java | zkservice.$('$win').command('update'); // the update command has already declared in ZKWebSocketComposer.java | ||
}; | }; | ||
− | </ | + | </syntaxhighlight> |
== Command Parameter Converter == | == Command Parameter Converter == | ||
− | + | === MVVM Example === | |
When a user triggers a command with some data from client to server, the data should be in a Map (or says Object) type. For example, | When a user triggers a command with some data from client to server, the data should be in a Map (or says Object) type. For example, | ||
− | < | + | <syntaxhighlight line lang="js"> |
zkbind.$('$win').command('update', {foo: 'myfoo', bar: {title: 'myBarTitle'}}); | zkbind.$('$win').command('update', {foo: 'myfoo', bar: {title: 'myBarTitle'}}); | ||
− | </ | + | </syntaxhighlight> |
In the Java code | In the Java code | ||
− | < | + | <syntaxhighlight line lang="java"> |
public static class Bar { | public static class Bar { | ||
private String title; | private String title; | ||
Line 188: | Line 210: | ||
count = desktop.<Integer>getStorage().getItem("count"); | count = desktop.<Integer>getStorage().getItem("count"); | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
As you can see above, the data will automatically be converted into a specific object type according to the method declaration. | As you can see above, the data will automatically be converted into a specific object type according to the method declaration. | ||
'''Note:''' developer can implement a custom <javadoc type="interface">org.zkoss.bind.Converter</javadoc> and specify it into [http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.bind.jsonBindingParamConverter.class the ZK library properties]. | '''Note:''' developer can implement a custom <javadoc type="interface">org.zkoss.bind.Converter</javadoc> and specify it into [http://books.zkoss.org/wiki/ZK_Configuration_Reference/zk.xml/The_Library_Properties/org.zkoss.bind.jsonBindingParamConverter.class the ZK library properties]. | ||
− | + | === MVC Example === | |
When a user triggers a command with some data from client to server, the data should be in an array type in order. For example, | When a user triggers a command with some data from client to server, the data should be in an array type in order. For example, | ||
− | < | + | <syntaxhighlight line lang="js"> |
zkservice.$('$win').command('update', [{foo: "myfoo"}, {bar: "mybar"}]); // the arguments should be in order within an array. | zkservice.$('$win').command('update', [{foo: "myfoo"}, {bar: "mybar"}]); // the arguments should be in order within an array. | ||
− | </ | + | </syntaxhighlight> |
In the Java code | In the Java code | ||
− | < | + | <syntaxhighlight line lang="java"> |
+ | ... | ||
public static class MyFoo { | public static class MyFoo { | ||
private String foo; | private String foo; | ||
Line 210: | Line 233: | ||
// omitted | // omitted | ||
} | } | ||
+ | |||
@Command | @Command | ||
public void update(MyFoo foo, MyBar bar) { | public void update(MyFoo foo, MyBar bar) { | ||
} | } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
As you can see above, the data will automatically be converted into a specific object type according to the method declaration. | As you can see above, the data will automatically be converted into a specific object type according to the method declaration. | ||
Latest revision as of 09:28, 25 October 2024
Websockets for default ZK client-server communications
- Available for ZK:
In addition to manual websocket use, ZK can be configured to use a websocket channel as a replacement for the default /zkau request and response cycles used to send events and updates between the client and the server.
In WEB-INF/zk.xml, add following lines to enable WebSocket connection:
<listener>
<listener-class>org.zkoss.zkmax.au.websocket.WebSocketWebAppInit</listener-class>
</listener>
To change the update URL, you could also add the following lines into WEB-INF/zk.xml. Optional: If not specified, "/zkwm" will be used by default.
<library-property>
<name>org.zkoss.zkmax.au.websocket.WebSocketEndPoint.urlPattern</name>
<value>/yourApp</value>
</library-property>
When WebSocket connection is enabled, we'll use WebSocketServerPush by default when server-push started. Note that we cannot guarantee the accessing of the information provided by http requests when WebSocket connection is enabled.
Manual use of Websockets: Employment/Purpose
Since ZK 8 ZK has supported a way to share the application data between a ZK application and a websocket application within the same session. Here we demonstrate how to use the Storage in a desktop scope to share the application data through the websocket channel.
Example
Websocket Server
1 @ServerEndpoint(value ="/echo/",
2 configurator = ZKWebSocket.class)
3 public class EchoServer {
4 @OnOpen
5 public void onOpen(Session session, EndpointConfig config) {
6 //since zk 8.6.4
7 ZKWebSocket.initZkDesktop(session, config);
8 }
9
10 @OnMessage
11 public void onMessage(String message, Session session){
12 Storage<Integer> storage = ZKWebSocket.getDesktopStorage(session);
13 if ("receive".equals(message)) {
14 Integer count = storage.getItem("count");
15 try {
16 session.getBasicRemote().sendText("Received..." + count);
17 } catch (Exception e) {
18 e.printStackTrace();
19 }
20 } else {
21 try {
22 storage.setItem("count", Integer.parseInt(message));
23 session.getBasicRemote().sendText("Sent..." + message);
24 } catch (IOException ex) {
25 ex.printStackTrace();
26 }
27 }
28 }
29 @OnClose
30 public void onClose(Session session){
31 }
32
33 }
As you can see above, in line 2, we have to register a ZKWebSocket class into the configurator of the ServerEndpoint annotation. And in line 12 we can use the method of ZKWebSocket.getDesktopStorage(Session) to receive the data storage from a websocket session (the storage is a thread-safe implementation). Note that the websocket session must have a dtid value which is sent from client as follows.
1 // Create a new instance of the websocket
2 var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid);
ZK Application
MVVM Example
1 <window id="win" apply="org.zkoss.bind.BindComposer"
2 viewModel="@id('vm') @init('org.zkoss.foo.ZKWebSocketViewModel')">
3 <groupbox title="ZK">
4 <hlayout>count: <label value="@load(vm.count)"/></hlayout>
5 <button label="add" onClick="@command('cmd')"/>
6 </groupbox>
7 </window>
1 @ToServerCommand("update")
2 public class ZKWebSocketViewModel {
3
4 private Integer count;
5
6 @Init
7 public void init(@ContextParam(ContextType.DESKTOP) Desktop desktop) {
8 count = 100;
9 syncToStorage(desktop);
10 }
11
12 @Command
13 @NotifyChange("count")
14 public void cmd(@ContextParam(ContextType.DESKTOP) Desktop desktop) {
15 count++;
16 syncToStorage(desktop);
17 }
18
19 @Command("update")
20 @NotifyChange("count")
21 public void doUpdate(@ContextParam(ContextType.DESKTOP) Desktop desktop) {
22 count = desktop.<Integer>getStorage().getItem("count");
23 }
24
25 private void syncToStorage(Desktop desktop) {
26 Storage<Integer> desktopStorage = desktop.getStorage();
27 desktopStorage.setItem("count", count);
28 }
29 public Integer getCount() {
30 return count;
31 }
32 }
As you can see above, in line 22 and 26, we can receive the data storage from the desktop object to share or update the application data into it, so that the websocket echo server can use or get the latest data from it or vice versa.
MVC Example
1 <window id="win" apply="org.zkoss.foo.ZKWebSocketComposer">
2 <groupbox title="ZK">
3 <hlayout>count: <label id="label" /></hlayout>
4 <button id="btn" label="add"/>
5 </groupbox>
6 </window>
1 public class ZKWebSocketComposer extends SelectorComposer<Window> {
2 @Wire Label label;
3 @Wire Button btn;
4 private Integer count;
5
6 @Override public void doAfterCompose(Window comp) throws Exception {
7 super.doAfterCompose(comp);
8 count = 100;
9 label.setValue("100");
10 syncToStorage();
11 }
12
13 @Listen("onClick = #btn")
14 public void doClick() {
15 count++;
16 label.setValue(String.valueOf(count));
17 syncToStorage();
18 }
19
20 private void syncToStorage() {
21 getSelf().getDesktop().getStorage().setItem("count", count);
22 }
23
24 @Command // this annotation is under the package of org.zkoss.zk.ui.annotation
25 public void update() {
26 count = getSelf().getDesktop().<Integer>getStorage().getItem("count");
27 label.setValue(String.valueOf(count));
28 }
29 }
As you can see above, in line 21 and 26, we can receive the data storage from the desktop object to share or update the application data into it, so that the websocket echo server can use or get the latest data from it or vice versa.
Note: in line 24 Command annotation has been added since the release of ZK 8.0.0, and it is used to receive a notification from client to server. For more details, please take a look at the #Communication section.
Communication
From Websocket server to ZK application
MVVM Example
Here is the MVVM way to send a command from client to server.
1 // Create a new instance of the websocket
2 var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid);
3
4 // receive a message from websocket, and notify ZK application to update the component data.
5 webSocket.onmessage = function(event) {
6 zkbind.$('$win').command('update'); // the update command has already declared in ZKWebSocketViewModel.java
7 };
MVC Example
Here is the MVC way to send a command from client to server.
1 // Create a new instance of the websocket
2 var webSocket = new WebSocket("ws://localhost:8080/zkwebsocket/echo/?dtid=" + zk.$('$win').desktop.uuid);
3
4 // receive a message from websocket, and notify ZK application to update the component data.
5 webSocket.onmessage = function(event) {
6 zkservice.$('$win').command('update'); // the update command has already declared in ZKWebSocketComposer.java
7 };
Command Parameter Converter
MVVM Example
When a user triggers a command with some data from client to server, the data should be in a Map (or says Object) type. For example,
1 zkbind.$('$win').command('update', {foo: 'myfoo', bar: {title: 'myBarTitle'}});
In the Java code
1 public static class Bar {
2 private String title;
3 public void setTitle(String title) { this.title = title; }
4 public String getTitle() { return title; }
5 }
6 @Command("update")
7 @NotifyChange("count")
8 public void doUpdate(@ContextParam(ContextType.DESKTOP) Desktop desktop, @BindingParam("foo") String myfoo, @BindingParam("bar") Bar mybar) {
9 count = desktop.<Integer>getStorage().getItem("count");
10 }
As you can see above, the data will automatically be converted into a specific object type according to the method declaration.
Note: developer can implement a custom Converter and specify it into the ZK library properties.
MVC Example
When a user triggers a command with some data from client to server, the data should be in an array type in order. For example,
1 zkservice.$('$win').command('update', [{foo: "myfoo"}, {bar: "mybar"}]); // the arguments should be in order within an array.
In the Java code
1 ...
2 public static class MyFoo {
3 private String foo;
4 public void setFoo(String foo) { this.foo = foo;}
5 public String getFoo() { return this.foo;}
6 }
7
8 public static class MyBar {
9 // omitted
10 }
11
12 @Command
13 public void update(MyFoo foo, MyBar bar) {
14 }
15 }
As you can see above, the data will automatically be converted into a specific object type according to the method declaration.
Note: developer can implement a custom Converter and specify it into the ZK library properties.