一、简介:
Comet4J(Comet for Java)是一个纯粹基于AJAX(XMLHTTPRequest)的服务器推送框架,消息以JSON方式传递,具备长轮询、长连接、自动选择三种工作模式。它是一个微型的即时推送框架(类似于goeasy),它分为服务端与客户端两部分,你只要将服务器端(JAR文件,目前仅支持Tomcat6、7)放入WEB-INF\lib,客户端(JavaScript文件)引入到页面,那么你的应用就具备了向客户端推送信息的能力,而你仅需要在服务器端调用Comet4J所提供发送方法,信息就会被主动的推送到客户的浏览器上。
二、使用前准备工作:
1. 修改tomcat配置文件conf/server.xml
修改之前为:
修改之后为:
2. 引入jar文件:
项目中引入comet4j-tomcat7.jar和comen4j.js(下载地址:)https://download.csdn.net/download/dai_haijiao/9985319)。
目前网上只流传有comet4j-tomcat7.jar和comet4j-tomcat6.jar两个版本,先说明一下:
若项目tomcat容器用的是tomcat7,则引用comet4j-tomcat7.jar,
若用的是tomcat6则引用comet4j-tomcat6.jar
3. 修改web.xml配置文件
Comet4JListener
org.comet4j.core.CometAppListener
ListenerClass
com.abc.tools.comet4j.CometUtil
ClientEntrance
CometServlet
org.comet4j.core.CometServlet
CometServlet
/conn
4. 创建推送工具类
a. Comet.java
package com.abc.tools.comet4j;
public class Comet {
/**
* 人像布控告警
*/
public static final String DISPATCHE_ALARM = "100001";
/**
* 设备告警
*/
public static final String DEVICE_ALARM = "100002";
/**
* 被迫下线
*/
public static final String FORCED_LOGOUT = "-1";
private String userId = "";
private String msgStatus = "";
private Object msgData = new Object();
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getMsgStatus() {
return msgStatus;
}
public void setMsgStatus(String msgStatus) {
this.msgStatus = msgStatus;
}
public Object getMsgData() {
return msgData;
}
public void setMsgData(Object msgData) {
this.msgData = msgData;
}
}
b.Constant.java
package com.abc.tools.comet4j;
public class Constant {
public static long EXPIRE_AFTER_ONE_HOUR = 30; // cache过期时间
public static String CHANNEL_MSGSTATUS = "msgStatus";
public static String CHANNEL_MSG_DATA = "msgData";
}
c. Cache.java
package com.abc.tools.comet4j;
public class Cache {
private String key;
private Object value;
private long timeOut;
private boolean expired;
public Cache() {
super();
}
public Cache(String key, String value, long timeOut, boolean expired) {
this.key = key;
this.value = value;
this.timeOut = timeOut;
this.expired = expired;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public long getTimeOut() {
return timeOut;
}
public void setTimeOut(long timeOut) {
this.timeOut = timeOut;
}
public boolean isExpired() {
return expired;
}
public void setExpired(boolean expired) {
this.expired = expired;
}
}
d. CacheManager.java
package com.abc.tools.comet4j;
import java.util.Date;
import java.util.HashMap;
public class CacheManager {
@SuppressWarnings("rawtypes")
private static HashMap cacheMap = new HashMap();
/**
* This class is singleton so private constructor is used.
*/
private CacheManager() {
super();
}
/**
* returns cache item from hashmap
*
* @param key
* @return Cache
*/
private synchronized static Cache getCache(String key) {
return (Cache) cacheMap.get(key);
}
/**
* Looks at the hashmap if a cache item exists or not
*
* @param key
* @return Cache
*/
private synchronized static boolean hasCache(String key) {
return cacheMap.containsKey(key);
}
/**
* Invalidates all cache
*/
public synchronized static void invalidateAll() {
cacheMap.clear();
}
/**
* Invalidates a single cache item
*
* @param key
*/
public synchronized static void invalidate(String key) {
cacheMap.remove(key);
}
/**
* Adds new item to cache hashmap
*
* @param key
* @return Cache
*/
@SuppressWarnings("unchecked")
private synchronized static void putCache(String key, Cache object) {
cacheMap.put(key, object);
}
/**
* Reads a cache item's content
*
* @param key
* @return
*/
public static Cache getContent(String key) {
if (hasCache(key)) {
Cache cache = getCache(key);
if (cacheExpired(cache)) {
cache.setExpired(true);
}
return cache;
} else {
return null;
}
}
/**
*
* @param key
* @param content
* @param ttl
*/
public static void putContent(String key, Object content, long ttl) {
Cache cache = new Cache();
cache.setKey(key);
cache.setValue(content);
cache.setTimeOut(ttl + new Date().getTime());
cache.setExpired(false);
putCache(key, cache);
}
public static void putContent(String key, Object content) {
Cache cache = new Cache();
cache.setKey(key);
cache.setValue(content);
cache.setExpired(false);
putCache(key, cache);
}
/** @modelguid {172828D6-3AB2-46C4-96E2-E72B34264031} */
private static boolean cacheExpired(Cache cache) {
if (cache == null) {
return false;
}
long milisNow = new Date().getTime();
long milisExpire = cache.getTimeOut();
if (milisExpire < 0) { // Cache never expires
return false;
} else if (milisNow >= milisExpire) {
return true;
} else {
return false;
}
}
}
e. CometUtil.java
package com.abc.tools.comet4j;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.comet4j.core.CometConnection;
import org.comet4j.core.CometContext;
import org.comet4j.core.CometEngine;
import org.comet4j.core.event.ConnectEvent;
import org.comet4j.core.listener.ConnectListener;
public class CometUtil extends ConnectListener implements ServletContextListener {
/**
* 初始化上下文
*/
public void contextInitialized(ServletContextEvent arg0) {
// CometContext : Comet4J上下文,负责初始化配置、引擎对象、连接器对象、消息缓存等。
CometContext cc = CometContext.getInstance();
// 注册频道,即标识哪些字段可用当成频道,用来作为向前台传送数据的“通道”
cc.registChannel(Constant.CHANNEL_MSGSTATUS);
cc.registChannel(Constant.CHANNEL_MSG_DATA);
// 添加监听器
CometEngine engine = CometContext.getInstance().getEngine();
engine.addConnectListener(this);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
}
@Override
public boolean handleEvent(ConnectEvent connEvent) {
// TODO Auto-generated method stub
final CometConnection conn = connEvent.getConn();
Object userId = conn.getRequest().getSession().getAttribute("userId");// userId是当前登录用户的id,用户登录成功后存于session中
if (null == userId) {
return false;
}
CacheManager.putContent(userId.toString(), connEvent);
return true;
}
private void doCache(final CometConnection conn, String userId) {
if (userId != null) {
CacheManager.putContent(conn.getId(), String.valueOf(userId), Constant.EXPIRE_AFTER_ONE_HOUR);
}
}
/**
* 推送给所有的客户端
*
* @param comet
*/
public void pushToAll(Comet comet) {
try {
CometEngine engine = CometContext.getInstance().getEngine();
// 推送到所有客户端
engine.sendToAll(Constant.CHANNEL_MSGSTATUS, comet.getMsgStatus());
engine.sendToAll(Constant.CHANNEL_MSG_DATA, comet.getMsgData());
} catch (Exception e) {
// TODO: handle exception
System.out.println(e.getMessage());
}
}
/**
* 推送给指定客户端
*
* @param comet
*/
public void pushTo(Comet comet) {
try {
Cache cache = CacheManager.getContent(comet.getUserId());
if (null == cache) {
return;
}
ConnectEvent connEvent = (ConnectEvent) cache.getValue();
final CometConnection conn = connEvent.getConn();
// 建立连接和用户的关系
doCache(conn, comet.getUserId());
final String connId = conn.getId();
CometEngine engine = CometContext.getInstance().getEngine();
if (CacheManager.getContent(connId).isExpired()) {
doCache(conn, comet.getUserId());
}
// 推送到指定的客户端
engine.sendTo(Constant.CHANNEL_MSGSTATUS, engine.getConnection(connId), comet.getMsgStatus());
engine.sendTo(Constant.CHANNEL_MSG_DATA, engine.getConnection(connId), comet.getMsgData());
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
三、后台进行推送实例
用法1(进行消息推送):
后台代码
package com.abc.controller.univiewinterface;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.abc.entity.DeviceAlarm;
import com.abc.entity.DispatcheAlarm;
import com.abc.entity.Result;
import com.abc.service.DeviceAlarmService;
import com.abc.service.DispatcheAlarmService;
import com.abc.tools.comet4j.Comet;
import com.abc.tools.comet4j.CometUtil;
import net.sf.json.JSONObject;
/**
* 告警控制层
*
* @author DaiHaijiao
*
*/
@Controller
@RequestMapping("/alarm/*")
public class AlarmController {
@Autowired
private DeviceAlarmService deviceAlarmService;
@Autowired
private DispatcheAlarmService dispatcheAlarmService;
private static Map map = null;
private static CometUtil cometUtil = null;
/**
* 设备告警
*
* @param param
*/
@SuppressWarnings("unlikely-arg-type")
@RequestMapping(value = "common.do", method = RequestMethod.POST, consumes = "application/json")
public @ResponseBody Result commonAlarm(@RequestBody String param) {
JSONObject json = JSONObject.fromObject(param);
// 保存设备告警到本地数据库
DeviceAlarm deviceAlarm = new DeviceAlarm();
String alarmContet = json.getString("contet");
deviceAlarm.setContent(alarmContet);// 告警内容
String alarmTime = json.getString("time");
deviceAlarm.setTime(alarmTime);// 告警时间
deviceAlarm = deviceAlarmService.add(deviceAlarm);
// 实例化消息推送类
if (cometUtil == null) {
cometUtil = new CometUtil();
}
// 进行推送消息设置
map.put("id", deviceAlarm.getId() + "");
map.put("msgStatus", Comet.DEVICE_ALARM);
map.put("content", alarmContet);
map.put("time", alarmTime);
// 推送消息到所有用户
Comet comet = new Comet();
comet.setMsgStatus(Comet.DEVICE_ALARM);
comet.setMsgData(map);
cometUtil.pushToAll(comet);
return Result.genSuccess();
}
/**
* 布控告警
*
* @param param
*/
@SuppressWarnings("unlikely-arg-type")
@RequestMapping(value = "faceSurveillance.do", method = RequestMethod.POST, consumes = "application/json")
public @ResponseBody Result faceSurveillanceAlarm(@RequestBody String param) {
JSONObject json = JSONObject.fromObject(param);
// 保存布控告警到本地数据库
DispatcheAlarm dispatcheAlarm = new DispatcheAlarm();
String alarmTime = json.getString("time");// 告警时间
String alarmContet = json.getString("contet");// 告警内容
String userId = json.getString("userId");// 要推送的用户
dispatcheAlarm.setContent(alarmContet);
dispatcheAlarm.setTime(alarmTime);
dispatcheAlarm = dispatcheAlarmService.add(dispatcheAlarm);
// 实例化消息推送类
if (cometUtil == null) {
cometUtil = new CometUtil();
}
// 进行推送消息设置
map = new HashMap();
map.put("id", dispatcheAlarm.getId() + "");
map.put("msgStatus", Comet.DISPATCHE_ALARM);
map.put("content", alarmContet);
map.put("time", alarmTime);
// 推送消息到指定用户
Comet comet = new Comet();
comet.setUserId(userId);
comet.setMsgStatus(Comet.DISPATCHE_ALARM);
comet.setMsgData(map);
cometUtil.pushTo(comet);
return Result.genSuccess();
}
}
页面接收消息
接收前准备:页面引入
在页面初始化js里增加消息接收相关代码,效果如下:
$(function() {
//...
//...Something...
//...
// 建立连接,conn 即web.xml中 CometServlet的
JS.Engine.start('../../conn');//根据自己项目情况进行修改
// 监听后台某个频道
JS.Engine.on({
// 对应服务端 “频道1” 的值 msgStatus
msgStatus : function(msgStatus) {
},
// 对应服务端 “频道2” 的值 msgData
msgData : function(msgData) {
if (msgData.msgStatus == "100001") {//布控告警
var id = msgData.id;
var content = msgData.content;
var time = msgData.time;
//...值已经拿到
//...To do something
//...
} else if (msgData.msgStatus == "100002") {//设备告警
var id = msgData.id;
var content = msgData.content;
var time = msgData.time;
//...值已经拿到
//...To do something
//...
}
},
});
//...
//...Something...
//...
})
用法2(实现同一个账号只能在一个地方登陆):
后台代码
/**
* 用户登录
*
* @param username
* @param password
* @return Result
*/
@RequestMapping(value = "login.do")
public @ResponseBody Result login(String username, HttpSession session, String password) {
try {
//用户登录
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(true);
subject.login(token);
Object userId = (User) subject.getPrincipal().getId();
CometUtil cometUtil = new CometUtil();// 实例化消息推送类
// set值
Comet comet = new Comet();
comet.setUserId(userId + "");
comet.setMsgStatus(Comet.FORCED_LOGOUT);
// (个推)推送消息到频道1
cometUtil.pushTo(comet);
session.setAttribute("userId", userId);//登录成功后session中放入userId(个推用)
return Result.genSuccess();
} catch (Exception e) {
e.printStackTrace();
return Result.genResult(Result.USER_PASSWORD_WRONG);
}
}
页面代码(页面除了需要引用comet4j.js还有引入jquery.cookie.js)
增加cookie是为了避免用户收到"您的账号在另一地点登录,您已被迫下线!"提示以后直接刷新页面!
$(function() {
var loginStatus = $.cookie("main_loginStatus");//登录状态
if (typeof (loginStatus) == "undefined") {
$.cookie("main_loginStatus", false);//false:用户不掉线标识
} else if (loginStatus == true || loginStatus == "true") {
$.cookie("main_loginStatus", null);//删除登录状态cookie
window.location.href = "../../logout";//请求登出方法
return false;
}
// 建立连接,conn 即web.xml中 CometServlet的
JS.Engine.start('../../conn');
// 监听后台某个频道
JS.Engine.on({
// 对应服务端 “频道1” 的值 msgStatus
msgStatus : function(msgStatus) {
if (msgStatus == "-1") {
$.cookie("main_loginStatus", true);//true:让用户自动掉线
//给用户提示(然后不管用户点击的是确定还是取消,都让他自动退出)
if (confirm("您的账号在另一地点登录,您已被迫下线!")) {
$.cookie("main_loginStatus", null);//删除登录状态cookie
window.location.href = "../../logout";//请求登出方法
} else {
$.cookie("main_loginStatus", null);//删除登录状态cookie
window.location.href = "../../logout";//请求登出方法
}
}
},
// 对应服务端 “频道2” 的值 msgData
msgData : function(msgData) {
},
});
})
用法3(自行去研究...)
四、使用中发现
1. 项目中引入comet4j-tomcat7.jar,且tomcat为8.0.X时,使用一切正常,“F12”查看控制台的“Network”,会发现“conn?cmd=conn&cv=0.0.2&ram=0.7725908122133494”访问正常,点击进去,控制台显示:
<{data:{channel:["msgStatus","msgData"],ws:"stream",timeout:60000,cld:"08be33b1-c409-4177-ba79-74e551379923"},channel:"",time:"1527580183535}">
2. 项目中引入comet4j-tomcat7.jar,且tomcat为8.5.X时,eclipse控制台会报错,信息如下:
五月 29, 2018 4:14:31 下午 org.apache.catalina.core.StandardHostValve invoke
严重: Exception Processing /conn
java.lang.NoClassDefFoundError: org/apache/catalina/comet/CometProcessor
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2277)
at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:811)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1254)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
at org.apache.catalina.core.StandardWrapper.servletSecurityAnnotationScan(StandardWrapper.java:1128)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: org.apache.catalina.comet.CometProcessor
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1285)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
... 23 more
五月 29, 2018 4:16:27 下午 org.apache.catalina.core.StandardHostValve invoke
严重: Exception Processing /conn
java.lang.NoClassDefFoundError: org/apache/catalina/comet/CometProcessor
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2277)
at org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:811)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1254)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
at org.apache.catalina.core.StandardWrapper.servletSecurityAnnotationScan(StandardWrapper.java:1128)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: org.apache.catalina.comet.CometProcessor
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1285)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
... 23 more
“F12”查看控制台的“Network”,会发现“conn?cmd=conn&cv=0.0.2&ram=0.7725908122133494”有“500”错误,点击进去,控制台显示如同eclipse控制台所报错误信息:
HTTP Status 500 – Internal Server Error
Type Exception Report
Message org/apache/catalina/comet/CometProcessor
Description The server encountered an unexpected condition that prevented it from fulfilling the request.
Exception
java.lang.NoClassDefFoundError: org/apache/catalina/comet/CometProcessor
java.lang.ClassLoader.defineClass1(Native Method)
java.lang.ClassLoader.defineClass(Unknown Source)
java.security.SecureClassLoader.defineClass(Unknown Source)
org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2277)
org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:811)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1254)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Unknown Source)
Root Cause
java.lang.ClassNotFoundException: org.apache.catalina.comet.CometProcessor
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1285)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
java.lang.ClassLoader.defineClass1(Native Method)
java.lang.ClassLoader.defineClass(Unknown Source)
java.security.SecureClassLoader.defineClass(Unknown Source)
org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2277)
org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:811)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1254)
org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1119)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:462)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1457)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Unknown Source)
Note The full stack trace of the root cause is available in the server logs.
综上,总结出:tomcat8.0.X版本是可以结合comet4j-tomcat7.jar进行使用的。