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