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.Collections;
020import java.util.List;
021import javax.xml.bind.annotation.XmlAccessType;
022import javax.xml.bind.annotation.XmlAccessorType;
023import javax.xml.bind.annotation.XmlAttribute;
024import javax.xml.bind.annotation.XmlRootElement;
025
026import org.apache.camel.AsyncProcessor;
027import org.apache.camel.ErrorHandlerFactory;
028import org.apache.camel.Exchange;
029import org.apache.camel.Expression;
030import org.apache.camel.Processor;
031import org.apache.camel.model.language.ExpressionDefinition;
032import org.apache.camel.processor.DynamicRouter;
033import org.apache.camel.processor.SendDynamicProcessor;
034import org.apache.camel.spi.Metadata;
035import org.apache.camel.spi.RouteContext;
036
037import static org.apache.camel.builder.ExpressionBuilder.headerExpression;
038
039/**
040 * Routes messages based on dynamic rules
041 */
042@Metadata(label = "eip,endpoint,routing")
043@XmlRootElement(name = "dynamicRouter")
044@XmlAccessorType(XmlAccessType.FIELD)
045public class DynamicRouterDefinition<Type extends ProcessorDefinition<Type>> extends NoOutputExpressionNode {
046
047    public static final String DEFAULT_DELIMITER = ",";
048
049    @XmlAttribute @Metadata(defaultValue = ",")
050    private String uriDelimiter;
051    @XmlAttribute
052    private Boolean ignoreInvalidEndpoints;
053    @XmlAttribute
054    private Integer cacheSize; 
055
056    public DynamicRouterDefinition() {
057    }
058
059    public DynamicRouterDefinition(Expression expression) {
060        super(expression);
061    }
062
063    @Override
064    public String toString() {
065        return "DynamicRouter[" + getExpression() + "]";
066    }
067    
068    @Override
069    public String getLabel() {
070        return "dynamicRouter[" + getExpression() + "]";
071    }
072
073    @Override
074    public List<ProcessorDefinition<?>> getOutputs() {
075        return Collections.emptyList();
076    }
077
078    @Override
079    public Processor createProcessor(RouteContext routeContext) throws Exception {
080        Expression expression = getExpression().createExpression(routeContext);
081        String delimiter = getUriDelimiter() != null ? getUriDelimiter() : DEFAULT_DELIMITER;
082
083        DynamicRouter dynamicRouter = new DynamicRouter(routeContext.getCamelContext(), expression, delimiter);
084        if (getIgnoreInvalidEndpoints() != null) {
085            dynamicRouter.setIgnoreInvalidEndpoints(getIgnoreInvalidEndpoints());
086        }
087        if (getCacheSize() != null) {
088            dynamicRouter.setCacheSize(getCacheSize());
089        }
090
091        // and wrap this in an error handler
092        ErrorHandlerFactory builder = routeContext.getRoute().getErrorHandlerBuilder();
093        // create error handler (create error handler directly to keep it light weight,
094        // instead of using ProcessorDefinition.wrapInErrorHandler)
095        AsyncProcessor errorHandler = (AsyncProcessor) builder.createErrorHandler(routeContext, dynamicRouter.newRoutingSlipProcessorForErrorHandler());
096        dynamicRouter.setErrorHandler(errorHandler);
097
098        return dynamicRouter;
099    }
100
101    /**
102     * Expression to call that returns the endpoint(s) to route to in the dynamic routing.
103     * <p/>
104     * <b>Important:</b> The expression will be called in a while loop fashion, until the expression returns <tt>null</tt>
105     * which means the dynamic router is finished.
106     */
107    @Override
108    public void setExpression(ExpressionDefinition expression) {
109        // override to include javadoc what the expression is used for
110        super.setExpression(expression);
111    }
112
113    public void setUriDelimiter(String uriDelimiter) {
114        this.uriDelimiter = uriDelimiter;
115    }
116
117    public String getUriDelimiter() {
118        return uriDelimiter;
119    }
120
121    public void setIgnoreInvalidEndpoints(Boolean ignoreInvalidEndpoints) {
122        this.ignoreInvalidEndpoints = ignoreInvalidEndpoints;
123    }
124
125    public Boolean getIgnoreInvalidEndpoints() {
126        return ignoreInvalidEndpoints;
127    }
128
129    // Fluent API
130    // -------------------------------------------------------------------------
131
132    public Integer getCacheSize() {
133        return cacheSize;
134    }
135
136    public void setCacheSize(Integer cacheSize) {
137        this.cacheSize = cacheSize;
138    }
139
140    @Override
141    @SuppressWarnings("unchecked")
142    public Type end() {
143        // allow end() to return to previous type so you can continue in the DSL
144        return (Type) super.end();
145    }
146
147    /**
148     * Ignore the invalidate endpoint exception when try to create a producer with that endpoint
149     *
150     * @return the builder
151     */
152    public DynamicRouterDefinition<Type> ignoreInvalidEndpoints() {
153        setIgnoreInvalidEndpoints(true);
154        return this;
155    }
156
157    /**
158     * Sets the uri delimiter to use
159     *
160     * @param uriDelimiter the delimiter
161     * @return the builder
162     */
163    public DynamicRouterDefinition<Type> uriDelimiter(String uriDelimiter) {
164        setUriDelimiter(uriDelimiter);
165        return this;
166    }
167    
168    /**
169     * Sets the maximum size used by the {@link org.apache.camel.impl.ProducerCache} which is used
170     * to cache and reuse producers when using this dynamic router, when uris are reused.
171     *
172     * @param cacheSize  the cache size, use <tt>0</tt> for default cache size, or <tt>-1</tt> to turn cache off.
173     * @return the builder
174     */
175    public DynamicRouterDefinition<Type> cacheSize(int cacheSize) {
176        setCacheSize(cacheSize);
177        return this;
178    }
179
180}