The setStateInternal method is called in several places when you look at the source code of the LifecycleBase class and other components. This article will cover this approach and the Lifecycle mechanism and implementation principles of Tomcat associated with this approach.

In article talked about the Tomcat 7 components of parent LifecycleBase class, the class implements an interface org. Apache. Catalina. Lifecycle, the following is a constant in the interface definition and method:

These string constant definitions in the Lifecycle interface are primarily used for the definition of event types, as will be discussed later in this article.

Focus on the following three methods:

/** * Add a LifecycleEvent listener to this component. * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener); /** * The life cycle listeners associated with this life cycle. If this * component has no listeners registered, a zero-length array is returned. */ public LifecycleListener[] findLifecycleListeners(); ** * Remove a LifecycleEvent listener from this component. ** @param listener The listener to Remove */ public void removeLifecycleListener(LifecycleListener listener); // Remove a listener from this componentCopy the code

The actions of these three methods are briefly explained in the comments to the code. The three methods involving org. Apache. Catalina. LifecycleListener interface, then look at the definition of the interface:


public interface LifecycleListener {


    /**
     * Acknowledge the occurrence of the specified event.
     *
     * @param event LifecycleEvent that has occurred
     */
    public void lifecycleEvent(LifecycleEvent event);


}
Copy the code

So simple, there is only one method, this method is used as an event (org. Apache. Catalina. LifecycleEvent) notify the listener implementation class, specific to the event listener implementation class decide how to deal with it.

Take a look at the LifecycleEvent implementation:

public final class LifecycleEvent extends EventObject { private static final long serialVersionUID = 1L; // ----------------------------------------------------------- Constructors /** * Construct a new LifecycleEvent with the specified parameters. * * @param lifecycle Component on which this event occurred * @param type Event type (required) * @param data Event data (if any) */ public LifecycleEvent(Lifecycle lifecycle, String type, Object data) { super(lifecycle); this.type = type; this.data = data; } // ----------------------------------------------------- Instance Variables /** * The event data associated with this event. */ private Object data = null; /** * The event type this instance represents. */ private String type = null; // ------------------------------------------------------------- Properties /** * Return the event data of this event. */ public Object getData() { return (this.data); } /** * Return the Lifecycle on which this event occurred. */ public Lifecycle getLifecycle() { return (Lifecycle) getSource(); } /** * Return the event type of this event. */ public String getType() { return (this.type); }}Copy the code

This class is also very simple, with data and type as built-in instance variables. The only thing that supports event definition is the use of the JDK’s built-in java.util.EventObject as the parent class. Here in event constructor org. Apache. Catalina. The Lifecycle of the class instance Lifecycle as the event source, save Lifecycle object reference, and provides getLifecycle method returns the reference.

So how does Tomcat implement listening and notification for these events?

Line 47 of the LifecycleBase class mentioned at the beginning of this article defines an instance variable lifecycle, which is used to register the various listeners defined on the component. Look to the lifecycle of the instance variables, it is not the org. Apache. Catalina. Lifecycle instances of the class, but the org. Apache. Catalina. Util. LifecycleSupport instances of the class. It is this utility class that provides event listening and event notification capabilities.

Look at how to components in the actual code under the release time notification, see as mentioned in the previous articles of org. Apache. Catalina. Core. StandardServer startInternal method of a class:

1 protected void startInternal() throws LifecycleException { 2 3 fireLifecycleEvent(CONFIGURE_START_EVENT, null); 4 setState(LifecycleState.STARTING); 5 6 globalNamingResources.start(); 7 8 // Start our defined Services 9 synchronized (services) { 10 for (int i = 0; i < services.length; i++) { 11 services[i].start(); 12} 13} 14}Copy the code

We already analyzed 9 to 13 lines of code, here to see the first three rows, it calls the superclass org. Apache. Catalina. Util. LifecycleBase fireLifecycleEvent method, The CONFIGURE_START_EVENT here is the constant defined in the Lifecycle interface at the beginning of this article, which means that a start configuration event has been published.

Org. Apache. Catalina. Util. LifecycleBase fireLifecycleEvent method in the class of the call is org. Apache. Catalina. Util. LifecycleSupport class FireLifecycleEvent method, which has the following code:


    public void fireLifecycleEvent(String type, Object data) {

        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        LifecycleListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].lifecycleEvent(event);

    }
Copy the code

Construct a LifecycleEvent object with the two parameters passed in, and then publish the newly constructed event object to all listeners registered with the component.

Here’s a question: when did you register the listener with the component?

Or in the StandardServer, for example, in the front, the use of the Digester, org. Apache. Catalina. Startup. Catalina class createStartDigester methods have so a piece of code:


