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.HashMap; 021import java.util.Iterator; 022import java.util.LinkedHashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.concurrent.ExecutorService; 027import java.util.concurrent.ScheduledExecutorService; 028import javax.xml.namespace.QName; 029 030import org.apache.camel.CamelContext; 031import org.apache.camel.Exchange; 032import org.apache.camel.spi.ExecutorServiceManager; 033import org.apache.camel.spi.RouteContext; 034import org.apache.camel.util.CamelContextHelper; 035import org.apache.camel.util.IntrospectionSupport; 036import org.apache.camel.util.ObjectHelper; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040/** 041 * Helper class for ProcessorDefinition and the other model classes. 042 */ 043public final class ProcessorDefinitionHelper { 044 045 private static final Logger LOG = LoggerFactory.getLogger(ProcessorDefinitionHelper.class); 046 047 private ProcessorDefinitionHelper() { 048 } 049 050 /** 051 * Looks for the given type in the list of outputs and recurring all the children as well. 052 * 053 * @param outputs list of outputs, can be null or empty. 054 * @param type the type to look for 055 * @return the found definitions, or <tt>null</tt> if not found 056 */ 057 public static <T> Iterator<T> filterTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type) { 058 return filterTypeInOutputs(outputs, type, -1); 059 } 060 061 /** 062 * Looks for the given type in the list of outputs and recurring all the children as well. 063 * 064 * @param outputs list of outputs, can be null or empty. 065 * @param type the type to look for 066 * @param maxDeep maximum levels deep to traverse 067 * @return the found definitions, or <tt>null</tt> if not found 068 */ 069 public static <T> Iterator<T> filterTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type, int maxDeep) { 070 List<T> found = new ArrayList<T>(); 071 doFindType(outputs, type, found, maxDeep); 072 return found.iterator(); 073 } 074 075 /** 076 * Looks for the given type in the list of outputs and recurring all the children as well. 077 * Will stop at first found and return it. 078 * 079 * @param outputs list of outputs, can be null or empty. 080 * @param type the type to look for 081 * @return the first found type, or <tt>null</tt> if not found 082 */ 083 public static <T> T findFirstTypeInOutputs(List<ProcessorDefinition<?>> outputs, Class<T> type) { 084 List<T> found = new ArrayList<T>(); 085 doFindType(outputs, type, found, -1); 086 if (found.isEmpty()) { 087 return null; 088 } 089 return found.iterator().next(); 090 } 091 092 /** 093 * Is the given child the first in the outputs from the parent? 094 * 095 * @param parentType the type the parent must be 096 * @param node the node 097 * @return <tt>true</tt> if first child, <tt>false</tt> otherwise 098 */ 099 public static boolean isFirstChildOfType(Class<?> parentType, ProcessorDefinition<?> node) { 100 if (node == null || node.getParent() == null) { 101 return false; 102 } 103 104 if (node.getParent().getOutputs().isEmpty()) { 105 return false; 106 } 107 108 if (!(node.getParent().getClass().equals(parentType))) { 109 return false; 110 } 111 112 return node.getParent().getOutputs().get(0).equals(node); 113 } 114 115 /** 116 * Is the given node parent(s) of the given type 117 * @param parentType the parent type 118 * @param node the current node 119 * @param recursive whether or not to check grand parent(s) as well 120 * @return <tt>true</tt> if parent(s) is of given type, <tt>false</tt> otherwise 121 */ 122 public static boolean isParentOfType(Class<?> parentType, ProcessorDefinition<?> node, boolean recursive) { 123 if (node == null || node.getParent() == null) { 124 return false; 125 } 126 127 if (parentType.isAssignableFrom(node.getParent().getClass())) { 128 return true; 129 } else if (recursive) { 130 // recursive up the tree of parents 131 return isParentOfType(parentType, node.getParent(), true); 132 } else { 133 // no match 134 return false; 135 } 136 } 137 138 /** 139 * Gets the route definition the given node belongs to. 140 * 141 * @param node the node 142 * @return the route, or <tt>null</tt> if not possible to find 143 */ 144 public static RouteDefinition getRoute(ProcessorDefinition<?> node) { 145 if (node == null) { 146 return null; 147 } 148 149 ProcessorDefinition<?> def = node; 150 // drill to the top 151 while (def != null && def.getParent() != null) { 152 def = def.getParent(); 153 } 154 155 if (def instanceof RouteDefinition) { 156 return (RouteDefinition) def; 157 } else { 158 // not found 159 return null; 160 } 161 } 162 163 /** 164 * Gets the route id the given node belongs to. 165 * 166 * @param node the node 167 * @return the route id, or <tt>null</tt> if not possible to find 168 */ 169 public static String getRouteId(ProcessorDefinition<?> node) { 170 RouteDefinition route = getRoute(node); 171 return route != null ? route.getId() : null; 172 } 173 174 /** 175 * Traverses the node, including its children (recursive), and gathers all the node ids. 176 * 177 * @param node the target node 178 * @param set set to store ids, if <tt>null</tt> a new set will be created 179 * @param onlyCustomId whether to only store custom assigned ids (ie. {@link org.apache.camel.model.OptionalIdentifiedDefinition#hasCustomIdAssigned()} 180 * @param includeAbstract whether to include abstract nodes (ie. {@link org.apache.camel.model.ProcessorDefinition#isAbstract()} 181 * @return the set with the found ids. 182 */ 183 public static Set<String> gatherAllNodeIds(ProcessorDefinition<?> node, Set<String> set, 184 boolean onlyCustomId, boolean includeAbstract) { 185 if (node == null) { 186 return set; 187 } 188 189 // skip abstract 190 if (node.isAbstract() && !includeAbstract) { 191 return set; 192 } 193 194 if (set == null) { 195 set = new LinkedHashSet<String>(); 196 } 197 198 // add ourselves 199 if (node.getId() != null) { 200 if (!onlyCustomId || node.hasCustomIdAssigned() && onlyCustomId) { 201 set.add(node.getId()); 202 } 203 } 204 205 // traverse outputs and recursive children as well 206 List<ProcessorDefinition<?>> children = node.getOutputs(); 207 if (children != null && !children.isEmpty()) { 208 for (ProcessorDefinition<?> child : children) { 209 // traverse children also 210 gatherAllNodeIds(child, set, onlyCustomId, includeAbstract); 211 } 212 } 213 214 return set; 215 } 216 217 private static <T> void doFindType(List<ProcessorDefinition<?>> outputs, Class<T> type, List<T> found, int maxDeep) { 218 219 // do we have any top level abstracts, then we should max deep one more level down 220 // as that is really what we want to traverse as well 221 if (maxDeep > 0) { 222 for (ProcessorDefinition<?> out : outputs) { 223 if (out.isAbstract() && out.isTopLevelOnly()) { 224 maxDeep = maxDeep + 1; 225 break; 226 } 227 } 228 } 229 230 // start from level 1 231 doFindType(outputs, type, found, 1, maxDeep); 232 } 233 234 @SuppressWarnings({"unchecked", "rawtypes"}) 235 private static <T> void doFindType(List<ProcessorDefinition<?>> outputs, Class<T> type, List<T> found, int current, int maxDeep) { 236 if (outputs == null || outputs.isEmpty()) { 237 return; 238 } 239 240 // break out 241 if (maxDeep > 0 && current > maxDeep) { 242 return; 243 } 244 245 for (ProcessorDefinition out : outputs) { 246 247 // send is much common 248 if (out instanceof SendDefinition) { 249 SendDefinition send = (SendDefinition) out; 250 List<ProcessorDefinition<?>> children = send.getOutputs(); 251 doFindType(children, type, found, ++current, maxDeep); 252 } 253 254 // special for choice 255 if (out instanceof ChoiceDefinition) { 256 ChoiceDefinition choice = (ChoiceDefinition) out; 257 258 // ensure to add ourself if we match also 259 if (type.isInstance(choice)) { 260 found.add((T)choice); 261 } 262 263 // only look at children if current < maxDeep (or max deep is disabled) 264 if (maxDeep < 0 || current < maxDeep) { 265 for (WhenDefinition when : choice.getWhenClauses()) { 266 if (type.isInstance(when)) { 267 found.add((T) when); 268 } 269 List<ProcessorDefinition<?>> children = when.getOutputs(); 270 doFindType(children, type, found, ++current, maxDeep); 271 } 272 273 // otherwise is optional 274 if (choice.getOtherwise() != null) { 275 List<ProcessorDefinition<?>> children = choice.getOtherwise().getOutputs(); 276 doFindType(children, type, found, ++current, maxDeep); 277 } 278 } 279 280 // do not check children as we already did that 281 continue; 282 } 283 284 // special for try ... catch ... finally 285 if (out instanceof TryDefinition) { 286 TryDefinition doTry = (TryDefinition) out; 287 288 // ensure to add ourself if we match also 289 if (type.isInstance(doTry)) { 290 found.add((T)doTry); 291 } 292 293 // only look at children if current < maxDeep (or max deep is disabled) 294 if (maxDeep < 0 || current < maxDeep) { 295 List<ProcessorDefinition<?>> doTryOut = doTry.getOutputsWithoutCatches(); 296 doFindType(doTryOut, type, found, ++current, maxDeep); 297 298 List<CatchDefinition> doTryCatch = doTry.getCatchClauses(); 299 for (CatchDefinition doCatch : doTryCatch) { 300 doFindType(doCatch.getOutputs(), type, found, ++current, maxDeep); 301 } 302 303 if (doTry.getFinallyClause() != null) { 304 doFindType(doTry.getFinallyClause().getOutputs(), type, found, ++current, maxDeep); 305 } 306 } 307 308 // do not check children as we already did that 309 continue; 310 } 311 312 // special for some types which has special outputs 313 if (out instanceof OutputDefinition) { 314 OutputDefinition outDef = (OutputDefinition) out; 315 316 // ensure to add ourself if we match also 317 if (type.isInstance(outDef)) { 318 found.add((T)outDef); 319 } 320 321 List<ProcessorDefinition<?>> outDefOut = outDef.getOutputs(); 322 doFindType(outDefOut, type, found, ++current, maxDeep); 323 324 // do not check children as we already did that 325 continue; 326 } 327 328 if (type.isInstance(out)) { 329 found.add((T)out); 330 } 331 332 // try children as well 333 List<ProcessorDefinition<?>> children = out.getOutputs(); 334 doFindType(children, type, found, ++current, maxDeep); 335 } 336 } 337 338 /** 339 * Is there any outputs in the given list. 340 * <p/> 341 * Is used for check if the route output has any real outputs (non abstracts) 342 * 343 * @param outputs the outputs 344 * @param excludeAbstract whether or not to exclude abstract outputs (e.g. skip onException etc.) 345 * @return <tt>true</tt> if has outputs, otherwise <tt>false</tt> is returned 346 */ 347 @SuppressWarnings({"unchecked", "rawtypes"}) 348 public static boolean hasOutputs(List<ProcessorDefinition<?>> outputs, boolean excludeAbstract) { 349 if (outputs == null || outputs.isEmpty()) { 350 return false; 351 } 352 if (!excludeAbstract) { 353 return !outputs.isEmpty(); 354 } 355 for (ProcessorDefinition output : outputs) { 356 if (output instanceof TransactedDefinition || output instanceof PolicyDefinition) { 357 // special for those as they wrap entire output, so we should just check its output 358 return hasOutputs(output.getOutputs(), excludeAbstract); 359 } 360 if (!output.isAbstract()) { 361 return true; 362 } 363 } 364 return false; 365 } 366 367 /** 368 * Determines whether a new thread pool will be created or not. 369 * <p/> 370 * This is used to know if a new thread pool will be created, and therefore is not shared by others, and therefore 371 * exclusive to the definition. 372 * 373 * @param routeContext the route context 374 * @param definition the node definition which may leverage executor service. 375 * @param useDefault whether to fallback and use a default thread pool, if no explicit configured 376 * @return <tt>true</tt> if a new thread pool will be created, <tt>false</tt> if not 377 * @see #getConfiguredExecutorService(org.apache.camel.spi.RouteContext, String, ExecutorServiceAwareDefinition, boolean) 378 */ 379 public static boolean willCreateNewThreadPool(RouteContext routeContext, ExecutorServiceAwareDefinition<?> definition, boolean useDefault) { 380 ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager(); 381 ObjectHelper.notNull(manager, "ExecutorServiceManager", routeContext.getCamelContext()); 382 383 if (definition.getExecutorService() != null) { 384 // no there is a custom thread pool configured 385 return false; 386 } else if (definition.getExecutorServiceRef() != null) { 387 ExecutorService answer = routeContext.getCamelContext().getRegistry().lookupByNameAndType(definition.getExecutorServiceRef(), ExecutorService.class); 388 // if no existing thread pool, then we will have to create a new thread pool 389 return answer == null; 390 } else if (useDefault) { 391 return true; 392 } 393 394 return false; 395 } 396 397 /** 398 * Will lookup in {@link org.apache.camel.spi.Registry} for a {@link ExecutorService} registered with the given 399 * <tt>executorServiceRef</tt> name. 400 * <p/> 401 * This method will lookup for configured thread pool in the following order 402 * <ul> 403 * <li>from the {@link org.apache.camel.spi.Registry} if found</li> 404 * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li> 405 * <li>if none found, then <tt>null</tt> is returned.</li> 406 * </ul> 407 * @param routeContext the route context 408 * @param name name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService} 409 * is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}. 410 * @param source the source to use the thread pool 411 * @param executorServiceRef reference name of the thread pool 412 * @return the executor service, or <tt>null</tt> if none was found. 413 */ 414 public static ExecutorService lookupExecutorServiceRef(RouteContext routeContext, String name, 415 Object source, String executorServiceRef) { 416 417 ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager(); 418 ObjectHelper.notNull(manager, "ExecutorServiceManager", routeContext.getCamelContext()); 419 ObjectHelper.notNull(executorServiceRef, "executorServiceRef"); 420 421 // lookup in registry first and use existing thread pool if exists 422 ExecutorService answer = routeContext.getCamelContext().getRegistry().lookupByNameAndType(executorServiceRef, ExecutorService.class); 423 if (answer == null) { 424 // then create a thread pool assuming the ref is a thread pool profile id 425 answer = manager.newThreadPool(source, name, executorServiceRef); 426 } 427 return answer; 428 } 429 430 /** 431 * Will lookup and get the configured {@link java.util.concurrent.ExecutorService} from the given definition. 432 * <p/> 433 * This method will lookup for configured thread pool in the following order 434 * <ul> 435 * <li>from the definition if any explicit configured executor service.</li> 436 * <li>from the {@link org.apache.camel.spi.Registry} if found</li> 437 * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li> 438 * <li>if none found, then <tt>null</tt> is returned.</li> 439 * </ul> 440 * The various {@link ExecutorServiceAwareDefinition} should use this helper method to ensure they support 441 * configured executor services in the same coherent way. 442 * 443 * @param routeContext the route context 444 * @param name name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService} 445 * is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}. 446 * @param definition the node definition which may leverage executor service. 447 * @param useDefault whether to fallback and use a default thread pool, if no explicit configured 448 * @return the configured executor service, or <tt>null</tt> if none was configured. 449 * @throws IllegalArgumentException is thrown if lookup of executor service in {@link org.apache.camel.spi.Registry} was not found 450 */ 451 public static ExecutorService getConfiguredExecutorService(RouteContext routeContext, String name, 452 ExecutorServiceAwareDefinition<?> definition, 453 boolean useDefault) throws IllegalArgumentException { 454 ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager(); 455 ObjectHelper.notNull(manager, "ExecutorServiceManager", routeContext.getCamelContext()); 456 457 // prefer to use explicit configured executor on the definition 458 if (definition.getExecutorService() != null) { 459 return definition.getExecutorService(); 460 } else if (definition.getExecutorServiceRef() != null) { 461 // lookup in registry first and use existing thread pool if exists 462 ExecutorService answer = lookupExecutorServiceRef(routeContext, name, definition, definition.getExecutorServiceRef()); 463 if (answer == null) { 464 throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef() + " not found in registry or as a thread pool profile."); 465 } 466 return answer; 467 } else if (useDefault) { 468 return manager.newDefaultThreadPool(definition, name); 469 } 470 471 return null; 472 } 473 474 /** 475 * Will lookup in {@link org.apache.camel.spi.Registry} for a {@link ScheduledExecutorService} registered with the given 476 * <tt>executorServiceRef</tt> name. 477 * <p/> 478 * This method will lookup for configured thread pool in the following order 479 * <ul> 480 * <li>from the {@link org.apache.camel.spi.Registry} if found</li> 481 * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li> 482 * <li>if none found, then <tt>null</tt> is returned.</li> 483 * </ul> 484 * @param routeContext the route context 485 * @param name name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService} 486 * is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}. 487 * @param source the source to use the thread pool 488 * @param executorServiceRef reference name of the thread pool 489 * @return the executor service, or <tt>null</tt> if none was found. 490 */ 491 public static ScheduledExecutorService lookupScheduledExecutorServiceRef(RouteContext routeContext, String name, 492 Object source, String executorServiceRef) { 493 494 ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager(); 495 ObjectHelper.notNull(manager, "ExecutorServiceManager", routeContext.getCamelContext()); 496 ObjectHelper.notNull(executorServiceRef, "executorServiceRef"); 497 498 // lookup in registry first and use existing thread pool if exists 499 ScheduledExecutorService answer = routeContext.getCamelContext().getRegistry().lookupByNameAndType(executorServiceRef, ScheduledExecutorService.class); 500 if (answer == null) { 501 // then create a thread pool assuming the ref is a thread pool profile id 502 answer = manager.newScheduledThreadPool(source, name, executorServiceRef); 503 } 504 return answer; 505 } 506 507 /** 508 * Will lookup and get the configured {@link java.util.concurrent.ScheduledExecutorService} from the given definition. 509 * <p/> 510 * This method will lookup for configured thread pool in the following order 511 * <ul> 512 * <li>from the definition if any explicit configured executor service.</li> 513 * <li>from the {@link org.apache.camel.spi.Registry} if found</li> 514 * <li>from the known list of {@link org.apache.camel.spi.ThreadPoolProfile ThreadPoolProfile(s)}.</li> 515 * <li>if none found, then <tt>null</tt> is returned.</li> 516 * </ul> 517 * The various {@link ExecutorServiceAwareDefinition} should use this helper method to ensure they support 518 * configured executor services in the same coherent way. 519 * 520 * @param routeContext the rout context 521 * @param name name which is appended to the thread name, when the {@link java.util.concurrent.ExecutorService} 522 * is created based on a {@link org.apache.camel.spi.ThreadPoolProfile}. 523 * @param definition the node definition which may leverage executor service. 524 * @param useDefault whether to fallback and use a default thread pool, if no explicit configured 525 * @return the configured executor service, or <tt>null</tt> if none was configured. 526 * @throws IllegalArgumentException is thrown if the found instance is not a ScheduledExecutorService type, 527 * or lookup of executor service in {@link org.apache.camel.spi.Registry} was not found 528 */ 529 public static ScheduledExecutorService getConfiguredScheduledExecutorService(RouteContext routeContext, String name, 530 ExecutorServiceAwareDefinition<?> definition, 531 boolean useDefault) throws IllegalArgumentException { 532 ExecutorServiceManager manager = routeContext.getCamelContext().getExecutorServiceManager(); 533 ObjectHelper.notNull(manager, "ExecutorServiceManager", routeContext.getCamelContext()); 534 535 // prefer to use explicit configured executor on the definition 536 if (definition.getExecutorService() != null) { 537 ExecutorService executorService = definition.getExecutorService(); 538 if (executorService instanceof ScheduledExecutorService) { 539 return (ScheduledExecutorService) executorService; 540 } 541 throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef() + " is not an ScheduledExecutorService instance"); 542 } else if (definition.getExecutorServiceRef() != null) { 543 ScheduledExecutorService answer = lookupScheduledExecutorServiceRef(routeContext, name, definition, definition.getExecutorServiceRef()); 544 if (answer == null) { 545 throw new IllegalArgumentException("ExecutorServiceRef " + definition.getExecutorServiceRef() + " not found in registry or as a thread pool profile."); 546 } 547 return answer; 548 } else if (useDefault) { 549 return manager.newDefaultScheduledThreadPool(definition, name); 550 } 551 552 return null; 553 } 554 555 /** 556 * Inspects the given definition and resolves any property placeholders from its properties. 557 * <p/> 558 * This implementation will check all the getter/setter pairs on this instance and for all the values 559 * (which is a String type) will be property placeholder resolved. 560 * 561 * @param routeContext the route context 562 * @param definition the definition 563 * @throws Exception is thrown if property placeholders was used and there was an error resolving them 564 * @see org.apache.camel.CamelContext#resolvePropertyPlaceholders(String) 565 * @see org.apache.camel.component.properties.PropertiesComponent 566 * @deprecated use {@link #resolvePropertyPlaceholders(org.apache.camel.CamelContext, Object)} 567 */ 568 @Deprecated 569 public static void resolvePropertyPlaceholders(RouteContext routeContext, Object definition) throws Exception { 570 resolvePropertyPlaceholders(routeContext.getCamelContext(), definition); 571 } 572 573 /** 574 * Inspects the given definition and resolves any property placeholders from its properties. 575 * <p/> 576 * This implementation will check all the getter/setter pairs on this instance and for all the values 577 * (which is a String type) will be property placeholder resolved. 578 * 579 * @param camelContext the Camel context 580 * @param definition the definition 581 * @throws Exception is thrown if property placeholders was used and there was an error resolving them 582 * @see org.apache.camel.CamelContext#resolvePropertyPlaceholders(String) 583 * @see org.apache.camel.component.properties.PropertiesComponent 584 */ 585 public static void resolvePropertyPlaceholders(CamelContext camelContext, Object definition) throws Exception { 586 LOG.trace("Resolving property placeholders for: {}", definition); 587 588 // find all getter/setter which we can use for property placeholders 589 Map<String, Object> properties = new HashMap<String, Object>(); 590 IntrospectionSupport.getProperties(definition, properties, null); 591 592 ProcessorDefinition<?> processorDefinition = null; 593 if (definition instanceof ProcessorDefinition) { 594 processorDefinition = (ProcessorDefinition<?>) definition; 595 } 596 // include additional properties which have the Camel placeholder QName 597 // and when the definition parameter is this (otherAttributes belong to this) 598 if (processorDefinition != null && processorDefinition.getOtherAttributes() != null) { 599 for (QName key : processorDefinition.getOtherAttributes().keySet()) { 600 if (Constants.PLACEHOLDER_QNAME.equals(key.getNamespaceURI())) { 601 String local = key.getLocalPart(); 602 Object value = processorDefinition.getOtherAttributes().get(key); 603 if (value != null && value instanceof String) { 604 // enforce a properties component to be created if none existed 605 CamelContextHelper.lookupPropertiesComponent(camelContext, true); 606 607 // value must be enclosed with placeholder tokens 608 String s = (String) value; 609 String prefixToken = camelContext.getPropertyPrefixToken(); 610 String suffixToken = camelContext.getPropertySuffixToken(); 611 if (prefixToken == null) { 612 throw new IllegalArgumentException("Property with name [" + local + "] uses property placeholders; however, no properties component is configured."); 613 } 614 615 if (!s.startsWith(prefixToken)) { 616 s = prefixToken + s; 617 } 618 if (!s.endsWith(suffixToken)) { 619 s = s + suffixToken; 620 } 621 value = s; 622 } 623 properties.put(local, value); 624 } 625 } 626 } 627 628 if (!properties.isEmpty()) { 629 LOG.trace("There are {} properties on: {}", properties.size(), definition); 630 // lookup and resolve properties for String based properties 631 for (Map.Entry<String, Object> entry : properties.entrySet()) { 632 // the name is always a String 633 String name = entry.getKey(); 634 Object value = entry.getValue(); 635 if (value instanceof String) { 636 // value must be a String, as a String is the key for a property placeholder 637 String text = (String) value; 638 text = camelContext.resolvePropertyPlaceholders(text); 639 if (text != value) { 640 // invoke setter as the text has changed 641 boolean changed = IntrospectionSupport.setProperty(camelContext.getTypeConverter(), definition, name, text); 642 if (!changed) { 643 throw new IllegalArgumentException("No setter to set property: " + name + " to: " + text + " on: " + definition); 644 } 645 if (LOG.isDebugEnabled()) { 646 LOG.debug("Changed property [{}] from: {} to: {}", new Object[]{name, value, text}); 647 } 648 } 649 } 650 } 651 } 652 } 653 654 /** 655 * Inspects the given definition and resolves known fields 656 * <p/> 657 * This implementation will check all the getter/setter pairs on this instance and for all the values 658 * (which is a String type) will check if it refers to a known field (such as on Exchange). 659 * 660 * @param definition the definition 661 */ 662 public static void resolveKnownConstantFields(Object definition) throws Exception { 663 LOG.trace("Resolving known fields for: {}", definition); 664 665 // find all String getter/setter 666 Map<String, Object> properties = new HashMap<String, Object>(); 667 IntrospectionSupport.getProperties(definition, properties, null); 668 669 if (!properties.isEmpty()) { 670 LOG.trace("There are {} properties on: {}", properties.size(), definition); 671 672 // lookup and resolve known constant fields for String based properties 673 for (Map.Entry<String, Object> entry : properties.entrySet()) { 674 String name = entry.getKey(); 675 Object value = entry.getValue(); 676 if (value instanceof String) { 677 // we can only resolve String typed values 678 String text = (String) value; 679 680 // is the value a known field (currently we only support constants from Exchange.class) 681 if (text.startsWith("Exchange.")) { 682 String field = ObjectHelper.after(text, "Exchange."); 683 String constant = ObjectHelper.lookupConstantFieldValue(Exchange.class, field); 684 if (constant != null) { 685 // invoke setter as the text has changed 686 IntrospectionSupport.setProperty(definition, name, constant); 687 if (LOG.isDebugEnabled()) { 688 LOG.debug("Changed property [{}] from: {} to: {}", new Object[]{name, value, constant}); 689 } 690 } else { 691 throw new IllegalArgumentException("Constant field with name: " + field + " not found on Exchange.class"); 692 } 693 } 694 } 695 } 696 } 697 } 698 699}