Establish a single two-way connection between the client and the server, which means that the client only needs to send a request to the server, and the server will process it. After processing, it will return it to the client, and the client can While waiting for this time to continue doing other work, the whole process is asynchronous. In this series of tutorials, users will be guided to use the new parsing Json API (JSR-353) in JAVA EE 7 in container GlassFish 4, and use jQuery and Bootstrap in combination. This article requires readers to have a certain basic principle knowledge of HTML 5 Websocket.
Renderings
Let’s first look at the renderings after completing this tutorial, as shown below:
Preparation
We are using JDK 7 and MAVN 3 to build the library. First, look at the part about Jave EE 7 in pom.xml:
<properties> <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir> <project.build.sourceEncoding>UTF-8</project.build.so urceEncoding> </properties> <dependencies> < dependency> <groupId>javax</groupId> <artifactId>javaee-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </de pendencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <sou rce>1.7</source> <target >1.7</target> <compilerArguments> <endorseddirs>${endorsed.dir}</endorseddirs> </compilerArguments> </configuration> </plugin> <plugin> <groupId> org.apache.maven.plugins</groupId > <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configurati on> </plugin> <plugin> <groupId>org.apache. maven.plugins</groupId> <artifactId>maven-dependent-plugin</artifactId> <version>2.6</version> [..] </plugin> </plugins> </build>
At the same time, in order to use GlassFish 4, the following plug-ins need to be added:
plugin> <groupId>org.glassfish.embedded</groupId> <artifactId>maven-embedded-glassfish-plugin</artifactId> <version>4.0</version> <confi guration> <goalPrefix>embedded-glassfish</goalPrefix> < app>${basedir}/target/${project.artifactId}-${project.version}.war</app> <autoDelete>true</autoDelete> <port>8080</port> <name> ${project .artifactId}</name> <contextRoot>hascode</contextRoot> </configuration> <executions> <execution> <goals> <goal>deploy</goal> </goals> </exe cution> </executions> </ plugin>
Setting up the Endpoint of the Websocket
Let's first look at the code of the server Websocket as follows, and then perform further analysis:
package com.hascode.tutorial; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.webso box.EncodeException; import javax.websocket.OnMessage; import javax.websocket .OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; @ServerEndpoint(va lue = "/chat/{room}", encoders = ChatMessageEncoder.class, decoders = ChatMessageDecoder.class) public class ChatEndpoint { private final Logger log = Logger.getLogger(getClass().getName()); @OnOpen public void open(f inal Session session, @PathParam("room") final String room) { log. info("session openend and bound to room: " + room); session.getUserProperties().put("room", room); } @OnMessage public void onMessage(final Session s ession, final ChatMessage chatMessage) { String room = ( String) session.getUserProperties().get("room"); try { for (Session s : session.getOpenSessions()) { if (s.isOpen() && room.equals(s.getUserPropert ies().get(" room"))) { s.getBasicRemote().sendObject(chatMessage); } } } catch (IOException | EncodeException e) { log.log(Level.WARNING, "onMessage failed ", e); } } }
The following analyzes the above code:
Use @ServerEndpoint to define a new endpoint, where the value specifies the URL and can use the PathParams parameter, just as it is used in JAX-RS.
Therefore, the value "/chat/{room}" allows the user to connect to a chat room through the following URL: ws://0.0.0.0:8080/hascode/chat/java
The value in braces (i.e. room) can be injected as a parameter in the endpoint's lifecycle callback method by using javax.websocket.server.PathParam.
In addition, we want to use an encoded and decoded class because we are using a DTO class that is used to transfer data on the server and client side.
When the user connects to the server for the first time and enters the room number to enter the chat room, the room number will be injected into the submission in the form of parameters, and the value is saved in the user's property map using session.getUserProperties.
When a chat participant sends information to the server through the tcp connection, it loops through all the open sessions, each session is bound to the specified chat room, and receives encoded and decoded information.
If we want to send simple text information or information in binary format, we can use session.getBasicRemote().sendBinary() or session.getBasicRemote().sendText()
Next, let's look at the code used to represent the information transfer entity (DTO:Data Transfer Object), as follows:
package com.hascode.tutorial; import java.util.Date; public class ChatMessage { private String message; private String sender; private Date received ; // Other getter,setter methods}
Conversion of chat messages
In this application, an encoding and decoding class will be written for converting between chat information and JSON format.
Let’s first look at the implementation of the decoding class, which will convert the chat information passed to the server into the ChatMessage entity class. Here, the Java API for JSON Processing (JSR353) specification is used to convert JSON format information into entity classes. The code is as follows, the rewritten willDecode method, which is returned to true by default.
package com.hascode.tutorial; import java.io.StringReader; import java.util.Date; import javax.json.Json; import javax.json.JsonObject; import jav ax.websocket.DecodeException; import javax.websocket.Decoder; import javax.websocket.EndpointConfig; public class ChatMessageDecoder implements Decoder.Text<ChatMessage> { @Override public void init(final Endpoint Config config) { } @Override public void destroy() { } @Override public ChatMessage decode(final String textMessage) throws DecodeException { ChatMessage chatMessage = new ChatMessage(); JsonObject obj = Json.createReader(new StringReader(textMessage)) .readObject(); chatMessage.setMe ssage(obj.getString("message")); chatMessage.setSender(obj.getString(" sender")); chatMessage.setReceived(new Date()); return chatMessage; } @Override public boolean willDecode(final String s) { return true; } }
Let’s look at the code of the encoding class. On the contrary, this class converts the ChatMessage class to Json format. The code is as follows:
package com.hascode.tutorial; import javax.json.Json; import javax.websocket.EncodeException; import javax.websocket.Encoder; import javax.we bsocket.EndpointConfig; public class ChatMessageEncoder implements Encoder.Text<ChatMessage> { @Override public void init(final EndpointConfig config) { } @Override public void destroy() { } @Override public String encode(final ChatMessage chatMessage) throws Enco deException { return Json.createObjectBuilder() .add("message", chatMessage.getMessage()) . add("sender", chatMessage.getSender()) .add("received", chatMessage.getReceived().toString()).build() .toString(); } }
Here you can see the powerful power of JSR-353. Just call Json.createObjectBuilder to easily convert a DTO object into JSON.
Build a simple client through Bootstrap and Javacsript
Finally, we comprehensively use the famous Bootstrap, jQuery framework and Javascript to design a simple client. We create a new index.html file in the src/main/weapp directory, and the code is as follows:
<!DOCTYPE html> <html lang="en"> <head> [..] <script> var wsocket; var serviceLocation = "ws://0.0.0.0:8080/hascode/chat/"; var $nickName; var $message; var $chatWindow; var room = ''; function onMessageReceived(evt) { //var msg = eval('(' + evt.data + ')'); var msg = JSON.parse( evt.data ); // native API var $messageLine = $('<tr><td>' + msg.received + '</td><td>' + msg.sender + '</td><td>' + msg .message + '</td></tr>'); $chatWindow.append($messageLine); } function sendMessage() { var msg = '{"message":"' + $message.val() + ' ", "sender":"' + $nickName.val() + '", "received":""}'; wsocket.send(msg); $message.val('').focus(); } function connectToChatserver() { room = $('#chatroom option:selected').val(); wsocket = new WebSocket(serviceLocation + room); wsocket.onmessage = onMe ssageReceived; } function leaveRoom() { wsocket.close(); $ chatWindow.empty(); $('.chat-wrapper').hide(); $('.chat-signin').show(); $nickName.focus(); } $(document).ready(function () { $nickName = $('#nickname'); $message = $('#message'); $chatWindow = $('#response'); $('.chat-wrapper').hide() ; $nickName.focus(); $('#enterRoom').click(function(evt) { evt.preventDefault(); connectToChatserver(); $('.chat-wrapper h2').text( 'Chat # '+ $nickName.val() + "@" + room); $('.chat-signin').hide(); $('.chat-wrapper').show(); $message.focus(); } ); $('#do-chat').submit(function(evt) { evt.preventDefault(); sendMessage() }); $('#leave-room').click(function(){ leaveRoom() : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : ; }); }); </script> </head> <body> <div> <form> <h2>Chat sign in</h2> <label for="nickname">Nickname</label> <input type ="text" placeholder="Nickname" id="nickname"> <div> <label for="chatroom">Chatroom</label> <select size="1" id="chatroom"> <option>arduino</ : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : : option> <option>java</option> <option>groovy</option> <option>scala</option> </select> </div> <button type="submit" id="enterRoom">Sign in< /button> </form> </div> <!-- /container --> <div> <form id="do-chat"> <h2></h2> <table id="response"></ table> <fieldset> <legend>Enter your message..</legend> <div> <input type="text" placeholder="Your message..." id="message" style="height: 60px"/> <input type="submit" value="Send message" /> <button type="button" id="leave-room">Leave room</button> </div> </fieldset> </form> </ div> </body> </html>
In the above code, please pay attention to the following points:
If you want to call websocket on the Javascript side, you must initiate the connection in the following way: ws://IP:PORT/CONTEXT_PATH/ENDPOINT_URL eg ws://0.0.0.0:8080/hascode/chat/java
The method of creating a Websocket connection is very simple, using var wsocket = new WebSocket('ws://0.0.0.0:8080/hascode/chat/java');
To obtain the information returned from the server, you only need to set the corresponding method of obtaining the return information in the callback function wsocket.onmessage.
Send a Websocket message to the server, using wsocket.send(), where the messages that can be sent can be text or binary data.
To close the connection, wsocket.close() is used.
Finally, we deploy the code through mvn package embedded-glassfish:run, and then we can see the effect of the screenshot at the beginning of this article.
The above is a chat room implemented using JavaEE7, Websockets and GlassFish4. I hope it will be helpful to everyone's learning.