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