package com.hundsun.jresplus.web.contain.pipeline; import java.io.IOException; import java.io.Writer; import java.net.SocketException; import java.util.PriorityQueue; import java.util.Queue; import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringEscapeUtils; import org.apache.velocity.context.InternalContextAdapter; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.runtime.Renderable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.alibaba.fastjson.JSON; import com.hundsun.jresplus.common.util.ArrayUtil; import com.hundsun.jresplus.web.contain.ResponseStringWriterWrapper; import com.hundsun.jresplus.web.contain.async.AsynchronousContain; /** * 管道输出器,使用到指令可触发Bigpipe渲染任务的执行 * @author XIE (xjj@hundsun.com) * */ public class PipelineExporter implements Renderable { private static final Logger logger = LoggerFactory .getLogger(PipelineExporter.class); private HttpServletRequest request; private HttpServletResponse response; private Queue<PipelineTask> taskQueue; private PipelineTask currentTask; private boolean isAbort = false; public PipelineExporter(HttpServletRequest request, HttpServletResponse response) { this.request = request; this.response = response; taskQueue = new PriorityQueue<PipelineTask>( PipelineTask.DEFAULT_QUEUE_SIZE, PipelineTask.COMPARATOR); } public boolean allTaskFinished() { return taskQueue == null || taskQueue.isEmpty(); } public boolean render(InternalContextAdapter context, Writer writer) throws IOException, MethodInvocationException, ParseErrorException, ResourceNotFoundException { response.flushBuffer(); AsynchronousContain.setAsyncConext(); renderTaskQueue(writer, taskQueue); renderEnd(writer); AsynchronousContain.removeAsyncConext(); return true; } private void renderTaskQueue(Writer writer, Queue<PipelineTask> taskQueue) throws IOException { while (!taskQueue.isEmpty()) { renderTask(writer, taskQueue.poll()); } } private void renderEnd(Writer writer) throws IOException { if (isAbort) { return; } writer.write("<script type=\"text/javascript\">BigPipe.start();</script>"); response.flushBuffer(); } private void renderTask(Writer writer, PipelineTask task) throws IOException { currentTask = task; RequestDispatcher dispatcher = request.getRequestDispatcher(task .getView()); PipelineRequestWrapper pipeRequest = new PipelineRequestWrapper( request, task.getParameters(), task.getView()); pipeRequest.setAttribute(PipelineTask.ATTR_KEY, task); ResponseStringWriterWrapper pipeResponse = new ResponseStringWriterWrapper( response); try { dispatcher.include(pipeRequest, pipeResponse); } catch (Exception e) { pipeResponse.getStringWriter().write(e.getMessage()); } String result = pipeResponse.getStringWriter().toString(); write(writer, task, result); writer.flush(); pipeResponse.flushBuffer(); try { response.flushBuffer(); } catch (Exception e) { String err = e.getMessage(); if (e.getCause() != null && (e.getCause() instanceof SocketException)) { err = e.getCause().getMessage(); } logger.info( "Pipe queue will be abort,because task[{}] render response flush error:{}", task.getView(), err); taskQueue.clear(); isAbort = true; return; } if (task.isHasSubTask()) { Queue<PipelineTask> subTask = task.getSubTask(); renderTaskQueue(writer, subTask); } } private void write(Writer writer, PipelineTask task, String result) throws IOException { writer.write("<script type=\"text/javascript\">\r\n"); writer.write("BigPipe.onArrive("); writer.write("{\"html\":\""); StringEscapeUtils.escapeJavaScript(writer, result); writer.write("\",\"id\":\""); writer.write(task.getId()); writer.write("\",\"css\":"); if (ArrayUtil.isEmpty(task.getCss())) { writer.write("[]"); } else { writer.write(JSON.toJSONString(task.getCss())); } writer.write(",\"js\":"); if (ArrayUtil.isEmpty(task.getJs())) { writer.write("[]"); } else { writer.write(JSON.toJSONString(task.getJs())); } writer.write(",\"jsCode\":\""); if (task.getJsCode().length() < 1) { writer.write(""); } else { writer.write(task.getJsCode().toString()); } writer.write("\"}"); writer.write(");\r\n</script>\r\n"); } public void writeTo(Writer writer, String result, String id) throws IOException { } public void addPipeTask(PipelineTask pipeTask) { if (currentTask == null) { taskQueue.add(pipeTask); } else { if (currentTask.isParentView(pipeTask.getView())) { logger.warn("The pipeline view[{}] is close-loop,so ignore!", pipeTask.getView()); return; } currentTask.addSubTask(pipeTask); } } }