    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<Valve>();
    066
    067    /**
    068     * The per-thread execution state for processing through this pipeline.
    069     */
    070    private ThreadLocal<Iterator<Valve>> state = new ThreadLocal<Iterator<Valve>>();
    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<Valve>(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}