Skip to content

Instantly share code, notes, and snippets.

@greycode
Created September 27, 2014 06:32
Show Gist options
  • Save greycode/088587fc6b55b2e8a4d3 to your computer and use it in GitHub Desktop.
Save greycode/088587fc6b55b2e8a4d3 to your computer and use it in GitHub Desktop.
Atmosphere Framework简易聊天应用
package org.demoweb.ws;
import java.io.IOException;
import org.atmosphere.cache.UUIDBroadcasterCache;
import org.atmosphere.client.TrackMessageSizeInterceptor;
import org.atmosphere.config.service.AtmosphereHandlerService;
import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResponse;
import org.atmosphere.handler.OnMessage;
import org.atmosphere.interceptor.AtmosphereResourceLifecycleInterceptor;
import org.atmosphere.interceptor.BroadcastOnPostAtmosphereInterceptor;
import org.atmosphere.interceptor.HeartbeatInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
@AtmosphereHandlerService(
path = "/ws/chat",
broadcasterCache = UUIDBroadcasterCache.class,
broadcaster = JGroupsBroadcaster.class,
interceptors = {
AtmosphereResourceLifecycleInterceptor.class,
BroadcastOnPostAtmosphereInterceptor.class,
TrackMessageSizeInterceptor.class, HeartbeatInterceptor.class })
public class ChatRoom extends OnMessage<String> {
private final Logger logger = LoggerFactory.getLogger(ChatRoom.class);
private final ObjectMapper mapper = new ObjectMapper();
@Override
public void onMessage(AtmosphereResponse response, String message)
throws IOException {
response.write(mapper.writeValueAsString(mapper.readValue(message,
Data.class)));
}
@Override
public void onOpen(AtmosphereResource resource) throws IOException {};
@Override
public void onDisconnect(AtmosphereResponse response) throws IOException {
// TODO Auto-generated method stub
super.onDisconnect(response);
//response.
}
// @Ready
// public void onReady(final AtmosphereResource r) {
//
// logger.info("Browser {} connected.", r.uuid());
// }
}
package org.demoweb.ws;
import java.util.Date;
public final class Data {
private String message;
private String author;
private long time;
public Data() {
this("","");
}
public Data(String author, String message) {
this.author = author;
this.message = message;
this.time = new Date().getTime();
}
public String getMessage() {
return message;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public void setMessage(String message) {
this.message = message;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}
/**
*
*/
$(function () {
"use strict";
var header = $('#header');
var content = $('#content');
var input = $('#input');
var status = $('#status');
var myName = false;
var author = null;
var logged = false;
var socket = $.atmosphere;
var subSocket;
var transport = 'websocket';
// alert(document.location.toString() + 'ws/chat')
// We are now ready to cut the request
var request = {
url: document.location.toString() + 'ws/chat',
contentType : "application/json",
trackMessageLength : true,
shared : true,
transport : transport ,
fallbackTransport: 'long-polling'};
request.onOpen = function(response) {
content.html($('<p>', { text: 'Atmosphere connected using ' + response.transport }));
input.removeAttr('disabled').focus();
status.text('Choose name:');
transport = response.transport;
if (response.transport == "local") {
subSocket.pushLocal("Name?");
}
};
request.onTransportFailure = function(errorMsg, request) {
jQuery.atmosphere.info(errorMsg);
if (window.EventSource) {
request.fallbackTransport = "sse";
transport = "see";
}
header.html($('<h3>', { text: 'Atmosphere Chat. Default transport is WebSocket, fallback is ' + request.fallbackTransport }));
};
request.onMessage = function (response) {
// We need to be logged first.
if (!myName) return;
var message = response.responseBody;
try {
var json = jQuery.parseJSON(message);
} catch (e) {
console.log('This doesn\'t look like a valid JSON: ', message.data);
return;
}
if (!logged) {
logged = true;
status.text(myName + ': ').css('color', 'blue');
input.removeAttr('disabled').focus();
subSocket.pushLocal(myName);
} else {
input.removeAttr('disabled');
var me = json.author == author;
var date = typeof(json.time) == 'string' ? parseInt(json.time) : json.time;
addMessage(json.author, json.message, me ? 'blue' : 'black', new Date(date));
}
};
request.onClose = function(response) {
logged = false;
}
subSocket = socket.subscribe(request);
input.keydown(function(e) {
if (e.keyCode === 13) {
var msg = $(this).val();
if (author == null) {
author = msg;
}
subSocket.push(jQuery.stringifyJSON({ author: author, message: msg }));
$(this).val('');
input.attr('disabled', 'disabled');
if (myName === false) {
myName = msg;
}
}
});
function addMessage(author, message, color, datetime) {
content.append('<p><span style="color:' + color + '">' + author + '</span> @ ' +
+ (datetime.getHours() < 10 ? '0' + datetime.getHours() : datetime.getHours()) + ':'
+ (datetime.getMinutes() < 10 ? '0' + datetime.getMinutes() : datetime.getMinutes())
+ ': ' + message + '</p>');
}
});
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<style>
#content {padding: 5px; background: #ddd; border-radius: 5px; border: 1px solid #CCC; margin-top: 10px;}
#header {padding: 5px; background: #f5deb3; border-radius: 5px; border: 1px solid #CCC; margin-top: 10px;}
#input {border-radius: 2px; border: 1px solid #ccc; margin-top: 10px; padding: 5px; width: 400px;}
#status {width: 88px; display: block; float: left; margin-top: 15px;}
</style>
</head>
<body>
<button id="btn-snap" type="button" class="btn btn-info" onclick="callJava()"><i class="icon-hand-right"></i>Snap</button>
<div id="header"><h3>Atmosphere Chat. Default transport is WebSocket, fallback is long-polling</h3></div>
<div id="content"></div>
<div>
<span id="status">Connecting...</span>
<input type="text" id="input" disabled="disabled"/>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="static/jquery/jquery-2.1.1.min.js"></script>
<script src="static/ws/atmosphere.js"></script>
<script src="static/ws/jquery.atmosphere.js"></script>
<script src="static/ws/home-ws.js"></script>
</body>
</html>
package org.demoweb.ws;
import java.io.Serializable;
import java.util.concurrent.CountDownLatch;
import org.atmosphere.config.service.BroadcasterService;
import org.atmosphere.cpr.AtmosphereConfig;
import org.atmosphere.cpr.Broadcaster;
import org.atmosphere.util.AbstractBroadcasterProxy;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.ReceiverAdapter;
import org.jgroups.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Simple {@link org.atmosphere.cpr.Broadcaster} implementation based on JGroups
*
* @author Jeanfrancois Arcand
*/
@BroadcasterService
public class JGroupsBroadcaster extends AbstractBroadcasterProxy {
private static final Logger logger = LoggerFactory.getLogger(JGroupsBroadcaster.class);
private JChannel jchannel;
private final CountDownLatch ready = new CountDownLatch(1);
public JGroupsBroadcaster(){}
public Broadcaster initialize(String id, AtmosphereConfig config) {
return super.initialize(id, null, config);
}
@Override
public void incomingBroadcast() {
try {
logger.info("Starting Atmosphere JGroups Clustering support with group name {}", getID());
jchannel = new JChannel();
jchannel.setReceiver(new ReceiverAdapter() {
/** {@inheritDoc} */
@Override
public void receive(final Message message) {
final Object msg = message.getObject();
if (msg != null && BroadcastMessage.class.isAssignableFrom(msg.getClass())) {
BroadcastMessage b = BroadcastMessage.class.cast(msg);
if (b.getTopicId().equalsIgnoreCase(getID())) {
broadcastReceivedMessage(b.getMessage());
}
}
}
});
jchannel.connect(getID());
} catch (Throwable t) {
logger.warn("failed to connect to JGroups channel", t);
} finally {
ready.countDown();
}
}
/**
* {@inheritDoc}
*/
@Override
public void outgoingBroadcast(Object message) {
try {
ready.await();
jchannel.send(new Message(null, null, new BroadcastMessage(getID(), message)));
} catch (Throwable e) {
logger.error("failed to send messge over Jgroups channel", e.getMessage());
}
}
/**
* {@inheritDoc}
*/
@Override
public synchronized void destroy() {
super.destroy();
if (!jchannel.isOpen()) return;
try {
Util.shutdown(jchannel);
} catch (Throwable t) {
Util.close(jchannel);
logger.warn("failed to properly shutdown jgroups channel, closing abnormally", t);
}
}
public static class BroadcastMessage implements Serializable {
private final String topicId;
private final Object message;
public BroadcastMessage(String topicId, Object message) {
this.topicId = topicId;
this.message = message;
}
public String getTopicId() {
return topicId;
}
public Object getMessage() {
return message;
}
}
}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>wsdemo</groupId>
<artifactId>wsdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<atmosphere.version>2.2.1</atmosphere.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-runtime</artifactId>
<version>${atmosphere.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.4.2</version>
</dependency>
<!--
<dependency>
<groupId>org.atmosphere</groupId>
<artifactId>atmosphere-jgroups</artifactId>
<version>2.2.0</version>
</dependency>
-->
<dependency>
<groupId>org.jgroups</groupId>
<artifactId>jgroups</artifactId>
<version>3.5.0.Final</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>wsdemo</display-name>
<servlet>
<description>AtmosphereServlet</description>
<servlet-name>AtmosphereServlet</servlet-name>
<servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
<!-- If you want to use Servlet 3.0 -->
<async-supported>true</async-supported>
<!-- List of init-param -->
<init-param>
<param-name>org.atmosphere.cpr.broadcaster.shareableThreadPool</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>AtmosphereServlet</servlet-name>
<!-- Any mapping -->
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment