All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.logicbus.backend.message.SSEMessage Maven / Gradle / Ivy

There is a newer version: 1.6.17
Show newest version
package com.logicbus.backend.message;

import com.alogic.xscript.*;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.log.LogInfo;
import com.alogic.xscript.plugins.Segment;
import com.anysoft.util.IOTools;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import com.anysoft.util.Settings;
import com.logicbus.backend.Context;
import com.logicbus.backend.message.tools.ContentDigest;
import com.logicbus.backend.message.tools.GzipTool;
import com.logicbus.backend.message.tools.JsonFactory;
import com.logicbus.backend.server.http.HttpContext;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Server-Sent Events 消息
 *
 * @since 1.6.13.32 [20210128 duanyy]
 *
 * @version 1.6.13.33 [20210203 duanyy] 
* - 支持客户端关闭连接的检测;
* * @version 1.6.15.2 [20211103 duanyy]
* - 增加Json文档输入
*/ public class SSEMessage implements Message{ protected static final Logger LOG = LoggerFactory.getLogger(SSEMessage.class); public final static String ID = "$sse"; public static final String END_OF_MSG = "\n\n"; public static final String END_OF_LINE = "\n"; public static final String TYPE_CMD = "cmd"; public static final String TYPE_MESSAGE = "message"; public static final String TYPE_DATA = "data"; public static final String TYPE_RAW = "raw"; public static final String TYPE_ERROR = "error"; public static final String CMD_QUIT = "quit"; public static final String FIELD_DATA = "data"; public static final String FIELD_EVENT = "event"; public static final String FIELD_SEPARATOR = ": "; public static final String FIELD_ID = "id"; protected static String formContentType = "application/x-www-form-urlencoded"; protected static JsonFactory jsonFactory = Settings.getToolkit(JsonFactory.class); protected static GzipTool gzipTool = Settings.getToolkit(GzipTool.class); protected static ContentDigest contentDigest = Settings.getToolkit(ContentDigest.class); static { formContentType = PropertiesConstants.getString(Settings.get(), "http.formContentType", "application/x-www-form-urlencoded"); } /** * Json结构的根节点 */ protected Map root = null; protected OutputStream out = null; protected String contentType = "text/event-stream;charset=utf-8"; protected String encoding = "utf-8"; public Map getRoot(){ return root; } @Override public String toString(){ return jsonFactory.toJsonString(root); } @Override public void init(Context ctx) { boolean gzipEnable = gzipTool.isRequestGzipEnable(ctx); String data = null; { byte [] inputData = ctx.getRequestRaw(); if (inputData != null){ try { if (gzipEnable){ inputData = gzipTool.decompress(ctx,inputData); } data = new String(inputData,ctx.getEncoding()); }catch (Exception ex){ } } } if (data == null){ //当客户端通过form来post的时候,Message不去读取输入流。 String _contentType = ctx.getRequestContentType(); if (_contentType == null || !_contentType.startsWith(formContentType)){ InputStream in = null; try { in = ctx.getInputStream(); if (gzipEnable){ in = gzipTool.getInputStream(ctx,in); } data = Context.readFromInputStream(in, ctx.getEncoding()); }catch (Exception ex){ LOG.error("Error when reading data from inputstream",ex); }finally{ IOTools.close(in); } } } if (contentDigest != null) { contentDigest.digest(ctx, data); } if (StringUtils.isNotEmpty(data)){ Object rootObj = jsonFactory.fromJsonString(data); if (rootObj instanceof Map){ root = (Map)rootObj; } } if (root == null){ root = new HashMap(); } contentType = "text/event-stream;charset=" + ctx.getEncoding(); encoding = ctx.getEncoding(); HttpServletResponse httpResponse = ((HttpContext)ctx).getResponse(); httpResponse.setStatus(HttpServletResponse.SC_OK); httpResponse.setHeader("Content-Type",getContentType()); httpResponse.setHeader("Cache-Control","no-cache"); httpResponse.setHeader("Connection","keep-alive"); try { out = httpResponse.getOutputStream(); out.flush(); }catch (Exception ex){ } } @Override public void finish(Context ctx, boolean closeStream) { writeCommand(CMD_QUIT,false); IOTools.close(out); } @Override public String getContentType() { return contentType; } @Override public long getContentLength() { return 0; } public boolean writeMessage(String data,boolean flush){ StringBuffer line = new StringBuffer(FIELD_DATA); line.append(FIELD_SEPARATOR).append(escapeCRLF(data)).append(END_OF_MSG); return writeRaw(line.toString(),flush); } public boolean writeCommand(String cmd,boolean flush){ return writeEvent(TYPE_CMD,cmd,flush); } public boolean writeEvent(String id,String event,String data,boolean flush){ StringBuffer line = new StringBuffer(); line.append(FIELD_ID).append(FIELD_SEPARATOR).append(id).append(END_OF_LINE); line.append(FIELD_EVENT).append(FIELD_SEPARATOR).append(event).append(END_OF_LINE); line.append(FIELD_DATA).append(FIELD_SEPARATOR).append(escapeCRLF(data)).append(END_OF_MSG); return writeRaw(line.toString(),flush); } public boolean writeEvent(String event,String data,boolean flush){ StringBuffer line = new StringBuffer(); line.append(FIELD_EVENT).append(FIELD_SEPARATOR).append(event).append(END_OF_LINE); line.append(FIELD_DATA).append(FIELD_SEPARATOR).append(escapeCRLF(data)).append(END_OF_MSG); return writeRaw(line.toString(),flush); } public boolean writeRaw(String raw,boolean flush){ try { out.write(raw.getBytes(encoding)); if (flush) { out.flush(); } }catch (Exception ex){ return false; } return true; } protected static String escapeCRLF(String value){ return value.replaceAll("\r|\n", ""); } /** * 写消息插件 */ public static class Writer extends AbstractLogiclet{ private String pid = ID; private String $type = TYPE_MESSAGE; private String $data; private boolean ref = false; private boolean raw = true; private String $flush = "true"; private String id; public Writer(String tag, Logiclet p) { super(tag, p); } @Override public void configure(Properties p){ pid = PropertiesConstants.getString(p,"pid",pid,true); $type = PropertiesConstants.getRaw(p,"type",$type); $data = PropertiesConstants.getRaw(p,"data",$data); ref = PropertiesConstants.getBoolean(p,"ref",ref); id = PropertiesConstants.getString(p,"id","$" + this.getXmlTag()); raw = PropertiesConstants.getBoolean(p,"raw",raw); $flush = PropertiesConstants.getRaw(p,"flush",$flush); } @Override protected void onExecute(XsObject root, XsObject current, LogicletContext ctx, ExecuteWatcher watcher) { SSEMessage msg = ctx.getObject(pid); if (msg != null){ String type = PropertiesConstants.transform(ctx,$type,TYPE_MESSAGE); String data = PropertiesConstants.transform(ctx,$data,""); if (ref){ if (StringUtils.isNotEmpty(data)){ data = raw?PropertiesConstants.getRaw(ctx,data,"") :PropertiesConstants.getString(ctx,data,""); } } if (StringUtils.isNotEmpty(data)){ boolean flush = PropertiesConstants.transform(ctx, $flush, true); switch(type){ case TYPE_MESSAGE: case TYPE_DATA: ctx.SetValue(id, BooleanUtils.toStringTrueFalse( msg.writeMessage(data,flush))); break; case TYPE_CMD: ctx.SetValue(id, BooleanUtils.toStringTrueFalse( msg.writeCommand(data, flush))); break; case TYPE_RAW: ctx.SetValue(id, BooleanUtils.toStringTrueFalse( msg.writeRaw(data, flush))); break; case TYPE_ERROR: default: ctx.SetValue(id, BooleanUtils.toStringTrueFalse( msg.writeEvent(type, data, flush))); } } }else{ String type = PropertiesConstants.transform(ctx,$type,TYPE_MESSAGE); String data = PropertiesConstants.transform(ctx,$data,""); if (ref){ if (StringUtils.isNotEmpty(data)){ data = raw?PropertiesConstants.getRaw(ctx,data,"") :PropertiesConstants.getString(ctx,data,""); } } log(data,ctx); } } } /** * 写消息插件 */ public static class Log extends Segment { private String pid = ID; private boolean flush = true; public Log(String tag, Logiclet p) { super(tag, p); } @Override public void configure(Properties p){ pid = PropertiesConstants.getString(p,"pid",pid,true); flush = PropertiesConstants.getBoolean(p,"flush",flush,true); } @Override public void log(LogInfo logInfo,Properties ctx){ if (ctx instanceof LogicletContext){ LogicletContext logicletContext = (LogicletContext)ctx; SSEMessage msg = logicletContext.getObject(pid); if (msg != null) { msg.writeMessage(logInfo.message(), flush); }else{ log(logInfo.message(),logInfo.level(),logInfo.activity()); } }else{ log(logInfo.message(),logInfo.level(),logInfo.activity()); } } @Override protected void onExecute(XsObject root,XsObject current, LogicletContext ctx, ExecuteWatcher watcher) { LogHandler oldHandler = ctx.getObject(LOG_ID); try { ctx.setObject(LOG_ID,this); super.onExecute(root,current,ctx,watcher); }finally { ctx.removeObject(LOG_ID); if (oldHandler != null){ ctx.setObject(LOG_ID,oldHandler); } } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy