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