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;
021
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlRootElement;
025import javax.xml.bind.annotation.XmlTransient;
026
027import org.apache.camel.CamelContext;
028import org.apache.camel.Predicate;
029import org.apache.camel.Processor;
030import org.apache.camel.processor.Pipeline;
031import org.apache.camel.spi.AsPredicate;
032import org.apache.camel.spi.InterceptStrategy;
033import org.apache.camel.spi.Metadata;
034import org.apache.camel.spi.RouteContext;
035
036/**
037 * Intercepts a message at each step in the route
038 *
039 * @version 
040 */
041@Metadata(label = "configuration")
042@XmlRootElement(name = "intercept")
043@XmlAccessorType(XmlAccessType.FIELD)
044public class InterceptDefinition extends OutputDefinition<InterceptDefinition> {
045    @XmlTransient
046    protected Processor output;
047    @XmlTransient
048    protected final List<Processor> intercepted = new ArrayList<>();
049
050    public InterceptDefinition() {
051    }
052
053    @Override
054    public String toString() {
055        return "Intercept[" + getOutputs() + "]";
056    }
057
058    @Override
059    public String getShortName() {
060        return "intercept";
061    }
062
063    @Override
064    public String getLabel() {
065        return "intercept";
066    }
067
068    @Override
069    public boolean isAbstract() {
070        return true;
071    }
072
073    @Override
074    public boolean isTopLevelOnly() {
075        return true;
076    }
077
078    @Override
079    public Processor createProcessor(final RouteContext routeContext) throws Exception {
080        // create the output processor
081        output = this.createChildProcessor(routeContext, true);
082
083        // add the output as a intercept strategy to the route context so its invoked on each processing step
084        routeContext.getInterceptStrategies().add(new InterceptStrategy() {
085            private Processor interceptedTarget;
086
087            public Processor wrapProcessorInInterceptors(CamelContext context, ProcessorDefinition<?> definition,
088                                                         Processor target, Processor nextTarget) throws Exception {
089                // store the target we are intercepting
090                this.interceptedTarget = target;
091
092                // remember the target that was intercepted
093                intercepted.add(interceptedTarget);
094
095                if (interceptedTarget != null) {
096                    // wrap in a pipeline so we continue routing to the next
097                    return Pipeline.newInstance(context, output, interceptedTarget);
098                } else {
099                    return output;
100                }
101            }
102
103            @Override
104            public String toString() {
105                return "intercept[" + (interceptedTarget != null ? interceptedTarget : output) + "]";
106            }
107        });
108
109        // remove me from the route so I am not invoked in a regular route path
110        routeContext.getRoute().getOutputs().remove(this);
111        // and return no processor to invoke next from me
112        return null;
113    }
114
115    /**
116     * Applies this interceptor only if the given predicate is true
117     *
118     * @param predicate the predicate
119     * @return the builder
120     */
121    public InterceptDefinition when(@AsPredicate Predicate predicate) {
122        WhenDefinition when = new WhenDefinition(predicate);
123        addOutput(when);
124        return this;
125    }
126
127    /**
128     * This method is <b>only</b> for handling some post configuration
129     * that is needed since this is an interceptor, and we have to do
130     * a bit of magic logic to fixup to handle predicates
131     * with or without proceed/stop set as well.
132     */
133    public void afterPropertiesSet() {
134        if (getOutputs().size() == 0) {
135            // no outputs
136            return;
137        }
138
139        ProcessorDefinition<?> first = getOutputs().get(0);
140        if (first instanceof WhenDefinition) {
141            WhenDefinition when = (WhenDefinition) first;
142            // move this outputs to the when, expect the first one
143            // as the first one is the interceptor itself
144            for (int i = 1; i < outputs.size(); i++) {
145                ProcessorDefinition<?> out = outputs.get(i);
146                when.addOutput(out);
147            }
148            // remove the moved from the original output, by just keeping the first one
149            ProcessorDefinition<?> keep = outputs.get(0);
150            clearOutput();
151            outputs.add(keep);
152        }
153    }
154
155    public Processor getInterceptedProcessor(int index) {
156        // avoid out of bounds
157        if (index <= intercepted.size() - 1) {
158            return intercepted.get(index);
159        } else {
160            return null;
161        }
162    }
163}