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.ArrayList;
020    import java.util.List;
021    import javax.xml.bind.annotation.XmlAccessType;
022    import javax.xml.bind.annotation.XmlAccessorType;
023    import javax.xml.bind.annotation.XmlRootElement;
024    import javax.xml.bind.annotation.XmlTransient;
025    
026    import org.apache.camel.Predicate;
027    import org.apache.camel.Processor;
028    import org.apache.camel.builder.PredicateBuilder;
029    import org.apache.camel.processor.Interceptor;
030    import org.apache.camel.spi.RouteContext;
031    
032    
033    /**
034     * Represents an XML <intercept/> element
035     *
036     * @version $Revision: 2669 $
037     */
038    @XmlRootElement(name = "intercept")
039    @XmlAccessorType(XmlAccessType.FIELD)
040    public class InterceptType extends OutputType<ProcessorType> {
041      
042        @XmlTransient
043        private ProceedType proceed = new ProceedType();
044        @XmlTransient
045        private Boolean stopIntercept = Boolean.FALSE;
046        @XmlTransient
047        private Boolean usePredicate = Boolean.FALSE;
048    
049        @Override
050        public String toString() {
051            return "Intercept[" + getOutputs() + "]";
052        }
053    
054        @Override
055        public String getShortName() {
056            return "intercept";
057        }
058    
059        @Override
060        public String getLabel() {
061            return "intercept";
062        }
063    
064        @Override
065        public Processor createProcessor(RouteContext routeContext) throws Exception {
066            Interceptor interceptor = new Interceptor();
067            routeContext.intercept(interceptor);
068    
069            final Processor interceptRoute = createOutputsProcessor(routeContext);
070            interceptor.setInterceptorLogic(interceptRoute);
071    
072            return interceptor;
073        }
074    
075        /**
076         * Applies this interceptor only if the given predicate is true
077         *
078         * @param predicate  the predicate
079         * @return the builder
080         */
081        public ChoiceType when(Predicate predicate) {
082            usePredicate = Boolean.TRUE;
083            ChoiceType choice = choice().when(PredicateBuilder.not(predicate));
084            choice.addOutput(proceed);
085            return choice.otherwise();
086        }
087    
088        public ProceedType getProceed() {
089            return proceed;
090        }
091    
092        public void stopIntercept() {
093            setStopIntercept(Boolean.TRUE);
094        }
095    
096        /**
097         * This method is <b>only</b> for handling some post configuration
098         * that is needed from the Spring DSL side as JAXB does not invoke the fluent
099         * builders, so we need to manually handle this afterwards, and since this is
100         * an interceptor it has to do a bit of magic logic to fixup to handle predicates
101         * with or without proceed/stop set as well.
102         */
103        public void afterPropertiesSet() {
104            List<ProcessorType<?>> list = new ArrayList<ProcessorType<?>>();
105            for (ProcessorType<?> out : outputs) {
106                if (out instanceof WhenType) {
107                    // JAXB does not invoke the when() fluent builder so we need to wrap the when in
108                    // a choice with the proceed as the when for the Java DSL does
109                    WhenType when = (WhenType) out;
110                    usePredicate = Boolean.TRUE;
111                    ChoiceType choice = new ChoiceType();
112                    choice.when(PredicateBuilder.not(when.getExpression()));
113                    choice.addOutput(proceed);
114                    list.add(choice);
115    
116                    ChoiceType otherwise = choice.otherwise();
117                    // add the children to the otherwise
118                    for (ProcessorType child : when.getOutputs()) {
119                        if (child instanceof StopType) {
120                            // notify we should stop
121                            stopIntercept();
122                        } else {
123                            otherwise.addOutput(child);
124                        }
125                    }
126                } else if (out instanceof StopType) {
127                    // notify we shuld stop
128                    stopIntercept();
129                } else {
130                    list.add(out);
131                }
132            }
133    
134            // replace old output with this redone output list
135            outputs.clear();
136            for (ProcessorType<?> out : list) {
137                addOutput(out);
138            }
139        }
140    
141        public InterceptType createProxy() {
142            InterceptType answer = new InterceptType();
143            answer.getOutputs().addAll(this.getOutputs());
144            
145            answer.setStopIntercept(getStopIntercept());
146    
147            // hack: now we need to replace the proceed of the proxy with its own
148            // a bit ugly, operating based on the assumption that the proceed is
149            // in its outputs (if proceed() was called) and/or in the
150            // outputs of the otherwise or last when clause for the predicated version.
151            if (answer.getOutputs().size() > 0) {
152                // this is for the predicate version or if a choice() is present
153                ChoiceType choice = null;
154                for (ProcessorType processor : answer.getOutputs()) {
155                    if (processor instanceof ChoiceType) {
156                        // special cases for predicates (choices)
157                        choice = (ChoiceType) processor;
158    
159                        // for the predicated version we add the proceed() to otherwise()
160                        // before knowing if stop() will follow, so let's make a small adjustment
161                        if (usePredicate && getStopIntercept()) {
162                            WhenType when = choice.getWhenClauses().get(0);
163                            when.getOutputs().remove(this.getProceed());
164                        }
165    
166                        // add proceed to the when clause
167                        addProceedProxy(this.getProceed(), answer.getProceed(),
168                            choice.getWhenClauses().get(choice.getWhenClauses().size() - 1), usePredicate && !getStopIntercept());
169    
170                        // force adding a proceed at the end (otherwise) if its not a stop type
171                        addProceedProxy(this.getProceed(), answer.getProceed(), choice.getOtherwise(), !getStopIntercept());
172    
173                        if (getStopIntercept()) {
174                            // must add proceed to when clause if stop is explictiy declared, otherwise when the
175                            // predicate test fails then there is no proceed
176                            // See example: InterceptorSimpleRouteTest (City Paris is never proceeded)  
177                            addProceedProxy(this.getProceed(), answer.getProceed(),
178                                choice.getWhenClauses().get(choice.getWhenClauses().size() - 1), usePredicate);
179                        }
180    
181                        break;
182                    }
183                }
184                if (choice == null) {
185                    // force adding a proceed at the end if its not a stop type
186                    addProceedProxy(this.getProceed(), answer.getProceed(), answer, !getStopIntercept());
187                }
188            }
189    
190            return answer;
191        }
192    
193        private void addProceedProxy(ProceedType orig, ProceedType proxy, ProcessorType<?> processor, boolean force) {
194            int index = processor.getOutputs().indexOf(orig);
195            if (index >= 0) {
196                processor.addOutput(proxy);
197                // replace original proceed with proxy
198                List<ProcessorType<?>> outs = processor.getOutputs();
199                outs.remove(proxy);
200                outs.set(index, proxy);
201            } else if (force) {
202                processor.addOutput(proxy);
203            }
204        }
205    
206        public void setStopIntercept(Boolean stop) {
207            this.stopIntercept = stop;
208        }
209    
210        public Boolean getStopIntercept() {
211            return stopIntercept;
212        }
213    
214    }