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