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