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: 52393 $
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 String getLabel() {
065            return "intercept";
066        }
067    
068        @Override
069        public Processor createProcessor(RouteContext routeContext) throws Exception {
070            Interceptor interceptor = new Interceptor();
071            routeContext.intercept(interceptor);
072    
073            final Processor interceptRoute = createOutputsProcessor(routeContext);
074            interceptor.setInterceptorLogic(interceptRoute);
075    
076            return interceptor;
077        }
078    
079        /**
080         * Applies this interceptor only if the given predicate is true
081         */
082        public ChoiceType when(Predicate predicate) {
083            usePredicate = Boolean.TRUE;
084            ChoiceType choice = choice().when(PredicateBuilder.not(predicate));
085            choice.addOutput(proceed);
086            return choice.otherwise();
087        }
088    
089        public ProceedType getProceed() {
090            return proceed;
091        }
092    
093        public void stopIntercept() {
094            setStopIntercept(Boolean.TRUE);
095        }
096    
097        @XmlElement(name = "stop", required = false)
098        public void setStop(String elementValue /* not used */) {
099            stopIntercept();
100        }    
101        
102        public InterceptType createProxy() {
103            InterceptType answer = new InterceptType();
104            answer.getOutputs().addAll(this.getOutputs());
105            
106            answer.setStopIntercept(getStopIntercept());
107    
108            // hack: now we need to replace the proceed of the proxy with its own
109            // a bit ugly, operating based on the assumption that the proceed is
110            // in its outputs (if proceed() was called) and/or in the
111            // outputs of the otherwise or last when clause for the predicated version.
112            if (answer.getOutputs().size() > 0) {
113                // this is for the predicate version or if a choice() is present
114                ChoiceType choice = null;
115                for (ProcessorType processor : answer.getOutputs()) {
116                    if (processor instanceof ChoiceType) {
117                        // special cases for predicates (choices)
118                        choice = (ChoiceType) processor;
119    
120                        // for the predicated version we add the proceed() to otherwise()
121                        // before knowing if stop() will follow, so let's make a small adjustment
122                        if (usePredicate.booleanValue() && getStopIntercept().booleanValue()) {
123                            WhenType when = choice.getWhenClauses().get(0);
124                            when.getOutputs().remove(this.getProceed());
125                        }
126    
127                        // add proceed to the when clause
128                        addProceedProxy(this.getProceed(), answer.getProceed(),
129                            choice.getWhenClauses().get(choice.getWhenClauses().size() - 1), usePredicate.booleanValue() && !getStopIntercept().booleanValue());
130    
131                        // force adding a proceed at the end (otherwise) if its not a stop type
132                        addProceedProxy(this.getProceed(), answer.getProceed(), choice.getOtherwise(), !getStopIntercept().booleanValue());
133    
134                        if (getStopIntercept().booleanValue()) {
135                            // must add proceed to when clause if stop is explictiy declared, otherwise when the
136                            // predicate test fails then there is no proceed
137                            // See example: InterceptorSimpleRouteTest (City Paris is never proceeded)  
138                            addProceedProxy(this.getProceed(), answer.getProceed(),
139                                choice.getWhenClauses().get(choice.getWhenClauses().size() - 1), usePredicate.booleanValue());
140                        }
141    
142                        break;
143                    }
144                }
145                if (choice == null) {
146                    // force adding a proceed at the end if its not a stop type
147                    addProceedProxy(this.getProceed(), answer.getProceed(), answer, !getStopIntercept().booleanValue());
148                }
149            }
150    
151            return answer;
152        }
153    
154        private void addProceedProxy(ProceedType orig, ProceedType proxy, ProcessorType<?> processor, boolean force) {
155            int index = processor.getOutputs().indexOf(orig);
156            if (index >= 0) {
157                processor.addOutput(proxy);
158                // replace original proceed with proxy
159                List<ProcessorType<?>> outs = processor.getOutputs();
160                outs.remove(proxy);
161                outs.set(index, proxy);
162            } else if (force) {
163                processor.addOutput(proxy);
164            }
165        }
166    
167        public void setStopIntercept(Boolean stop) {
168            this.stopIntercept = stop;
169        }
170    
171        public Boolean getStopIntercept() {
172            return stopIntercept;
173        }
174    
175    }