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