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