/** * Copyright (c)2010-2011 Enterprise Website Content Management System(EWCMS), All rights reserved. * EWCMS PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * http://www.ewcms.com */ package com.ewcms.web.pubsub; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.CometEvent; import org.apache.catalina.CometProcessor; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 该Servlet需要通过Tomcat Comet来实现推送机制,Tomcat 6.0以上版本支持。<br/> * 详细帮助<a href="http://tomcat.apache.org/tomcat-6.0-doc/aio.html">tomcat comet</a>。 * * @author wangwei */ public class PubsubServlet extends HttpServlet implements CometProcessor { private static final long serialVersionUID = 6492410972635853800L; private static final Logger logger = LoggerFactory.getLogger(PubsubServlet.class); private static final String INITIALDELAY_PARAM_NAME = "initialDelay"; private static final String DELAY_PARAM_NAME = "delay"; private static final String PUBSUB_CONTEXT = "pubsub"; private final Map<String,String> pathSender = new HashMap<String,String>(); private final Map<String,PubsubSenderable> senders = Collections.synchronizedMap(new HashMap<String,PubsubSenderable>()); private long initialDelay = 5; private long delay = 15; private void initInitialDelay(){ String value = this.getInitParameter(INITIALDELAY_PARAM_NAME); if(StringUtils.isNumeric(value)){ initialDelay = Long.valueOf(value); } } private void initDelay(){ String value = this.getInitParameter(DELAY_PARAM_NAME); if(StringUtils.isNumeric(value)){ delay =Long.valueOf(value); } } private void initPathMapSender(){ Enumeration<?> names = this.getInitParameterNames(); for(;names.hasMoreElements();){ String path = (String)names.nextElement(); if(path.equals(INITIALDELAY_PARAM_NAME) || path.equals(DELAY_PARAM_NAME)){ continue; } String className = this.getInitParameter(path); String pubSubPath = StringUtils.substringAfter(path, PUBSUB_CONTEXT); pathSender.put(pubSubPath, className); } } @Override public void init() throws ServletException { initInitialDelay(); initDelay(); initPathMapSender(); } @Override public void destroy() { } /** * Comet event处理过程 * * 这部分代码主要参考<a href = "http://tomcat.apache.org/tomcat-6.0-doc/aio.html#Example_code">ChatServlet</a>实现。 * * @param event * The Comet event that will be processed * @throws IOException * @throws ServletException */ public void event(CometEvent event) throws IOException, ServletException { HttpServletRequest request = event.getHttpServletRequest(); HttpServletResponse response = event.getHttpServletResponse(); if (event.getEventType() == CometEvent.EventType.BEGIN) { logger.debug("Begin for session:{} ", request.getSession(true).getId()); String fullContentType = "text/html;charset=UTF-8"; response.setContentType(fullContentType); addConnection(request.getPathInfo(),response); } else if (event.getEventType() == CometEvent.EventType.ERROR) { logger.debug("Error for session: {}", request.getSession(true).getId()); removeConnection(response); event.close(); } else if (event.getEventType() == CometEvent.EventType.END) { logger.debug("End for session:{} ", request.getSession(true).getId()); removeConnection(response); event.close(); } else if (event.getEventType() == CometEvent.EventType.READ) { InputStream is = request.getInputStream(); byte[] buf = new byte[512]; do { int n = is.read(buf); // can throw an IOException if (n > 0) { logger.debug("Read {} bytes:{} for session:{}", new Object[]{n ,new String(buf, 0, n), request.getSession(true).getId()}); } else if (n < 0) { return; } } while (is.available() > 0); } } private PubsubSenderable createPubsubSender(String path){ String name = null; if(StringUtils.indexOf(path, '/') == -1){ name = pathSender.get(path); }else{ String root = StringUtils.substringBeforeLast(path, "/"); name = pathSender.get(root); if(name == null){ String middle = StringUtils.substringAfter(root, "/"); name= pathSender.get(middle); } } if(StringUtils.isNotBlank(name)){ try { Class<?> clazz= Class.forName(name); Class<?>[] argsClass = new Class<?>[]{String.class,ServletContext.class}; Constructor<?> cons = clazz.getConstructor(argsClass); return (PubsubSenderable)cons.newInstance(new Object[]{path,this.getServletContext()}); } catch (Exception e) { logger.error("PubsubSender create error:{}",e.toString()); } } return new NoneSender(); } private void addConnection(String path,HttpServletResponse connection){ synchronized(senders){ PubsubSenderable sender = senders.get(path); if(sender == null){ sender = createPubsubSender(path); sender.setDelay(delay); sender.setInitialDelay(initialDelay); sender.start(); senders.put(path, sender); } sender.addClient(connection); } } private void removeConnection(HttpServletResponse connection){ synchronized(senders){ for(PubsubSenderable sender : senders.values()){ sender.removeClient(connection); } } } }