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.Collection;
021    import java.util.List;
022    import javax.xml.bind.annotation.XmlAccessType;
023    import javax.xml.bind.annotation.XmlAccessorType;
024    import javax.xml.bind.annotation.XmlAttribute;
025    import javax.xml.bind.annotation.XmlElement;
026    import javax.xml.bind.annotation.XmlElementRef;
027    import javax.xml.bind.annotation.XmlRootElement;
028    import javax.xml.bind.annotation.XmlTransient;
029    
030    import org.apache.camel.CamelContext;
031    import org.apache.camel.Expression;
032    import org.apache.camel.LoggingLevel;
033    import org.apache.camel.Predicate;
034    import org.apache.camel.Processor;
035    import org.apache.camel.Route;
036    import org.apache.camel.builder.ErrorHandlerBuilder;
037    import org.apache.camel.builder.ExpressionBuilder;
038    import org.apache.camel.builder.ExpressionClause;
039    import org.apache.camel.processor.CatchProcessor;
040    import org.apache.camel.processor.RedeliveryPolicy;
041    import org.apache.camel.spi.RouteContext;
042    import org.apache.camel.util.CamelContextHelper;
043    import org.apache.camel.util.CastUtils;
044    import org.apache.camel.util.ObjectHelper;
045    
046    import static org.apache.camel.builder.PredicateBuilder.toPredicate;
047    
048    /**
049     * Represents an XML <onException/> element
050     *
051     * @version $Revision: 1021867 $
052     */
053    @XmlRootElement(name = "onException")
054    @XmlAccessorType(XmlAccessType.FIELD)
055    public class OnExceptionDefinition extends ProcessorDefinition<OnExceptionDefinition> {
056    
057        @XmlElement(name = "exception")
058        private List<String> exceptions = new ArrayList<String>();
059        @XmlElement(name = "onWhen", required = false)
060        private WhenDefinition onWhen;
061        @XmlElement(name = "retryWhile", required = false)
062        private ExpressionSubElementDefinition retryWhile;
063        @XmlElement(name = "redeliveryPolicy", required = false)
064        private RedeliveryPolicyDefinition redeliveryPolicy;
065        @XmlElement(name = "handled", required = false)
066        private ExpressionSubElementDefinition handled;
067        @XmlElement(name = "continued", required = false)
068        private ExpressionSubElementDefinition continued;
069        @XmlAttribute(name = "onRedeliveryRef", required = false)
070        private String onRedeliveryRef;
071        @XmlAttribute(name = "useOriginalMessage", required = false)
072        private Boolean useOriginalMessagePolicy;
073        @XmlElementRef
074        private List<ProcessorDefinition> outputs = new ArrayList<ProcessorDefinition>();
075        @XmlTransient
076        private List<Class> exceptionClasses;
077        @XmlTransient
078        private Processor errorHandler;
079        @XmlTransient
080        private Predicate handledPolicy;
081        @XmlTransient
082        private Predicate continuedPolicy;
083        @XmlTransient
084        private Predicate retryWhilePolicy;
085        @XmlTransient
086        private Processor onRedelivery;
087    
088        public OnExceptionDefinition() {
089        }
090    
091        public OnExceptionDefinition(List<Class> exceptionClasses) {
092            this.exceptionClasses = CastUtils.cast(exceptionClasses);
093        }
094    
095        public OnExceptionDefinition(Class exceptionType) {
096            exceptionClasses = new ArrayList<Class>();
097            exceptionClasses.add(exceptionType);
098        }
099    
100        @Override
101        public String getShortName() {
102            return "onException";
103        }
104    
105        @Override
106        public String toString() {
107            return "OnException[" + getExceptionClasses() + (onWhen != null ? " " + onWhen : "") + " -> " + getOutputs() + "]";
108        }
109    
110        @Override
111        public boolean isAbstract() {
112            return true;
113        }
114    
115        /**
116         * Allows an exception handler to create a new redelivery policy for this exception type
117         *
118         * @param context      the camel context
119         * @param parentPolicy the current redelivery policy
120         * @return a newly created redelivery policy, or return the original policy if no customization is required
121         *         for this exception handler.
122         */
123        public RedeliveryPolicy createRedeliveryPolicy(CamelContext context, RedeliveryPolicy parentPolicy) {
124            if (redeliveryPolicy != null) {
125                return redeliveryPolicy.createRedeliveryPolicy(context, parentPolicy);
126            } else if (errorHandler != null) {
127                // lets create a new error handler that has no retries
128                RedeliveryPolicy answer = parentPolicy.copy();
129                answer.setMaximumRedeliveries(0);
130                return answer;
131            }
132            return parentPolicy;
133        }
134    
135        public void addRoutes(RouteContext routeContext, Collection<Route> routes) throws Exception {
136            setHandledFromExpressionType(routeContext);
137            setContinuedFromExpressionType(routeContext);
138            setRetryWhileFromExpressionType(routeContext);
139    
140            // only one of handled or continued is allowed
141            if (getHandledPolicy() != null && getContinuedPolicy() != null) {
142                throw new IllegalArgumentException("Only one of handled or continued is allowed to be configured on: " + this);
143            }
144    
145            // lookup onRedelivery if ref is provided
146            if (ObjectHelper.isNotEmpty(onRedeliveryRef)) {
147                // if ref is provided then use mandatory lookup to fail if not found
148                Processor onRedelivery = CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), onRedeliveryRef, Processor.class);
149                setOnRedelivery(onRedelivery);
150            }
151    
152            // lets attach this on exception to the route error handler
153            errorHandler = routeContext.createProcessor(this);
154            ErrorHandlerBuilder builder = routeContext.getRoute().getErrorHandlerBuilder();
155            builder.addErrorHandlers(this);
156        }
157    
158        @Override
159        public CatchProcessor createProcessor(RouteContext routeContext) throws Exception {
160            Processor childProcessor = this.createChildProcessor(routeContext, false);
161    
162            Predicate when = null;
163            if (onWhen != null) {
164                when = onWhen.getExpression().createPredicate(routeContext);
165            }
166    
167            Predicate handle = null;
168            if (handled != null) {
169                handle = handled.createPredicate(routeContext);
170            }
171    
172            return new CatchProcessor(getExceptionClasses(), childProcessor, when, handle);
173        }
174    
175        // Fluent API
176        //-------------------------------------------------------------------------
177    
178        @Override
179        public OnExceptionDefinition onException(Class exceptionType) {
180            getExceptionClasses().add(exceptionType);
181            return this;
182        }
183    
184        /**
185         * Sets whether the exchange should be marked as handled or not.
186         *
187         * @param handled handled or not
188         * @return the builder
189         */
190        public OnExceptionDefinition handled(boolean handled) {
191            Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled));
192            return handled(expression);
193        }
194    
195        /**
196         * Sets whether the exchange should be marked as handled or not.
197         *
198         * @param handled predicate that determines true or false
199         * @return the builder
200         */
201        public OnExceptionDefinition handled(Predicate handled) {
202            setHandledPolicy(handled);
203            return this;
204        }
205    
206        /**
207         * Sets whether the exchange should be marked as handled or not.
208         *
209         * @param handled expression that determines true or false
210         * @return the builder
211         */
212        public OnExceptionDefinition handled(Expression handled) {
213            setHandledPolicy(toPredicate(handled));
214            return this;
215        }
216    
217        /**
218         * Sets whether the exchange should handle and continue routing from the point of failure.
219         * <p/>
220         * If this option is enabled then its considered handled as well.
221         *
222         * @param continued continued or not
223         * @return the builder
224         */
225        public OnExceptionDefinition continued(boolean continued) {
226            Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(continued));
227            return continued(expression);
228        }
229    
230        /**
231         * Sets whether the exchange should be marked as handled or not.
232         * <p/>
233         * If this option is enabled then its considered handled as well.
234         *
235         * @param continued predicate that determines true or false
236         * @return the builder
237         */
238        public OnExceptionDefinition continued(Predicate continued) {
239            setContinuedPolicy(continued);
240            return this;
241        }
242    
243        /**
244         * Sets whether the exchange should be marked as handled or not.
245         * <p/>
246         * If this option is enabled then its considered handled as well.
247         *
248         * @param continued expression that determines true or false
249         * @return the builder
250         */
251        public OnExceptionDefinition continued(Expression continued) {
252            setContinuedPolicy(toPredicate(continued));
253            return this;
254        }
255    
256        /**
257         * Sets an additional predicate that should be true before the onException is triggered.
258         * <p/>
259         * To be used for fine grained controlling whether a thrown exception should be intercepted
260         * by this exception type or not.
261         *
262         * @param predicate predicate that determines true or false
263         * @return the builder
264         */
265        public OnExceptionDefinition onWhen(Predicate predicate) {
266            setOnWhen(new WhenDefinition(predicate));
267            return this;
268        }
269    
270        /**
271         * Creates an expression to configure an additional predicate that should be true before the
272         * onException is triggered.
273         * <p/>
274         * To be used for fine grained controlling whether a thrown exception should be intercepted
275         * by this exception type or not.
276         *
277         * @return the expression clause to configure
278         */
279        public ExpressionClause<OnExceptionDefinition> onWhen() {
280            onWhen = new WhenDefinition();
281            ExpressionClause<OnExceptionDefinition> clause = new ExpressionClause<OnExceptionDefinition>(this);
282            onWhen.setExpression(clause);
283            return clause;
284        }
285    
286        /**
287         * Sets the retry while predicate.
288         * <p/>
289         * Will continue retrying until predicate returns <tt>false</tt>.
290         *
291         * @param retryWhile predicate that determines when to stop retrying
292         * @return the builder
293         */
294        public OnExceptionDefinition retryWhile(Predicate retryWhile) {
295            setRetryWhilePolicy(retryWhile);
296            return this;
297        }
298    
299        /**
300         * Sets the retry while expression.
301         * <p/>
302         * Will continue retrying until expression evaluates to <tt>false</tt>.
303         *
304         * @param retryWhile expression that determines when to stop retrying
305         * @return the builder
306         */
307        public OnExceptionDefinition retryWhile(Expression retryWhile) {
308            setRetryWhilePolicy(toPredicate(retryWhile));
309            return this;
310        }
311    
312        /**
313         * Sets the initial redelivery delay
314         *
315         * @param delay the initial redelivery delay
316         * @return the builder
317         * @deprecated
318         */
319        @Deprecated
320        public OnExceptionDefinition redeliverDelay(long delay) {
321            getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
322            return this;
323        }
324    
325        /**
326         * Sets the back off multiplier
327         *
328         * @param backOffMultiplier the back off multiplier
329         * @return the builder
330         */
331        public OnExceptionDefinition backOffMultiplier(double backOffMultiplier) {
332            getOrCreateRedeliveryPolicy().backOffMultiplier(backOffMultiplier);
333            return this;
334        }
335    
336        /**
337         * Sets the collision avoidance factor
338         *
339         * @param collisionAvoidanceFactor the factor
340         * @return the builder
341         */
342        public OnExceptionDefinition collisionAvoidanceFactor(double collisionAvoidanceFactor) {
343            getOrCreateRedeliveryPolicy().collisionAvoidanceFactor(collisionAvoidanceFactor);
344            return this;
345        }
346    
347        /**
348         * Sets the collision avoidance percentage
349         *
350         * @param collisionAvoidancePercent the percentage
351         * @return the builder
352         */
353        public OnExceptionDefinition collisionAvoidancePercent(double collisionAvoidancePercent) {
354            getOrCreateRedeliveryPolicy().collisionAvoidancePercent(collisionAvoidancePercent);
355            return this;
356        }
357    
358        /**
359         * Sets the initial redelivery delay
360         *
361         * @param delay delay in millis
362         * @return the builder
363         */
364        public OnExceptionDefinition redeliveryDelay(long delay) {
365            getOrCreateRedeliveryPolicy().redeliveryDelay(delay);
366            return this;
367        }
368    
369        /**
370         * Allow synchronous delayed redelivery.
371         *
372         * @see org.apache.camel.processor.RedeliveryPolicy#setAsyncDelayedRedelivery(boolean)
373         * @return the builder
374         */
375        public OnExceptionDefinition asyncDelayedRedelivery() {
376            getOrCreateRedeliveryPolicy().setAsyncDelayedRedelivery(true);
377            return this;
378        }
379    
380        /**
381         * Sets the logging level to use when retries has exhausted
382         *
383         * @param retriesExhaustedLogLevel the logging level
384         * @return the builder
385         */
386        public OnExceptionDefinition retriesExhaustedLogLevel(LoggingLevel retriesExhaustedLogLevel) {
387            getOrCreateRedeliveryPolicy().retriesExhaustedLogLevel(retriesExhaustedLogLevel);
388            return this;
389        }
390    
391        /**
392         * Sets the logging level to use for logging retry attempts
393         *
394         * @param retryAttemptedLogLevel the logging level
395         * @return the builder
396         */
397        public OnExceptionDefinition retryAttemptedLogLevel(LoggingLevel retryAttemptedLogLevel) {
398            getOrCreateRedeliveryPolicy().retryAttemptedLogLevel(retryAttemptedLogLevel);
399            return this;
400        }
401    
402        /**
403         * Sets whether to log stacktrace for failed messages.
404         */
405        public OnExceptionDefinition logStackTrace(boolean logStackTrace) {
406            getOrCreateRedeliveryPolicy().setLogStackTrace(logStackTrace);
407            return this;
408        }
409    
410        /**
411         * Sets whether to log stacktrace for failed redelivery attempts
412         */
413        public OnExceptionDefinition logRetryStackTrace(boolean logRetryStackTrace) {
414            getOrCreateRedeliveryPolicy().setLogRetryStackTrace(logRetryStackTrace);
415            return this;
416        }
417    
418        /**
419         * Sets whether to log errors even if its handled
420         */
421        public OnExceptionDefinition logHandled(boolean logHandled) {
422            getOrCreateRedeliveryPolicy().setLogHandled(logHandled);
423            return this;
424        }
425    
426        /**
427         * Sets whether to log errors even if its continued
428         */
429        public OnExceptionDefinition logContinued(boolean logContinued) {
430            getOrCreateRedeliveryPolicy().setLogContinued(logContinued);
431            return this;
432        }
433    
434        /**
435         * Sets whether to log retry attempts
436         */
437        public OnExceptionDefinition logRetryAttempted(boolean logRetryAttempted) {
438            getOrCreateRedeliveryPolicy().setLogRetryAttempted(logRetryAttempted);
439            return this;
440        }
441    
442        /**
443         * Sets whether to log exhausted exceptions
444         */
445        public OnExceptionDefinition logExhausted(boolean logExhausted) {
446            getOrCreateRedeliveryPolicy().setLogExhausted(logExhausted);
447            return this;
448        }
449    
450        /**
451         * Sets the maximum redeliveries
452         * <ul>
453         * <li>5 = default value</li>
454         * <li>0 = no redeliveries</li>
455         * <li>-1 = redeliver forever</li>
456         * </ul>
457         *
458         * @param maximumRedeliveries the value
459         * @return the builder
460         */
461        public OnExceptionDefinition maximumRedeliveries(int maximumRedeliveries) {
462            getOrCreateRedeliveryPolicy().maximumRedeliveries(maximumRedeliveries);
463            return this;
464        }
465    
466        /**
467         * Turn on collision avoidance.
468         *
469         * @return the builder
470         */
471        public OnExceptionDefinition useCollisionAvoidance() {
472            getOrCreateRedeliveryPolicy().useCollisionAvoidance();
473            return this;
474        }
475    
476        /**
477         * Turn on exponential backk off
478         *
479         * @return the builder
480         */
481        public OnExceptionDefinition useExponentialBackOff() {
482            getOrCreateRedeliveryPolicy().useExponentialBackOff();
483            return this;
484        }
485    
486        /**
487         * Sets the maximum delay between redelivery
488         *
489         * @param maximumRedeliveryDelay the delay in millis
490         * @return the builder
491         */
492        public OnExceptionDefinition maximumRedeliveryDelay(long maximumRedeliveryDelay) {
493            getOrCreateRedeliveryPolicy().maximumRedeliveryDelay(maximumRedeliveryDelay);
494            return this;
495        }
496    
497        /**
498         * Sets a reference to a {@link RedeliveryPolicy} to lookup in the {@link org.apache.camel.spi.Registry} to be used.
499         *
500         * @param redeliveryPolicyRef reference to use for lookup
501         * @return the builder
502         */
503        public OnExceptionDefinition redeliveryPolicyRef(String redeliveryPolicyRef) {
504            getOrCreateRedeliveryPolicy().setRef(redeliveryPolicyRef);
505            return this;
506        }
507    
508        /**
509         * Sets the delay pattern with delay intervals.
510         *
511         * @param delayPattern the delay pattern
512         * @return the builder
513         */
514        public OnExceptionDefinition delayPattern(String delayPattern) {
515            getOrCreateRedeliveryPolicy().setDelayPattern(delayPattern);
516            return this;
517        }
518    
519        /**
520         * @deprecated this method will be removed in Camel 3.0, please use {@link #useOriginalMessage()}
521         * @see #useOriginalMessage()
522         */
523        @Deprecated
524        public OnExceptionDefinition useOriginalBody() {
525            setUseOriginalMessagePolicy(Boolean.TRUE);
526            return this;
527        }
528    
529        /**
530         * Will use the original input message when an {@link org.apache.camel.Exchange} is moved to the dead letter queue.
531         * <p/>
532         * <b>Notice:</b> this only applies when all redeliveries attempt have failed and the {@link org.apache.camel.Exchange} is doomed for failure.
533         * <br/>
534         * Instead of using the current inprogress {@link org.apache.camel.Exchange} IN body we use the original IN body instead. This allows
535         * you to store the original input in the dead letter queue instead of the inprogress snapshot of the IN body.
536         * For instance if you route transform the IN body during routing and then failed. With the original exchange
537         * store in the dead letter queue it might be easier to manually re submit the {@link org.apache.camel.Exchange} again as the IN body
538         * is the same as when Camel received it. So you should be able to send the {@link org.apache.camel.Exchange} to the same input.
539         * <p/>
540         * By default this feature is off.
541         *
542         * @return the builder
543         */
544        public OnExceptionDefinition useOriginalMessage() {
545            setUseOriginalMessagePolicy(Boolean.TRUE);
546            return this;
547        }
548    
549        /**
550         * Sets a processor that should be processed <b>before</b> a redelivery attempt.
551         * <p/>
552         * Can be used to change the {@link org.apache.camel.Exchange} <b>before</b> its being redelivered.
553         */
554        public OnExceptionDefinition onRedelivery(Processor processor) {
555            setOnRedelivery(processor);
556            return this;
557        }
558    
559        // Properties
560        //-------------------------------------------------------------------------
561    
562        public List<ProcessorDefinition> getOutputs() {
563            return outputs;
564        }
565    
566        public void setOutputs(List<ProcessorDefinition> outputs) {
567            this.outputs = outputs;
568        }
569    
570        public List<Class> getExceptionClasses() {
571            if (exceptionClasses == null) {
572                exceptionClasses = createExceptionClasses();
573            }
574            return exceptionClasses;
575        }
576    
577        public void setExceptionClasses(List<Class> exceptionClasses) {
578            this.exceptionClasses = exceptionClasses;
579        }
580    
581        public List<String> getExceptions() {
582            return exceptions;
583        }
584    
585        public void setExceptions(List<String> exceptions) {
586            this.exceptions = exceptions;
587        }
588    
589        public Processor getErrorHandler() {
590            return errorHandler;
591        }
592    
593        public RedeliveryPolicyDefinition getRedeliveryPolicy() {
594            return redeliveryPolicy;
595        }
596    
597        public void setRedeliveryPolicy(RedeliveryPolicyDefinition redeliveryPolicy) {
598            this.redeliveryPolicy = redeliveryPolicy;
599        }
600    
601        public Predicate getHandledPolicy() {
602            return handledPolicy;
603        }
604    
605        public void setHandled(ExpressionSubElementDefinition handled) {
606            this.handled = handled;
607        }
608    
609        public ExpressionSubElementDefinition getContinued() {
610            return continued;
611        }
612    
613        public void setContinued(ExpressionSubElementDefinition continued) {
614            this.continued = continued;
615        }
616    
617        public ExpressionSubElementDefinition getHandled() {
618            return handled;
619        }
620    
621        public void setHandledPolicy(Predicate handledPolicy) {
622            this.handledPolicy = handledPolicy;
623        }
624    
625        public Predicate getContinuedPolicy() {
626            return continuedPolicy;
627        }
628    
629        public void setContinuedPolicy(Predicate continuedPolicy) {
630            this.continuedPolicy = continuedPolicy;
631        }
632    
633        public WhenDefinition getOnWhen() {
634            return onWhen;
635        }
636    
637        public void setOnWhen(WhenDefinition onWhen) {
638            this.onWhen = onWhen;
639        }
640    
641        public ExpressionSubElementDefinition getRetryWhile() {
642            return retryWhile;
643        }
644    
645        public void setRetryWhile(ExpressionSubElementDefinition retryWhile) {
646            this.retryWhile = retryWhile;
647        }
648    
649        public Predicate getRetryWhilePolicy() {
650            return retryWhilePolicy;
651        }
652    
653        public void setRetryWhilePolicy(Predicate retryWhilePolicy) {
654            this.retryWhilePolicy = retryWhilePolicy;
655        }
656    
657        public Processor getOnRedelivery() {
658            return onRedelivery;
659        }
660    
661        public void setOnRedelivery(Processor onRedelivery) {
662            this.onRedelivery = onRedelivery;
663        }
664    
665        public String getOnRedeliveryRef() {
666            return onRedeliveryRef;
667        }
668    
669        public void setOnRedeliveryRef(String onRedeliveryRef) {
670            this.onRedeliveryRef = onRedeliveryRef;
671        }
672    
673        public Boolean getUseOriginalMessagePolicy() {
674            return useOriginalMessagePolicy;
675        }
676    
677        @XmlTransient
678        public boolean isUseOriginalMessage() {
679            // should be false by default
680            return useOriginalMessagePolicy != null ? useOriginalMessagePolicy : false;
681        }
682    
683        public void setUseOriginalMessagePolicy(Boolean useOriginalMessagePolicy) {
684            this.useOriginalMessagePolicy = useOriginalMessagePolicy;
685        }
686    
687        public boolean isAsyncDelayedRedelivery() {
688            if (getRedeliveryPolicy() != null) {
689                return getRedeliveryPolicy().getAsyncDelayedRedelivery() != null && getRedeliveryPolicy().getAsyncDelayedRedelivery();
690            }
691            return false;
692        }
693    
694        // Implementation methods
695        //-------------------------------------------------------------------------
696    
697        protected RedeliveryPolicyDefinition getOrCreateRedeliveryPolicy() {
698            if (redeliveryPolicy == null) {
699                redeliveryPolicy = new RedeliveryPolicyDefinition();
700            }
701            return redeliveryPolicy;
702        }
703    
704        protected List<Class> createExceptionClasses() {
705            List<String> list = getExceptions();
706            List<Class> answer = new ArrayList<Class>(list.size());
707            for (String name : list) {
708                Class<Throwable> type = CastUtils.cast(ObjectHelper.loadClass(name, getClass().getClassLoader()), Throwable.class);
709                answer.add(type);
710            }
711            return answer;
712        }
713    
714        private void setHandledFromExpressionType(RouteContext routeContext) {
715            if (getHandled() != null && handledPolicy == null && routeContext != null) {
716                handled(getHandled().createPredicate(routeContext));
717            }
718        }
719    
720        private void setContinuedFromExpressionType(RouteContext routeContext) {
721            if (getContinued() != null && continuedPolicy == null && routeContext != null) {
722                continued(getContinued().createPredicate(routeContext));
723            }
724        }
725    
726        private void setRetryWhileFromExpressionType(RouteContext routeContext) {
727            if (getRetryWhile() != null && retryWhilePolicy == null && routeContext != null) {
728                retryWhile(getRetryWhile().createPredicate(routeContext));
729            }
730        }
731    }