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     */
017    package org.apache.camel.model;
018    
019    import java.util.Collection;
020    import java.util.Collections;
021    import java.util.List;
022    
023    import javax.xml.bind.annotation.XmlAccessType;
024    import javax.xml.bind.annotation.XmlAccessorType;
025    import javax.xml.bind.annotation.XmlElement;
026    import javax.xml.bind.annotation.XmlRootElement;
027    import javax.xml.bind.annotation.XmlTransient;
028    
029    import org.apache.camel.Intercept;
030    import org.apache.camel.Predicate;
031    import org.apache.camel.Processor;
032    import org.apache.camel.builder.PredicateBuilder;
033    import org.apache.camel.processor.Interceptor;
034    import org.apache.camel.spi.RouteContext;
035    
036    
037    /**
038     * Represents an XML <intercept/> element
039     *
040     * @version $Revision: 50606 $
041     */
042    @XmlRootElement(name = "intercept")
043    @XmlAccessorType(XmlAccessType.FIELD)
044    public class InterceptType extends OutputType<ProcessorType> {
045      
046        @XmlTransient
047        private ProceedType proceed = new ProceedType();
048        @XmlTransient
049        private Boolean stopIntercept = Boolean.FALSE;
050        @XmlTransient
051        private Boolean usePredicate = Boolean.FALSE;
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 Processor createProcessor(RouteContext routeContext) throws Exception {
065            Interceptor interceptor = new Interceptor();
066            routeContext.intercept(interceptor);
067    
068            final Processor interceptRoute = createOutputsProcessor(routeContext);
069            interceptor.setInterceptorLogic(interceptRoute);
070    
071            return interceptor;
072        }
073    
074        /**
075         * Applies this interceptor only if the given predicate is true
076         */
077        public ChoiceType when(Predicate predicate) {
078            usePredicate = Boolean.TRUE;
079            ChoiceType choice = choice().when(PredicateBuilder.not(predicate));
080            choice.addOutput(proceed);
081            return choice.otherwise();
082        }
083    
084        public ProceedType getProceed() {
085            return proceed;
086        }
087    
088        public void stopIntercept() {
089            setStopIntercept(Boolean.TRUE);
090        }
091    
092        @XmlElement(name = "stop", required = false)
093        public void setStop(String elementValue /* not used */) {
094            stopIntercept();
095        }    
096        
097        public InterceptType createProxy() {
098            InterceptType answer = new InterceptType();
099            answer.getOutputs().addAll(this.getOutputs());
100            
101            answer.setStopIntercept(getStopIntercept());
102    
103            // hack: now we need to replace the proceed of the proxy with its own
104            // a bit ugly, operating based on the assumption that the proceed is
105            // in its outputs (if proceed() was called) and/or in the
106            // outputs of the otherwise or last when clause for the predicated version.
107            if (answer.getOutputs().size() > 0) {
108                // this is for the predicate version or if a choice() is present
109                ChoiceType choice = null;
110                for (ProcessorType processor : answer.getOutputs()) {
111                    if (processor instanceof ChoiceType) {
112                        // special cases for predicates (choices)
113                        choice = (ChoiceType) processor;
114    
115                        // for the predicated version we add the proceed() to otherwise()
116                        // before knowing if stop() will follow, so let's make a small adjustment
117                        if (usePredicate.booleanValue() && getStopIntercept().booleanValue()) {
118                            WhenType when = choice.getWhenClauses().get(0);
119                            when.getOutputs().remove(this.getProceed());
120                        }
121    
122                        // add proceed to the when clause
123                        addProceedProxy(this.getProceed(), answer.getProceed(),
124                            choice.getWhenClauses().get(choice.getWhenClauses().size() - 1), usePredicate.booleanValue() && !getStopIntercept().booleanValue());
125    
126                        // force adding a proceed at the end (otherwise) if its not a stop type
127                        addProceedProxy(this.getProceed(), answer.getProceed(), choice.getOtherwise(), !getStopIntercept().booleanValue());
128    
129                        if (getStopIntercept().booleanValue()) {
130                            // must add proceed to when clause if stop is explictiy declared, otherwise when the
131                            // predicate test fails then there is no proceed
132                            // See example: InterceptorSimpleRouteTest (City Paris is never proceeded)  
133                            addProceedProxy(this.getProceed(), answer.getProceed(),
134                                choice.getWhenClauses().get(choice.getWhenClauses().size() - 1), usePredicate.booleanValue());
135                        }
136    
137                        break;
138                    }
139                }
140                if (choice == null) {
141                    // force adding a proceed at the end if its not a stop type
142                    addProceedProxy(this.getProceed(), answer.getProceed(), answer, !getStopIntercept().booleanValue());
143                }
144            }
145    
146            return answer;
147        }
148    
149        private void addProceedProxy(ProceedType orig, ProceedType proxy, ProcessorType<?> processor, boolean force) {
150            int index = processor.getOutputs().indexOf(orig);
151            if (index >= 0) {
152                processor.addOutput(proxy);
153                // replace original proceed with proxy
154                List<ProcessorType<?>> outs = processor.getOutputs();
155                outs.remove(proxy);
156                outs.set(index, proxy);
157            } else if (force) {
158                processor.addOutput(proxy);
159            }
160        }
161    
162        public void setStopIntercept(Boolean stop) {
163            this.stopIntercept = stop;
164        }
165    
166        public Boolean getStopIntercept() {
167            return stopIntercept;
168        }
169    
170    }