写在前面
源码 。
本文看下log日志如何设计,并开发valve和pipeline内容,最终将log应用到valve和pipeline中。
1:log
毫无疑问,先定义日志接口:
/** * 日志底层接口 */publicinterfaceLogger{publicstaticfinalintFATAL=Integer.MIN_VALUE;publicstaticfinalintERROR=1;publicstaticfinalintWARNING=2;publicstaticfinalintINFORMATION=3;publicstaticfinalintDEBUG=4;publicStringgetInfo();publicintgetVerbosity();publicvoidsetVerbosity(intverbosity);publicvoidlog(Stringmessage);publicvoidlog(Exceptionexception,Stringmsg);publicvoidlog(Stringmessage,Throwablethrowable);publicvoidlog(Stringmessage,intverbosity);publicvoidlog(Stringmessage,Throwablethrowable,intverbosity);}定义base实现类:
packagemonitomcat.server.logger;// ...publicabstractclassLoggerBaseimplementsLogger{protectedintdebug=0;protectedstaticfinalStringinfo="com.minit.logger.LoggerBase/1.0";protectedintverbosity=ERROR;// ...publicabstractvoidlog(Stringmsg);/** * 核心方法 首先打印异常堆栈,之后调用抽象方法public abstract void log(String msg);执行子类具体动作 * @param msg * @param throwable */publicvoidlog(Stringmsg,Throwablethrowable){CharArrayWriterbuf=newCharArrayWriter();PrintWriterwriter=newPrintWriter(buf);writer.println(msg);throwable.printStackTrace(writer);ThrowablerootCause=null;if(throwableinstanceofServletException)rootCause=((ServletException)throwable).getRootCause();if(rootCause!=null){writer.println("----- Root Cause -----");rootCause.printStackTrace(writer);}log(buf.toString());}// ...}其中public abstract void log(String msg);抽象方法需要在子类中给出具体的实现,如下给出的实现类们:
分别是标准输出,标准错误输出,文件输出。
我们来修改bootstrap,修改context容器的日志为文件输出:
packagemonitomcat.server.startup;// .../** * 总的服务器对象,负责管理类工作的对象(类似于公司的管理层,负责管理,但不负责具体干活,但管理本身不也是一种职责嘛!所以也可以用单一职责来进行说明) */publicclassBootstrap{// ...publicstaticvoidmain(String[]args){// ...StandardContextservletContainer=newStandardContext();// 设置日志servletContainer.setLogger(newFileLogger());// ...}}写日志输出到文件:
packagemonitomcat.server.connector.http;// ...publicclassHttpConnectorimplementsRunnable{// ...publicvoidrun(){// ...while(true){Socketsocket=null;try{// ...log("connector receive new request, assign to processor");// ...}catch(Exceptione){e.printStackTrace();}}}publicvoidstart(){Threadthread=newThread(this);// ...log("httpConnector.starting "+threadName);}// ...}测试一下:
2:pipeline和valve
本部分内容要达到的目的是通过责任链的方式来拦截一层一层容器的执行,比如可以添加日志,权限校验等。
其中valve是执行的单元,接口如下:
packagemonitomcat.server;importjava.io.IOException;importjavax.servlet.ServletException;/** * 容器执行逻辑单元 */publicinterfaceValve{publicStringgetInfo();publicContainergetContainer();publicvoidsetContainer(Containercontainer);publicvoidinvoke(Requestrequest,Responseresponse,ValveContextcontext)throwsIOException,ServletException;}base实现类:
packagemonitomcat.server.valves;importmonitomcat.server.Container;importmonitomcat.server.Valve;publicabstractclassValveBaseimplementsValve{protectedContainercontainer=null;protectedintdebug=0;protectedstaticStringinfo="com.minit.valves.ValveBase/0.1";publicContainergetContainer(){return(container);}publicvoidsetContainer(Containercontainer){this.container=container;}publicintgetDebug(){return(this.debug);}publicvoidsetDebug(intdebug){this.debug=debug;}publicStringgetInfo(){return(info);}}为了维护valve的执行状态,定义执行的上下文接口:
/** * valve执行上下文,执行具体的链式调用动作 */publicinterfaceValveContext{publicStringgetInfo();publicvoidinvokeNext(Requestrequest,Responseresponse)throwsIOException,ServletException;}pipeline接口:
publicinterfacePipeline{publicValvegetBasic();publicvoidsetBasic(Valvevalve);publicvoidaddValve(Valvevalve);publicValve[]getValves();publicvoidinvoke(Requestrequest,Responseresponse)throwsIOException,ServletException;publicvoidremoveValve(Valvevalve);}pipeline实现类:
packagemonitomcat.server.core;// ...publicclassStandardPipelineimplementsPipeline{// ...publicvoidinvoke(Requestrequest,Responseresponse)throwsIOException,ServletException{System.out.println("StandardPipeline invoke()");// Invoke the first Valve in this pipeline for this request(newStandardPipelineValveContext()).invokeNext(request,response);}// ...protectedclassStandardPipelineValveContextimplementsValveContext{protectedintstage=0;publicStringgetInfo(){returninfo;}publicvoidinvokeNext(Requestrequest,Responseresponse)throwsIOException,ServletException{System.out.println("StandardPipelineValveContext invokeNext()");intsubscript=stage;stage=stage+1;// Invoke the requested Valve for the current request threadif(subscript<valves.length){valves[subscript].invoke(request,response,this);}elseif((subscript==valves.length)&&(basic!=null)){basic.invoke(request,response,this);}else{thrownewServletException("standardPipeline.noValve");}}}}这里StandardPipelineValveContext是ValveContext的实现类,是一个内部类,执行具体的链式调用动作,所以pipeline这里更像是一个管理者角色,并不是真正干活的,但要维护一些信息,比如valves数组。
这里有一点需要注意,必须要保证容器能够一层一层的调用,所以这里定义basic valve的概念,负责调用下一层container的pipeline,定义context的basic valve:
packagemonitomcat.server.core;// ...finalclassStandardContextValveextendsValveBase{// ...publicvoidinvoke(Requestrequest,Responseresponse,ValveContextvalveContext)throwsIOException,ServletException{// ...try{System.out.println("Call service()");servletWrapper.invoke(request,response);}// ...}}负责调用wrapper container的pipeline。
定义wrapper basic valve:
packagemonitomcat.server.core;// ...publicclassStandardWrapperValveextendsValveBase{@Overridepublicvoidinvoke(Requestrequest,Responseresponse,ValveContextcontext)throwsIOException,ServletException{// ...if(instance!=null){instance.service(requestFacade,responseFacade);}}}负责调用最终的servlet,就执行到头了。接着我们只需要为每一层的容器定义其pipeline,然后设置basic valve以及其他valve就可以了,这里pipeline的创建,以及添加valve的动作因为是公共的,所以我们放在base容器中:
packagemonitomcat.server.core;// ...publicabstractclassContainerBaseimplementsContainer,Pipeline{// ...protectedPipelinepipeline=newStandardPipeline(this);publicPipelinegetPipeline(){return(this.pipeline);}publicvoidinvoke(Requestrequest,Responseresponse)throwsIOException,ServletException{// System.out.println("ContainerBase invoke()");pipeline.invoke(request,response);}publicsynchronizedvoidaddValve(Valvevalve){pipeline.addValve(valve);}publicValvegetBasic(){return(pipeline.getBasic());}publicValve[]getValves(){return(pipeline.getValves());}publicsynchronizedvoidremoveValve(Valvevalve){pipeline.removeValve(valve);}publicvoidsetBasic(Valvevalve){pipeline.setBasic(valve);}}发起执行的动作在ServletProcessor中,如下:
publicclassServletProcessor{privateHttpConnectorconnector;publicServletProcessor(HttpConnectorconnector){this.connector=connector;}// public void process(HttpRequestImpl request, HttpResponseImpl response) throws IOException, ServletException {publicvoidprocess(Requestrequest,Responseresponse)throwsIOException,ServletException{this.connector.getContainer().invoke(request,response);}}这样程序就按照如下图的方式执行了:
写在后面
参考文章列表
手把手带你写一个 MiniTomcat 。