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.List;
021
022import javax.xml.bind.annotation.XmlAccessType;
023import javax.xml.bind.annotation.XmlAccessorType;
024import javax.xml.bind.annotation.XmlAttribute;
025import javax.xml.bind.annotation.XmlRootElement;
026
027import org.apache.camel.ExchangePattern;
028import org.apache.camel.Expression;
029import org.apache.camel.NoSuchLanguageException;
030import org.apache.camel.Processor;
031import org.apache.camel.builder.ExpressionBuilder;
032import org.apache.camel.processor.SendDynamicProcessor;
033import org.apache.camel.spi.Language;
034import org.apache.camel.spi.Metadata;
035import org.apache.camel.spi.RouteContext;
036import org.apache.camel.util.ObjectHelper;
037import org.apache.camel.util.Pair;
038import org.apache.camel.util.URISupport;
039
040/**
041 * Sends the message to a dynamic endpoint
042 * <p/>
043 * You can specify multiple languages in the uri separated by the plus sign, such as <tt>mock:+language:xpath:/order/@uri</tt>
044 * where <tt>mock:</tt> would be a prefix to a xpath expression.
045 * <p/>
046 * For more dynamic behavior use <a href="http://camel.apache.org/recipient-list.html">Recipient List</a> or
047 * <a href="http://camel.apache.org/dynamic-router.html">Dynamic Router</a> EIP instead.
048 */
049@Metadata(label = "eip,endpoint,routing")
050@XmlRootElement(name = "toD")
051@XmlAccessorType(XmlAccessType.FIELD)
052public class ToDynamicDefinition extends NoOutputDefinition<ToDynamicDefinition> {
053
054    @XmlAttribute @Metadata(required = "true")
055    private String uri;
056    @XmlAttribute
057    private ExchangePattern pattern;
058    @XmlAttribute
059    private Integer cacheSize;
060    @XmlAttribute
061    private Boolean ignoreInvalidEndpoint;
062
063    public ToDynamicDefinition() {
064    }
065
066    public ToDynamicDefinition(String uri) {
067        this.uri = uri;
068    }
069
070    @Override
071    public Processor createProcessor(RouteContext routeContext) throws Exception {
072        ObjectHelper.notEmpty(uri, "uri", this);
073
074        Expression exp = createExpression(routeContext);
075
076        SendDynamicProcessor processor = new SendDynamicProcessor(uri, exp);
077        processor.setCamelContext(routeContext.getCamelContext());
078        processor.setPattern(pattern);
079        if (cacheSize != null) {
080            processor.setCacheSize(cacheSize);
081        }
082        if (ignoreInvalidEndpoint != null) {
083            processor.setIgnoreInvalidEndpoint(ignoreInvalidEndpoint);
084        }
085        return processor;
086    }
087
088    protected Expression createExpression(RouteContext routeContext) {
089        List<Expression> list = new ArrayList<Expression>();
090
091        String[] parts = safeSplitRaw(uri);
092        for (String part : parts) {
093            // the part may have optional language to use, so you can mix languages
094            String value = ObjectHelper.after(part, "language:");
095            if (value != null) {
096                String before = ObjectHelper.before(value, ":");
097                String after = ObjectHelper.after(value, ":");
098                if (before != null && after != null) {
099                    // maybe its a language, must have language: as prefix
100                    try {
101                        Language partLanguage = routeContext.getCamelContext().resolveLanguage(before);
102                        if (partLanguage != null) {
103                            Expression exp = partLanguage.createExpression(after);
104                            list.add(exp);
105                            continue;
106                        }
107                    } catch (NoSuchLanguageException e) {
108                        // ignore
109                    }
110                }
111            }
112            // fallback and use simple language
113            Language lan = routeContext.getCamelContext().resolveLanguage("simple");
114            Expression exp = lan.createExpression(part);
115            list.add(exp);
116        }
117
118        Expression exp;
119        if (list.size() == 1) {
120            exp = list.get(0);
121        } else {
122            exp = ExpressionBuilder.concatExpression(list);
123        }
124
125        return exp;
126    }
127
128    @Override
129    public String toString() {
130        return "DynamicTo[" + getLabel() + "]";
131    }
132
133    // Fluent API
134    // -------------------------------------------------------------------------
135
136    /**
137     * Sets the optional {@link ExchangePattern} used to invoke this endpoint
138     */
139    public ToDynamicDefinition pattern(ExchangePattern pattern) {
140        setPattern(pattern);
141        return this;
142    }
143
144    /**
145     * Sets the maximum size used by the {@link org.apache.camel.impl.ConsumerCache} which is used to cache and reuse producers.
146     *
147     * @param cacheSize  the cache size, use <tt>0</tt> for default cache size, or <tt>-1</tt> to turn cache off.
148     * @return the builder
149     */
150    public ToDynamicDefinition cacheSize(int cacheSize) {
151        setCacheSize(cacheSize);
152        return this;
153    }
154
155    /**
156     * Ignore the invalidate endpoint exception when try to create a producer with that endpoint
157     *
158     * @return the builder
159     */
160    public ToDynamicDefinition ignoreInvalidEndpoint() {
161        setIgnoreInvalidEndpoint(true);
162        return this;
163    }
164
165    // Properties
166    // -------------------------------------------------------------------------
167
168    public String getUri() {
169        return uri;
170    }
171
172    /**
173     * The uri of the endpoint to send to. The uri can be dynamic computed using the {@link org.apache.camel.language.simple.SimpleLanguage} expression.
174     */
175    public void setUri(String uri) {
176        this.uri = uri;
177    }
178
179    public ExchangePattern getPattern() {
180        return pattern;
181    }
182
183    public void setPattern(ExchangePattern pattern) {
184        this.pattern = pattern;
185    }
186
187    public Integer getCacheSize() {
188        return cacheSize;
189    }
190
191    public void setCacheSize(Integer cacheSize) {
192        this.cacheSize = cacheSize;
193    }
194
195    public Boolean getIgnoreInvalidEndpoint() {
196        return ignoreInvalidEndpoint;
197    }
198
199    public void setIgnoreInvalidEndpoint(Boolean ignoreInvalidEndpoint) {
200        this.ignoreInvalidEndpoint = ignoreInvalidEndpoint;
201    }
202
203    // Utilities
204    // -------------------------------------------------------------------------
205
206    /**
207     * We need to split the string safely for each + sign, but avoid splitting within RAW(...).
208     */
209    private static String[] safeSplitRaw(String s) {
210        List<String> list = new ArrayList<>();
211
212        if (!s.contains("+")) {
213            // no plus sign so there is only one part, so no need to split
214            list.add(s);
215        } else {
216            // there is a plus sign so we need to split in a safe manner
217            List<Pair<Integer>> rawPairs = URISupport.scanRaw(s);
218            StringBuilder sb = new StringBuilder();
219            char chars[] = s.toCharArray();
220            for (int i = 0; i < chars.length; i++) {
221                char ch = chars[i];
222                if (ch != '+' || URISupport.isRaw(i, rawPairs)) {
223                    sb.append(ch);
224                } else {
225                    list.add(sb.toString());
226                    sb.setLength(0);
227                }
228            }
229            // any leftover?
230            if (sb.length() > 0) {
231                list.add(sb.toString());
232                sb.setLength(0);
233            }
234        }
235
236        return list.toArray(new String[list.size()]);
237    }
238
239}