JavaEE7+Websockets+GlassFish4打造聊天室

在客户机和服务器之间建立单一的双向连接,这就意味着客户只需要发送一个请求到服务端,那么服务端则会进行处理,处理好后则将其返回给客户端,客户端则可以在等待这个时间继续去做其他工作,整个过程是异步的。在本系列教程中,将指导用户如何在JAVA EE 7的容器GlassFish 4中,使用JAVA EE 7中的全新的解析Json API(JSR-353),以及综合运用jQuery和Bootstrap。本文要求读者有一定的HTML 5 Websocket的基础原理知识。

效果图

我们先来看下在完成这个教程后的效果图,如下所示:

准备工作

我们使用的是JDK 7 和MAVN 3进行库的构建工作,首先看pom.xml中关于Jave EE 7的部分:

<properties>

<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

<dependencies>

<dependency>

<groupId>javax</groupId>

<artifactId>javaee-api</artifactId>

<version>7.0</version>

<scope>provided</scope>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-compiler-plugin</artifactId>

<version>3.1</version>

<configuration>

<source>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>

</configuration>

</plugin>

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-dependency-plugin</artifactId>

<version>2.6</version>

[..]

</plugin>

</plugins>

</build>

同时,为了能使用GlassFish 4,需要增加如下的插件:

plugin>

<groupId>org.glassfish.embedded</groupId>

<artifactId>maven-embedded-glassfish-plugin</artifactId>

<version>4.0</version>

<configuration>

<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>

</execution>

</executions>

</plugin>

设置Websocket的Endpoint

我们先来看服务端Websocket的代码如下,然后再做进一步解析:

package com.hascode.tutorial;

import java.io.IOException;

import java.util.logging.Level;

import java.util.logging.Logger;

import javax.websocket.EncodeException;

import javax.websocket.OnMessage;

import javax.websocket.OnOpen;

import javax.websocket.Session;

import javax.websocket.server.PathParam;

import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/chat/{room}", encoders = ChatMessageEncoder.class, decoders = ChatMessageDecoder.class)

public class ChatEndpoint {

private final Logger log = Logger.getLogger(getClass().getName());

@OnOpen

public void open(final 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 session, final ChatMessage chatMessage) {

String room = (String) session.getUserProperties().get("room");

try {

for (Session s : session.getOpenSessions()) {

if (s.isOpen()

&& room.equals(s.getUserProperties().get("room"))) {

s.getBasicRemote().sendObject(chatMessage);

}

}

} catch (IOException | EncodeException e) {

log.log(Level.WARNING, "onMessage failed", e);

}

}

}

下面分析下上面的代码:

使用@ ServerEndpoint定义一个新的endpoint,其中的值指定了URL并且可以使用PathParams参数,就象在JAX-RS中的用法一样。

所以值“/chat/{room}”允许用户通过如下形式的URL去连接某个聊天室:ws://0.0.0.0:8080/hascode/chat/java

在大括号中的值(即room),可以通过使用javax.websocket.server.PathParam,在endpoint的生命周期回调方法中以参数的方式注入。

此外,我们要使用一个编码和解码的类,因为我们使用的是一个DTO形式的类,用于在服务端和客户端传送数据。

当用户第一次连接到服务端,输入要进入聊天室的房号,则这个房号以参数的方式注入提交,并且使用session.getUserProperties将值保存在用户的属性map中。

当一个聊天参与者通过tcp连接发送信息到服务端,则循环遍历所有已打开的session,每个session被绑定到指定的聊天室中,并且接收编码和解码的信息。

如果我们想发送简单的文本信息或和二进制格式的信息,则可以使用session.getBasicRemote().sendBinary() 或session.getBasicRemote().sendText()

接下来我们看下用于代表信息传递实体(DTO:Data Transfer Object)的代码,如下:

package com.hascode.tutorial;

import java.util.Date;

public class ChatMessage {

private String message;

private String sender;

private Date received;

// 其他getter,setter方法

}

聊天消息的转换