     1	        // Configure the actions we will be using
     2	        digester.addObjectCreate("Server",
     3	                                 "org.apache.catalina.core.StandardServer",
     4	                                 "className");
     5	        digester.addSetProperties("Server");
     6	        digester.addSetNext("Server",
     7	                            "setServer",
     8	                            "org.apache.catalina.Server");
     9	
    10	        digester.addObjectCreate("Server/GlobalNamingResources",
    11	                                 "org.apache.catalina.deploy.NamingResources");
    12	        digester.addSetProperties("Server/GlobalNamingResources");
    13	        digester.addSetNext("Server/GlobalNamingResources",
    14	                            "setGlobalNamingResources",
    15	                            "org.apache.catalina.deploy.NamingResources");
    16	
    17	        digester.addObjectCreate("Server/Listener",
    18	                                 null, // MUST be specified in the element
    19	                                 "className");
    20	        digester.addSetProperties("Server/Listener");
    21	        digester.addSetNext("Server/Listener",
    22	                            "addLifecycleListener",
    23	                            "org.apache.catalina.LifecycleListener");
Copy the code

Line 17 to 24, calls the org. Apache. Catalina. Core. StandardServer addLifecycleListener method in a class, An object instance is constructed from the className attribute defined by the Listener node under the Server node configured in server.xml and is used as an input parameter to the addLifecycleListener method. All the listeners will achieve the above mentioned org. Apache. Catalina. LifecycleListener interface. The Listener Server node has several, here to org. Apache. Catalina. Core. JasperListener, for example.

In constructing the org. Apache. Catalina. Core. After JasperListener objects of a class, call addLifecycleListener method, This method does not directly in the org. Apache. Catalina. Core. StandardServer class definition, but it is the parent of the org. Apache. Catalina. Util. LifecycleBase:


    @Override
    public void addLifecycleListener(LifecycleListener listener) {
        lifecycle.addLifecycleListener(listener);
    }
Copy the code

Here is called the org. Apache. Catalina. Util. LifecycleSupport addLifecycleListener method of a class:

/** * Add a lifecycle event listener to this component. * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener) { synchronized (listenersLock) { LifecycleListener results[] = new LifecycleListener[listeners.length + 1]; for (int i = 0; i < listeners.length; i++) results[i] = listeners[i]; results[listeners.length] = listener; listeners = results; }}Copy the code

LifecycleSupport is a utility class that holds an internal array of listener object instances, as shown on line 68 of the class:


/** 
 * The set of registered LifecycleListeners for event notifications. 
 */  
private LifecycleListener listeners[] = new LifecycleListener[0];  
Copy the code

Internally, the addLifecycleListener method above implements synchronization to add a listener object to the array.

Lifecycle in Tomcat should be understood by calling the addLifecycleListener method of the LifecycleSupport utility class to add a listener. When an event needs to be published, the tool class’s fireLifecycleEvent method is called to publish the event to all listeners registered on the component, and the listener’s internal implementation decides whether to process the event or not.

Take the front see a listener org. Apache. Catalina. Core. JasperListener example:

1 public class JasperListener 2 implements LifecycleListener { 3 4 private static final Log log = LogFactory.getLog(JasperListener.class); 5 6 /** 7 * The string manager for this package. 8 */ 9 protected static final StringManager sm = 10 StringManager.getManager(Constants.Package); 11 12 13 // ---------------------------------------------- LifecycleListener Methods 14 15 16 /** 17 * Primary entry point for startup and shutdown events. 18 * 19 * @param event The event that has occurred 20 */ 21 @Override 22 public void lifecycleEvent(LifecycleEvent event) { 23 24 if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) { 25 try { 26  // Set JSP factory 27 Class.forName("org.apache.jasper.compiler.JspRuntimeContext", 28 true, 29 this.getClass().getClassLoader()); 30 } catch (Throwable t) { 31 ExceptionUtils.handleThrowable(t); 32 // Should not occur, obviously 33 log.warn("Couldn't initialize Jasper", t); 34 } 35 // Another possibility is to do directly: 36 // JspFactory.setDefaultFactory(new JspFactoryImpl()); 37} 38 39} 40 41 42}Copy the code

Focusing on the implementation of the lifecycleEvent method from the interface, you can see that this listener only cares about events with event type BEFORE_INIT_EVENT. If the event is published, Will do the follow-up processing (here will produce a org.apache.jasper.com piler JspRuntimeContext object).

Lifecycle related class UML diagram:

Events are published using a push pattern, in which all specific observers of the topic are notified each time an event is published, and each observer then decides whether the event needs to be followed up.

Here take a look at this article the beginning setStateInternal method, to org. Apache. Catalina. Core. StandardServer class as an example, see above startInternal method of line 4: setState(LifecycleState.STARTING);

It calls the superclass org. Apache. Catalina. Util. LifecycleBase setState method of:


    /**
     * Provides a mechanism for sub-classes to update the component state.
     * Calling this method will automatically fire any associated
     * {@link Lifecycle} event. It will also check that any attempted state
     * transition is valid for a sub-class.
     * 
     * @param state The new state for this component
     */
    protected synchronized void setState(LifecycleState state)
            throws LifecycleException {
        setStateInternal(state, null, true);
    }
Copy the code

Call one of the class’s synchronized methods setStateInternal from within this class:

1 private synchronized void setStateInternal(LifecycleState state, 2 Object data, boolean check) throws LifecycleException { 3 4 if (log.isDebugEnabled()) { 5 log.debug(sm.getString("lifecycleBase.setState", this, state)); 6 } 7 8 if (check) { 9 // Must have been triggered by one of the abstract methods (assume 10 // code in this class is correct) 11 // null is never a valid state 12 if (state == null) { 13 invalidTransition("null"); 14 // Unreachable code - here to stop eclipse complaining about 15 // a possible NPE further down the method 16 return; 17 } 18 19 // Any method can transition to failed 20 // startInternal() permits STARTING_PREP to STARTING 21 // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to 22 // STOPPING 23 if (! (state == LifecycleState.FAILED || 24 (this.state == LifecycleState.STARTING_PREP && 25 state == LifecycleState.STARTING) || 26 (this.state == LifecycleState.STOPPING_PREP && 27 state == LifecycleState.STOPPING) || 28  (this.state == LifecycleState.FAILED && 29 state == LifecycleState.STOPPING))) { 30 // No other transition permitted 31  invalidTransition(state.name()); 32 } 33 } 34 35 this.state = state; 36 String lifecycleEvent = state.getLifecycleEvent(); 37 if (lifecycleEvent ! = null) { 38 fireLifecycleEvent(lifecycleEvent, data); 39}} 40Copy the code

Focus on lines 35 through 39. Line 35 assigns the LifecycleState instance of the entry parameter to the instance variable in this class and saves it. Line 36 retrives the LifecycleEvent event of the LifecycleState instance. The fireLifecycleEvent method is called to publish the event.

Now that you’ve seen the LifecycleState class, let’s look at the definition of the LifecycleState class:

1 public enum LifecycleState { 2 NEW(false, null), 3 INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT), 4 INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT), 5 STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT), 6 STARTING(true, Lifecycle.START_EVENT), 7 STARTED(true, Lifecycle.AFTER_START_EVENT), 8 STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT), 9 STOPPING(false, Lifecycle.STOP_EVENT), 10 STOPPED(false, Lifecycle.AFTER_STOP_EVENT), 11 DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT), 12 DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT), 13 FAILED(false, null), 14 MUST_STOP(true, null), 15 MUST_DESTROY(false, null); 16 17 private final boolean available; 18 private final String lifecycleEvent; 19 20 private LifecycleState(boolean available, String lifecycleEvent) { 21 this.available = available; 22 this.lifecycleEvent = lifecycleEvent; 23 } 24 25 /** 26 * May the public methods other than property getters/setters and lifecycle 27 * methods be called for a component in this state? It returns 28 * <code>true</code> for any component in any of the following states: 29 * <ul> 30 * <li>{@link #STARTING}</li> 31 * <li>{@link #STARTED}</li> 32 * <li>{@link #STOPPING_PREP}</li> 33 * <li>{@link #MUST_STOP}</li> 34 * </ul> 35 */ 36 public boolean isAvailable() { 37 return available; 38 } 39 40 /** 41 * 42 */ 43 public String getLifecycleEvent() { 44 return lifecycleEvent; 46 45}}Copy the code

This class is an enumeration containing two instance variables, a Boolean value indicating availability and a string indicating the event type. Look at the enumerated values that have been defined and see that this string will either have no value or will be a string constant defined in Lifecycle. This class essentially wraps another layer of string constants defined in Lifecycle.

To go back to the call to the setStateInternal method you often see in component code at the beginning, you actually publish an event to a listener registered in that component.