Tomcat source code analysis series (VIII) wrapper

Time:2020-4-5

Preface
The first article talks about the startup of Context. In Context’s startInternal method, the start method of the sub container is invoked. The sub container of Context is Wrapper, and the implementation class of Wrapper is StandardWrapper. The standardwrapper does not overload the initinternal method.
1. Standardwrapper? Startinternal method

/**
 * Start this component and implement the requirements
 * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
 *
 * @exception LifecycleException if this component detects a fatal error
 *  that prevents this component from being used
 */
@Override
protected synchronized void startInternal() throws LifecycleException {

    // Send j2ee.state.starting notification
    if (this.getObjectName() != null) {
        Notification notification = new Notification("j2ee.state.starting",
                                                    this.getObjectName(),
                                                    sequenceNumber++);
        broadcaster.sendNotification(notification);
    }

    // Start up this component
    super.startInternal();

    setAvailable(0L);

    // Send j2ee.state.running notification
    if (this.getObjectName() != null) {
        Notification notification =
            new Notification("j2ee.state.running", this.getObjectName(),
                            sequenceNumber++);
        broadcaster.sendNotification(notification);
    }

}

The logic of startInternal is also very simple, that is, calling the startInternal method of the parent class ContainerBase, and then calling setAvailable (0L) to set the value of the available attribute.

/**
 * The date and time at which this servlet will become available (in
 * milliseconds since the epoch), or zero if the servlet is available.
 * If this value equals Long.MAX_VALUE, the unavailability of this
 * servlet is considered permanent.
 */
protected long available = 0L;


/**
 * Set the available date/time for this servlet, in milliseconds since the
 * epoch.  If this date/time is Long.MAX_VALUE, it is considered to mean
 * that unavailability is permanent and any request for this servlet will return
 * an SC_NOT_FOUND error. If this date/time is in the future, any request for
 * this servlet will return an SC_SERVICE_UNAVAILABLE error.
 *
 * @param available The new available date/time
 */
@Override
public void setAvailable(long available) {
    long oldAvailable = this.available;
    if (available > System.currentTimeMillis())
        this.available = available;
    else
        this.available = 0L;
    support.firePropertyChange("available", Long.valueOf(oldAvailable),
                               Long.valueOf(this.available));
}

As can be seen from the available annotation, its function is to represent the available time of the servlet.

2. Standardwrapper load method
In the last article, we talked about the startInternal method of Context. One thing we did was to call the load method of Wrapper (called in StandardContext#loadOnStartup). In StandardContext#startInternal, first call the start method of Wrapper, then call Wrapper’s load method.

/**
 * The (single) possibly uninitialized instance of this servlet.
 */
protected volatile Servlet instance = null;


/**
 * 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.
 * <p>
 * <b>IMPLEMENTATION NOTE</b>:  Servlets whose classnames begin with
 * <code>org.apache.catalina.</code> (so-called "container" servlets)
 * are loaded by the same classloader that loaded this class, rather than
 * the classloader for the current web application.
 * This gives such classes access to Catalina internals, which are
 * prevented for classes loaded for web applications.
 *
 * @exception ServletException if the servlet init() method threw
 *  an exception
 * @exception ServletException if some other loading problem occurs
 */
@Override
public synchronized void load() throws ServletException {
    instance = loadServlet();

    if (!instanceInitialized) {
        initServlet(instance);
    }

    if (isJspServlet) {
        StringBuilder oname = new StringBuilder(getDomain());

        oname.append(":type=JspMonitor");

        oname.append(getWebModuleKeyProperties());

        oname.append(",name=");
        oname.append(getName());

        oname.append(getJ2EEKeyProperties());

        try {
            jspMonitorON = new ObjectName(oname.toString());
            Registry.getRegistry(null, null)
                .registerComponent(instance, jspMonitorON, null);
        } catch (Exception ex) {
            log.info(sm.getString("standardWrapper.jspMonitorError", instance));
        }
    }
}

The logic of the load method is very simple. First, call loadservlet() to get a servlet object. That is to create a servlet object by calling the class name specified by the servletclass attribute and the instancemanager ා newinstance (servletclass) method.
Then initServlet (instance) is called to initialize the Servlet object, that is, the init method of calling the Servlet object.

You can see that there is a servlet attribute in the wrapper, which is exactly the wrapper of the servlet.

3. Standardwrapper × backgroundprocess method

/**
 * Execute a periodic task, such as reloading, etc. This method will be
 * invoked inside the classloading context of this container. Unexpected
 * throwables will be caught and logged.
 */
@Override
public void backgroundProcess() {
    super.backgroundProcess();

    if (!getState().isAvailable())
        return;

    if (getServlet() instanceof PeriodicEventListener) {
        ((PeriodicEventListener) getServlet()).periodicEvent();
    }
}

The backgroundprocess method is very simple. It just calls the backgroundprocess method of the parent class containerbase.


Summary
This paper analyzes the start-up of wrapper container. The key point of wrapper during the start-up of Tomcat is to initialize the servlet.