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