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                    return Pipeline.newInstance(context, output, interceptedTarget);
092                } else {
093                    return output;
094                }
095            }
096
097            @Override
098            public String toString() {
099                return "intercept[" + (interceptedTarget != null ? interceptedTarget : output) + "]";
100            }
101        });
102
103        // remove me from the route so I am not invoked in a regular route path
104        routeContext.getRoute().getOutputs().remove(this);
105        // and return no processor to invoke next from me
106        return null;
107    }
108
109    /**
110     * Applies this interceptor only if the given predicate is true
111     *
112     * @param predicate the predicate
113     * @return the builder
114     */
115    public InterceptDefinition when(@AsPredicate Predicate predicate) {
116        WhenDefinition when = new WhenDefinition(predicate);
117        addOutput(when);
118        return this;
119    }
120
121    /**
122     * This method is <b>only</b> for handling some post configuration
123     * that is needed since this is an interceptor, and we have to do
124     * a bit of magic logic to fixup to handle predicates
125     * with or without proceed/stop set as well.
126     */
127    public void afterPropertiesSet() {
128        if (getOutputs().size() == 0) {
129            // no outputs
130            return;
131        }
132
133        ProcessorDefinition<?> first = getOutputs().get(0);
134        if (first instanceof WhenDefinition) {
135            WhenDefinition when = (WhenDefinition) first;
136            // move this outputs to the when, expect the first one
137            // as the first one is the interceptor itself
138            for (int i = 1; i < outputs.size(); i++) {
139                ProcessorDefinition<?> out = outputs.get(i);
140                when.addOutput(out);
141            }
142            // remove the moved from the original output, by just keeping the first one
143            ProcessorDefinition<?> keep = outputs.get(0);
144            clearOutput();
145            outputs.add(keep);
146        }
147    }
148
149    public Processor getInterceptedProcessor(int index) {
150        // avoid out of bounds
151        if (index <= intercepted.size() - 1) {
152            return intercepted.get(index);
153        } else {
154            return null;
155        }
156    }
157}