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.Arrays; 021import java.util.Iterator; 022import java.util.List; 023import javax.xml.bind.annotation.XmlAccessType; 024import javax.xml.bind.annotation.XmlAccessorType; 025import javax.xml.bind.annotation.XmlRootElement; 026import javax.xml.bind.annotation.XmlTransient; 027 028import org.apache.camel.Expression; 029import org.apache.camel.Predicate; 030import org.apache.camel.Processor; 031import org.apache.camel.builder.ExpressionBuilder; 032import org.apache.camel.processor.TryProcessor; 033import org.apache.camel.spi.AsPredicate; 034import org.apache.camel.spi.Metadata; 035import org.apache.camel.spi.RouteContext; 036import org.apache.camel.util.ExpressionToPredicateAdapter; 037 038/** 039 * Marks the beginning of a try, catch, finally block 040 * 041 * @version 042 */ 043@Metadata(label = "error") 044@XmlRootElement(name = "doTry") 045@XmlAccessorType(XmlAccessType.FIELD) 046public class TryDefinition extends OutputDefinition<TryDefinition> { 047 @XmlTransient 048 private List<CatchDefinition> catchClauses; 049 @XmlTransient 050 private FinallyDefinition finallyClause; 051 @XmlTransient 052 private boolean initialized; 053 @XmlTransient 054 private List<ProcessorDefinition<?>> outputsWithoutCatches; 055 056 public TryDefinition() { 057 } 058 059 @Override 060 public String toString() { 061 return "DoTry[" + getOutputs() + "]"; 062 } 063 064 @Override 065 public String getLabel() { 066 return "doTry"; 067 } 068 069 @Override 070 public Processor createProcessor(RouteContext routeContext) throws Exception { 071 Processor tryProcessor = createOutputsProcessor(routeContext, getOutputsWithoutCatches()); 072 if (tryProcessor == null) { 073 throw new IllegalArgumentException("Definition has no children on " + this); 074 } 075 076 List<Processor> catchProcessors = new ArrayList<Processor>(); 077 if (catchClauses != null) { 078 for (CatchDefinition catchClause : catchClauses) { 079 catchProcessors.add(createProcessor(routeContext, catchClause)); 080 } 081 } 082 083 FinallyDefinition finallyDefinition = finallyClause; 084 if (finallyDefinition == null) { 085 finallyDefinition = new FinallyDefinition(); 086 finallyDefinition.setParent(this); 087 } 088 Processor finallyProcessor = createProcessor(routeContext, finallyDefinition); 089 090 // must have either a catch or finally 091 if (finallyClause == null && catchClauses == null) { 092 throw new IllegalArgumentException("doTry must have one or more catch or finally blocks on " + this); 093 } 094 095 return new TryProcessor(tryProcessor, catchProcessors, finallyProcessor); 096 } 097 098 // Fluent API 099 // ------------------------------------------------------------------------- 100 101 /** 102 * Handles the given exception 103 * 104 * @param exceptionType the exception 105 * @return the try builder 106 */ 107 @SuppressWarnings("unchecked") 108 public TryDefinition doCatch(Class<? extends Throwable> exceptionType) { 109 // this method is introduced to avoid compiler warnings about the 110 // generic Class arrays in the case we've got only one single Class 111 // to build a TryDefinition for 112 return doCatch(new Class[] {exceptionType}); 113 } 114 115 /** 116 * Handles the given exception(s) 117 * 118 * @param exceptionType the exception(s) 119 * @return the try builder 120 */ 121 public TryDefinition doCatch(Class<? extends Throwable>... exceptionType) { 122 popBlock(); 123 List<Class<? extends Throwable>> list = Arrays.asList(exceptionType); 124 CatchDefinition answer = new CatchDefinition(list); 125 addOutput(answer); 126 pushBlock(answer); 127 return this; 128 } 129 130 /** 131 * The finally block for a given handle 132 * 133 * @return the try builder 134 */ 135 public TryDefinition doFinally() { 136 popBlock(); 137 FinallyDefinition answer = new FinallyDefinition(); 138 addOutput(answer); 139 pushBlock(answer); 140 return this; 141 } 142 143 /** 144 * Sets an additional predicate that should be true before the onCatch is triggered. 145 * <p/> 146 * To be used for fine grained controlling whether a thrown exception should be intercepted 147 * by this exception type or not. 148 * 149 * @param predicate predicate that determines true or false 150 * @return the builder 151 */ 152 public TryDefinition onWhen(@AsPredicate Predicate predicate) { 153 // we must use a delegate so we can use the fluent builder based on TryDefinition 154 // to configure all with try .. catch .. finally 155 // set the onWhen predicate on all the catch definitions 156 Iterator<CatchDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(getOutputs(), CatchDefinition.class); 157 while (it.hasNext()) { 158 CatchDefinition doCatch = it.next(); 159 doCatch.setOnWhen(new WhenDefinition(predicate)); 160 } 161 return this; 162 } 163 164 /** 165 * Sets whether the exchange should be marked as handled or not. 166 * 167 * @param handled handled or not 168 * @return the builder 169 * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception 170 * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)} 171 */ 172 @Deprecated 173 public TryDefinition handled(boolean handled) { 174 Expression expression = ExpressionBuilder.constantExpression(Boolean.toString(handled)); 175 return handled(expression); 176 } 177 178 /** 179 * Sets whether the exchange should be marked as handled or not. 180 * 181 * @param handled predicate that determines true or false 182 * @return the builder 183 * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception 184 * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)} 185 */ 186 @Deprecated 187 public TryDefinition handled(@AsPredicate Predicate handled) { 188 // we must use a delegate so we can use the fluent builder based on TryDefinition 189 // to configure all with try .. catch .. finally 190 // set the handled on all the catch definitions 191 Iterator<CatchDefinition> it = ProcessorDefinitionHelper.filterTypeInOutputs(getOutputs(), CatchDefinition.class); 192 while (it.hasNext()) { 193 CatchDefinition doCatch = it.next(); 194 doCatch.setHandledPolicy(handled); 195 } 196 return this; 197 } 198 199 /** 200 * Sets whether the exchange should be marked as handled or not. 201 * 202 * @param handled expression that determines true or false 203 * @return the builder 204 * @deprecated will be removed in Camel 3.0. Instead of using handled(false) you can re-throw the exception 205 * from a {@link Processor} or use the {@link ProcessorDefinition#throwException(Exception)} 206 */ 207 @Deprecated 208 public TryDefinition handled(@AsPredicate Expression handled) { 209 return handled(ExpressionToPredicateAdapter.toPredicate(handled)); 210 } 211 212 // Properties 213 // ------------------------------------------------------------------------- 214 215 public List<CatchDefinition> getCatchClauses() { 216 if (catchClauses == null) { 217 checkInitialized(); 218 } 219 return catchClauses; 220 } 221 222 public FinallyDefinition getFinallyClause() { 223 if (finallyClause == null) { 224 checkInitialized(); 225 } 226 return finallyClause; 227 } 228 229 public List<ProcessorDefinition<?>> getOutputsWithoutCatches() { 230 if (outputsWithoutCatches == null) { 231 checkInitialized(); 232 } 233 return outputsWithoutCatches; 234 } 235 236 public void setOutputs(List<ProcessorDefinition<?>> outputs) { 237 initialized = false; 238 super.setOutputs(outputs); 239 } 240 241 @Override 242 public void addOutput(ProcessorDefinition<?> output) { 243 initialized = false; 244 super.addOutput(output); 245 } 246 247 @Override 248 protected void preCreateProcessor() { 249 // force re-creating initialization to ensure its up-to-date 250 initialized = false; 251 checkInitialized(); 252 } 253 254 /** 255 * Checks whether or not this object has been initialized 256 */ 257 protected void checkInitialized() { 258 if (!initialized) { 259 initialized = true; 260 outputsWithoutCatches = new ArrayList<ProcessorDefinition<?>>(); 261 catchClauses = new ArrayList<CatchDefinition>(); 262 finallyClause = null; 263 264 for (ProcessorDefinition<?> output : outputs) { 265 if (output instanceof CatchDefinition) { 266 catchClauses.add((CatchDefinition)output); 267 } else if (output instanceof FinallyDefinition) { 268 if (finallyClause != null) { 269 throw new IllegalArgumentException("Multiple finally clauses added: " + finallyClause 270 + " and " + output); 271 } else { 272 finallyClause = (FinallyDefinition)output; 273 } 274 } else { 275 outputsWithoutCatches.add(output); 276 } 277 } 278 } 279 } 280}