001package org.apache.turbine.pipeline;
002
003
004/*
005 * Licensed to the Apache Software Foundation (ASF) under one
006 * or more contributor license agreements.  See the NOTICE file
007 * distributed with this work for additional information
008 * regarding copyright ownership.  The ASF licenses this file
009 * to you under the Apache License, Version 2.0 (the
010 * "License"); you may not use this file except in compliance
011 * with the License.  You may obtain a copy of the License at
012 *
013 *   http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing,
016 * software distributed under the License is distributed on an
017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018 * KIND, either express or implied.  See the License for the
019 * specific language governing permissions and limitations
020 * under the License.
021 */
022
023
024import java.io.IOException;
025
026import org.apache.turbine.annotation.AnnotationProcessor;
027import org.apache.turbine.util.TurbineException;
028
029/**
030 * Flexible implementation of a {@link org.apache.turbine.pipeline.Pipeline}.
031 * Originally based on code from Catalina and ideas from Apache httpd.
032 *
033 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
034 * @author <a href="mailto:jvanzyl@zenplex.com">Jason van Zyl</a>
035 * @author <a href="mailto:peter@courcoux.biz">Peter Courcoux</a>
036 */
037public class TurbinePipeline
038    implements Pipeline, ValveContext
039{
040    /**
041     * The "Turbine Classic" pipeline.
042     */
043    public static final String CLASSIC_PIPELINE =
044        "WEB-INF/conf/turbine-classic-pipeline.xml";
045
046    /**
047     * Name of this pipeline.
048     */
049    protected String name;
050
051    /**
052     * The set of Valves associated with this Pipeline.
053     */
054    protected Valve[] valves = new Valve[0];
055
056    /**
057     * The per-thread execution state for processing through this
058     * pipeline.  The actual value is a java.lang.Integer object
059     * containing the subscript into the <code>values</code> array, or
060     * a subscript equal to <code>values.length</code> if the basic
061     * Valve is currently being processed.
062     */
063    protected ThreadLocal<Integer> state= new ThreadLocal<Integer>();
064
065    /**
066     * @see org.apache.turbine.pipeline.Pipeline#initialize()
067     */
068    @Override
069    public void initialize()
070        throws Exception
071    {
072        if (state==null)
073        {
074            state = new ThreadLocal<Integer>();
075        }
076
077        // Valve implementations are added to this Pipeline using the
078        // Mapper.
079
080        // Initialize the valves
081        for (int i = 0; i < valves.length; i++)
082        {
083            AnnotationProcessor.process(valves[i]);
084            valves[i].initialize();
085        }
086    }
087
088    /**
089     * Set the name of this pipeline.
090     *
091     * @param name Name of this pipeline.
092     */
093    public void setName(String name)
094    {
095        this.name = name;
096    }
097
098    /**
099     * Get the name of this pipeline.
100     *
101     * @return String Name of this pipeline.
102     */
103    public String getName()
104    {
105        return name;
106    }
107
108    /**
109     * @see org.apache.turbine.pipeline.Pipeline#addValve(Valve)
110     */
111    @Override
112    public void addValve(Valve valve)
113    {
114        // Add this Valve to the set associated with this Pipeline
115        synchronized (valves)
116        {
117            Valve[] results = new Valve[valves.length + 1];
118            System.arraycopy(valves, 0, results, 0, valves.length);
119            results[valves.length] = valve;
120            valves = results;
121        }
122    }
123
124    /**
125     * @see org.apache.turbine.pipeline.Pipeline#getValves()
126     */
127    @Override
128    public Valve[] getValves()
129    {
130        synchronized (valves)
131        {
132            Valve[] results = new Valve[valves.length];
133            System.arraycopy(valves, 0, results, 0, valves.length);
134            return results;
135        }
136    }
137
138    /**
139     * @see org.apache.turbine.pipeline.Pipeline#removeValve(Valve)
140     */
141    @Override
142    public void removeValve(Valve valve)
143    {
144        synchronized (valves)
145        {
146            // Locate this Valve in our list
147            int index = -1;
148            for (int i = 0; i < valves.length; i++)
149            {
150                if (valve == valves[i])
151                {
152                    index = i;
153                    break;
154                }
155            }
156            if (index < 0)
157            {
158                return;
159            }
160
161            // Remove this valve from our list
162            Valve[] results = new Valve[valves.length - 1];
163            int n = 0;
164            for (int i = 0; i < valves.length; i++)
165            {
166                if (i == index)
167                {
168                    continue;
169                }
170                results[n++] = valves[i];
171            }
172            valves = results;
173        }
174    }
175
176    /**
177     * @see org.apache.turbine.pipeline.Pipeline#invoke(PipelineData)
178     */
179    @Override
180    public void invoke(PipelineData pipelineData)
181        throws TurbineException, IOException
182    {
183        // Initialize the per-thread state for this thread
184        state.set(Integer.valueOf(0));
185
186        // Invoke the first Valve in this pipeline for this request
187        invokeNext(pipelineData);
188    }
189
190    /**
191     * @see org.apache.turbine.pipeline.ValveContext#invokeNext(PipelineData)
192     */
193    @Override
194    public void invokeNext(PipelineData pipelineData)
195        throws TurbineException, IOException
196    {
197        // Identify the current subscript for the current request thread
198        Integer current = state.get();
199        int subscript = current.intValue();
200
201        if (subscript < valves.length)
202        {
203            // Invoke the requested Valve for the current request
204            // thread and increment its thread-local state.
205            state.set(Integer.valueOf(subscript + 1));
206            valves[subscript].invoke(pipelineData, this);
207        }
208    }
209}