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