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 */ 017package org.apache.camel.model; 018 019import java.util.ArrayList; 020import java.util.List; 021 022import javax.xml.bind.annotation.XmlAccessType; 023import javax.xml.bind.annotation.XmlAccessorType; 024import javax.xml.bind.annotation.XmlElement; 025import javax.xml.bind.annotation.XmlElementRef; 026import javax.xml.bind.annotation.XmlRootElement; 027import javax.xml.bind.annotation.XmlTransient; 028 029import org.apache.camel.CamelContext; 030import org.apache.camel.Expression; 031import org.apache.camel.Predicate; 032import org.apache.camel.Processor; 033import org.apache.camel.builder.ExpressionBuilder; 034import org.apache.camel.processor.CatchProcessor; 035import org.apache.camel.spi.AsPredicate; 036import org.apache.camel.spi.Metadata; 037import org.apache.camel.spi.RouteContext; 038import org.apache.camel.util.ExpressionToPredicateAdapter; 039 040/** 041 * Catches exceptions as part of a try, catch, finally block 042 * 043 * @version 044 */ 045@Metadata(label = "error") 046@XmlRootElement(name = "doCatch") 047@XmlAccessorType(XmlAccessType.FIELD) 048public class CatchDefinition extends ProcessorDefinition<CatchDefinition> { 049 @XmlElement(name = "exception") 050 private List<String> exceptions = new ArrayList<>(); 051 @XmlElement(name = "onWhen") @AsPredicate 052 private WhenDefinition onWhen; 053 @XmlElement(name = "handled") @AsPredicate 054 private ExpressionSubElementDefinition handled; 055 @XmlElementRef 056 private List<ProcessorDefinition<?>> outputs = new ArrayList<>(); 057 @XmlTransient 058 private List<Class<? extends Throwable>> exceptionClasses; 059 @XmlTransient 060 private Predicate handledPolicy; 061 062 public CatchDefinition() { 063 } 064 065 public CatchDefinition(List<Class<? extends Throwable>> exceptionClasses) { 066 this.exceptionClasses = exceptionClasses; 067 } 068 069 public CatchDefinition(Class<? extends Throwable> exceptionType) { 070 exceptionClasses = new ArrayList<>(); 071 exceptionClasses.add(exceptionType); 072 } 073 074 @Override 075 public String toString() { 076 return "DoCatch[ " + getExceptionClasses() + " -> " + getOutputs() + "]"; 077 } 078 079 @Override 080 public String getShortName() { 081 return "doCatch"; 082 } 083 084 @Override 085 public String getLabel() { 086 return "doCatch[ " + getExceptionClasses() + "]"; 087 } 088 089 @Override 090 public CatchProcessor createProcessor(RouteContext routeContext) throws Exception { 091 // create and load exceptions if not done 092 if (exceptionClasses == null) { 093 exceptionClasses = createExceptionClasses(routeContext.getCamelContext()); 094 } 095 096 // must have at least one exception 097 if (exceptionClasses.isEmpty()) { 098 throw new IllegalArgumentException("At least one Exception must be configured to catch"); 099 } 100 101 // parent must be a try 102 if (!(getParent() instanceof TryDefinition)) { 103 throw new IllegalArgumentException("This doCatch should have a doTry as its parent on " + this); 104 } 105 106 // do catch does not mandate a child processor 107 Processor childProcessor = this.createChildProcessor(routeContext, false); 108 109 Predicate when = null; 110 if (onWhen != null) { 111 when = onWhen.getExpression().createPredicate(routeContext); 112 } 113 114 Predicate handle = handledPolicy; 115 if (handled != null) { 116 handle = handled.createPredicate(routeContext); 117 } 118 119 return new CatchProcessor(exceptionClasses, childProcessor, when, handle); 120 } 121 122 @Override 123 public List<ProcessorDefinition<?>> getOutputs() { 124 return outputs; 125 } 126 127 public void setOutputs(List<ProcessorDefinition<?>> outputs) { 128 this.outputs = outputs; 129 } 130 131 public boolean isOutputSupported() { 132 return true; 133 } 134 135 public List<Class<? extends Throwable>> getExceptionClasses() { 136 return exceptionClasses; 137 } 138 139 public void setExceptionClasses(List<Class<? extends Throwable>> exceptionClasses) { 140 this.exceptionClasses = exceptionClasses; 141 } 142 143 // Fluent API 144 //------------------------------------------------------------------------- 145 /** 146 * The exceptions to catch. 147 * 148 * @param exceptionClasses a list of the exception classes 149 * @return the builder 150 */ 151 public CatchDefinition exceptionClasses(List<Class<? extends Throwable>> exceptionClasses) { 152 setExceptionClasses(exceptionClasses); 153 return this; 154 } 155 156 /** 157 * The exception(s) to catch. 158 * 159 * @param exceptions one or more exceptions 160 * @return the builder 161 */ 162 public CatchDefinition exception(Class<? extends Throwable>... exceptions) { 163 if (exceptionClasses == null) { 164 exceptionClasses = new ArrayList<>(); 165 } 166 if (exceptions != null) { 167 for (Class<? extends Throwable> exception : exceptions) { 168 exceptionClasses.add(exception); 169 } 170 } 171 return this; 172 } 173 174 /** 175 * Sets an additional predicate that should be true before the onCatch is triggered. 176 * <p/> 177 * To be used for fine grained controlling whether a thrown exception should be intercepted 178 * by this exception type or not. 179 * 180 * @param predicate predicate that determines true or false 181 * @return the builder 182 */ 183 public CatchDefinition onWhen(@AsPredicate Predicate predicate) { 184 setOnWhen(new WhenDefinition(predicate)); 185 return this; 186 } 187 188 /** 189 * Sets whether the exchange should be marked as handled or not. 190 * 191 * @param handled handled or not 192 * @return the builder 193 * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception 194 * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)} 195 */ 196 @Deprecated 197 public CatchDefinition handled(boolean handled) { 198 Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled)); 199 return handled(expression); 200 } 201 202 /** 203 * Sets whether the exchange should be marked as handled or not. 204 * 205 * @param handled predicate that determines true or false 206 * @return the builder 207 * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception 208 * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)} 209 */ 210 @Deprecated 211 public CatchDefinition handled(@AsPredicate Predicate handled) { 212 setHandledPolicy(handled); 213 return this; 214 } 215 216 /** 217 * Sets whether the exchange should be marked as handled or not. 218 * 219 * @param handled expression that determines true or false 220 * @return the builder 221 * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception 222 * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)} 223 */ 224 @Deprecated 225 public CatchDefinition handled(@AsPredicate Expression handled) { 226 setHandledPolicy(ExpressionToPredicateAdapter.toPredicate(handled)); 227 return this; 228 } 229 230 /** 231 * Sets the exception class that the CatchType want to catch 232 * 233 * @param exception the exception of class 234 * @return the builder 235 */ 236 public CatchDefinition exceptionClasses(Class<? extends Throwable> exception) { 237 List<Class<? extends Throwable>> list = getExceptionClasses(); 238 list.add(exception); 239 return this; 240 } 241 242 public List<String> getExceptions() { 243 return exceptions; 244 } 245 246 public void setExceptions(List<String> exceptions) { 247 this.exceptions = exceptions; 248 } 249 250 public WhenDefinition getOnWhen() { 251 return onWhen; 252 } 253 254 public void setOnWhen(WhenDefinition onWhen) { 255 this.onWhen = onWhen; 256 } 257 258 public Predicate getHandledPolicy() { 259 return handledPolicy; 260 } 261 262 public void setHandledPolicy(Predicate handledPolicy) { 263 this.handledPolicy = handledPolicy; 264 } 265 266 public ExpressionSubElementDefinition getHandled() { 267 return handled; 268 } 269 270 public void setHandled(ExpressionSubElementDefinition handled) { 271 this.handled = handled; 272 } 273 274 protected List<Class<? extends Throwable>> createExceptionClasses(CamelContext context) throws ClassNotFoundException { 275 // must use the class resolver from CamelContext to load classes to ensure it can 276 // be loaded in all kind of environments such as JEE servers and OSGi etc. 277 List<String> list = getExceptions(); 278 List<Class<? extends Throwable>> answer = new ArrayList<>(list.size()); 279 for (String name : list) { 280 Class<Throwable> type = context.getClassResolver().resolveMandatoryClass(name, Throwable.class); 281 answer.add(type); 282 } 283 return answer; 284 } 285}