从零开始手写Tomcat的教程11节----StandardWrapper

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始手写Tomcat的教程11节----StandardWrapper相关的知识,希望对你有一定的参考价值。

从零开始手写Tomcat的教程11节----StandardWrapper


本节内容与之前这节有着很大的关系,建议各位先回顾一下:

从零开始手写Tomcat的教程4节—Tomcat默认连接器



方法调用序列






SingleThreadModel类



StandardWrapper类




这里实现的是针对某个方法的同步执行,思路有两个: 同步锁,对象池实现对象复用


分配servlet实例

    public Servlet allocate() throws ServletException;

// If not SingleThreadedModel, return the same instance every time
        if (!singleThreadModel) 
        ...
        

synchronized (instancePool) 
//return an instance of the servlet from the pool

    /**
     * The (single) initialized instance of this servlet.
     */
    private Servlet instance = null;

 // If not SingleThreadedModel, return the same instance every time
        if (!singleThreadModel) 

            // Load and initialize our instance if necessary
            //为了实现单例模式,采用的是双重锁校验
            if (instance == null) 
                synchronized (this) 
                    if (instance == null) 
                        try 
                            instance = loadServlet();
                         catch (ServletException e) 
                            throw e;
                         catch (Throwable e) 
                            throw new ServletException
                                (sm.getString("standardWrapper.allocate"), e);
                        
                    
                
            

            if (!singleThreadModel) 
                if (debug >= 2)
                    log("  Returning non-STM instance");
                countAllocated++;
                return (instance);
            

        

    /**
     * Stack containing the STM instances.
     */
    private Stack instancePool = null;

    /**
     * Maximum number of STM instances.
     */
    private int maxInstances = 20;

    /**
     * Number of instances currently loaded for a STM servlet.
     */
    private int nInstances = 0;

记录已经创建的存活的servlet对象实例个数,包括STM和non-STM的servlet类实例

    /**
     * The count of allocations that are currently active (even if they
     * are for the same instance, as will be true on a non-STM servlet).
     */
    private int countAllocated = 0;

下面是allocate方法的第二部分:

synchronized (instancePool) 
           
            while (countAllocated >= nInstances) 
                // Allocate a new instance if possible, or else wait
                if (nInstances < maxInstances) 
                    try 
                        instancePool.push(loadServlet());
                        nInstances++;
                     catch (ServletException e) 
                        throw e;
                     catch (Throwable e) 
                        throw new ServletException
                            (sm.getString("standardWrapper.allocate"), e);
                    
                 else 
                    try 
                        instancePool.wait();
                     catch (InterruptedException e) 
                        ;
                    
                
            
            if (debug >= 2)
                log("  Returning allocated STM instance");
            countAllocated++;
            return (Servlet) instancePool.pop();

        


allocate方法完整源码:

 /**
     * Allocate an initialized instance of this Servlet that is ready to have
     * its <code>service()</code> method called.  If the servlet class does
     * not implement <code>SingleThreadModel</code>, the (only) initialized
     * instance may be returned immediately.  If the servlet class implements
     * <code>SingleThreadModel</code>, the Wrapper implementation must ensure
     * that this instance is not allocated again until it is deallocated by a
     * call to <code>deallocate()</code>.
     *
     * @exception ServletException if the servlet init() method threw
     *  an exception
     * @exception ServletException if a loading error occurs
     */
    public Servlet allocate() throws ServletException 

        if (debug >= 1)
            log("Allocating an instance");

        // If we are currently unloading this servlet, throw an exception
        if (unloading)
            throw new ServletException
              (sm.getString("standardWrapper.unloading", getName()));

        // If not SingleThreadedModel, return the same instance every time
        if (!singleThreadModel) 

            // Load and initialize our instance if necessary
            if (instance == null) 
                synchronized (this) 
                    if (instance == null) 
                        try 
                            instance = loadServlet();
                         catch (ServletException e) 
                            throw e;
                         catch (Throwable e) 
                            throw new ServletException
                                (sm.getString("standardWrapper.allocate"), e);
                        
                    
                
            

            if (!singleThreadModel) 
                if (debug >= 2)
                    log("  Returning non-STM instance");
                countAllocated++;
                return (instance);
            

        

        synchronized (instancePool) 

            while (countAllocated >= nInstances) 
                // Allocate a new instance if possible, or else wait
                if (nInstances < maxInstances) 
                    try 
                        instancePool.push(loadServlet());
                        nInstances++;
                     catch (ServletException e) 
                        throw e;
                     catch (Throwable e) 
                        throw new ServletException
                            (sm.getString("standardWrapper.allocate"), e);
                    
                 else 
                    try 
                        instancePool.wait();
                     catch (InterruptedException e) 
                        ;
                    
                
            
            if (debug >= 2)
                log("  Returning allocated STM instance");
            countAllocated++;
            return (Servlet) instancePool.pop();

        

    

