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 transient 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.indexOf("://") != -1) { 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 @SuppressWarnings("unchecked") 264 public static void setReferenceProperties(CamelContext context, Object bean, Map<String, Object> parameters) throws Exception { 265 Iterator<Map.Entry<String, Object>> it = parameters.entrySet().iterator(); 266 while (it.hasNext()) { 267 Map.Entry<String, Object> entry = it.next(); 268 String name = entry.getKey(); 269 Object v = entry.getValue(); 270 String value = v != null ? v.toString() : null; 271 if (value != null && isReferenceParameter(value)) { 272 boolean hit = IntrospectionSupport.setProperty(context, context.getTypeConverter(), bean, name, null, value, true); 273 if (hit) { 274 // must remove as its a valid option and we could configure it 275 it.remove(); 276 } 277 } 278 } 279 } 280 281 /** 282 * Is the given parameter a reference parameter (starting with a # char) 283 * 284 * @param parameter the parameter 285 * @return <tt>true</tt> if its a reference parameter 286 */ 287 public static boolean isReferenceParameter(String parameter) { 288 return parameter != null && parameter.trim().startsWith("#"); 289 } 290 291 /** 292 * Resolves a reference parameter by making a lookup in the registry. 293 * 294 * @param <T> type of object to lookup. 295 * @param context Camel context to use for lookup. 296 * @param value reference parameter value. 297 * @param type type of object to lookup. 298 * @return lookup result. 299 * @throws IllegalArgumentException if referenced object was not found in registry. 300 */ 301 public static <T> T resolveReferenceParameter(CamelContext context, String value, Class<T> type) { 302 return resolveReferenceParameter(context, value, type, true); 303 } 304 305 /** 306 * Resolves a reference parameter by making a lookup in the registry. 307 * 308 * @param <T> type of object to lookup. 309 * @param context Camel context to use for lookup. 310 * @param value reference parameter value. 311 * @param type type of object to lookup. 312 * @return lookup result (or <code>null</code> only if 313 * <code>mandatory</code> is <code>false</code>). 314 * @throws IllegalArgumentException if object was not found in registry and 315 * <code>mandatory</code> is <code>true</code>. 316 */ 317 public static <T> T resolveReferenceParameter(CamelContext context, String value, Class<T> type, boolean mandatory) { 318 String valueNoHash = value.replaceAll("#", ""); 319 if (mandatory) { 320 return CamelContextHelper.mandatoryLookup(context, valueNoHash, type); 321 } else { 322 return CamelContextHelper.lookup(context, valueNoHash, type); 323 } 324 } 325 326 /** 327 * Resolves a reference list parameter by making lookups in the registry. 328 * The parameter value must be one of the following: 329 * <ul> 330 * <li>a comma-separated list of references to beans of type T</li> 331 * <li>a single reference to a bean type T</li> 332 * <li>a single reference to a bean of type java.util.List</li> 333 * </ul> 334 * 335 * @param context Camel context to use for lookup. 336 * @param value reference parameter value. 337 * @param elementType result list element type. 338 * @return list of lookup results. 339 * @throws IllegalArgumentException if any referenced object was not found in registry. 340 */ 341 @SuppressWarnings({"unchecked", "rawtypes"}) 342 public static <T> List<T> resolveReferenceListParameter(CamelContext context, String value, Class<T> elementType) { 343 if (value == null) { 344 return Collections.emptyList(); 345 } 346 List<String> elements = Arrays.asList(value.split(",")); 347 if (elements.size() == 1) { 348 Object bean = resolveReferenceParameter(context, elements.get(0).trim(), Object.class); 349 if (bean instanceof List) { 350 // The bean is a list 351 return (List) bean; 352 } else { 353 // The bean is a list element 354 return Arrays.asList(elementType.cast(bean)); 355 } 356 } else { // more than one list element 357 List<T> result = new ArrayList<T>(elements.size()); 358 for (String element : elements) { 359 result.add(resolveReferenceParameter(context, element.trim(), elementType)); 360 } 361 return result; 362 } 363 } 364 365 /** 366 * Gets the route id for the given endpoint in which there is a consumer listening. 367 * 368 * @param endpoint the endpoint 369 * @return the route id, or <tt>null</tt> if none found 370 */ 371 public static String getRouteIdFromEndpoint(Endpoint endpoint) { 372 if (endpoint == null || endpoint.getCamelContext() == null) { 373 return null; 374 } 375 376 List<Route> routes = endpoint.getCamelContext().getRoutes(); 377 for (Route route : routes) { 378 if (route.getEndpoint().equals(endpoint) 379 || route.getEndpoint().getEndpointKey().equals(endpoint.getEndpointKey())) { 380 return route.getId(); 381 } 382 } 383 return null; 384 } 385 386 /** 387 * A helper method for Endpoint implementations to create new Ids for Endpoints which also implement 388 * {@link org.apache.camel.spi.HasId} 389 */ 390 public static String createEndpointId() { 391 return "endpoint" + ENDPOINT_COUNTER.incrementAndGet(); 392 } 393 394 /** 395 * Lookup the id the given endpoint has been enlisted with in the {@link org.apache.camel.spi.Registry}. 396 * 397 * @param endpoint the endpoint 398 * @return the endpoint id, or <tt>null</tt> if not found 399 */ 400 public static String lookupEndpointRegistryId(Endpoint endpoint) { 401 if (endpoint == null || endpoint.getCamelContext() == null) { 402 return null; 403 } 404 405 Map<String, Endpoint> map = endpoint.getCamelContext().getRegistry().lookupByType(Endpoint.class); 406 for (Map.Entry<String, Endpoint> entry : map.entrySet()) { 407 if (entry.getValue().equals(endpoint)) { 408 return entry.getKey(); 409 } 410 } 411 412 // not found 413 return null; 414 } 415 416 /** 417 * Browses the {@link BrowsableEndpoint} within the given range, and returns the messages as a XML payload. 418 * 419 * @param endpoint the browsable endpoint 420 * @param fromIndex from range 421 * @param toIndex to range 422 * @param includeBody whether to include the message body in the XML payload 423 * @return XML payload with the messages 424 * @throws IllegalArgumentException if the from and to range is invalid 425 * @see MessageHelper#dumpAsXml(org.apache.camel.Message) 426 */ 427 public static String browseRangeMessagesAsXml(BrowsableEndpoint endpoint, Integer fromIndex, Integer toIndex, Boolean includeBody) { 428 if (fromIndex == null) { 429 fromIndex = 0; 430 } 431 if (toIndex == null) { 432 toIndex = Integer.MAX_VALUE; 433 } 434 if (fromIndex > toIndex) { 435 throw new IllegalArgumentException("From index cannot be larger than to index, was: " + fromIndex + " > " + toIndex); 436 } 437 438 List<Exchange> exchanges = endpoint.getExchanges(); 439 if (exchanges.size() == 0) { 440 return null; 441 } 442 443 StringBuilder sb = new StringBuilder(); 444 sb.append("<messages>"); 445 for (int i = fromIndex; i < exchanges.size() && i <= toIndex; i++) { 446 Exchange exchange = exchanges.get(i); 447 Message msg = exchange.hasOut() ? exchange.getOut() : exchange.getIn(); 448 String xml = MessageHelper.dumpAsXml(msg, includeBody); 449 sb.append("\n").append(xml); 450 } 451 sb.append("\n</messages>"); 452 return sb.toString(); 453 } 454 455 }