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}