All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.catalina.core.StandardPipeline Maven / Gradle / Ivy

There is a newer version: 4.1.2.181
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2016 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed 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.
 */
// Portions Copyright [2016] [Payara Foundation]
package org.apache.catalina.core;


import org.apache.catalina.*;
import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.catalina.connector.*;
import org.apache.catalina.util.LifecycleSupport;
import org.apache.catalina.valves.ValveBase;
import org.glassfish.web.valve.GlassFishValve;
import org.glassfish.web.valve.GlassFishValveAdapter;
import org.glassfish.web.valve.TomcatValveAdapter;

import javax.management.ObjectName;
import javax.servlet.ServletException;
import javax.servlet.http.HttpUpgradeHandler;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.MessageFormat;
import java.util.ResourceBundle;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;

/** CR 6411114 (Lifecycle implementation moved to ValveBase)
import org.apache.tomcat.util.modeler.Registry;
*/


/**
 * Standard implementation of a processing Pipeline 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.
 *
 * IMPLEMENTATION WARNING - This implementation assumes that no
 * calls to addValve() or removeValve 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 final Logger log = LogFacade.getLogger();
    private static final ResourceBundle rb = log.getResourceBundle();

    // ----------------------------------------------------------- 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 GlassFishValve basic = null;


    /**
     * The Container with which this Pipeline is associated.
     */
    protected Container container = null;


    /**
     * Descriptive information about this implementation.
     */
    protected static final String info = "org.apache.catalina.core.StandardPipeline/1.0";


    /**
     * The lifecycle event support for this component.
     */
    protected LifecycleSupport lifecycle = new LifecycleSupport(this);


    /**
     * Has this component been started yet?
     */
    protected boolean started = false;

    // START OF IASRI# 4647091
    /*
     * The per-thread execution state for processing through this pipeline.
     * The actual value is a java.lang.Integer object containing the subscript
     * into the values array, or a subscript equal to
     * values.length if the basic Valve is currently being
     * processed.
     */
    // protected ThreadLocal state = new ThreadLocal();
    // END OF IASRI# 4647091

    /**
     * The set of Valves (not including the Basic one, if any) associated with
     * this Pipeline.
     */
    protected GlassFishValve valves[] = new GlassFishValve[0];


    // The first Tomcat-style valve in the pipeline, if any
    private Valve firstTcValve;

    // The last Tomcat-style valve (immediately preceding the basic valve)
    // in the pipeline, if any
    private Valve lastTcValve;


    // --------------------------------------------------------- Public Methods


    /**
     * Return descriptive information about this implementation class.
     */
    public String getInfo() {

        return 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);
    }


    /**
     * Gets the (possibly empty) list of lifecycle listeners
     * associated with this Pipeline.
     */
    public List 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 IllegalStateException if this component has already been
     *  started
     * @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
                    (rb.getString(LogFacade.PIPLINE_STARTED));

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

        started = true;

        // Start the Valves in our pipeline (including the basic), if any
        for (int i = 0; i < valves.length; i++) {
            if (valves[i] instanceof Lifecycle)
                ((Lifecycle) valves[i]).start();
            /** CR 6411114 (MBean registration moved to ValveBase.start())
            registerValve(valves[i]);
            */
        }
        if ((basic != null) && (basic instanceof Lifecycle))
            ((Lifecycle) basic).start();

        /** CR 6411114 (MBean registration moved to ValveBase.start())
        if( basic!=null )
            registerValve(basic);
        */

        // 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 IllegalStateException if this component has not been started
     * @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
                    (rb.getString(LogFacade.PIPLINE_NOT_STARTED));

        started = false;

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);

        // Stop the Valves in our pipeline (including the basic), if any
        if ((basic != null) && (basic instanceof Lifecycle))
            ((Lifecycle) basic).stop();
        /** CR 6411114 (MBean deregistration moved to ValveBase.stop())
        if( basic!=null ) {
            unregisterValve(basic);
        }
        */
        for (int i = 0; i < valves.length; i++) {
            if (valves[i] instanceof Lifecycle)
                ((Lifecycle) valves[i]).stop();
            /** CR 6411114 (MBean deregistration moved to ValveBase.stop())
            unregisterValve(valves[i]);
            */

        }

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

    }


    // ------------------------------------------------------- Pipeline Methods


    /**
     * 

Return the Valve instance that has been distinguished as the basic * Valve for this Pipeline (if any). */ public GlassFishValve getBasic() { return (this.basic); } /** *

Set the Valve instance that has been distinguished as the basic * Valve for this Pipeline (if any). Prior to setting the basic Valve, * the Valve's setContainer() will be called, if it * implements Contained, with the owning Container as an * argument. The method may throw an IllegalArgumentException * if this Valve chooses not to be associated with this Container, or * IllegalStateException if it is already associated with * a different Container.

* * @param valve Valve to be distinguished as the basic Valve */ public void setBasic(GlassFishValve valve) { // Change components if necessary GlassFishValve oldBasic = null; synchronized (this) { oldBasic = this.basic; } if (oldBasic == valve) { return; } // Stop the old component if necessary if (oldBasic != null) { synchronized (this) { if (started && (oldBasic instanceof Lifecycle)) { try { ((Lifecycle) oldBasic).stop(); } catch (LifecycleException e) { log.log(Level.SEVERE, LogFacade.SET_BASIC_STOP_EXCEPTION, e); } } } if (oldBasic instanceof Contained) { try { ((Contained) oldBasic).setContainer(null); } catch (Throwable t) { // Ignore } } } // Start the new component if necessary if (valve == null) { return; } if (valve instanceof Contained) { ((Contained) valve).setContainer(this.container); } /** CR 6411114 if (valve instanceof Lifecycle) { */ // START CR 6411114 // Start the valve if the pipeline has already been started if (started && (valve instanceof Lifecycle)) { // END CR 6411114 try { ((Lifecycle) valve).start(); } catch (LifecycleException e) { log.log(Level.SEVERE, LogFacade.SET_BASIC_START_EXCEPTION, e); return; } } synchronized (this) { this.basic = valve; } } /** *

Add a new Valve to the end of the pipeline associated with this * Container. Prior to adding the Valve, the Valve's * setContainer() method will be called, if it implements * Contained, with the owning Container as an argument. * The method may throw an * IllegalArgumentException if this Valve chooses not to * be associated with this Container, or IllegalStateException * if it is already associated with a different Container.

* * @param valve Valve to be added * * @exception IllegalArgumentException if this Container refused to * accept the specified Valve * @exception IllegalArgumentException if the specified Valve refuses to be * associated with this Container * @exception IllegalStateException if the specified Valve is already * associated with a different Container */ public void addValve(GlassFishValve valve) { if (firstTcValve != null) { // Wrap GlassFish-style valve inside Tomcat-style valve addValve(new TomcatValveAdapter(valve)); return; } // 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.log(Level.SEVERE, LogFacade.ADD_VALVE_EXCEPTION, e); } } /** CR 6411114 (MBean registration moved to ValveBase.start()) // Register the newly added valve registerValve(valve); */ } // Add this Valve to the set associated with this Pipeline GlassFishValve results[] = new GlassFishValve[valves.length +1]; System.arraycopy(valves, 0, results, 0, valves.length); results[valves.length] = valve; valves = results; } /** * Add Tomcat-style valve. */ public synchronized void addValve(Valve valve) { /* * Check if this is a GlassFish-style valve that was compiled * against the old org.apache.catalina.Valve interface (from * GlassFish releases prior to V3), which has since been renamed * to org.glassfish.web.valve.GlassFishValve (in V3) */ if (isGlassFishValve(valve)) { try { addValve(new GlassFishValveAdapter(valve)); } catch (Exception e) { String msg = MessageFormat.format(rb.getString(LogFacade.ADD_TOMCAT_STYLE_VALVE_EXCEPTION), valve); log.log(Level.SEVERE, msg, e); } return; } if (valve instanceof Contained) ((Contained) valve).setContainer(this.container); // Start the new Valve if necessary if (started) { if (valve instanceof Lifecycle) { try { ((Lifecycle) valve).start(); } catch (LifecycleException e) { log.log(Level.SEVERE, LogFacade.ADD_VALVE_EXCEPTION, e); } } } if (firstTcValve == null) { firstTcValve = lastTcValve = valve; } else { lastTcValve.setNext(valve); lastTcValve = valve; } if (basic != null) { valve.setNext((Valve) basic); } } /** * 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 GlassFishValve[] getValves() { if (basic == null) { return (valves); } GlassFishValve results[] = new GlassFishValve[valves.length + 1]; System.arraycopy(valves, 0, results, 0, valves.length); results[valves.length] = basic; return (results); } /** * @return true if this pipeline has any non basic valves, false * otherwise */ public boolean hasNonBasicValves() { return ((valves != null && valves.length > 0) || firstTcValve != null); } public ObjectName[] getValveObjectNames() { ObjectName oname[]=new ObjectName[valves.length + 1]; for( int i=0; i 0) || (basic != null)) { // Set the status so that if there are no valves (other than the // basic one), the basic valve's request processing logic will // be invoked int status = GlassFishValve.INVOKE_NEXT; // Iterate over all the valves in the pipeline and invoke // each valve's processing logic and then move onto to the // next valve in the pipeline only if the previous valve indicated // that the pipeline should proceed. int i; for (i = 0; i < valves.length; i++) { Request req = request; Response resp = response; if (chaining) { req = getRequest(request); resp = getResponse(request, response); } status = valves[i].invoke(req, resp); if (status != GlassFishValve.INVOKE_NEXT) break; } // Save a reference to the valve[], to ensure that postInvoke() // is invoked on the original valve[], in case a valve gets added // or removed during the invocation of the basic valve (e.g., // in case access logging is enabled or disabled by some kind of // admin servlet), in which case the indices used for postInvoke // invocations below would be off GlassFishValve[] savedValves = valves; // Invoke the basic valve's request processing and post-request // logic only if the pipeline was not aborted (i.e. no valve // returned END_PIPELINE). // In addition, the basic valve needs to be invoked by the // pipeline only if no Tomcat-style valves have been added. // Otherwise, it will be invoked by the last Tomcat-style valve // directly. if (status == GlassFishValve.INVOKE_NEXT) { if (firstTcValve != null) { firstTcValve.invoke( (org.apache.catalina.connector.Request) request, (org.apache.catalina.connector.Response) response); } else if (basic != null) { Request req = request; Response resp = response; if (chaining) { req = getRequest(request); resp = getResponse(request, response); } basic.invoke(req, resp); postInvoke(basic, req, resp); } } // Invoke the post-request processing logic only on those valves // that returned a status of INVOKE_NEXT for (int j = i - 1; j >= 0; j--) { Request req = request; Response resp = response; if (chaining) { req = getRequest(request); resp = getResponse(request, response); } postInvoke(savedValves[j], req, resp); } savedValves = null; } else { throw new ServletException (rb.getString(LogFacade.NO_VALVES_IN_PIPELINE_EXCEPTION)); } // Calls the protocol handler's init method if the request is marked to be upgraded if (request instanceof org.apache.catalina.connector.Request) { org.apache.catalina.connector.Request req = (org.apache.catalina.connector.Request) request; if (req.isUpgrade()) { HttpUpgradeHandler handler = req.getHttpUpgradeHandler(); if (handler != null) { WebConnectionImpl wc = new WebConnectionImpl( req.getInputStream(), ((org.apache.catalina.connector.Response)req.getResponse()).getOutputStream()); wc.setRequest(req); req.setWebConnection(wc); if (response instanceof org.apache.catalina.connector.Response) { wc.setResponse((org.apache.catalina.connector.Response) response); } Context context = req.getContext(); try { context.fireContainerEvent(ContainerEvent.BEFORE_UPGRADE_HANDLER_INITIALIZED, handler); req.initialiseHttpUpgradeHandler(wc); } finally { context.fireContainerEvent(ContainerEvent.AFTER_UPGRADE_HANDLER_INITIALIZED, handler); } } else { log.log(Level.SEVERE, LogFacade.PROTOCOL_HANDLER_REQUIRED_EXCEPTION); } //req.setUpgrade(false); } } } private void postInvoke(final GlassFishValve savedValve, final Request request, final Response response) throws IOException, ServletException{ if(request.getRequest().isAsyncSupported() && request.getRequest().isAsyncStarted()){ request.getRequest().getAsyncContext().addListener(new AsyncListener() { @Override public void onComplete(AsyncEvent event) throws IOException { try { savedValve.postInvoke(request, response); } catch (ServletException ex) { log.log(Level.SEVERE, LogFacade.INTERNAL_ERROR, ex); } } @Override public void onTimeout(AsyncEvent event) throws IOException { try { savedValve.postInvoke(request, response); } catch (ServletException ex) { log.log(Level.SEVERE, LogFacade.INTERNAL_ERROR, ex); } } @Override public void onError(AsyncEvent event) throws IOException { try { savedValve.postInvoke(request, response); } catch (ServletException ex) { log.log(Level.SEVERE, LogFacade.INTERNAL_ERROR, ex); } } @Override public void onStartAsync(AsyncEvent event) throws IOException {} }); } else { savedValve.postInvoke(request, response); } } private Request getRequest(Request request) { Request r = (Request) request.getNote(Globals.WRAPPED_REQUEST); if (r == null) { r = request; } return r; } private Response getResponse(Request request, Response response) { Response r = (Response) request.getNote(Globals.WRAPPED_RESPONSE); if (r == null) { r = response; } return r; } /** * 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 setContainer(null) method * will be called if it implements Contained. * * @param valve Valve to be removed */ public void removeValve(GlassFishValve valve) { // Locate this Valve in our list int j = -1; for (int i = 0; i < valves.length; i++) { if (valve == valves[i]) { j = i; break; } } if (j < 0) return; // Remove this valve from our list GlassFishValve results[] = new GlassFishValve[valves.length - 1]; int n = 0; for (int i = 0; i < valves.length; i++) { if (i == j) continue; results[n++] = valves[i]; } valves = results; try { if (valve instanceof Contained) ((Contained) valve).setContainer(null); } catch (Throwable t) { ; } // Stop this valve if necessary if (started) { if (valve instanceof ValveBase) { if (((ValveBase)valve).isStarted()) { try { ((Lifecycle) valve).stop(); } catch (LifecycleException e) { log.log(Level.SEVERE, LogFacade.REMOVE_VALVE_EXCEPTION, e); } } } else if (valve instanceof Lifecycle) { try { ((Lifecycle) valve).stop(); } catch (LifecycleException e) { log.log(Level.SEVERE, LogFacade.REMOVE_VALVE_EXCEPTION, e); } } /** CR 6411114 (MBean deregistration moved to ValveBase.stop()) // Unregister the removed valve unregisterValve(valve); */ } } // ------------------------------------------------------ Protected Methods /** * Log a message on the Logger associated with our Container (if any). * * @param message Message to be logged */ protected void log(String message) { org.apache.catalina.Logger logger = null; if (container != null) { logger = container.getLogger(); String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_PIPELINE_INFO), new Object[] {container.getName(), message}); if (logger != null) { logger.log(msg); } else { if (log.isLoggable(Level.INFO)) { log.log(Level.INFO, msg); } } } else { if (log.isLoggable(Level.INFO)) { String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_PIPELINE_NULL_INFO), message); log.log(Level.INFO, msg); } } } /** * Logs the given message to the Logger associated with the Container * (if any) of this StandardPipeline. * * @param message the message * @param t the Throwable */ protected void log(String message, Throwable t) { org.apache.catalina.Logger logger = null; if (container != null) { logger = container.getLogger(); String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_PIPELINE_INFO), new Object[] {container.getName(), message}); if (logger != null) { logger.log(msg, t, org.apache.catalina.Logger.WARNING); } else { log.log(Level.WARNING, msg, t); } } else { String msg = MessageFormat.format(rb.getString(LogFacade.STANDARD_PIPELINE_NULL_INFO), message); log.log(Level.WARNING, msg, t);// INFO set to WARNING } } // ------------------------------------------------------ Private Methods /* * Checks if the give valve is a GlassFish-style valve that was compiled * against the old org.apache.catalina.Valve interface (from * GlassFish releases prior to V3), which has since been renamed * to org.glassfish.web.valve.GlassFishValve (in V3). * * The check is done by checking that it is not an abstract method with * return type int. Note that invoke method in the original Tomcat-based * Valve interface is declared to be void. * * @param valve the valve to check * * @return true if the given valve is a GlassFish-style valve, false * otherwise */ private boolean isGlassFishValve(Valve valve) { try { Method m = valve.getClass().getMethod( "invoke", org.apache.catalina.Request.class, org.apache.catalina.Response.class); return (m != null && int.class.equals(m.getReturnType()) && (!Modifier.isAbstract(m.getModifiers()))); } catch (Exception e) { return false; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy