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 */ 017 package org.apache.camel.util; 018 019 import java.util.ArrayList; 020 import java.util.Arrays; 021 import java.util.Collections; 022 import java.util.Iterator; 023 import java.util.List; 024 import java.util.Map; 025 import java.util.concurrent.atomic.AtomicLong; 026 import java.util.regex.PatternSyntaxException; 027 028 import org.apache.camel.CamelContext; 029 import org.apache.camel.Endpoint; 030 import org.apache.camel.Exchange; 031 import org.apache.camel.Message; 032 import org.apache.camel.PollingConsumer; 033 import org.apache.camel.Processor; 034 import org.apache.camel.ResolveEndpointFailedException; 035 import org.apache.camel.Route; 036 import org.apache.camel.spi.BrowsableEndpoint; 037 import org.slf4j.Logger; 038 import org.slf4j.LoggerFactory; 039 040 /** 041 * Some helper methods for working with {@link Endpoint} instances 042 * 043 * @version 044 */ 045 public final class EndpointHelper { 046 047 private static final Logger LOG = LoggerFactory.getLogger(EndpointHelper.class); 048 private static final AtomicLong ENDPOINT_COUNTER = new AtomicLong(0); 049 050 private EndpointHelper() { 051 //Utility Class 052 } 053 054 /** 055 * Creates a {@link PollingConsumer} and polls all pending messages on the endpoint 056 * and invokes the given {@link Processor} to process each {@link Exchange} and then closes 057 * down the consumer and throws any exceptions thrown. 058 */ 059 public static void pollEndpoint(Endpoint endpoint, Processor processor, long timeout) throws Exception { 060 PollingConsumer consumer = endpoint.createPollingConsumer(); 061 try { 062 consumer.start(); 063 064 while (true) { 065 Exchange exchange = consumer.receive(timeout); 066 if (exchange == null) { 067 break; 068 } else { 069 processor.process(exchange); 070 } 071 } 072 } finally { 073 try { 074 consumer.stop(); 075 } catch (Exception e) { 076 LOG.warn("Failed to stop PollingConsumer: " + e, e); 077 } 078 } 079 } 080 081 /** 082 * Creates a {@link PollingConsumer} and polls all pending messages on the 083 * endpoint and invokes the given {@link Processor} to process each 084 * {@link Exchange} and then closes down the consumer and throws any 085 * exceptions thrown. 086 */ 087 public static void pollEndpoint(Endpoint endpoint, Processor processor) throws Exception { 088 pollEndpoint(endpoint, processor, 1000L); 089 } 090 091 /** 092 * Matches the endpoint with the given pattern. 093 * <p/> 094 * The endpoint will first resolve property placeholders using {@link CamelContext#resolvePropertyPlaceholders(String)}. 095 * <p/> 096 * The match rules are applied in this order: 097 * <ul> 098 * <li>exact match, returns true</li> 099 * <li>wildcard match (pattern ends with a * and the uri starts with the pattern), returns true</li> 100 * <li>regular expression match, returns true</li> 101 * <li>otherwise returns false</li> 102 * </ul> 103 * 104 * @param context the Camel context, if <tt>null</tt> then property placeholder resolution is skipped. 105 * @param uri the endpoint uri 106 * @param pattern a pattern to match 107 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 108 */ 109 public static boolean matchEndpoint(CamelContext context, String uri, String pattern) { 110 if (context != null) { 111 try { 112 uri = context.resolvePropertyPlaceholders(uri); 113 } catch (Exception e) { 114 throw new ResolveEndpointFailedException(uri, e); 115 } 116 } 117 118 // normalize uri so we can do endpoint hits with minor mistakes and parameters is not in the same order 119 try { 120 uri = URISupport.normalizeUri(uri); 121 } catch (Exception e) { 122 throw new ResolveEndpointFailedException(uri, e); 123 } 124 125 // we need to test with and without scheme separators (//) 126 if (uri.contains("://")) { 127 // try without :// also 128 String scheme = ObjectHelper.before(uri, "://"); 129 String path = ObjectHelper.after(uri, "://"); 130 if (matchPattern(scheme + ":" + path, pattern)) { 131 return true; 132 } 133 } else { 134 // try with :// also 135 String scheme = ObjectHelper.before(uri, ":"); 136 String path = ObjectHelper.after(uri, ":"); 137 if (matchPattern(scheme + "://" + path, pattern)) { 138 return true; 139 } 140 } 141 142 // and fallback to test with the uri as is 143 return matchPattern(uri, pattern); 144 } 145 146 /** 147 * Matches the endpoint with the given pattern. 148 * @see #matchEndpoint(org.apache.camel.CamelContext, String, String) 149 * 150 * @deprecated use {@link #matchEndpoint(org.apache.camel.CamelContext, String, String)} instead. 151 */ 152 @Deprecated 153 public static boolean matchEndpoint(String uri, String pattern) { 154 return matchEndpoint(null, uri, pattern); 155 } 156 157 /** 158 * Matches the name with the given pattern. 159 * <p/> 160 * The match rules are applied in this order: 161 * <ul> 162 * <li>exact match, returns true</li> 163 * <li>wildcard match (pattern ends with a * and the name starts with the pattern), returns true</li> 164 * <li>regular expression match, returns true</li> 165 * <li>otherwise returns false</li> 166 * </ul> 167 * 168 * @param name the name 169 * @param pattern a pattern to match 170 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 171 */ 172 public static boolean matchPattern(String name, String pattern) { 173 if (name == null || pattern == null) { 174 return false; 175 } 176 177 if (name.equals(pattern)) { 178 // exact match 179 return true; 180 } 181 182 if (matchWildcard(name, pattern)) { 183 return true; 184 } 185 186 if (matchRegex(name, pattern)) { 187 return true; 188 } 189 190 // no match 191 return false; 192 } 193 194 /** 195 * Matches the name with the given pattern. 196 * <p/> 197 * The match rules are applied in this order: 198 * <ul> 199 * <li>wildcard match (pattern ends with a * and the name starts with the pattern), returns true</li> 200 * <li>otherwise returns false</li> 201 * </ul> 202 * 203 * @param name the name 204 * @param pattern a pattern to match 205 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 206 */ 207 private static boolean matchWildcard(String name, String pattern) { 208 // we have wildcard support in that hence you can match with: file* to match any file endpoints 209 if (pattern.endsWith("*") && name.startsWith(pattern.substring(0, pattern.length() - 1))) { 210 return true; 211 } 212 return false; 213 } 214 215 /** 216 * Matches the name with the given pattern. 217 * <p/> 218 * The match rules are applied in this order: 219 * <ul> 220 * <li>regular expression match, returns true</li> 221 * <li>otherwise returns false</li> 222 * </ul> 223 * 224 * @param name the name 225 * @param pattern a pattern to match 226 * @return <tt>true</tt> if match, <tt>false</tt> otherwise. 227 */ 228 private static boolean matchRegex(String name, String pattern) { 229 // match by regular expression 230 try { 231 if (name.matches(pattern)) { 232 return true; 233 } 234 } catch (PatternSyntaxException e) { 235 // ignore 236 } 237 return false; 238 } 239 240 /** 241 * Sets the regular properties on the given bean 242 * 243 * @param context the camel context 244 * @param bean the bean 245 * @param parameters parameters 246 * @throws Exception is thrown if setting property fails 247 */ 248 public static void setProperties(CamelContext context, Object bean, Map<String, Object> parameters) throws Exception { 249 IntrospectionSupport.setProperties(context.getTypeConverter(), bean, parameters); 250 } 251 252 /** 253 * Sets the reference properties on the given bean 254 * <p/> 255 * This is convention over configuration, setting all reference parameters (using {@link #isReferenceParameter(String)} 256 * by looking it up in registry and setting it on the bean if possible. 257 * 258 * @param context the camel context 259 * @param bean the bean 260 * @param parameters parameters 261 * @throws Exception is thrown if setting property fails 262 */ 263 public static void setReferenceProperties(CamelContext context, Object bean, Map<String, Object> parameters) throws Exception { 264 Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator(); 265 while (it.hasNext()) { 266 Map.Entry<String, Object> entry = it.next(); 267 String name = entry.getKey(); 268 Object v = entry.getValue(); 269 String value = v != null ? v.toString() : null; 270 if (value != null && isReferenceParameter(value)) { 271 boolean hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), bean, name, null, value, true); 272 if (hit) { 273 // must remove as its a valid option and we could configure it 274 it.remove(); 275 } 276 } 277 } 278 } 279 280 /** 281 * Is the given parameter a reference parameter (starting with a # char) 282 * 283 * @param parameter the parameter 284 * @return <tt>true</tt> if its a reference parameter 285 */ 286 public static boolean isReferenceParameter(String parameter) { 287 return parameter != null && parameter.trim().startsWith("#"); 288 } 289 290 /** 291 * Resolves a reference parameter by making a lookup in the registry. 292 * 293 * @param <T> type of object to lookup. 294 * @param context Camel context to use for lookup. 295 * @param value reference parameter value. 296 * @param type type of object to lookup. 297 * @return lookup result. 298 * @throws IllegalArgumentException if referenced object was not found in registry. 299 */ 300 public static <T> T resolveReferenceParameter(CamelContext context, String value, Class<T> type) { 301 return resolveReferenceParameter(context, value, type, true); 302 } 303 304 /** 305 * Resolves a reference parameter by making a lookup in the registry. 306 * 307 * @param <T> type of object to lookup. 308 * @param context Camel context to use for lookup. 309 * @param value reference parameter value. 310 * @param type type of object to lookup. 311 * @return lookup result (or <code>null</code> only if 312 * <code>mandatory</code> is <code>false</code>). 313 * @throws IllegalArgumentException if object was not found in registry and 314 * <code>mandatory</code> is <code>true</code>. 315 */ 316 public static <T> T resolveReferenceParameter(CamelContext context, String value, Class<T> type, boolean mandatory) { 317 String valueNoHash = value.replaceAll("#", ""); 318 if (mandatory) { 319 return CamelContextHelper.mandatoryLookup(context, valueNoHash, type); 320 } else { 321 return CamelContextHelper.lookup(context, valueNoHash, type); 322 } 323 } 324 325 /** 326 * Resolves a reference list parameter by making lookups in the registry. 327 * The parameter value must be one of the following: 328 * <ul> 329 * <li>a comma-separated list of references to beans of type T</li> 330 * <li>a single reference to a bean type T</li> 331 * <li>a single reference to a bean of type java.util.List</li> 332 * </ul> 333 * 334 * @param context Camel context to use for lookup. 335 * @param value reference parameter value. 336 * @param elementType result list element type. 337 * @return list of lookup results. 338 * @throws IllegalArgumentException if any referenced object was not found in registry. 339 */ 340 @SuppressWarnings({"unchecked", "rawtypes"}) 341 public static <T> List<T> resolveReferenceListParameter(CamelContext context, String value, Class<T> elementType) { 342 if (value == null) { 343 return Collections.emptyList(); 344 } 345 List<String> elements = Arrays.asList(value.split(",")); 346 if (elements.size() == 1) { 347 Object bean = resolveReferenceParameter(context, elements.get(0).trim(), Object.class); 348 if (bean instanceof List) { 349 // The bean is a list 350 return (List) bean; 351 } else { 352 // The bean is a list element 353 return Arrays.asList(elementType.cast(bean)); 354 } 355 } else { // more than one list element 356 List<T> result = new ArrayList<T>(elements.size()); 357 for (String element : elements) { 358 result.add(resolveReferenceParameter(context, element.trim(), elementType)); 359 } 360 return result; 361 } 362 } 363 364 /** 365 * Gets the route id for the given endpoint in which there is a consumer listening. 366 * 367 * @param endpoint the endpoint 368 * @return the route id, or <tt>null</tt> if none found 369 */ 370 public static String getRouteIdFromEndpoint(Endpoint endpoint) { 371 if (endpoint == null || endpoint.getCamelContext() == null) { 372 return null; 373 } 374 375 List<Route> routes = endpoint.getCamelContext().getRoutes(); 376 for (Route route : routes) { 377 if (route.getEndpoint().equals(endpoint) 378 || route.getEndpoint().getEndpointKey().equals(endpoint.getEndpointKey())) { 379 return route.getId(); 380 } 381 } 382 return null; 383 } 384 385 /** 386 * A helper method for Endpoint implementations to create new Ids for Endpoints which also implement 387 * {@link org.apache.camel.spi.HasId} 388 */ 389 public static String createEndpointId() { 390 return "endpoint" + ENDPOINT_COUNTER.incrementAndGet(); 391 } 392 393 /** 394 * Lookup the id the given endpoint has been enlisted with in the {@link org.apache.camel.spi.Registry}. 395 * 396 * @param endpoint the endpoint 397 * @return the endpoint id, or <tt>null</tt> if not found 398 */ 399 public static String lookupEndpointRegistryId(Endpoint endpoint) { 400 if (endpoint == null || endpoint.getCamelContext() == null) { 401 return null; 402 } 403 404 Map<String, Endpoint> map = endpoint.getCamelContext().getRegistry().findByTypeWithName(Endpoint.class); 405 for (Map.Entry<String, Endpoint> entry : map.entrySet()) { 406 if (entry.getValue().equals(endpoint)) { 407 return entry.getKey(); 408 } 409 } 410 411 // not found 412 return null; 413 } 414 415 /** 416 * Browses the {@link BrowsableEndpoint} within the given range, and returns the messages as a XML payload. 417 * 418 * @param endpoint the browsable endpoint 419 * @param fromIndex from range 420 * @param toIndex to range 421 * @param includeBody whether to include the message body in the XML payload 422 * @return XML payload with the messages 423 * @throws IllegalArgumentException if the from and to range is invalid 424 * @see MessageHelper#dumpAsXml(org.apache.camel.Message) 425 */ 426 public static String browseRangeMessagesAsXml(BrowsableEndpoint endpoint, Integer fromIndex, Integer toIndex, Boolean includeBody) { 427 if (fromIndex == null) { 428 fromIndex = 0; 429 } 430 if (toIndex == null) { 431 toIndex = Integer.MAX_VALUE; 432 } 433 if (fromIndex > toIndex) { 434 throw new IllegalArgumentException("From index cannot be larger than to index, was: " + fromIndex + " > " + toIndex); 435 } 436 437 List<Exchange> exchanges = endpoint.getExchanges(); 438 if (exchanges.size() == 0) { 439 return null; 440 } 441 442 StringBuilder sb = new StringBuilder(); 443 sb.append("<messages>"); 444 for (int i = fromIndex; i < exchanges.size() && i <= toIndex; i++) { 445 Exchange exchange = exchanges.get(i); 446 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 447 String xml = MessageHelper.dumpAsXml(msg, includeBody); 448 sb.append("\n").append(xml); 449 } 450 sb.append("\n</messages>"); 451 return sb.toString(); 452 } 453 454 }