在这个应用中,将编写一个编码和解码类,用于在聊天信息和JSON格式间进行转换。

先来看下解码类的实现,这将会把传递到服务端的聊天信息转换为ChatMessage实体类。在这里,使用的是Java API for JSON Processing(JSR353)规范去将JSON格式的信息转换为实体类,代码如下,其中重写的willDecode方法,这里默认返回为true。

package com.hascode.tutorial;

import java.io.StringReader;

import java.util.Date;

import javax.json.Json;

import javax.json.JsonObject;

import javax.websocket.DecodeException;

import javax.websocket.Decoder;

import javax.websocket.EndpointConfig;

public class ChatMessageDecoder implements Decoder.Text<ChatMessage> {

@Override

public void init(final EndpointConfig 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.setMessage(obj.getString("message"));

chatMessage.setSender(obj.getString("sender"));

chatMessage.setReceived(new Date());

return chatMessage;

}

@Override

public boolean willDecode(final String s) {

return true;

}

}

同样再看下编码类的代码,这个类相反,是将ChatMessage类转换为Json格式,代码如下:

package com.hascode.tutorial;

import javax.json.Json;

import javax.websocket.EncodeException;

import javax.websocket.Encoder;

import javax.websocket.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 EncodeException {

return Json.createObjectBuilder()

.add("message", chatMessage.getMessage())

.add("sender", chatMessage.getSender())

.add("received", chatMessage.getReceived().toString()).build()

.toString();

}

}

这里可以看到JSR-353的强大威力,只需要调用Json.createObjectBuilder就可以轻易把一个DTO对象转化为JSON了。

通过Bootstrap、Javacsript搭建简易客户端

最后,我们综合运用著名的Bootstrap、jQuery框架和Javascript设计一个简易的客户端。我们在src/main/weapp目录下新建立index.html文件,代码如下:

<!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 class="received">' + msg.received

+ '</td><td class="user label label-info">' + msg.sender

+ '</td><td class="message badge">' + 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 = onMessageReceived;

}

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 class="container chat-signin">

<form class="form-signin">

<h2 class="form-signin-heading">Chat sign in</h2>

<label for="nickname">Nickname</label> <input type="text"

class="input-block-level" placeholder="Nickname" id="nickname">

<div class="btn-group">

<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 class="btn btn-large btn-primary" type="submit"

id="enterRoom">Sign in</button>

</form>

</div>

<!-- /container -->

<div class="container chat-wrapper">

<form id="do-chat">

<h2 class="alert alert-success"></h2>

<table id="response" class="table table-bordered"></table>

<fieldset>

<legend>Enter your message..</legend>

<div class="controls">

<input type="text" class="input-block-level" placeholder="Your message..." id="message" style="height:60px"/>

<input type="submit" class="btn btn-large btn-block btn-primary"

value="Send message" />

<button class="btn btn-large btn-block" type="button" id="leave-room">Leave

room</button>

</div>

</fieldset>

</form>

</div>

</body>

</html>

在上面的代码中,要注意如下几点:

在Javascript端要调用websocket的话,要用如下的方式发起连接即可:ws://IP:PORT/CONTEXT_PATH/ENDPOINT_URL e.g ws://0.0.0.0:8080/hascode/chat/java

创建一个Websocket连接的方法很简单,使用的是var wsocket = new WebSocket(‘ws://0.0.0.0:8080/hascode/chat/java');

要获得来自服务端返回的信息,只需要在回调函数wsocket.onmessage中设置对应的获取返回信息的方法即可。

发送一个Websocket消息到服务端,使用的方法是wsocket.send(),其中可以发送的消息可以文本或者二进制数据。

关闭连接使用的是wsocket.close()。

最后,我们通过mvn package embedded-glassfish:run进行代码的部署,然后就可以看到本文开始部分截图的效果。

以上就是用JavaEE7、Websockets和GlassFish4实现的聊天室,希望对大家的学习有所帮助。

以上是 JavaEE7+Websockets+GlassFish4打造聊天室 的全部内容, 来源链接: utcz.com/p/209041.html

回到顶部