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.List;
021 import java.util.concurrent.ExecutorService;
022
023 import javax.xml.bind.annotation.XmlAccessType;
024 import javax.xml.bind.annotation.XmlAccessorType;
025 import javax.xml.bind.annotation.XmlAttribute;
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.Processor;
031 import org.apache.camel.model.language.ExpressionDefinition;
032 import org.apache.camel.processor.EvaluateExpressionProcessor;
033 import org.apache.camel.processor.Pipeline;
034 import org.apache.camel.processor.RecipientList;
035 import org.apache.camel.processor.aggregate.AggregationStrategy;
036 import org.apache.camel.processor.aggregate.UseLatestAggregationStrategy;
037 import org.apache.camel.spi.RouteContext;
038 import org.apache.camel.util.CamelContextHelper;
039
040 /**
041 * Represents an XML <recipientList/> element
042 *
043 * @version
044 */
045 @XmlRootElement(name = "recipientList")
046 @XmlAccessorType(XmlAccessType.FIELD)
047 public class RecipientListDefinition<Type extends ProcessorDefinition<Type>> extends NoOutputExpressionNode implements ExecutorServiceAwareDefinition<RecipientListDefinition<Type>> {
048 @XmlTransient
049 private AggregationStrategy aggregationStrategy;
050 @XmlTransient
051 private ExecutorService executorService;
052 @XmlAttribute
053 private String delimiter;
054 @XmlAttribute
055 private Boolean parallelProcessing;
056 @XmlAttribute
057 private String strategyRef;
058 @XmlAttribute
059 private String executorServiceRef;
060 @XmlAttribute
061 private Boolean stopOnException;
062 @XmlAttribute
063 private Boolean ignoreInvalidEndpoints;
064 @XmlAttribute
065 private Boolean streaming;
066 @XmlAttribute
067 private Long timeout;
068 @XmlAttribute
069 private String onPrepareRef;
070 @XmlTransient
071 private Processor onPrepare;
072 @XmlAttribute
073 private Boolean shareUnitOfWork;
074
075 public RecipientListDefinition() {
076 }
077
078 public RecipientListDefinition(ExpressionDefinition expression) {
079 super(expression);
080 }
081
082 public RecipientListDefinition(Expression expression) {
083 super(expression);
084 }
085
086 @Override
087 public String toString() {
088 return "RecipientList[" + getExpression() + "]";
089 }
090
091 @Override
092 public String getShortName() {
093 return "recipientList";
094 }
095
096 @Override
097 public String getLabel() {
098 return "recipientList[" + getExpression() + "]";
099 }
100
101 @Override
102 public Processor createProcessor(RouteContext routeContext) throws Exception {
103 final Expression expression = getExpression().createExpression(routeContext);
104
105 RecipientList answer;
106 if (delimiter != null) {
107 answer = new RecipientList(routeContext.getCamelContext(), expression, delimiter);
108 } else {
109 answer = new RecipientList(routeContext.getCamelContext(), expression);
110 }
111 answer.setAggregationStrategy(createAggregationStrategy(routeContext));
112 answer.setParallelProcessing(isParallelProcessing());
113 answer.setStreaming(isStreaming());
114 answer.setShareUnitOfWork(isShareUnitOfWork());
115 if (onPrepareRef != null) {
116 onPrepare = CamelContextHelper.mandatoryLookup(routeContext.getCamelContext(), onPrepareRef, Processor.class);
117 }
118 if (onPrepare != null) {
119 answer.setOnPrepare(onPrepare);
120 }
121 if (stopOnException != null) {
122 answer.setStopOnException(isStopOnException());
123 }
124 if (ignoreInvalidEndpoints != null) {
125 answer.setIgnoreInvalidEndpoints(ignoreInvalidEndpoints);
126 }
127 if (getTimeout() != null) {
128 answer.setTimeout(getTimeout());
129 }
130
131 boolean shutdownThreadPool = ProcessorDefinitionHelper.willCreateNewThreadPool(routeContext, this, isParallelProcessing());
132 ExecutorService threadPool = ProcessorDefinitionHelper.getConfiguredExecutorService(routeContext, "RecipientList", this, isParallelProcessing());
133 answer.setExecutorService(threadPool);
134 answer.setShutdownExecutorService(shutdownThreadPool);
135 long timeout = getTimeout() != null ? getTimeout() : 0;
136 if (timeout > 0 && !isParallelProcessing()) {
137 throw new IllegalArgumentException("Timeout is used but ParallelProcessing has not been enabled.");
138 }
139
140 // create a pipeline with two processors
141 // the first is the eval processor which evaluates the expression to use
142 // the second is the recipient list
143 List<Processor> pipe = new ArrayList<Processor>(2);
144
145 // the eval processor must be wrapped in error handler, so in case there was an
146 // error during evaluation, the error handler can deal with it
147 // the recipient list is not in error handler, as its has its own special error handling
148 // when sending to the recipients individually
149 Processor evalProcessor = new EvaluateExpressionProcessor(expression);
150 evalProcessor = super.wrapInErrorHandler(routeContext, evalProcessor);
151
152 pipe.add(evalProcessor);
153 pipe.add(answer);
154
155 // wrap in nested pipeline so this appears as one processor
156 // (threads definition does this as well)
157 return new Pipeline(routeContext.getCamelContext(), pipe) {
158 @Override
159 public String toString() {
160 return "RecipientList[" + expression + "]";
161 }
162 };
163 }
164
165 private AggregationStrategy createAggregationStrategy(RouteContext routeContext) {
166 if (aggregationStrategy == null && strategyRef != null) {
167 aggregationStrategy = routeContext.lookup(strategyRef, AggregationStrategy.class);
168 }
169 if (aggregationStrategy == null) {
170 // fallback to use latest
171 aggregationStrategy = new UseLatestAggregationStrategy();
172 }
173 return aggregationStrategy;
174 }
175
176 // Fluent API
177 // -------------------------------------------------------------------------
178
179 @Override
180 @SuppressWarnings("unchecked")
181 public Type end() {
182 // allow end() to return to previous type so you can continue in the DSL
183 return (Type) super.end();
184 }
185
186 /**
187 * Set the aggregationStrategy
188 *
189 * @param aggregationStrategy the strategy
190 * @return the builder
191 */
192 public RecipientListDefinition<Type> aggregationStrategy(AggregationStrategy aggregationStrategy) {
193 setAggregationStrategy(aggregationStrategy);
194 return this;
195 }
196
197 /**
198 * Set the aggregationStrategy
199 *
200 * @param aggregationStrategyRef a reference to a strategy to lookup
201 * @return the builder
202 */
203 public RecipientListDefinition<Type> aggregationStrategyRef(String aggregationStrategyRef) {
204 setStrategyRef(aggregationStrategyRef);
205 return this;
206 }
207
208 /**
209 * Ignore the invalidate endpoint exception when try to create a producer with that endpoint
210 *
211 * @return the builder
212 */
213 public RecipientListDefinition<Type> ignoreInvalidEndpoints() {
214 setIgnoreInvalidEndpoints(true);
215 return this;
216 }
217
218 /**
219 * Doing the recipient list work in parallel
220 *
221 * @return the builder
222 */
223 public RecipientListDefinition<Type> parallelProcessing() {
224 setParallelProcessing(true);
225 return this;
226 }
227
228 /**
229 * Doing the recipient list work in streaming model
230 *
231 * @return the builder
232 */
233 public RecipientListDefinition<Type> streaming() {
234 setStreaming(true);
235 return this;
236 }
237
238 /**
239 * Will now stop further processing if an exception or failure occurred during processing of an
240 * {@link org.apache.camel.Exchange} and the caused exception will be thrown.
241 * <p/>
242 * Will also stop if processing the exchange failed (has a fault message) or an exception
243 * was thrown and handled by the error handler (such as using onException). In all situations
244 * the recipient list will stop further processing. This is the same behavior as in pipeline, which
245 * is used by the routing engine.
246 * <p/>
247 * The default behavior is to <b>not</b> stop but continue processing till the end
248 *
249 * @return the builder
250 */
251 public RecipientListDefinition<Type> stopOnException() {
252 setStopOnException(true);
253 return this;
254 }
255
256 public RecipientListDefinition<Type> executorService(ExecutorService executorService) {
257 setExecutorService(executorService);
258 return this;
259 }
260
261 public RecipientListDefinition<Type> executorServiceRef(String executorServiceRef) {
262 setExecutorServiceRef(executorServiceRef);
263 return this;
264 }
265
266 /**
267 * Uses the {@link Processor} when preparing the {@link org.apache.camel.Exchange} to be used send.
268 * This can be used to deep-clone messages that should be send, or any custom logic needed before
269 * the exchange is send.
270 *
271 * @param onPrepare the processor
272 * @return the builder
273 */
274 public RecipientListDefinition<Type> onPrepare(Processor onPrepare) {
275 setOnPrepare(onPrepare);
276 return this;
277 }
278
279 /**
280 * Uses the {@link Processor} when preparing the {@link org.apache.camel.Exchange} to be send.
281 * This can be used to deep-clone messages that should be send, or any custom logic needed before
282 * the exchange is send.
283 *
284 * @param onPrepareRef reference to the processor to lookup in the {@link org.apache.camel.spi.Registry}
285 * @return the builder
286 */
287 public RecipientListDefinition<Type> onPrepareRef(String onPrepareRef) {
288 setOnPrepareRef(onPrepareRef);
289 return this;
290 }
291
292 /**
293 * Sets a timeout value in millis to use when using parallelProcessing.
294 *
295 * @param timeout timeout in millis
296 * @return the builder
297 */
298 public RecipientListDefinition<Type> timeout(long timeout) {
299 setTimeout(timeout);
300 return this;
301 }
302
303 /**
304 * Shares the {@link org.apache.camel.spi.UnitOfWork} with the parent and each of the sub messages.
305 *
306 * @return the builder.
307 * @see org.apache.camel.spi.SubUnitOfWork
308 */
309 public RecipientListDefinition<Type> shareUnitOfWork() {
310 setShareUnitOfWork(true);
311 return this;
312 }
313
314 // Properties
315 //-------------------------------------------------------------------------
316
317 public String getDelimiter() {
318 return delimiter;
319 }
320
321 public void setDelimiter(String delimiter) {
322 this.delimiter = delimiter;
323 }
324
325 public Boolean getParallelProcessing() {
326 return parallelProcessing;
327 }
328
329 public void setParallelProcessing(Boolean parallelProcessing) {
330 this.parallelProcessing = parallelProcessing;
331 }
332
333 public boolean isParallelProcessing() {
334 return parallelProcessing != null && parallelProcessing;
335 }
336
337 public String getStrategyRef() {
338 return strategyRef;
339 }
340
341 public void setStrategyRef(String strategyRef) {
342 this.strategyRef = strategyRef;
343 }
344
345 public String getExecutorServiceRef() {
346 return executorServiceRef;
347 }
348
349 public void setExecutorServiceRef(String executorServiceRef) {
350 this.executorServiceRef = executorServiceRef;
351 }
352
353 public Boolean getIgnoreInvalidEndpoints() {
354 return ignoreInvalidEndpoints;
355 }
356
357 public void setIgnoreInvalidEndpoints(Boolean ignoreInvalidEndpoints) {
358 this.ignoreInvalidEndpoints = ignoreInvalidEndpoints;
359 }
360
361 public boolean isIgnoreInvalidEndpoints() {
362 return ignoreInvalidEndpoints != null && ignoreInvalidEndpoints;
363 }
364
365 public Boolean getStopOnException() {
366 return stopOnException;
367 }
368
369 public void setStopOnException(Boolean stopOnException) {
370 this.stopOnException = stopOnException;
371 }
372
373 public boolean isStopOnException() {
374 return stopOnException != null && stopOnException;
375 }
376
377 public AggregationStrategy getAggregationStrategy() {
378 return aggregationStrategy;
379 }
380
381 public void setAggregationStrategy(AggregationStrategy aggregationStrategy) {
382 this.aggregationStrategy = aggregationStrategy;
383 }
384
385 public ExecutorService getExecutorService() {
386 return executorService;
387 }
388
389 public void setExecutorService(ExecutorService executorService) {
390 this.executorService = executorService;
391 }
392
393 public Boolean getStreaming() {
394 return streaming;
395 }
396
397 public void setStreaming(Boolean streaming) {
398 this.streaming = streaming;
399 }
400
401 public boolean isStreaming() {
402 return streaming != null && streaming;
403 }
404
405 public Long getTimeout() {
406 return timeout;
407 }
408
409 public void setTimeout(Long timeout) {
410 this.timeout = timeout;
411 }
412
413 public String getOnPrepareRef() {
414 return onPrepareRef;
415 }
416
417 public void setOnPrepareRef(String onPrepareRef) {
418 this.onPrepareRef = onPrepareRef;
419 }
420
421 public Processor getOnPrepare() {
422 return onPrepare;
423 }
424
425 public void setOnPrepare(Processor onPrepare) {
426 this.onPrepare = onPrepare;
427 }
428
429 public Boolean getShareUnitOfWork() {
430 return shareUnitOfWork;
431 }
432
433 public void setShareUnitOfWork(Boolean shareUnitOfWork) {
434 this.shareUnitOfWork = shareUnitOfWork;
435 }
436
437 public boolean isShareUnitOfWork() {
438 return shareUnitOfWork != null && shareUnitOfWork;
439 }
440
441 }