/* * 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 javax.management.ObjectName; import org.apache.catalina.Contained; import org.apache.catalina.Container; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleListener; import org.apache.catalina.Pipeline; import org.apache.catalina.Valve; import org.apache.catalina.util.LifecycleSupport; import org.apache.catalina.util.StringManager; import org.apache.catalina.valves.ValveBase; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.modeler.Registry; /** * 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 implements Pipeline, Contained, Lifecycle { private static Log log = LogFactory.getLog(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; /** * Descriptive information about this implementation. */ protected String info = "org.apache.catalina.core.StandardPipeline/1.0"; /** * The lifecycle event support for this component. */ protected LifecycleSupport lifecycle = new LifecycleSupport(this); /** * The string manager for this package. */ protected static StringManager sm = StringManager.getManager(Constants.Package); /** * Has this component been started yet? */ protected boolean started = false; /** * The first valve associated with this Pipeline. */ protected Valve first = null; // --------------------------------------------------------- Public Methods /** * Return descriptive information about this implementation class. */ public String getInfo() { return (this.info); } // ------------------------------------------------------ Contained Methods /** * Return the Container with which this Pipeline is associated. */ public Container getContainer() { return (this.container); } /** * Set the Container with which this Pipeline is associated. * * @param container The new associated container */ public void setContainer(Container container) { this.container = container; } // ------------------------------------------------------ Lifecycle Methods /** * Add a lifecycle event listener to this component. * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); } /** * Get the lifecycle listeners associated with this lifecycle. If this * Lifecycle has no listeners registered, a zero-length array is returned. */ public LifecycleListener[] findLifecycleListeners() { return lifecycle.findLifecycleListeners(); } /** * Remove a lifecycle event listener from this component. * * @param listener The listener to remove */ public void removeLifecycleListener(LifecycleListener listener) { lifecycle.removeLifecycleListener(listener); } /** * Prepare for active use of the public methods of this Component. * * @exception LifecycleException if this component detects a fatal error * that prevents it from being started */ public synchronized void start() throws LifecycleException { // Validate and update our current component state if (started) throw new LifecycleException (sm.getString("standardPipeline.alreadyStarted")); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); started = true; // 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(); registerValve(current); current = current.getNext(); } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(START_EVENT, null); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); } /** * Gracefully shut down active use of the public methods of this Component. * * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ public synchronized void stop() throws LifecycleException { // Validate and update our current component state if (!started) throw new LifecycleException (sm.getString("standardPipeline.notStarted")); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(STOP_EVENT, null); started = false; // 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(); unregisterValve(current); current = current.getNext(); } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null); } private void registerValve(Valve valve) { if( valve instanceof ValveBase && ((ValveBase)valve).getObjectName()==null ) { try { String domain=((ContainerBase)container).getDomain(); if( container instanceof StandardContext ) { domain=((StandardContext)container).getEngineName(); } if( container instanceof StandardWrapper) { Container ctx=((StandardWrapper)container).getParent(); domain=((StandardContext)ctx).getEngineName(); } ObjectName vname=((ValveBase)valve).createObjectName( domain, ((ContainerBase)container).getJmxName()); if( vname != null ) { ((ValveBase)valve).setObjectName(vname); Registry.getRegistry(null, null).registerComponent (valve, vname, valve.getClass().getName()); ((ValveBase)valve).setController (((ContainerBase)container).getJmxName()); } } catch( Throwable t ) { log.info( "Can't register valve " + valve , t ); } } } private void unregisterValve(Valve valve) { if( valve instanceof ValveBase ) { try { ValveBase vb=(ValveBase)valve; if( vb.getController()!=null && vb.getController() == ((ContainerBase)container).getJmxName() ) { ObjectName vname=vb.getObjectName(); Registry.getRegistry(null, null).getMBeanServer() .unregisterMBean(vname); ((ValveBase)valve).setObjectName(null); } } catch( Throwable t ) { log.info( "Can't unregister valve " + valve , t ); } } } // ------------------------------------------------------- Pipeline Methods /** * <p>Return the Valve instance that has been distinguished as the basic * Valve for this Pipeline (if any). */ public Valve getBasic() { return (this.basic); } /** * <p>Set the Valve instance that has been distinguished as the basic * Valve for this Pipeline (if any). Prioer to setting the basic Valve, * the Valve's <code>setContainer()</code> will be called, if it * implements <code>Contained</code>, with the owning Container as an * argument. The method may throw an <code>IllegalArgumentException</code> * if this Valve chooses not to be associated with this Container, or * <code>IllegalStateException</code> if it is already associated with * a different Container.</p> * * @param valve Valve to be distinguished as the basic Valve */ 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 (started && (oldBasic instanceof Lifecycle)) { try { ((Lifecycle) oldBasic).stop(); } catch (LifecycleException e) { log.error("StandardPipeline.setBasic: stop", e); } } if (oldBasic instanceof Contained) { try { ((Contained) oldBasic).setContainer(null); } catch (Throwable t) { ; } } } // Start the new component if necessary if (valve == null) return; if (valve instanceof Contained) { ((Contained) valve).setContainer(this.container); } if (valve instanceof Lifecycle) { try { ((Lifecycle) valve).start(); } catch (LifecycleException e) { log.error("StandardPipeline.setBasic: 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; } /** * <p>Add a new Valve to the end of the pipeline associated with this * Container. Prior to adding the Valve, the Valve's * <code>setContainer()</code> method will be called, if it implements * <code>Contained</code>, with the owning Container as an argument. * The method may throw an * <code>IllegalArgumentException</code> if this Valve chooses not to * be associated with this Container, or <code>IllegalStateException</code> * if it is already associated with a different Container.</p> * * @param valve Valve to be added * * @exception IllegalArgumentException if this Container refused to * accept the specified Valve * @exception IllegalArgumentException if the specifie Valve refuses to be * associated with this Container * @exception IllegalStateException if the specified Valve is already * associated with a different Container */ 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 (started) { if (valve instanceof Lifecycle) { try { ((Lifecycle) valve).start(); } catch (LifecycleException e) { log.error("StandardPipeline.addValve: start: ", e); } } // Register the newly added valve registerValve(valve); } // 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(); } } } /** * Return the set of Valves in the pipeline associated with this * Container, including the basic Valve (if any). If there are no * such Valves, a zero-length array is returned. */ public Valve[] getValves() { ArrayList valveList = new ArrayList(); Valve current = first; if (current == null) { current = basic; } while (current != null) { valveList.add(current); current = current.getNext(); } return ((Valve[]) valveList.toArray(new Valve[0])); } public ObjectName[] getValveObjectNames() { ArrayList valveList = new ArrayList(); Valve current = first; if (current == null) { current = basic; } while (current != null) { if (current instanceof ValveBase) { valveList.add(((ValveBase) current).getObjectName()); } current = current.getNext(); } return ((ObjectName[]) valveList.toArray(new ObjectName[0])); } /** * Remove the specified Valve from the pipeline associated with this * Container, if it is found; otherwise, do nothing. If the Valve is * found and removed, the Valve's <code>setContainer(null)</code> method * will be called if it implements <code>Contained</code>. * * @param valve Valve to be removed */ 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); // Stop this valve if necessary if (started) { if (valve instanceof Lifecycle) { try { ((Lifecycle) valve).stop(); } catch (LifecycleException e) { log.error("StandardPipeline.removeValve: stop: ", e); } } // Unregister the removed valave unregisterValve(valve); } } public Valve getFirst() { if (first != null) { return first; } else { return basic; } } }