Using WebSocket on the client and on the server while keeping their ability to talk to each other
1.1 --- a/chat/client/src/main/java/org/apidesign/demo/chat/ChatClient.java Fri Apr 22 06:14:38 2016 +0200
1.2 +++ b/chat/client/src/main/java/org/apidesign/demo/chat/ChatClient.java Fri Apr 22 08:53:13 2016 +0200
1.3 @@ -30,6 +30,7 @@
1.4 import net.java.html.json.Property;
1.5 import org.apidesign.demo.chat.shared.Message;
1.6 import org.apidesign.demo.chat.shared.Query;
1.7 +import org.apidesign.demo.chat.shared.Reply;
1.8
1.9 /**
1.10 *
1.11 @@ -56,7 +57,7 @@
1.12 Message msg = new Message();
1.13 msg.setUser(m.getUser());
1.14 msg.setComment(m.getComment());
1.15 - m.postComment(m.getUrl(), msg);
1.16 + m.socket(m.getUrl(), new Query().assignPost(msg));
1.17 }
1.18
1.19 static void cantChat(ChatModel model, Exception err) {
1.20 @@ -64,44 +65,22 @@
1.21 }
1.22
1.23
1.24 - @OnReceive(url = "{url}/chat", method = "POST", data = Message.class, onError = "cantChat")
1.25 - static void postComment(ChatModel m, Message addedMessage) {
1.26 - if (addedMessage.getComment().equals(m.getComment())) {
1.27 - m.setComment("");
1.28 + @OnReceive(url = "{url}/chat", method = "WebSocket", data = Query.class, onError = "cantChat")
1.29 + static void socket(ChatModel m, Reply reply) {
1.30 + if (reply == null) {
1.31 + m.getMsgs().add(new Message("System", "Connected to server at " + m.getUrl() + "/chat!", 0));
1.32 + return;
1.33 }
1.34 - }
1.35 -
1.36 - @OnReceive(url = "{url}/chat?since=0", onError = "cantChat")
1.37 - static void initialRead(ChatModel m, Query q) {
1.38 m.getMsgs().clear();
1.39 - m.getMsgs().addAll(q.getMessages());
1.40 - moreMessages(m);
1.41 - }
1.42 -
1.43 - @OnReceive(url = "{url}/chat?since={since}", onError = "cantChat")
1.44 - static void updateMsgs(ChatModel m, Query q) {
1.45 - m.getMsgs().addAll(q.getMessages());
1.46 - moreMessages(m);
1.47 - }
1.48 -
1.49 - private static void moreMessages(ChatModel m) {
1.50 - long now = 0;
1.51 - for (Message msg : m.getMsgs()) {
1.52 - if (now < msg.getSince()) {
1.53 - now = msg.getSince();
1.54 - }
1.55 - }
1.56 - m.updateMsgs(m.getUrl(), "" + (now + 1));
1.57 + m.getMsgs().addAll(reply.getMessages());
1.58 }
1.59
1.60 public static void onPageLoad() {
1.61 ChatModel chm = new ChatModel();
1.62 - chm.setUrl("http://localhost:8080");
1.63 - Message m = new Message();
1.64 - m.setComment("Waiting for messages from the server...");
1.65 - m.setUser("system");
1.66 + chm.setUrl("ws://localhost:8080");
1.67 + Message m = new Message("System", "Waiting for messages from the server...", 0);
1.68 chm.getMsgs().add(m);
1.69 chm.applyBindings();
1.70 - chm.initialRead(chm.getUrl());
1.71 + chm.socket(chm.getUrl(), null);
1.72 }
1.73 }
2.1 --- a/chat/server/pom.xml Fri Apr 22 06:14:38 2016 +0200
2.2 +++ b/chat/server/pom.xml Fri Apr 22 08:53:13 2016 +0200
2.3 @@ -8,82 +8,89 @@
2.4 </parent>
2.5 <artifactId>chat-server</artifactId>
2.6 <packaging>jar</packaging>
2.7 - <name>chat Jersey based Server</name>
2.8 + <name>Chat WebSocket Server</name>
2.9 <properties>
2.10 <maven.compiler.source>1.7</maven.compiler.source>
2.11 <maven.compiler.target>1.7</maven.compiler.target>
2.12 + <grizzly.version>2.3.8</grizzly.version>
2.13 </properties>
2.14 - <dependencies>
2.15 - <dependency>
2.16 - <groupId>org.netbeans.html</groupId>
2.17 - <artifactId>net.java.html.json</artifactId>
2.18 - <version>${net.java.html.version}</version>
2.19 - <type>jar</type>
2.20 - </dependency>
2.21 - <dependency>
2.22 - <groupId>org.glassfish.jersey.media</groupId>
2.23 - <artifactId>html-json</artifactId>
2.24 - <version>${jersey.version}</version>
2.25 - <scope>runtime</scope>
2.26 - <exclusions>
2.27 - <!-- use explicitly specified versions -->
2.28 - <exclusion>
2.29 - <groupId>org.netbeans.html</groupId>
2.30 - <artifactId>net.java.html.json</artifactId>
2.31 - </exclusion>
2.32 - <exclusion>
2.33 - <groupId>org.netbeans.html</groupId>
2.34 - <artifactId>ko-ws-tyrus</artifactId>
2.35 - </exclusion>
2.36 - </exclusions>
2.37 - </dependency>
2.38 - <dependency>
2.39 - <groupId>org.testng</groupId>
2.40 - <artifactId>testng</artifactId>
2.41 - <scope>test</scope>
2.42 - </dependency>
2.43 - <dependency>
2.44 - <groupId>org.glassfish.jersey.connectors</groupId>
2.45 - <artifactId>jersey-grizzly-connector</artifactId>
2.46 - <version>${jersey.version}</version>
2.47 - </dependency>
2.48 - <dependency>
2.49 - <groupId>org.glassfish.jersey.containers</groupId>
2.50 - <artifactId>jersey-container-grizzly2-http</artifactId>
2.51 - <version>${jersey.version}</version>
2.52 - <type>jar</type>
2.53 - </dependency>
2.54 - <dependency>
2.55 - <groupId>org.glassfish.jersey.core</groupId>
2.56 - <artifactId>jersey-common</artifactId>
2.57 - <version>${jersey.version}</version>
2.58 - <scope>runtime</scope>
2.59 - </dependency>
2.60 - <dependency>
2.61 - <artifactId>ko-ws-tyrus</artifactId>
2.62 - <groupId>org.netbeans.html</groupId>
2.63 - <type>jar</type>
2.64 - <version>${net.java.html.version}</version>
2.65 - <scope>runtime</scope>
2.66 - <exclusions>
2.67 - <!-- use explicitly specified versions -->
2.68 - <exclusion>
2.69 - <groupId>org.glassfish.grizzly</groupId>
2.70 - <artifactId>grizzly-framework</artifactId>
2.71 - </exclusion>
2.72 - <exclusion>
2.73 - <groupId>org.glassfish.grizzly</groupId>
2.74 - <artifactId>grizzly-http-server</artifactId>
2.75 - </exclusion>
2.76 - </exclusions>
2.77 - </dependency>
2.78 - <dependency>
2.79 - <groupId>org.apidesign.demo</groupId>
2.80 - <artifactId>chat-shared</artifactId>
2.81 - <version>${project.version}</version>
2.82 - <type>jar</type>
2.83 - </dependency>
2.84 - </dependencies>
2.85 + <dependencies>
2.86 + <!-- compile + runtime -->
2.87 + <dependency>
2.88 + <groupId>org.netbeans.html</groupId>
2.89 + <artifactId>net.java.html</artifactId>
2.90 + <version>${net.java.html.version}</version>
2.91 + <type>jar</type>
2.92 + </dependency>
2.93 + <dependency>
2.94 + <groupId>org.netbeans.html</groupId>
2.95 + <artifactId>net.java.html.json</artifactId>
2.96 + <version>${net.java.html.version}</version>
2.97 + <type>jar</type>
2.98 + </dependency>
2.99 + <dependency>
2.100 + <artifactId>javax.websocket-api</artifactId>
2.101 + <groupId>javax.websocket</groupId>
2.102 + <type>jar</type>
2.103 + <version>1.0</version>
2.104 + </dependency>
2.105 +
2.106 + <!-- tyrus runtime -->
2.107 + <dependency>
2.108 + <groupId>org.glassfish.tyrus</groupId>
2.109 + <artifactId>tyrus-client</artifactId>
2.110 + <version>1.3.1</version>
2.111 + <scope>runtime</scope>
2.112 + </dependency>
2.113 + <dependency>
2.114 + <groupId>org.glassfish.tyrus</groupId>
2.115 + <artifactId>tyrus-container-grizzly-client</artifactId>
2.116 + <version>1.3.1</version>
2.117 + <scope>runtime</scope>
2.118 + </dependency>
2.119 +
2.120 +
2.121 +
2.122 + <!-- test only deps -->
2.123 + <dependency>
2.124 + <groupId>org.netbeans.html</groupId>
2.125 + <artifactId>ko4j</artifactId>
2.126 + <version>${net.java.html.version}</version>
2.127 + <type>jar</type>
2.128 + </dependency>
2.129 + <dependency>
2.130 + <groupId>org.glassfish.grizzly</groupId>
2.131 + <artifactId>grizzly-http-server-core</artifactId>
2.132 + <version>${grizzly.version}</version>
2.133 + <type>jar</type>
2.134 + </dependency>
2.135 + <dependency>
2.136 + <groupId>org.glassfish.grizzly</groupId>
2.137 + <artifactId>grizzly-websockets-server</artifactId>
2.138 + <version>${grizzly.version}</version>
2.139 + <type>jar</type>
2.140 + </dependency>
2.141 + <dependency>
2.142 + <groupId>org.glassfish.grizzly</groupId>
2.143 + <artifactId>grizzly-http-server</artifactId>
2.144 + <version>${grizzly.version}</version>
2.145 + </dependency>
2.146 + <dependency>
2.147 + <groupId>org.glassfish.grizzly</groupId>
2.148 + <artifactId>grizzly-http-servlet</artifactId>
2.149 + <version>${grizzly.version}</version>
2.150 + </dependency>
2.151 + <dependency>
2.152 + <groupId>javax.servlet</groupId>
2.153 + <artifactId>javax.servlet-api</artifactId>
2.154 + <version>3.1.0</version>
2.155 + </dependency>
2.156 + <dependency>
2.157 + <groupId>${project.groupId}</groupId>
2.158 + <artifactId>chat-shared</artifactId>
2.159 + <version>${project.version}</version>
2.160 + </dependency>
2.161 + </dependencies>
2.162 <build>
2.163 <plugins>
2.164 <plugin>
3.1 --- a/chat/server/src/main/java/org/apidesign/demo/chat/server/ChatServerResource.java Fri Apr 22 06:14:38 2016 +0200
3.2 +++ b/chat/server/src/main/java/org/apidesign/demo/chat/server/ChatServerResource.java Fri Apr 22 08:53:13 2016 +0200
3.3 @@ -24,27 +24,14 @@
3.4 package org.apidesign.demo.chat.server;
3.5
3.6 import java.util.ArrayList;
3.7 -import java.util.IdentityHashMap;
3.8 import java.util.List;
3.9 -import java.util.Map;
3.10 import java.util.logging.Logger;
3.11 -import javax.inject.Singleton;
3.12 -import javax.ws.rs.Consumes;
3.13 -import javax.ws.rs.DefaultValue;
3.14 -import javax.ws.rs.GET;
3.15 -import javax.ws.rs.POST;
3.16 -import javax.ws.rs.Path;
3.17 -import javax.ws.rs.Produces;
3.18 -import javax.ws.rs.QueryParam;
3.19 -import javax.ws.rs.container.AsyncResponse;
3.20 -import javax.ws.rs.container.Suspended;
3.21 -import javax.ws.rs.core.MediaType;
3.22 import org.apidesign.demo.chat.shared.Message;
3.23 -import org.apidesign.demo.chat.shared.Query;
3.24 +import org.glassfish.grizzly.websockets.WebSocket;
3.25 +import org.glassfish.grizzly.websockets.WebSocketApplication;
3.26
3.27 /** Server side of the chat application.*/
3.28 -@Path("/chat/") @Singleton
3.29 -public final class ChatServerResource {
3.30 +public final class ChatServerResource extends WebSocketApplication {
3.31 private static final Logger LOG = Logger.getLogger(ChatServerResource.class.getName());
3.32 private static final long started = System.currentTimeMillis() - 10;
3.33
3.34 @@ -56,7 +43,9 @@
3.35 welcome.setSince(10);
3.36 msgs.add(welcome);
3.37 }
3.38 -
3.39 +
3.40 +
3.41 +/*
3.42 private final Map<AsyncResponse, Long> awaiting = new IdentityHashMap<>();
3.43
3.44 @Produces(MediaType.APPLICATION_JSON)
3.45 @@ -100,4 +89,10 @@
3.46 handleAwaiting(msg.getSince());
3.47 return msg;
3.48 }
3.49 +*/
3.50 +
3.51 + @Override
3.52 + public void onMessage(WebSocket socket, String text) {
3.53 + super.onMessage(socket, text);
3.54 + }
3.55 }
4.1 --- a/chat/server/src/main/java/org/apidesign/demo/chat/server/Main.java Fri Apr 22 06:14:38 2016 +0200
4.2 +++ b/chat/server/src/main/java/org/apidesign/demo/chat/server/Main.java Fri Apr 22 08:53:13 2016 +0200
4.3 @@ -23,28 +23,29 @@
4.4 */
4.5 package org.apidesign.demo.chat.server;
4.6
4.7 -import java.io.IOException;
4.8 import java.net.Inet4Address;
4.9 import java.net.InterfaceAddress;
4.10 import java.net.NetworkInterface;
4.11 import java.net.URI;
4.12 import java.util.Enumeration;
4.13 -import javax.ws.rs.container.ContainerRequestContext;
4.14 -import javax.ws.rs.container.ContainerResponseContext;
4.15 -import javax.ws.rs.container.ContainerResponseFilter;
4.16 +import org.glassfish.grizzly.PortRange;
4.17 import org.glassfish.grizzly.http.server.HttpServer;
4.18 -import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
4.19 -import org.glassfish.jersey.server.ResourceConfig;
4.20 +import org.glassfish.grizzly.http.server.NetworkListener;
4.21 +import org.glassfish.grizzly.websockets.WebSocketAddOn;
4.22 +import org.glassfish.grizzly.websockets.WebSocketEngine;
4.23
4.24 /** Starts REST server based on Jersey.
4.25 */
4.26 -final class Main implements ContainerResponseFilter {
4.27 +final class Main {
4.28 public static void main(String... args) throws Exception {
4.29 - ResourceConfig rc = new ResourceConfig(
4.30 - ChatServerResource.class, Main.class
4.31 - );
4.32 + HttpServer server = HttpServer.createSimpleServer(null, new PortRange(8080, 8080));
4.33 + final WebSocketAddOn addon = new WebSocketAddOn();
4.34 + for (NetworkListener listener : server.getListeners()) {
4.35 + listener.registerAddOn(addon);
4.36 + }
4.37 + WebSocketEngine.getEngine().register("", "/chat", new ChatServerResource());
4.38 + server.start();
4.39 URI u = new URI("http://0.0.0.0:8080/");
4.40 - HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u, rc);
4.41 System.err.println("Server running on following IP addresses:");
4.42 dumpIPs();
4.43 System.err.println("Press Enter to shutdown the server");
4.44 @@ -52,17 +53,6 @@
4.45 server.stop();
4.46 }
4.47
4.48 - @Override
4.49 - public void filter(
4.50 - ContainerRequestContext requestContext,
4.51 - ContainerResponseContext r
4.52 - ) throws IOException {
4.53 - r.getHeaders().add("Access-Control-Allow-Origin", "*");
4.54 - r.getHeaders().add("Access-Control-Allow-Credentials", "true");
4.55 - r.getHeaders().add("Access-Control-Allow-Headers", "Content-Type");
4.56 - r.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
4.57 - }
4.58 -
4.59 private static void dumpIPs() throws Exception {
4.60 Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
4.61 while (en.hasMoreElements()) {
5.1 --- a/chat/shared/src/main/java/org/apidesign/demo/chat/shared/MessageImpl.java Fri Apr 22 06:14:38 2016 +0200
5.2 +++ b/chat/shared/src/main/java/org/apidesign/demo/chat/shared/MessageImpl.java Fri Apr 22 08:53:13 2016 +0200
5.3 @@ -51,9 +51,17 @@
5.4 return delta + "min";
5.5 }
5.6
5.7 - @Model(className = "Query", properties = {
5.8 + @Model(className = "Query", builder = "assign", properties = {
5.9 + @Property(name = "since", type = int.class),
5.10 + @Property(name = "post", type = Message.class),
5.11 + })
5.12 + class QueryCntrl {
5.13 + }
5.14 +
5.15 + @Model(className = "Reply", properties = {
5.16 + @Property(name = "since", type = int.class),
5.17 @Property(name = "messages", type = Message.class, array = true)
5.18 })
5.19 - class QryMsgs {
5.20 + class ReplyCntrl {
5.21 }
5.22 }