StandardPipeline.java
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.core;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.management.ObjectName;
import org.apache.catalina.Contained;
import org.apache.catalina.Container;
import org.apache.catalina.JmxEnabled;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Valve;
import org.apache.catalina.util.LifecycleBase;
import org.apache.catalina.util.ToStringUtil;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
/**
* Standard implementation of a processing <b>Pipeline</b> that will invoke a series of Valves that have been configured
* to be called in order. This implementation can be used for any type of Container. <b>IMPLEMENTATION WARNING</b> -
* This implementation assumes that no calls to <code>addValve()</code> or <code>removeValve</code> are allowed while a
* request is currently being processed. Otherwise, the mechanism by which per-thread state is maintained will need to
* be modified.
*
* @author Craig R. McClanahan
*/
public class StandardPipeline extends LifecycleBase implements Pipeline {
private static final Log log = LogFactory.getLog(StandardPipeline.class);
private static final StringManager sm = StringManager.getManager(StandardPipeline.class);
// ----------------------------------------------------------- Constructors
/**
* Construct a new StandardPipeline instance with no associated Container.
*/
public StandardPipeline() {
this(null);
}
/**
* Construct a new StandardPipeline instance that is associated with the specified Container.
*
* @param container The container we should be associated with
*/
public StandardPipeline(Container container) {
super();
setContainer(container);
}
// ----------------------------------------------------- Instance Variables
/**
* The basic Valve (if any) associated with this Pipeline.
*/
protected Valve basic = null;
/**
* The Container with which this Pipeline is associated.
*/
protected Container container = null;
/**
* The first valve associated with this Pipeline.
*/
protected Valve first = null;
// --------------------------------------------------------- Public Methods
@Override
public boolean isAsyncSupported() {
Valve valve = (first != null) ? first : basic;
boolean supported = true;
while (supported && valve != null) {
supported = supported & valve.isAsyncSupported();
valve = valve.getNext();
}
return supported;
}
@Override
public void findNonAsyncValves(Set<String> result) {
Valve valve = (first != null) ? first : basic;
while (valve != null) {
if (!valve.isAsyncSupported()) {
result.add(valve.getClass().getName());
}
valve = valve.getNext();
}
}
// ------------------------------------------------------ Contained Methods
@Override
public Container getContainer() {
return this.container;
}
@Override
public void setContainer(Container container) {
this.container = container;
}
@Override
protected void initInternal() {
// NOOP
}
@Override
protected void startInternal() throws LifecycleException {
// Start the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle) {
((Lifecycle) current).start();
}
current = current.getNext();
}
setState(LifecycleState.STARTING);
}
@Override
protected void stopInternal() throws LifecycleException {
setState(LifecycleState.STOPPING);
// Stop the Valves in our pipeline (including the basic), if any
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof Lifecycle) {
((Lifecycle) current).stop();
}
current = current.getNext();
}
}
@Override
protected void destroyInternal() {
Valve[] valves = getValves();
for (Valve valve : valves) {
removeValve(valve);
}
}
@Override
public String toString() {
return ToStringUtil.toString(this);
}
// ------------------------------------------------------- Pipeline Methods
@Override
public Valve getBasic() {
return this.basic;
}
@Override
public void setBasic(Valve valve) {
// Change components if necessary
Valve oldBasic = this.basic;
if (oldBasic == valve) {
return;
}
// Stop the old component if necessary
if (oldBasic != null) {
if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) {
try {
((Lifecycle) oldBasic).stop();
} catch (LifecycleException e) {
log.error(sm.getString("standardPipeline.basic.stop"), e);
}
}
if (oldBasic instanceof Contained) {
try {
((Contained) oldBasic).setContainer(null);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
}
}
}
// Start the new component if necessary
if (valve == null) {
return;
}
if (valve instanceof Contained) {
((Contained) valve).setContainer(this.container);
}
if (getState().isAvailable() && valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error(sm.getString("standardPipeline.basic.start"), e);
return;
}
}
// Update the pipeline
Valve current = first;
while (current != null) {
if (current.getNext() == oldBasic) {
current.setNext(valve);
break;
}
current = current.getNext();
}
this.basic = valve;
}
@Override
public void addValve(Valve valve) {
// Validate that we can add this Valve
if (valve instanceof Contained) {
((Contained) valve).setContainer(this.container);
}
// Start the new component if necessary
if (getState().isAvailable()) {
if (valve instanceof Lifecycle) {
try {
((Lifecycle) valve).start();
} catch (LifecycleException e) {
log.error(sm.getString("standardPipeline.valve.start"), e);
}
}
}
// Add this Valve to the set associated with this Pipeline
if (first == null) {
first = valve;
valve.setNext(basic);
} else {
Valve current = first;
while (current != null) {
if (current.getNext() == basic) {
current.setNext(valve);
valve.setNext(basic);
break;
}
current = current.getNext();
}
}
container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
}
@Override
public Valve[] getValves() {
List<Valve> valveList = new ArrayList<>();
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
valveList.add(current);
current = current.getNext();
}
return valveList.toArray(new Valve[0]);
}
public ObjectName[] getValveObjectNames() {
List<ObjectName> valveList = new ArrayList<>();
Valve current = first;
if (current == null) {
current = basic;
}
while (current != null) {
if (current instanceof JmxEnabled) {
valveList.add(((JmxEnabled) current).getObjectName());
}
current = current.getNext();
}
return valveList.toArray(new ObjectName[0]);
}
@Override
public void removeValve(Valve valve) {
Valve current;
if (first == valve) {
first = first.getNext();
current = null;
} else {
current = first;
}
while (current != null) {
if (current.getNext() == valve) {
current.setNext(valve.getNext());
break;
}
current = current.getNext();
}
if (first == basic) {
first = null;
}
if (valve instanceof Contained) {
((Contained) valve).setContainer(null);
}
if (valve instanceof Lifecycle) {
// Stop this valve if necessary
if (getState().isAvailable()) {
try {
((Lifecycle) valve).stop();
} catch (LifecycleException e) {
log.error(sm.getString("standardPipeline.valve.stop"), e);
}
}
try {
((Lifecycle) valve).destroy();
} catch (LifecycleException e) {
log.error(sm.getString("standardPipeline.valve.destroy"), e);
}
}
container.fireContainerEvent(Container.REMOVE_VALVE_EVENT, valve);
}
@Override
public Valve getFirst() {
if (first != null) {
return first;
}
return basic;
}
}