`

jetty源码阅读总结1

 
阅读更多

jetty启动分析
jetty启动命令:java -jar /usr/alibaba/jetty/start.jar -Djetty.home=/usr/alibaba/jetty --ini=/home/admin/deploy/jetty/start.ini

如果加上--help参数,则显示jetty启动的参数设置和配置设置,如下:
#java -jar /usr/alibaba/jetty/start.jar -Djetty.home=/usr/alibaba/jetty --ini=/home/admin/deploy/jetty/start.ini --help
properties={jetty.home=/usr/alibaba/install/jetty-distribution-7.2.0, path=/home/admin/deploy/jetty/lib/ext, OPTIONS=Server,ajp,ext,jmx,jsp,resources,websocket, lib=/home/admin/deploy/jetty/lib/ext}启动时所加载的配置选项。
Usage: java -jar start.jar [options...] [properties...] [configs...]

  The start.jar builds a classpath and executes a main java class with
  a classloader built from that classpath.  By default the start.jar
  mechanism is configured to start the jetty server, but it can be 
  configured to start any java main class.   start.java里面会执行main.java这个类的main函数,由这个函数去读取start.conf配置,这个配置文件配置的jetty的相关环境变量和配置文件路径,以                                                 及接下来需要执行的main类。


Command Line Options:
  --help           This help / usage information.  显示帮助信息
  
  --version        Print the version information for Jetty and
                   dependent jars, then exit.    显示jetty的版本
                   
  --list-options   List the details of each classpath OPTION  显示启动需要的options信息,包括:=Server,ajp,ext,jmx,jsp,resources,websocket等,可以自己指定jetty启动的特性选项。每个option的实现是在独立的jar里,
                                                              这里配置上了jetty启动时就会去load这个jar包。
  
  --list-config    List the start.config file.     显示main类启动的配置,对应配置文件start.config
                                    
  --dry-run        Print the command line that the start.jar generates,
                   then exit. This may be used to generate command lines
                   when the start.ini includes -X or -D arguments.     显示用java -jar执行时的classpath路径等完整的java命令信息,在调试模式下有用 // Show Command Line to execute Jetty
                   
  --exec           Run the generated command line (see --dry-run) in 
                   a sub processes.  This can be used when start.ini
                   contains -X or -D arguments, but creates an extra
                   JVM instance.                                重新启动一个jvm进程 // Show Command Line to execute Jetty
                     
  --stop           Stop the running Jetty instance.    停止jetty进程
  
  --daemon         Start in daemon mode with stderr and stdout 
                   redirected to ${jetty.log}/start.log     是否开启守护进程模式
  
  --config=<file>  Specify an alternate start.config file.  
                   The default is the start.config file inside
                   the start.jar. The default can also be specified
                   with the START system property.  重新指定config配置的路径
  
  --ini=<file>     Load command line arguments from a file. If 
                   no --ini options are specified, then the 
                   start.ini file will be read if it exists.
                   A --ini option with no file indicates that
                   start.ini should not be read.   启动jetty进程的配置
                   
  --pre=<file>     Specify a configuration file that is to be processed
                   before any configuration files listed in start.ini    在加载start.ini里面执行的xml配置文件之前需要先加载的配置文件

System Properties:   启动时可以设置jetty的系统环境变量
  These are set with a command line like "java -Dname=value ..." and are
  accessible via the java.lang.System#getProperty(String) API.
  Some key system properties are:
  
    org.eclipse.jetty.util.log.class=[class]    jetty加载的日志类
      A Low Level Jetty Logger Implementation to use
      (default: org.eclipse.jetty.util.log.Slf4jLog)
      
    org.eclipse.jetty.util.log.DEBUG=[boolean] 是否打开日志debug模式
      Debug logging for the stderr and javautil Loggers. Slf4j
      and other loggers must be separately configured for debug.
      (default: false)
      
    org.eclipse.jetty.util.log.IGNORED=[boolean]  是否打开日志的ignored模式
      Ignored exceptions are logged, independent of DEBUG settings
      (default: false)

    org.eclipse.jetty.util.log.SOURCE=[boolean]源代码的位置是否打印在错误日志上
      The source location of logs is logged in the stderr Logger.
      (default: false)
      
    com.sun.management.jmxremote     是否开通JMX功能
      Enable remote JMX management in Sun JVMS.
      
      
Properties:  jetty启动的参数设置
  These are set with a command line like "java -jar start.jar name=value"
  and only affect the start mechanism.  Some of these are defined in the 
  default start.config and will not be available if another configuration
  file is used. NOTE: Not all properties are listed here:

    path=[directory] 
      An additional class path element to add to the started class path. Typically
      this is used to add directories of classes and/or resources
      
    lib=[directory]
      An additional library directory to add to the started class path. This must
      be a (deep) directory of jars
      
    STOP.PORT=[number]
      The port to use to stop the running Jetty server.
      Required along with STOP.KEY if you want to use the --stop option above.
      
    STOP.KEY=[alphanumeric]
      The passphrase defined to stop the server.
      Requried along with STOP.PORT if you want to use the --stop option above.
      
    DEBUG=true
      Enable debug on the start mechanism and sets the
      org.eclipse.jetty.util.log.stderr.DEBUG system property to true. 
      (default: false)
      
    OPTIONS=[option,option,...]  options列表
      Enable classpath OPTIONS. Each options represents one or more jars 
      to be added to the classpath. The options are defined in 
      the start.config file and can be listed with --help or --list-options.
      By convention, options starting with a capital letter (eg Server) 
      are aggregations of other available options. Available OPTIONS:

        All
        Client
        Server
        ajp
        annotations
        client
        default
        deploy
        ext
        jmx
        jndi
        jsp
        jta
        plus
        policy
        resources
        rewrite
        security
        server
        servlet
        servlets
        setuid
        webapp
        websocket
        xml


Available Configurations:  默认配置列表
  By convention, configuration files are kept in $JETTY_HOME/etc.
  The known configuration files are:
  
    etc/jetty-ajp.xml  ajp支持配置
    etc/jetty-bio-ssl.xml  阻塞io的ssl支持
    etc/jetty-bio.xml  阻塞io
    etc/jetty-contexts.xml  contexts上下文配置支持
    etc/jetty-debug.xml  debug模式支持
    etc/jetty-deploy.xml  war部署支持
    etc/jetty-fileserver.xml  文件服务器支持
    etc/jetty-ipaccess.xml  ip过滤支持
    etc/jetty-jmx.xml   jmx支持
    etc/jetty-logging.xml  日志系统支持
    etc/jetty-plus.xml  增强功能支持  jaas jndi
    etc/jetty-policy.xml java的policy权限支持
    etc/jetty-proxy.xml  代理服务器支持
    etc/jetty-requestlog.xml   cookie log日志支持
    etc/jetty-rewrite.xml   url rewrite支持
    etc/jetty-ssl.xml   ssl支持
    etc/jetty-stats.xml  统计功能支持
    etc/jetty-testrealm.xml  配置权限登录service
    etc/jetty-webapps.xml   web app功能支持
    etc/jetty-xinetd.xml  使用inetd/xinetd配置jetty
    etc/jetty.xml   基础配置


Defaults:  默认配置
  A start.ini file may be used to specify default arguments to start.jar,
  which are used if no command line arguments are provided and override 
  the defaults in the start.config file. If --ini options are provided on 
  the command line, then start.ini will no be read. The current start.ini 
  arguments are:

    OPTIONS=Server,jsp,jmx,resources,websocket,ext
    etc/jetty.xml
    etc/jetty-deploy.xml
    etc/jetty-webapps.xml
    etc/jetty-contexts.xml
    etc/jetty-testrealm.xml


main函数启动:org.eclipse.jetty.start.main 需要配置启动参数,start.ini的路径
config类的说明: The behaviour of Main is controlled by the <code>"org/eclipse/start/start.config
main类调用org.eclipse.jetty.start.config去加载所有的配置和配置文件,然后通过加载org/eclipse/start/start.config,这个配置文件里面有两行:
# The main class to run
org.eclipse.jetty.xml.XmlConfiguration.class
${start.class}.class                             property start.class

然后main类就回反射start.config配置的这个class类去执行它的main方法,
org.eclipse.jetty.xml.XmlConfiguration这个类就是具体解析jetty.xml等所有xml配置文件中配置,
都是关于server类的配置,这个类通过解析所有的xml文件通过反射去生成最终的org.eclipse.jetty.server.server(这个类就是jetty的服务器最核心的类)



XmlConfiguration.java的main方法代码:
   public static void main( final String[] args ) //args就是start.ini里面配置的加载的xml文件
    {

        AccessController.doPrivileged( new PrivilegedAction()
        {
            public Object run()
            {
                try
                {

                    Properties properties=null;
                    
                    // Look for properties from start.jar
                    try
                    {
                        Class<?> config = XmlConfiguration.class.getClassLoader().loadClass("org.eclipse.jetty.start.Config");
                        properties=(Properties)config.getMethod("getProperties").invoke(null);
                        Log.debug("org.eclipse.jetty.start.Config properties = {}",properties); //加载config里面的环境变量
                    }
                    catch(NoClassDefFoundError e)
                    {
                        Log.ignore(e);
                    }
                    catch(ClassNotFoundException e)
                    {
                        Log.ignore(e);
                    }
                    catch(Exception e)
                    {
                        Log.warn(e);
                    }
                    
                    
                    // If no start.config properties, use clean slate
                    if (properties==null)
                        properties = new Properties();
                    
                    // For all arguments, load properties or parse XMLs 
                    XmlConfiguration last = null;
                    Object[] obj = new Object[args.length];
                    for ( int i = 0; i < args.length; i++ ) //对每个配置文件进行解析,反射得到对应的反射出来的类
                    {
                        if ( args[i].toLowerCase().endsWith( ".properties" ) )
                        {
                            properties.load( Resource.newResource( args[i] ).getInputStream() );
                        }
                        else
                        {
                            XmlConfiguration configuration =
                                new XmlConfiguration( Resource.newResource( args[i] ).getURL() );
                            if ( last != null )
                                configuration.getIdMap().putAll( last.getIdMap() );
                            if ( properties.size() > 0 )
                                configuration.setProperties( properties );
                            obj[i] = configuration.configure();  //对每个配置文件进行解析,反射得到对应的反射出来的类,主要是Server这个类
                            last = configuration;
                        }
                    }

                    // For all objects created by XmlConfigurations, start them if they are lifecycles.
                    for ( int i = 0; i < args.length; i++ )
                    {
                        if ( obj[i] instanceof LifeCycle )
                        {
                            LifeCycle lc = (LifeCycle) obj[i];
                            if ( !lc.isRunning() ) //对每个反射出来的类,如果是实现LifeCycle,则启动对应的类,
                                lc.start();
                        }
                    }
                }
                catch (AccessControlException ace)
                {
                    ace.printStackTrace(System.err);
                }
                catch ( Exception e )
                {
                    Log.warn( Log.EXCEPTION, e );
                }
                return null;
            }
        } );
    }

LifeCycle接口:jetty的各个组件的生命周期管理的接口,




jetty服务器的配置文件配置的都是"org.eclipse.jetty.server.Server"这个类,这个类包装了连接器,处理器,context上下文等信息,是jetty的主入口

在jetty.xml等配置文件能够配置的标签为:
<Configure id="Server" class="org.eclipse.jetty.server.Server">下面的子节点标签只能是下面列出来的几个:
         String tag = node.getTag();
                if ("Set".equals(tag))  //通过调用server类的set方法去注入相关参数或者bean
                    set(obj, node);
                else if ("Put".equals(tag)) //map注入时使用put方法去插入
                    put(obj, node);
                else if ("Call".equals(tag)) //调用call的方法
                    call(obj, node);
                else if ("Get".equals(tag))  //调用get方法,通过get取出值之后再通过Ref注入到需要的bean中
                    get(obj, node);
                else if ("New".equals(tag))  //new一个新的对象
                    newObj(obj, node);
                else if ("Array".equals(tag))  //new 一个Array对象
                    newArray(obj, node);
                else if ("Ref".equals(tag))  //引用一个bean
                    refObj(obj, node);
                else if ("Property".equals(tag))  //基本类型注入
                    propertyObj(obj, node);
                else
                    throw new IllegalStateException("Unknown tag: " + tag);


<Configure id="Server" class="org.eclipse.jetty.server.Server">

    <!-- =========================================================== -->
    <!-- configure rewrite handler                                   --> 
    <!-- =========================================================== -->
    <Get id="oldhandler" name="handler"/>

    <Set name="handler">
     <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
      <Set name="handler"><Ref id="oldhandler"/></Set>


    <Ref id="DeploymentManager">
          <Call name="addAppProvider">
            <Arg>
              <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
                <Set name="monitoredDir"><Property name="jetty.home" default="." />/webapps</Set>


线程池分析
jetty.xml
<Configure id="Server" class="org.eclipse.jetty.server.Server">

    <!-- =========================================================== -->
    <!-- Server Thread Pool                                          -->
    <!-- =========================================================== -->
    <Set name="ThreadPool">
      <!-- Default queued blocking threadpool -->
      <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
        <Set name="minThreads">10</Set>
        <Set name="maxThreads">200</Set>
////////////////////////////////////////////////////////
这里能设置的属性:参见org.eclipse.jetty.util.thread.QueuedThreadPool 线程池实现
    private BlockingQueue<Runnable> _jobs;  任务队列
    private String _name;  线程池名字
    private int _maxIdleTimeMs=60000;  线程最大空闲时间 
    private int _maxThreads=254;  最大线程数
    private int _minThreads=8;  最小线程数
    private int _maxQueued=-1;  任务队列数,每个线程队列的线程容量是_minThreads,每次以_minThreads增长
    private int _priority=Thread.NORM_PRIORITY;  线程优先级
    private boolean _daemon=false; 是否以守护线程启动
    private int _maxStopTime=100;  服务停止时 让还在运行的线程还能运行的最长时间,在doStop()中使用 单位毫秒

    ....
       dostart()方法中
        if (_jobs==null)
        {
            _jobs=_maxQueued>0 ?new ArrayBlockingQueue<Runnable>(_maxQueued)
                :new BlockingArrayQueue<Runnable>(_minThreads,_minThreads);
        }
    ....
   
   ThreadPool可以用以下三种实现:
   http://daizuan.iteye.com/blog/1114372

QueuedThreadPool 使用jdk1.5的并发包得特性(AtomicInteger,ConcurrentLinkedQueue)来实现
ExecutorThreadPool 使用jdk1.5自带的ThreadPoolExecutor
OldQueuedThreadPool 功能跟QueuedThreadPool类似,但是实现是用加锁等jdk1.5以前已有的特性去实现,性能应该不如QueuedThreadPool

////////////////////////////////////////////////////////
      </New>
    </Set>

    <!-- =========================================================== -->
    <!-- Set connectors                                              -->
    <!-- =========================================================== -->

    <Call name="addConnector">
      <Arg>
          <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
            <Set name="host"><Property name="jetty.host" /></Set>
            <Set name="port"><Property name="jetty.port" default="8080"/></Set>
            <Set name="maxIdleTime">300000</Set>
            <Set name="Acceptors">2</Set>
            <Set name="statsOn">false</Set>
            <Set name="confidentialPort">8443</Set>
            <Set name="lowResourcesConnections">20000</Set>
            <Set name="lowResourcesMaxIdleTime">5000</Set>
          </New>
      </Arg>
    </Call>

  //////////////////////////////////////////////////////////////////////////////

Connector分析: 



AbstractConnector.java能够设置的参数:
 private String _host;
    private int _port = 0;
    private String _integralScheme = HttpSchemes.HTTPS; 加密协议
    private int _integralPort = 0;  加密端口
    private String _confidentialScheme = HttpSchemes.HTTPS; 信任协议
    private int _confidentialPort = 0;  信任端口
    private int _acceptQueueSize = 0; backlog 请求等待队列长度
    private int _acceptors = 1;  等待请求连接的线程数
    private int _acceptorPriorityOffset = 0;  连接器线程的优先级 current.setPriority(old_priority - _acceptorPriorityOffset);
    private boolean _useDNS;   是否使用DNS查找主机名,主机名会带入request中
    private boolean _forwarded; //是否把请求头带入request中
    private String _hostHeader;  //请求头
    
    private String _forwardedHostHeader = "X-Forwarded-Host"; 
    private String _forwardedServerHeader = "X-Forwarded-Server"; 
    private String _forwardedForHeader = "X-Forwarded-For"; 
    private String _forwardedProtoHeader = "X-Forwarded-Proto"; 
    private boolean _reuseAddress = true;

    protected int _maxIdleTime = 200000;  //线程最大空闲时间
    protected int _lowResourceMaxIdleTime = -1; 在请求队列大于空闲线程时,线程的空闲时间设置
    protected int _soLingerTime = -1;  

    public void setStatsOn(boolean on)  是否开启统计功能

    /** connections to server */
    private final CounterStatistic _connectionStats = new CounterStatistic(); 
    /** requests per connection */
    private final SampleStatistic _requestStats = new SampleStatistic(); 
    /** duration of a connection */
    private final SampleStatistic _connectionDurationStats = new SampleStatistic(); 

SO_LINGER选项
1) 设置该选项:public void setSoLinger(boolean on, int seconds) throws SocketException
2) 读取该选项:public int getSoLinger() throws SocketException
3) SO_LINGER选项用来控制Socket关闭时的行为。
l socket.setSoLinger(true,0):执行Socket的close()方法时,该方法也会立即返回,但底层的Socket也会立即关闭,所有未发送完的剩余数据被丢弃。
l socket.setSoLinger(true,3600):执行Socket的close()方法时,该方法不会立即返回,而进入阻塞状态,同时,底层的Socket会尝试发送剩余的数据。只有满足以下两个条件之一,close()方法才返回:
n 底层的Socket已经发送完所有的剩余数据。
n 尽管底层的Socket还没有发送完所有的剩余数据,但已经阻塞了3600秒。close()方法的阻塞时间超过3600秒,也会返回,剩余未发送的数据被丢弃。
以上两种情况内,当close()方法返回后,底层的Socket会被关闭,断开连接。
4) setSoLinger(boolean on ,int second)方法中的seconds参数以秒为单位,而不是以毫秒为单位。
socket用法:http://www.cnblogs.com/jerrychoi/archive/2010/04/15/1712931.html

    安全协议在下面两个类会被调用:



在ConstraintSecurityHandler.java会根据不同的安全选项进行不同的处理
 UserDataConstraint dataConstraint = roleInfo.getUserDataConstraint();
        if (dataConstraint == null || dataConstraint == UserDataConstraint.None)
        {
            return true;
        }
        HttpConnection connection = HttpConnection.getCurrentConnection();
        Connector connector = connection.getConnector();

        if (dataConstraint == UserDataConstraint.Integral)
        {
            if (connector.isIntegral(request))
                return true;
            if (connector.getConfidentialPort() > 0)
            {
                String url = connector.getIntegralScheme() + "://" + request.getServerName() + ":" + connector.getIntegralPort() + request.getRequestURI();
                if (request.getQueryString() != null)
                    url += "?" + request.getQueryString();
                response.setContentLength(0);
                response.sendRedirect(url);
            }
            else
                response.sendError(Response.SC_FORBIDDEN,"!Integral");

            request.setHandled(true);
            return false;
        }
        else if (dataConstraint == UserDataConstraint.Confidential)
        {
            if (connector.isConfidential(request))
                return true;

            if (connector.getConfidentialPort() > 0)
            {
                String url = connector.getConfidentialScheme() + "://" + request.getServerName() + ":" + connector.getConfidentialPort()
                        + request.getRequestURI();
                if (request.getQueryString() != null)
                    url += "?" + request.getQueryString();

                response.setContentLength(0);
                response.sendRedirect(url);
            }
            else
                response.sendError(Response.SC_FORBIDDEN,"!Confidential");
            
            request.setHandled(true);
            return false;
        }

Connect的类图结构:




AbstractNIOConnector 下面的SelectChannelConnector和BlockingChannelConnector是通过连接字通道来配置NIO。
NIO跟BIO的区别:在socket连接上设置Blocking为true,表示是阻塞同步,如果为false表示异步非阻塞。
   
BlockingChannelConnector.java
        SocketChannel channel = _acceptChannel.accept();
        channel.configureBlocking(true);
        Socket socket=channel.socket();
        configure(socket);

        BlockingChannelEndPoint connection=new BlockingChannelEndPoint(channel);
        connection.dispatch();

SelectChannelConnector.java
      SocketChannel channel = server.accept();
                        channel.configureBlocking(false);
                        Socket socket = channel.socket();
                        configure(socket);
                        _manager.register(channel);

SelectChannelConnector连接器接受到请求之后,注册到SelectorManager上,由SelectorManager的doSelect去做异步处理。异步的实现方式是采用jdk1.6的select
机制。


SocketConnector这个是阻塞IO的一种实现。在这个类里有说明:This Connector should only be used if NIO is not available.


SocketConnector —— 当连接请求相对较少或者NIO特性不可用时,可以使用这个连接器; 
BlockingChannelConnector —— 连接请求相对较少时用(要求NIO特性可用) 
SelectChannelConnector —— 当连接请求量比较大,或者需要异步处理Ajax请求 
SslSocketConnector —— 没有NIO特性的SSL连接器 
SslSelectChannelConnector —— 具有NIO特性的SSL连接器 
AJPConnector —— 提供对apache mod_jk或者mod_proxy_ajp请求的支持,它是针对AJP协议的实现。


Handler分析:
  //////////////////////////////////////////////////////////////////////////////

    <!-- =========================================================== -->
    <!-- Set handler Collection Structure                            --> 
    <!-- =========================================================== -->
    <Set name="handler">
      <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
        <Set name="handlers">
         <Array type="org.eclipse.jetty.server.Handler">
           <Item>
             <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
           </Item>
           <Item>
             <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
           </Item>
         </Array>
        </Set>
      </New>
    </Set>
    <Call name="addBean">
      <Arg>
        <New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
          <Set name="contexts">
            <Ref id="Contexts" />  //上面的contexts在这里引用
          </Set>
          <Call name="setContextAttribute">
            <Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
            <Arg>.*/jsp-api-[^/]*\.jar$|.*/jsp-[^/]*\.jar$</Arg>
          </Call>
        </New>
      </Arg>
    </Call>
    <Ref id="DeploymentManager">
          <Call name="addAppProvider">
            <Arg>
              <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">//web应用加载器
                <Set name="monitoredDir"><Property name="jetty.home" default="." />/webapps</Set> //web应用加载目录配置
                <Set name="defaultsDescriptor"><Property name="jetty.home" default="."/>/etc/webdefault.xml</Set>//web应用默认配置
                <Set name="scanInterval">1</Set> //热部署扫描间隔 单位:秒
                <Set name="contextXmlDir"><Property name="jetty.home" default="." />/contexts</Set> //context配置文件路径
                <Set name="extractWars">false</Set> //是否解压war包
              </New>
            </Arg>
          </Call>
    </Ref>
</Configure>

   /////////////////////////////////////////////////////////////////////



处理器组件主要用于处理已经接收到的请求。它的主要API是一个处理函数: 
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException 
参数说明: 
target —— 请求的目标,通常是一个URI,或者一个资源名称。有点类似于Servlet的名字。 
baseRequest —— 未被封装的请求对象 
request —— 可能是Request对象(Jetty的HttpServletRequest实现),或者是一个封装过的请求。用户可以通过使用HttpConnection.getCurrentConnection()方法来访问Request对象。 
response —— Response对象(Jetty的HttpServletResponse实现),或者是一个封装过的响应。用户可以通过使用HttpConnection.getCurrentConnection()方法来访问Request对象。

一个Handler实现可以直接处理请求,或者将请求转发给其它的Handler(如Servlet等),也可以对请求中包含的数据进行修改后再转发。根据处理器的功能,可以分为三类: 
1.路由处理器 – 这类处理器的主要作用是将请求进行路由,转发至合适的处理器进行处理,常用的有HandlerCollection, ContextHandlerCollection 
2.过滤器 – 对请求的数据进行分析处理,然后再转发给其它处理器。如HandlerWrapper, ContextHandler, SessionHandler 
3.处理器 – 这类处理器就是Servlet之类的了,它主要的功能是业务逻辑处理,并返回用户请求的数据。如ResourceHandler, ServletHandler。 

Handler的类图结构:
  

Handler主要有介绍下面几个:
HandlerCollection.java: Handler集合,按照list的顺序执行。
     public class HandlerCollection extends AbstractHandlerContainer
    {
       private final boolean _mutableWhenRunning;  默认为false,可以通过构造函数传入。如果为true,则在启动后不能再增加handler。
       private volatile Handler[] _handlers;  handler集合,通过配置文件注入
       private boolean _parallelStart=true;   可以通过配置文件改变,如果为true,等需要等待所有handler启动完成之后才算启动完成。
    
    这里用到java并发包得countdownwatch。
            if (_parallelStart)
            {
                final CountDownLatch latch = new CountDownLatch(_handlers.length);
                for (int i=0;i<_handlers.length;i++)
                {
                    final int h=i;
                    getServer().getThreadPool().dispatch(
                            new Runnable()
                            {
                                public void run()
                                {
                                    try{_handlers[h].start();}
                                    catch(Throwable e){mex.add(e);}
                                    finally{latch.countDown();}
                                }
                            }
                    );

                }
                latch.await();
            }
            else
            {
                for (int i=0;i<_handlers.length;i++)
                {
                    try{_handlers[i].start();}
                    catch(Throwable e){mex.add(e);}
                }
            }

 ContextHandlerCollection.java  由DeployManager.java的org.eclipse.jetty.deploy.providers.WebAppProvide类扫描monitoredDir属性下面的war包后,创建WebAppContext,put到
     ContextHandlerCollection的handler集合中,WebAppContext是处理J2ee标准下得war包应用的。
       



        Context是一种特殊的处理器(Handler),它的主要功能是将一组处理同一URI路径上的请求的处理器组织起来。一般的Context实现都会具备以下功能: 
        1.上下文路径 —— 用于标识哪些请求会被该上下文处理,如”/myapp” 
        2.静态资源路径 —— 或者说是Web应用的路径 如/www/WebRoot 
        3.类加载器 —— 用于加载分配至该Context的类,如/www/WebRoot/WEB-INF/classes

        Jetty中的Context实现有 
        1.ContextHandler 
        2.ServletContext 
        3.WebApplicationContext

        Web应用的上下文是通过使用web.xml描述文件将安全处理器(SecurityHandler)、会话处理器(SessionHandler)、Servlet等处理器组织在一起的。

DefaultHandler.java:处理没有被上面其他的handler处理的请求,包括favicon.ico,404页面,OPTIONS and TRACE methods
    boolean _serveIcon=true; 是否显示服务器的icon
    boolean _showContexts=true; 如果RequestURI不是/,如果这个值为true,则显示应用的context信息,

DebugHandler.java 调试模式下打印request和response的信息
      <New id="DebugHandler" class="org.eclipse.jetty.server.handler.DebugHandler">
        <Set name="handler"><Ref id="oldhandler"/></Set>
        <Set name="outputStream">
          <New class="org.eclipse.jetty.util.RolloverFileOutputStream">
            <Arg type="String"><Property name="jetty.logs" default="./logs"/>/yyyy_mm_dd.debug.log</Arg>
            <Arg type="boolean">true</Arg> <!-- append -->
            <Arg type="int">90</Arg> <!-- retain days -->
          </New>
        </Set>

RequestLogHandler.java 打印request路径的handler

               <Call name="addHandler">
        <Arg>
          <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler">
                 <Set name="requestLog">
                 <New id="RequestLogImpl" class="org.eclipse.jetty.server.NCSARequestLog">
                    <Set name="filename"><Property name="jetty.logs" default="./logs"/>/yyyy_mm_dd.request.log</Set>
                             <Set name="filenameDateFormat">yyyy_MM_dd</Set>
                              <Set name="retainDays">90</Set>
                             <Set name="append">true</Set>
                              <Set name="extended">false</Set>
                               <Set name="logCookies">false</Set>
                               <Set name="LogTimeZone">GMT</Set>
                               </New>
                     </Set>
               </New>
        </Arg>
      </Call>

IPAccessHandler.java 配置应用访问的ip白名单和黑名单
        public class IPAccessHandler extends HandlerWrapper
        {
            IPAddressMap<PathMap> _white = new IPAddressMap<PathMap>();
            IPAddressMap<PathMap> _black = new IPAddressMap<PathMap>();
         配置如下:
         <New id="IPAccessHandler" class="org.eclipse.jetty.server.handler.IPAccessHandler">
              <Set name="handler"><Ref id="oldhandler"/></Set>
              <Set name="white">
                <Array type="String">
                  <Item>127.0.0.1</Item>
                  <Item>127.0.0.2/*.html</Item>
                </Array>
              </Set>
              <Set name="black">
                <Array type="String">
                  <Item>127.0.0.1/blacklisted</Item>
                  <Item>127.0.0.2/black.html</Item>
                </Array>
              </Set>

RewriteHandler.java 配置rewrite规则的handler,详细见
public class RewriteHandler extends HandlerWrapper
{
    
    private RuleContainer _rules;

 配置如下:
     <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
      <Set name="handler"><Ref id="oldhandler"/></Set>
      <Set name="rewriteRequestURI">true</Set>
      <Set name="rewritePathInfo">false</Set>
      <Set name="originalPathAttribute">requestedPath</Set>

      <!-- Add rule to protect against IE ssl bug -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.MsieSslRule"/>
        </Arg>
      </Call>

      <!-- protect favicon handling -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
            <Set name="pattern">/favicon.ico</Set>
            <Set name="name">Cache-Control</Set>
            <Set name="value">Max-Age=3600,public</Set>
            <Set name="terminating">true</Set>
          </New>
        </Arg>
      </Call>

      <!-- redirect from the welcome page to a specific page -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
            <Set name="pattern">/rewrite/</Set>
            <Set name="replacement">/rewrite/info.html</Set>
          </New>
        </Arg>
      </Call>

      <!-- replace the entire request URI -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
            <Set name="pattern">/some/old/context</Set>
            <Set name="replacement">/rewritten/newcontext</Set>
          </New>
        </Arg>
      </Call>

      <!-- replace the beginning of the request URI -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
            <Set name="pattern">/rewrite/for/*</Set>
            <Set name="replacement">/rewritten/</Set>
          </New>
        </Arg>
      </Call>
      
      <!-- reverse the order of the path sections -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.RewriteRegexRule">
            <Set name="regex">(.*?)/reverse/([^/]*)/(.*)</Set>
            <Set name="replacement">$1/reverse/$3/$2</Set>
          </New>
        </Arg>
      </Call>

      <!-- add a cookie to each path visited -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.CookiePatternRule">
            <Set name="pattern">/*</Set>
            <Set name="name">visited</Set>
            <Set name="value">yes</Set>
          </New>
        </Arg>
      </Call>
      
      <!--  actual redirect, instead of internal rewrite -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.RedirectPatternRule">
            <Set name="pattern">/redirect/*</Set>
            <Set name="location">/redirected</Set>
          </New>
        </Arg>
      </Call>

      <!-- add a response rule -->
      <Call name="addRule">
        <Arg>
           <New class="org.eclipse.jetty.rewrite.handler.ResponsePatternRule">
             <Set name="pattern">/400Error</Set>
             <Set name="code">400</Set>
             <Set name="reason">ResponsePatternRule Demo</Set>
          </New>
        </Arg>
      </Call>

     </New>
    </Set>

SessionHandler.java  session管理,在cookie和URI中查找  Look for a requested session ID in cookies and URI parameters,默认sessionid的key为JSESSIONID
                     有两种实现:HashSessionManager和JDBCSessionManager。前面是存放在内存的session管理,后面是基于数据库的session管理。
StatisticsHandler.java 服务器监控统计的handler
ConstraintSecurityHandler.java 安全验证的handler
ErrorPageErrorHandler.java 处理出错页面的handler
ServletHandler.java This handler does not implement the full J2EE features and is intended to be used when a full web application is not required.                           Specifically filters and request wrapping are not supported.

ResourceHandler.java 资源文件处理handler

   /////////////////////////////////////////// /////////////////////////
 
    <!-- =========================================================== -->
    <!-- extra options                                               -->
    <!-- =========================================================== -->
    <Set name="stopAtShutdown">true</Set>    stop的时候先去把相关的lifecycle接口的实现先stop掉
    <Set name="sendServerVersion">true</Set>  是否发送jetty的版本,在HttpConnection类调用
    <Set name="sendDateHeader">true</Set>    是否发送头信息,在requestHandler调用
    <Set name="gracefulShutdown">1000</Set>   等待connect close和context shutdown之后休眠1000毫秒再stop相关的connector,handler等

</Configure>


context:


各个子模块的学习:(待续)

Deploy:

rewrite:
   由RewriteHandler.java处理,在rewrite处理完之后调用上级handler去处理。
    <Configure id="Server" class="org.eclipse.jetty.server.Server">

    <!-- =========================================================== -->
    <!-- configure rewrite handler                                   --> 
    <!-- =========================================================== -->
    <Get id="oldhandler" name="handler"/>

    <Set name="handler">
     <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
      <Set name="handler"><Ref id="oldhandler"/></Set>
      <Set name="rewriteRequestURI">true</Set>
      <Set name="rewritePathInfo">false</Set>
      <Set name="originalPathAttribute">requestedPath</Set>

      <!-- Add rule to protect against IE ssl bug -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.MsieSslRule"/>
        </Arg>
      </Call>

      <!-- protect favicon handling -->
      <Call name="addRule">
        <Arg>
          <New class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
            <Set name="pattern">/favicon.ico</Set>
            <Set name="name">Cache-Control</Set>
            <Set name="value">Max-Age=3600,public</Set>
            <Set name="terminating">true</Set>
          </New>
        </Arg>
      </Call>
      ......
   rewrite规则包括:
           MsieSslRule.java修复IE6以下的bug,增加response.setHeader(HttpHeaders.CONNECTION, HttpHeaderValues.CLOSE);
           HeaderPatternRule.java 在request更新头字段
           RewritePatternRule.java 把确定的请求重写为另外一个url
           RewriteRegexRule.java 用正则表达式去重写url
           CookiePatternRule.java 在cookie中增加一个参数
           RedirectPatternRule.java 重写redirect 的url
           ResponsePatternRule.java 修改response的输出
           ForwardedSchemeHeaderRule.java 重写forward的schema

jmx:

jndi:
  参考资料:http://baike.baidu.com/view/209575.htm
  http://blog.csdn.net/lovingprince/article/details/6364767

OSGI:

security:

   对应handler:ConstraintSecurityHandler.java继承自SecurityHandler.java抽象类
   JDBCLoginService.java默认实现
   验证方式:了解了这几种验证的代码实现 都继承子LoginAuthenticator.java抽象类,这个抽象类实现Authenticator.java这个接口,采用适配器模式
       BasicAuthenticator.java    
       ClientCertAuthenticator.java
       DigestAuthenticator.java
       FormAuthenticator.java

   在SecurityHandler.java的生命周期方法dostart()时,会根据配置从factory中获得对应的验证器
     if (_authenticator==null && _authenticatorFactory!=null && _identityService!=null)
        {
            _authenticator=_authenticatorFactory.getAuthenticator(getServer(),ContextHandler.getCurrentContext(),this, _identityService, _loginService);
            if (_authenticator!=null)
                _authMethod=_authenticator.getAuthMethod();
        }

    对应的DefaultAuthenticatorFactory.java工厂方法中的处理:
    public Authenticator getAuthenticator(Server server, ServletContext context, AuthConfiguration configuration, IdentityService identityService,                                                  LoginService loginService)
    {
        String auth=configuration.getAuthMethod();
        Authenticator authenticator=null;
        
        if (auth==null || Constraint.__BASIC_AUTH.equalsIgnoreCase(auth))
            authenticator=new BasicAuthenticator();
        else if (Constraint.__DIGEST_AUTH.equalsIgnoreCase(auth))
            authenticator=new DigestAuthenticator();
        else if (Constraint.__FORM_AUTH.equalsIgnoreCase(auth))
            authenticator=new FormAuthenticator();
        if (Constraint.__CERT_AUTH.equalsIgnoreCase(auth)||Constraint.__CERT_AUTH2.equalsIgnoreCase(auth))
            authenticator=new ClientCertAuthenticator();
        
        return authenticator;
    }

policy:

servlet:

servlets:
    servlet中的Filter部分
         CGI.java 处理cgi请求的,
                   主要处理:通过分析请求得到执行的CGI脚本,使用java的exec执行脚本后把输出copy到serlvetoutputstream,需要准备相关的环境变量
                   Process p=(dir==null)?Runtime.getRuntime().exec(execCmd,env.getEnvArray()):Runtime.getRuntime().exec(execCmd,env.getEnvArray(),dir);
         CloseableDoSFilter.java 允许通过jettyAPI去关闭请求连接
         DoSFilter.java  防止dos攻击的filter。只要通过限制每秒最大处理请求数,每个请求最长处理时间,以及通过ip和端口去方式dos攻击。
         ConcatServlet.java 可以在参数里面设置多个资源合并一次下载。
                            &lt;script type="text/javascript" src="../concat?/js/behaviour.js&/js/ajax.js&/chat/chat.js"&gt;&lt;/script&gt;
        处理代码:
        String[] parts = q.split("\\&");......
        if (type!=null)
            resp.setContentType(type);

        for (int i=0;i<parts.length;i++)
        {
            RequestDispatcher dispatcher=_context.getRequestDispatcher(parts[i]);
            if (dispatcher!=null)
                dispatcher.include(req,resp);
        }
       CrossOriginFilter.java: 跨域请求合法性处理   http://hi.baidu.com/aullik5/blog/item/12f2f8ec552da74878f0553f.html
                               通过在配置文件中配置允许跨域请求的域allowedOrigins(默认都允许)来验证来自请求头Origin这个字段是否在合法性域中,并且还可                                 以配置允许请求方法(默认为get,put)和请求的允许头字段allowedHeaders(默认= "X-Requested-With,Content-Type,Accept";)来                                  进行跨域请求处理。
       GzipFilter.java gzip压缩支持  通过对ServletOutputStream进行GZIPOutputStream装饰进行压缩支持。
       MultiPartFilter.java 文件上传支持
       ProxyServlet.java  代理serlvet支持  通过HttpExchange.java实现,注意代理serlvet有几个头字段:               
                // Proxy headers
                exchange.setRequestHeader("Via","1.1 (jetty)");
                if (!xForwardedFor)
                {
                    exchange.addRequestHeader("X-Forwarded-For",
                            request.getRemoteAddr());
                    exchange.addRequestHeader("X-Forwarded-Proto",
                            request.getScheme());
                    exchange.addRequestHeader("X-Forwarded-Host",
                            request.getServerName());
                    exchange.addRequestHeader("X-Forwarded-Server",
                            request.getLocalName());
                }
      PutFilter.java A Filter that handles PUT, DELETE and MOVE methods.  实现简单,就是文件操作
      QoSFilter.java 限制服务进程处理的同时处理请求的数量,保证正在处理的请求的服务质量,保证服务器负载正常。
                     通过信号量Semaphore来处理最大的请求数量,新请求进来,如果请求队列中未达到正在处理的最大请求数量,则获得信号量处理当前请求,否则挂                        起当前请求,知道能获得信号量。
      UserAgentFilter.java UserAgent缓存处理,快速匹配当前的Agent放入到request.setAttribute(_attribute,ua);
      WelcomeFilter.java 如果没有配置welcome page的话可以使用这个filter。
                        String path=((HttpServletRequest)request).getServletPath();
                        if (welcome!=null && path.endsWith("/"))
                            request.getRequestDispatcher(path+welcome).forward(request,response);
                        else
                            chain.doFilter(request, response);
webApp:

webSocket:
   服务器端和客户端保持一个TCP的连接,需要浏览器支持,协议内容不一样,可以实现服务器推的技术。
   协议内容可以参考:http://feiyezi.iteye.com/blog/1060481 http://www.w3.org/TR/websockets/



jetty相关资料链接:
http://blog.romebuilder.com/2010/12/157/
http://blog.csdn.net/lovingprince/article/details/6202669
http://blog.csdn.net/lovingprince/article/details/6202859
http://blog.csdn.net/lovingprince/article/details/6202859  请求处理过程分析
 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics