tomcat system architecture

Architecture

tomcat

  • Server used to manage service, for outside provide a interface’s service
    for inside maintain service’s collection.
    • manage services’ lifecycle
    • search service for request
    • stop service so on
  • Service consist by connector and container
    • Connector be responsible for give request to container
    • Container be responsible for deal with request

Server

Forcus on two method addService(Service service) and findService(String name)

org.apache.catalina.core.StandardServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* Add a new Service to the set of defined Services.
*
* @param service The Service to be added
*/
@Override
public void addService(Service service) {

service.setServer(this);

synchronized (servicesLock) {
Service results[] = new Service[services.length + 1];
System.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;

if (getState().isAvailable()) {
try {
service.start();
} catch (LifecycleException e) {
// Ignore
}
}

// Report this property change to interested listeners
support.firePropertyChange("service", null, service);
}

}

  • set current service’s server
  • extends service array, place new service in the last index
  • check service’s lefe state
  • report event to interested listeners

Interface Lifecycle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
start()
-----------------------------
| |
| init() |
NEW ->-- INITIALIZING |
| | | | ------------------<-----------------------
| | |auto | | |
| | \|/ start() \|/ \|/ auto auto stop() |
| | INITIALIZED -->-- STARTING_PREP -->- STARTING -->- STARTED -->--- |
| | | | |
| |destroy()| | |
| -->-----<-- ------------------------<-------------------------------- ^
| | | |
| | \|/ auto auto start() |
| | STOPPING_PREP ---->---- STOPPING ------>----- STOPPED ----->-----
| \|/ ^ | ^
| | stop() | | |
| | -------------------------- | |
| | | | |
| | | destroy() destroy() | |
| | FAILED ---->------ DESTROYING ---<----------------- |
| | ^ | |
| | destroy() | |auto |
| -------->----------------- \|/ |
| DESTROYED |
| |
| stop() |
--->------------------------------>------------------------------
  • Any state can transition to FAILED.

  • Calling start() while a component is in states STARTING_PREP, STARTING or
    STARTED has no effect.

  • Calling start() while a component is in state NEW will cause init() to be
    called immediately after the start() method is entered.

  • Calling stop() while a component is in states STOPPING_PREP, STOPPING or
    STOPPED has no effect.

  • Calling stop() while a component is in state NEW transitions the component
    to STOPPED. This is typically encountered when a component fails to start and
    does not start all its sub-components. When the component is stopped, it will
    try to stop all sub-components - even those it didn’t start.

  • Attempting any other transition will throw LifecycleException.

Anaylsis

1
2
3
4
5
6
7
8
I Lifecycle
|
--- A LifecycleBase
| |
| --- A LifecycleMBeanBase
| |
| --- ...
--- ...

All component in tomcat implemnts Lifecycle interface,
father level component manage sub-level component.
So when top component changes, state will transmit to all conponments.
Focus on anaylsis start progress.

org.apache.catalina.util.LifecycleBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public final synchronized void start() throws LifecycleException {

......

try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal();

......

} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}
}

org.apache.catalina.core.StandardServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Start nested components ({@link Service}s) 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 void startInternal() throws LifecycleException {

fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);

globalNamingResources.start();

// Start our defined Services
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}

All sub-components will be notify through fireLifecycleEvent
All registed service will be start.

org.apache.catalina.util.LifecycleSupport

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Notify all lifecycle event listeners that a particular event has
* occurred for this Container. The default implementation performs
* this notification synchronously using the calling thread.
*
* @param type Event type
* @param data Event data
*/
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);

}

Be called[defined] in LifecycleBase , method name is the same.

Service

A service packaged multi connector and a container
We forcus on addConnector(Connector connector) setContainer(Container container)

org.apache.catalina.core.StandardService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* Set the <code>Container</code> that handles requests for all
* <code>Connectors</code> associated with this Service.
*
* @param container The new Container
*/
@Override
public void setContainer(Container container) {

Container oldContainer = this.container;
if ((oldContainer != null) && (oldContainer instanceof Engine))
((Engine) oldContainer).setService(null);
this.container = container;
if ((this.container != null) && (this.container instanceof Engine))
((Engine) this.container).setService(this);
if (getState().isAvailable() && (this.container != null)) {
try {
this.container.start();
} catch (LifecycleException e) {
// Ignore
}
}
if (getState().isAvailable() && (oldContainer != null)) {
try {
oldContainer.stop();
} catch (LifecycleException e) {
// Ignore
}
}

// Report this property change to interested listeners
support.firePropertyChange("container", oldContainer, this.container);

}

  1. set new input container
  2. reversal setting container’s service
  3. if state ok, start container
  4. if old container exists, stop it
  5. report event
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Add a new Connector to the set of defined Connectors, and associate it
* with this Service's Container.
*
* @param connector The Connector to be added
*/
@Override
public void addConnector(Connector connector) {

synchronized (connectorsLock) {
connector.setService(this);
Connector results[] = new Connector[connectors.length + 1];
System.arraycopy(connectors, 0, results, 0, connectors.length);
results[connectors.length] = connector;
connectors = results;

if (getState().isAvailable()) {
try {
connector.start();
} catch (LifecycleException e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}

// Report this property change to interested listeners
support.firePropertyChange("connector", null, connector);
}

}
  1. Setting connector’s service.
  2. Similar with server manage services, use array storage connector.
  3. Extends connector array, place new connector in the last index, implements dynamic array.
  4. start connector, report event.

Design Model

observer
All registed observers listener will be call by reported event
Observer may do something, or just ignore.