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