载入servlet实例

    public synchronized void load() throws ServletException 
        instance = loadServlet();
    

    /**
     * Load and initialize an instance of this servlet, if there is not already
     * at least one initialized instance.  This can be used, for example, to
     * load servlets that are marked in the deployment descriptor to be loaded
     * at server startup time.
     */
    public synchronized Servlet loadServlet() throws ServletException 

        // Nothing to do if we already have an instance or an instance pool
        if (!singleThreadModel && (instance != null))
            return instance;

        PrintStream out = System.out;
        SystemLogHandler.startCapture();

        Servlet servlet = null;

        try 
            // If this "servlet" is really a JSP file, get the right class.
            String actualClass = servletClass;
            if ((actualClass == null) && (jspFile != null)) 
                Wrapper jspWrapper = (Wrapper)
                    ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
                if (jspWrapper != null)
                    actualClass = jspWrapper.getServletClass();
            

            // Complain if no servlet class has been specified
            if (actualClass == null) 
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.notClass", getName()));
            

            // Acquire an instance of the class loader to be used
            Loader loader = getLoader();
            if (loader == null) 
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.missingLoader", getName()));
            

            ClassLoader classLoader = loader.getClassLoader();

            // Special case class loader for a container provided servlet
            if (isContainerProvidedServlet(actualClass)) 
                classLoader = this.getClass().getClassLoader();
                log(sm.getString
                      ("standardWrapper.containerServlet", getName()));
            

            // Load the specified servlet class from the appropriate class loader
            Class classClass = null;
            try 
                if (classLoader != null) 
                    classClass = classLoader.loadClass(actualClass);
                 else 
                    classClass = Class.forName(actualClass);
                
             catch (ClassNotFoundException e) 
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.missingClass", actualClass),
                     e);
            
            if (classClass == null) 
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.missingClass", actualClass));
            

            // Instantiate and initialize an instance of the servlet class itself
            try 
                servlet = (Servlet) classClass.newInstance();
             catch (ClassCastException e) 
                unavailable(null);
                // Restore the context ClassLoader
                throw new ServletException
                    (sm.getString("standardWrapper.notServlet", actualClass), e);
             catch (Throwable e) 
                unavailable(null);
                // Restore the context ClassLoader
                throw new ServletException
                    (sm.getString("standardWrapper.instantiate", actualClass), e);
            

            // Check if loading the servlet in this web application should be 
            // allowed
            if (!isServletAllowed(servlet)) 
                throw new SecurityException
                    (sm.getString("standardWrapper.privilegedServlet", 
                                  actualClass));
            
    

            // Special handling for ContainerServlet instances
            if ((servlet instanceof ContainerServlet) &&
                isContainerProvidedServlet(actualClass)) 
                ((ContainerServlet) servlet).setWrapper(this);
            
    

            // Call the initialization method of this servlet
            try 
                instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
                                                  servlet);
                servlet.init(facade);


              // Invoke jspInit on JSP pages
                if ((loadOnStartup > 0) && (jspFile != null)) 
                    // Invoking jspInit
                    HttpRequestBase req = new HttpRequestBase();
                    HttpResponseBase res = new HttpResponseBase();
                    req.setServletPath(jspFile);
                    req.setQueryString("jsp_precompile=true");
                    servlet.service(req, res);
                

                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                                  servlet);

            // Register our newly initialized instance
            singleThreadModel = servlet instanceof SingleThreadModel;
            if (singleThreadModel) 
                if (instancePool == null)
                    instancePool = new Stack();
            
            fireContainerEvent("load", this);

 finally 
            String log = SystemLogHandler.stopCapture();
            if (log != null && log.length() > 0) 
                if (getServletContext() != null) 
                    getServletContext().log(log);
                 else 
                    out.println(log);
                
            
        

        return servlet;

loadServlet方法完整源码:

    /**
     * Load and initialize an instance of this servlet, if there is not already
     * at least one initialized instance.  This can be used, for example, to
     * load servlets that are marked in the deployment descriptor to be loaded
     * at server startup time.
     */
    public synchronized Servlet loadServlet() throws ServletException 

        // Nothing to do if we already have an instance or an instance pool
        if (!singleThreadModel && (instance != null))
            return instance;

        PrintStream out = System.out;
        SystemLogHandler.startCapture();
        Servlet servlet = null;
        try 
            // If this "servlet" is really a JSP file, get the right class.
            // HOLD YOUR NOSE - this is a kludge that avoids having to do special
            // case Catalina-specific code in Jasper - it also requires that the
            // servlet path be replaced by the <jsp-file> element content in
            // order to be completely effective
            String actualClass = servletClass;
            if ((actualClass == null) && (jspFile != null)) 
                Wrapper jspWrapper = (Wrapper)
                    ((Context) getParent()).findChild(org.apache.catalina.core.Constants.JSP_SERVLET_NAME);
                if (jspWrapper != null)
                    actualClass = jspWrapper.getServletClass();
            
    
            // Complain if no servlet class has been specified
            if (actualClass == null) 
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.notClass", getName()));
            
    
            // Acquire an instance of the class loader to be used
            Loader loader = getLoader();
            if (loader == null) 
                unavailable(null);
                throw new ServletException
                    (sm.getString(从零开始手写Tomcat的教程6节----生命周期

从零开始手写Tomcat的教程9节---Session管理

从零开始手写Tomcat的教程5节---servlet容器

从零开始手写Tomcat的教程10节---安全性

从零开始手写Tomcat的教程4节---Tomcat默认连接器

从零开始手写Tomcat的教程7节---日志记录器