📄 httppushhandler.java
字号:
/*------------------------------------------------------------------------------Name: HttpPushHandler.javaProject: xmlBlaster.orgCopyright: xmlBlaster.org, see xmlBlaster-LICENSE fileComment: Handling callback over http------------------------------------------------------------------------------*/package org.xmlBlaster.protocol.http;import java.util.logging.Logger;import java.util.logging.Level;import org.xmlBlaster.util.ReplaceVariable;import org.xmlBlaster.util.Global;import org.xmlBlaster.client.I_XmlBlasterAccess;import org.xmlBlaster.client.I_Callback;import org.xmlBlaster.client.key.UpdateKey;import org.xmlBlaster.client.qos.UpdateQos;import java.io.*;import java.util.*;import javax.servlet.*;import javax.servlet.http.*;/** * This handles and hides the different http push modes when sending * data back to the browser through method update(). * <br /> * Push mode works with keeping a permanent http connection * <p /> * TODO: * HTTP 1.1 specifies rfc2616 that the connection stays open as the * default case. How must this code be changed? * <p /> * See Java Servlet Programming from Jason Hunter * @author Marcel Ruff xmlBlaster@marcelruff.info */public class HttpPushHandler implements I_Callback { private String ME = "HttpPushHandler"; private static Logger log = Logger.getLogger(HttpPushHandler.class.getName()); /** * Ping the browser every 10 seconds. * <br /> * You need to adjust ping() in persistenWindow/index.html as well, * if you change the value here. */ private final long PING_INTERVAL = 10000L; private I_XmlBlasterAccess xmlBlasterAccess; private I_Callback callbackInterceptor; private HttpServletRequest req = null; private HttpServletResponse res = null; private String sessionId = null; /** Current http connection state */ private boolean closed = false; private ServletOutputStream outMulti; private PrintWriter outPlain; /** The header of the HTML page */ private String head; /** The tail of the HTML page */ private String tail; /** Check it the browser is ready to accept more messages */ private boolean browserIsReady = false; /** handlesMultipart is true for netscape browser */ private boolean handlesMultipart = false; /** Check browser and holds the http connection */ private HttpPingThread pingThread = null; /** Queue to hold messages (class PushDataItem) until the browser is ready for them */ private Vector pushQueue = null; private boolean firstPush = true; /** * Use this constructor if you are too lazy to pass a HTML header, a default will be used. * @param req The request object * @param res The response object * @param sessionId The browser id * @param loginName For loggin only * @param xmlBlasterAccess Not yet logged in */ public HttpPushHandler(HttpServletRequest req, HttpServletResponse res, String sessionId, String loginName, I_XmlBlasterAccess xmlBlasterAccess) throws ServletException, IOException { this.req = req; this.res = res; this.sessionId = sessionId; this.xmlBlasterAccess = xmlBlasterAccess; String browserId = req.getRemoteAddr() + "-" + loginName + "-" + sessionId; this.ME = "HttpPushHandler-" + browserId; // Setting HTTP headers to prevent caching /* !!! activate when migrating to servlet 2.2 !!! res.addHeader("Expires", "Tue, 31 Dec 1997 23:59:59 GMT"); res.addHeader("Cache-Control", "no-cache"); res.addHeader("Pragma", "no-cache"); */ initialize(null, null); pushQueue = new Vector(); setBrowserIsReady(true); log.fine(ME + " Creating PingThread ..."); pingThread = new HttpPingThread(this, PING_INTERVAL, browserId); } public I_XmlBlasterAccess getXmlBlasterAccess() { return this.xmlBlasterAccess; } /** */ private void initialize(String head, String tail) throws IOException { if (log.isLoggable(Level.FINE)) log.fine("Creating HttpPushHandler ..."); this.head = head; this.tail = tail; this.handlesMultipart = doesHandleMultipart(); if (handlesMultipart) this.outMulti = res.getOutputStream(); else this.outPlain = res.getWriter(); log.fine("Initialize ..."); if (this.head == null) { this.head = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">" + "<HTML>\n" + "<HEAD>\n" + " <meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>\n" + " <meta http-equiv='Pragma' content='no-cache'>\n" + " <meta http-equiv='Cache-Control' content='no-cache'>\n" + " <meta http-equiv='Expires' content='Tue, 31 Dec 1997 23:59:59 GMT'>\n" + " <TITLE>BlasterHttpProxy Connection</TITLE>\n" + "</HEAD>\n" + "<BODY>\n" + " <br> \n" + " <br> \n" + " <form ACTION=\"\" METHOD=\"POST\" TARGET=\"callback\">\n" + " <INPUT TYPE=\"HIDDEN\" NAME=\"NoName\" VALUE=\"NoValue\" />\n" + " </form>\n" + " <script language='JavaScript' type='text/javascript'>\n"; } if (handlesMultipart) { if (this.tail == null) { this.tail = "</script>\n</BODY></HTML>"; } res.setContentType("multipart/x-mixed-replace;boundary=End"); outMulti.println(); outMulti.println("--End"); } else { res.setContentType("text/html"); // bugfix, thanks to Just van den Broecke <just@justobjects.nl> } } /** * Don't forget to call this method when you want to close the connection. */ public void shutdownBrowserConnection() { try { setClosed(true); setBrowserIsReady(false); if (handlesMultipart) { outMulti.close(); } else { outPlain.close(); } pingThread.stopThread(); log.info("Closed push connection to browser"); } catch(Exception e) { log.severe("Error occurred while de-initializing the push handler :"+e.toString()); } } /** * If you implement I_ProxyInterceptor and register it here, * your update() implementation is called and may manipulate the * received message from xmlBlaster before it is sent to the browser. * @param interceptor Your optional implementation */ public void setProxyInterceptor( I_Callback interceptor ) { this.callbackInterceptor = interceptor; } /** * Delegates the cleanup call to HttpPushHandler */ public void cleanup() { if (log.isLoggable(Level.FINER)) log.finer("Entering cleanup(" + sessionId + ") ..."); try { if (this.sessionId != null) { BlasterHttpProxy.cleanup(this.sessionId); } if (this.xmlBlasterAccess != null) { this.xmlBlasterAccess.disconnect(null); log.info("XmlBlaster connection removed"); this.xmlBlasterAccess = null; } this.callbackInterceptor = null; shutdownBrowserConnection(); } catch (Exception e) { e.printStackTrace(); log.severe("Can't destroy http connection for sessionId=" + sessionId + ": " + e.toString()); } } public void startPing() throws ServletException, IOException { pingThread.start(); ping("refresh"); } /** * check's whether the HTTP connection is closed or not */ public boolean closed() { return closed; } /** */ public void setClosed(boolean closed) { if (log.isLoggable(Level.FINE)) log.fine("Setting closed from " + this.closed + " to " + closed); this.closed = closed; } /** * check's whether the browser is ready to accept more messages or not * [if (parent.browserReady != null) parent.browserReady();]. * This shows, that the browser had processed the whole message. * If the Browser implements this javascript function, it could send * a browserReady signal back to the BlasterHttpProxyServlet. * This feature solves the problem, that the browser got messages too fast * and could not process all content of the message. */ public final boolean isBrowserReady() { return browserIsReady; } /** */ public void setBrowserIsReady(boolean ready) { if (closed()) { this.browserIsReady=false; return; } this.browserIsReady = ready; if (log.isLoggable(Level.FINE)) log.fine("Setting browserReady=" + browserIsReady); pong(); // Use this as a browser alive as well, since some internet proxies seem to hold back my pongs //send queue if browser is ready if (browserIsReady) { try { pushToBrowser(); } catch(Exception e) { log.severe("sending push queue to browser failed. ["+e.toString()+"]"); shutdownBrowserConnection(); } } } /** * Determine if the browser can handle multipart pushs. * Only Netscape returns true. */ private boolean doesHandleMultipart() throws IOException { String browser = req.getHeader("User-Agent"); // netscape 4.74: "Mozilla/4.74 [de] (X11; U; Linux 2.2.16 i686)" // opera 4.0b2 (Identify as Opera): "Mozilla/ (Linux; U) Opera 4.01 [en]" boolean doesMulti = false; if (browser == null) doesMulti = false; else if (browser.indexOf("Mozilla") != -1 && browser.indexOf("MSIE") == -1 && browser.indexOf("Opera") == -1) doesMulti = true; // Only real mozillas support multipart request if (doesMulti) log.info("Checking browser = " + browser + ". Assuming it supports 'multipart requests'"); else log.info("Checking browser = " + browser + ". We won't use 'multipart requests'"); return doesMulti; } /** * Updating data to the browser (callback/push mode). * The data must be Javascript code * @param str
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -