001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.model;
018
019import java.util.ArrayList;
020import java.util.List;
021import javax.xml.bind.annotation.XmlAccessType;
022import javax.xml.bind.annotation.XmlAccessorType;
023import javax.xml.bind.annotation.XmlRootElement;
024import javax.xml.bind.annotation.XmlTransient;
025
026import org.apache.camel.CamelContext;
027import org.apache.camel.Predicate;
028import org.apache.camel.Processor;
029import org.apache.camel.processor.Pipeline;
030import org.apache.camel.spi.AsPredicate;
031import org.apache.camel.spi.InterceptStrategy;
032import org.apache.camel.spi.Metadata;
033import org.apache.camel.spi.RouteContext;
034
035/**
036 * Intercepts a message at each step in the route
037 *
038 * @version 
039 */
040@Metadata(label = "configuration")
041@XmlRootElement(name = "intercept")
042@XmlAccessorType(XmlAccessType.FIELD)
043public class InterceptDefinition extends OutputDefinition<InterceptDefinition> {
044    @XmlTransient
045    protected Processor output;
046    @XmlTransient
047    protected final List<Processor> intercepted = new ArrayList<Processor>();
048
049    public InterceptDefinition() {
050    }
051
052    @Override
053    public String toString() {
054        return "Intercept[" + getOutputs() + "]";
055    }
056
057    @Override
058    public String getLabel() {
059        return "intercept";
060    }
061
062    @Override
063    public boolean isAbstract() {
064        return true;
065    }
066
067    @Override
068    public boolean isTopLevelOnly() {
069        return true;
070    }
071
072    @Override
073    public Processor createProcessor(final RouteContext routeContext) throws Exception {
074        // create the output processor
075        output = this.createChildProcessor(routeContext, true);
076
077        // add the output as a intercept strategy to the route context so its invoked on each processing step
078        routeContext.getInterceptStrategies().add(new InterceptStrategy() {
079            private Processor interceptedTarget;
080
081            public Processor wrapProcessorInInterceptors(CamelContext context, ProcessorDefinition<?> definition,
082                                                         Processor target, Processor nextTarget) throws Exception {
083                // store the target we are intercepting
084                this.interceptedTarget = target;
085
086                // remember the target that was intercepted
087                intercepted.add(interceptedTarget);
088
089                if (interceptedTarget != null) {
090                    // wrap in a pipeline so we continue routing to the next
091                    List<Processor> list = new ArrayList<Processor>(2);
092                    list.add(output);
093                    list.add(interceptedTarget);
094                    return new Pipeline(context, list);
095                } else {
096                    return output;
097                }
098            }
099
100            @Override
101            public String toString() {
102                return "intercept[" + (interceptedTarget != null ? interceptedTarget : output) + "]";
103            }
104        });
105
106        // remove me from the route so I am not invoked in a regular route path
107        routeContext.getRoute().getOutputs().remove(this);
108        // and return no processor to invoke next from me
109        return null;
110    }
111
112    /**
113     * Applies this interceptor only if the given predicate is true
114     *
115     * @param predicate the predicate
116     * @return the builder
117     */
118    public InterceptDefinition when(@AsPredicate Predicate predicate) {
119        WhenDefinition when = new WhenDefinition(predicate);
120        addOutput(when);
121        return this;
122    }
123
124    /**
125     * This method is <b>only</b> for handling some post configuration
126     * that is needed since this is an interceptor, and we have to do
127     * a bit of magic logic to fixup to handle predicates
128     * with or without proceed/stop set as well.
129     */
130    public void afterPropertiesSet() {
131        if (getOutputs().size() == 0) {
132            // no outputs
133            return;
134        }
135
136        ProcessorDefinition<?> first = getOutputs().get(0);
137        if (first instanceof WhenDefinition) {
138            WhenDefinition when = (WhenDefinition) first;
139            // move this outputs to the when, expect the first one
140            // as the first one is the interceptor itself
141            for (int i = 1; i < outputs.size(); i++) {
142                ProcessorDefinition<?> out = outputs.get(i);
143                when.addOutput(out);
144            }
145            // remove the moved from the original output, by just keeping the first one
146            ProcessorDefinition<?> keep = outputs.get(0);
147            clearOutput();
148            outputs.add(keep);
149        }
150    }
151
152    public Processor getInterceptedProcessor(int index) {
153        // avoid out of bounds
154        if (index <= intercepted.size() - 1) {
155            return intercepted.get(index);
156        } else {
157            return null;
158        }
159    }
160}