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.impl; 018 019import java.util.HashMap; 020import java.util.Map; 021 022import org.apache.camel.CamelContext; 023import org.apache.camel.CamelContextAware; 024import org.apache.camel.Component; 025import org.apache.camel.Consumer; 026import org.apache.camel.Endpoint; 027import org.apache.camel.EndpointConfiguration; 028import org.apache.camel.Exchange; 029import org.apache.camel.ExchangePattern; 030import org.apache.camel.PollingConsumer; 031import org.apache.camel.ResolveEndpointFailedException; 032import org.apache.camel.spi.ExceptionHandler; 033import org.apache.camel.spi.HasId; 034import org.apache.camel.spi.UriParam; 035import org.apache.camel.support.ServiceSupport; 036import org.apache.camel.util.EndpointHelper; 037import org.apache.camel.util.IntrospectionSupport; 038import org.apache.camel.util.ObjectHelper; 039import org.apache.camel.util.URISupport; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043/** 044 * A default endpoint useful for implementation inheritance. 045 * <p/> 046 * Components which leverages <a 047 * href="http://camel.apache.org/asynchronous-routing-engine.html">asynchronous 048 * processing model</a> should check the {@link #isSynchronous()} to determine 049 * if asynchronous processing is allowed. The <tt>synchronous</tt> option on the 050 * endpoint allows Camel end users to dictate whether they want the asynchronous 051 * model or not. The option is default <tt>false</tt> which means asynchronous 052 * processing is allowed. 053 * 054 * @version 055 */ 056public abstract class DefaultEndpoint extends ServiceSupport implements Endpoint, HasId, CamelContextAware { 057 058 private static final Logger LOG = LoggerFactory.getLogger(DefaultEndpoint.class); 059 private final String id = EndpointHelper.createEndpointId(); 060 private transient String endpointUriToString; 061 private String endpointUri; 062 private EndpointConfiguration endpointConfiguration; 063 private CamelContext camelContext; 064 private Component component; 065 @UriParam(label = "consumer", optionalPrefix = "consumer.", description = "Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while" 066 + " the consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and handled by the routing Error Handler." 067 + " By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, that will be logged at WARN or ERROR level and ignored.") 068 private boolean bridgeErrorHandler; 069 @UriParam(label = "consumer,advanced", optionalPrefix = "consumer.", description = "To let the consumer use a custom ExceptionHandler." 070 + " Notice if the option bridgeErrorHandler is enabled then this options is not in use." 071 + " By default the consumer will deal with exceptions, that will be logged at WARN or ERROR level and ignored.") 072 private ExceptionHandler exceptionHandler; 073 @UriParam(label = "consumer,advanced", 074 description = "Sets the exchange pattern when the consumer creates an exchange.") 075 // no default value set on @UriParam as the MEP is sometimes InOnly or InOut depending on the component in use 076 private ExchangePattern exchangePattern = ExchangePattern.InOnly; 077 // option to allow end user to dictate whether async processing should be 078 // used or not (if possible) 079 @UriParam(defaultValue = "false", label = "advanced", 080 description = "Sets whether synchronous processing should be strictly used, or Camel is allowed to use asynchronous processing (if supported).") 081 private boolean synchronous; 082 // these options are not really in use any option related to the consumer has a specific option on the endpoint 083 // and consumerProperties was added from the very start of Camel. 084 private Map<String, Object> consumerProperties; 085 // pooling consumer options only related to EventDrivenPollingConsumer which are very seldom in use 086 // so lets not expose them in the component docs as it will be included in every component 087 private int pollingConsumerQueueSize = 1000; 088 private boolean pollingConsumerBlockWhenFull = true; 089 private long pollingConsumerBlockTimeout; 090 091 /** 092 * Constructs a fully-initialized DefaultEndpoint instance. This is the 093 * preferred method of constructing an object from Java code (as opposed to 094 * Spring beans, etc.). 095 * 096 * @param endpointUri the full URI used to create this endpoint 097 * @param component the component that created this endpoint 098 */ 099 protected DefaultEndpoint(String endpointUri, Component component) { 100 this.camelContext = component == null ? null : component.getCamelContext(); 101 this.component = component; 102 this.setEndpointUri(endpointUri); 103 } 104 105 /** 106 * Constructs a DefaultEndpoint instance which has <b>not</b> been created 107 * using a {@link Component}. 108 * <p/> 109 * <b>Note:</b> It is preferred to create endpoints using the associated 110 * component. 111 * 112 * @param endpointUri the full URI used to create this endpoint 113 * @param camelContext the Camel Context in which this endpoint is operating 114 */ 115 @Deprecated 116 protected DefaultEndpoint(String endpointUri, CamelContext camelContext) { 117 this(endpointUri); 118 this.camelContext = camelContext; 119 } 120 121 /** 122 * Constructs a partially-initialized DefaultEndpoint instance. 123 * <p/> 124 * <b>Note:</b> It is preferred to create endpoints using the associated 125 * component. 126 * 127 * @param endpointUri the full URI used to create this endpoint 128 */ 129 @Deprecated 130 protected DefaultEndpoint(String endpointUri) { 131 this.setEndpointUri(endpointUri); 132 } 133 134 /** 135 * Constructs a partially-initialized DefaultEndpoint instance. Useful when 136 * creating endpoints manually (e.g., as beans in Spring). 137 * <p/> 138 * Please note that the endpoint URI must be set through properties (or 139 * overriding {@link #createEndpointUri()} if one uses this constructor. 140 * <p/> 141 * <b>Note:</b> It is preferred to create endpoints using the associated 142 * component. 143 */ 144 protected DefaultEndpoint() { 145 } 146 147 public int hashCode() { 148 return getEndpointUri().hashCode() * 37 + 1; 149 } 150 151 @Override 152 public boolean equals(Object object) { 153 if (object instanceof DefaultEndpoint) { 154 DefaultEndpoint that = (DefaultEndpoint)object; 155 // must also match the same CamelContext in case we compare endpoints from different contexts 156 String thisContextName = this.getCamelContext() != null ? this.getCamelContext().getName() : null; 157 String thatContextName = that.getCamelContext() != null ? that.getCamelContext().getName() : null; 158 return ObjectHelper.equal(this.getEndpointUri(), that.getEndpointUri()) && ObjectHelper.equal(thisContextName, thatContextName); 159 } 160 return false; 161 } 162 163 @Override 164 public String toString() { 165 if (endpointUriToString == null) { 166 String value = null; 167 try { 168 value = getEndpointUri(); 169 } catch (RuntimeException e) { 170 // ignore any exception and use null for building the string value 171 } 172 // ensure to sanitize uri so we do not show sensitive information such as passwords 173 endpointUriToString = URISupport.sanitizeUri(value); 174 } 175 return endpointUriToString; 176 } 177 178 /** 179 * Returns a unique String ID which can be used for aliasing without having 180 * to use the whole URI which is not unique 181 */ 182 public String getId() { 183 return id; 184 } 185 186 public String getEndpointUri() { 187 if (endpointUri == null) { 188 endpointUri = createEndpointUri(); 189 if (endpointUri == null) { 190 throw new IllegalArgumentException("endpointUri is not specified and " + getClass().getName() 191 + " does not implement createEndpointUri() to create a default value"); 192 } 193 } 194 return endpointUri; 195 } 196 197 public EndpointConfiguration getEndpointConfiguration() { 198 if (endpointConfiguration == null) { 199 endpointConfiguration = createEndpointConfiguration(getEndpointUri()); 200 } 201 return endpointConfiguration; 202 } 203 204 /** 205 * Sets a custom {@link EndpointConfiguration} 206 * 207 * @param endpointConfiguration a custom endpoint configuration to be used. 208 */ 209 @Deprecated 210 public void setEndpointConfiguration(EndpointConfiguration endpointConfiguration) { 211 this.endpointConfiguration = endpointConfiguration; 212 } 213 214 public String getEndpointKey() { 215 if (isLenientProperties()) { 216 // only use the endpoint uri without parameters as the properties is 217 // lenient 218 String uri = getEndpointUri(); 219 if (uri.indexOf('?') != -1) { 220 return ObjectHelper.before(uri, "?"); 221 } else { 222 return uri; 223 } 224 } else { 225 // use the full endpoint uri 226 return getEndpointUri(); 227 } 228 } 229 230 public CamelContext getCamelContext() { 231 return camelContext; 232 } 233 234 /** 235 * Returns the component that created this endpoint. 236 * 237 * @return the component that created this endpoint, or <tt>null</tt> if 238 * none set 239 */ 240 public Component getComponent() { 241 return component; 242 } 243 244 public void setCamelContext(CamelContext camelContext) { 245 this.camelContext = camelContext; 246 } 247 248 public PollingConsumer createPollingConsumer() throws Exception { 249 // should not call configurePollingConsumer when its EventDrivenPollingConsumer 250 if (LOG.isDebugEnabled()) { 251 LOG.debug("Creating EventDrivenPollingConsumer with queueSize: {} blockWhenFull: {} blockTimeout: {}", 252 new Object[]{getPollingConsumerQueueSize(), isPollingConsumerBlockWhenFull(), getPollingConsumerBlockTimeout()}); 253 } 254 EventDrivenPollingConsumer consumer = new EventDrivenPollingConsumer(this, getPollingConsumerQueueSize()); 255 consumer.setBlockWhenFull(isPollingConsumerBlockWhenFull()); 256 consumer.setBlockTimeout(getPollingConsumerBlockTimeout()); 257 return consumer; 258 } 259 260 public Exchange createExchange(Exchange exchange) { 261 return exchange.copy(); 262 } 263 264 public Exchange createExchange() { 265 return createExchange(getExchangePattern()); 266 } 267 268 public Exchange createExchange(ExchangePattern pattern) { 269 return new DefaultExchange(this, pattern); 270 } 271 272 /** 273 * Returns the default exchange pattern to use when creating an exchange. 274 */ 275 public ExchangePattern getExchangePattern() { 276 return exchangePattern; 277 } 278 279 /** 280 * Sets the default exchange pattern when creating an exchange. 281 */ 282 public void setExchangePattern(ExchangePattern exchangePattern) { 283 this.exchangePattern = exchangePattern; 284 } 285 286 /** 287 * Returns whether synchronous processing should be strictly used. 288 */ 289 public boolean isSynchronous() { 290 return synchronous; 291 } 292 293 /** 294 * Sets whether synchronous processing should be strictly used, or Camel is 295 * allowed to use asynchronous processing (if supported). 296 * 297 * @param synchronous <tt>true</tt> to enforce synchronous processing 298 */ 299 public void setSynchronous(boolean synchronous) { 300 this.synchronous = synchronous; 301 } 302 303 public boolean isBridgeErrorHandler() { 304 return bridgeErrorHandler; 305 } 306 307 /** 308 * Allows for bridging the consumer to the Camel routing Error Handler, which mean any exceptions occurred while 309 * the consumer is trying to pickup incoming messages, or the likes, will now be processed as a message and 310 * handled by the routing Error Handler. 311 * <p/> 312 * By default the consumer will use the org.apache.camel.spi.ExceptionHandler to deal with exceptions, 313 * that will be logged at WARN/ERROR level and ignored. 314 */ 315 public void setBridgeErrorHandler(boolean bridgeErrorHandler) { 316 this.bridgeErrorHandler = bridgeErrorHandler; 317 } 318 319 public ExceptionHandler getExceptionHandler() { 320 return exceptionHandler; 321 } 322 323 /** 324 * To let the consumer use a custom ExceptionHandler. 325 + Notice if the option bridgeErrorHandler is enabled then this options is not in use. 326 + By default the consumer will deal with exceptions, that will be logged at WARN/ERROR level and ignored. 327 */ 328 public void setExceptionHandler(ExceptionHandler exceptionHandler) { 329 this.exceptionHandler = exceptionHandler; 330 } 331 332 /** 333 * Gets the {@link org.apache.camel.PollingConsumer} queue size, when {@link org.apache.camel.impl.EventDrivenPollingConsumer} 334 * is being used. Notice some Camel components may have their own implementation of {@link org.apache.camel.PollingConsumer} and 335 * therefore not using the default {@link org.apache.camel.impl.EventDrivenPollingConsumer} implementation. 336 * <p/> 337 * The default value is <tt>1000</tt> 338 */ 339 public int getPollingConsumerQueueSize() { 340 return pollingConsumerQueueSize; 341 } 342 343 /** 344 * Sets the {@link org.apache.camel.PollingConsumer} queue size, when {@link org.apache.camel.impl.EventDrivenPollingConsumer} 345 * is being used. Notice some Camel components may have their own implementation of {@link org.apache.camel.PollingConsumer} and 346 * therefore not using the default {@link org.apache.camel.impl.EventDrivenPollingConsumer} implementation. 347 * <p/> 348 * The default value is <tt>1000</tt> 349 */ 350 public void setPollingConsumerQueueSize(int pollingConsumerQueueSize) { 351 this.pollingConsumerQueueSize = pollingConsumerQueueSize; 352 } 353 354 /** 355 * Whether to block when adding to the internal queue off when {@link org.apache.camel.impl.EventDrivenPollingConsumer} 356 * is being used. Notice some Camel components may have their own implementation of {@link org.apache.camel.PollingConsumer} and 357 * therefore not using the default {@link org.apache.camel.impl.EventDrivenPollingConsumer} implementation. 358 * <p/> 359 * Setting this option to <tt>false</tt>, will result in an {@link java.lang.IllegalStateException} being thrown 360 * when trying to add to the queue, and its full. 361 * <p/> 362 * The default value is <tt>true</tt> which will block the producer queue until the queue has space. 363 */ 364 public boolean isPollingConsumerBlockWhenFull() { 365 return pollingConsumerBlockWhenFull; 366 } 367 368 /** 369 * Set whether to block when adding to the internal queue off when {@link org.apache.camel.impl.EventDrivenPollingConsumer} 370 * is being used. Notice some Camel components may have their own implementation of {@link org.apache.camel.PollingConsumer} and 371 * therefore not using the default {@link org.apache.camel.impl.EventDrivenPollingConsumer} implementation. 372 * <p/> 373 * Setting this option to <tt>false</tt>, will result in an {@link java.lang.IllegalStateException} being thrown 374 * when trying to add to the queue, and its full. 375 * <p/> 376 * The default value is <tt>true</tt> which will block the producer queue until the queue has space. 377 */ 378 public void setPollingConsumerBlockWhenFull(boolean pollingConsumerBlockWhenFull) { 379 this.pollingConsumerBlockWhenFull = pollingConsumerBlockWhenFull; 380 } 381 382 /** 383 * Sets the timeout in millis to use when adding to the internal queue off when {@link org.apache.camel.impl.EventDrivenPollingConsumer} 384 * is being used. 385 * 386 * @see #setPollingConsumerBlockWhenFull(boolean) 387 */ 388 public long getPollingConsumerBlockTimeout() { 389 return pollingConsumerBlockTimeout; 390 } 391 392 /** 393 * Sets the timeout in millis to use when adding to the internal queue off when {@link org.apache.camel.impl.EventDrivenPollingConsumer} 394 * is being used. 395 * 396 * @see #setPollingConsumerBlockWhenFull(boolean) 397 */ 398 public void setPollingConsumerBlockTimeout(long pollingConsumerBlockTimeout) { 399 this.pollingConsumerBlockTimeout = pollingConsumerBlockTimeout; 400 } 401 402 public void configureProperties(Map<String, Object> options) { 403 Map<String, Object> consumerProperties = IntrospectionSupport.extractProperties(options, "consumer."); 404 if (consumerProperties != null && !consumerProperties.isEmpty()) { 405 setConsumerProperties(consumerProperties); 406 } 407 } 408 409 /** 410 * Sets the bean properties on the given bean. 411 * <p/> 412 * This is the same logical implementation as {@link DefaultComponent#setProperties(Object, java.util.Map)} 413 * 414 * @param bean the bean 415 * @param parameters properties to set 416 */ 417 protected void setProperties(Object bean, Map<String, Object> parameters) throws Exception { 418 // set reference properties first as they use # syntax that fools the regular properties setter 419 EndpointHelper.setReferenceProperties(getCamelContext(), bean, parameters); 420 EndpointHelper.setProperties(getCamelContext(), bean, parameters); 421 } 422 423 /** 424 * A factory method to lazily create the endpointUri if none is specified 425 */ 426 protected String createEndpointUri() { 427 return null; 428 } 429 430 /** 431 * A factory method to lazily create the endpoint configuration if none is specified 432 */ 433 @Deprecated 434 protected EndpointConfiguration createEndpointConfiguration(String uri) { 435 // using this factory method to be backwards compatible with the old code 436 if (getComponent() != null) { 437 // prefer to use component endpoint configuration 438 try { 439 return getComponent().createConfiguration(uri); 440 } catch (Exception e) { 441 throw ObjectHelper.wrapRuntimeCamelException(e); 442 } 443 } else if (getCamelContext() != null) { 444 // fallback and use a mapped endpoint configuration 445 return new MappedEndpointConfiguration(getCamelContext(), uri); 446 } 447 // not configuration possible 448 return null; 449 } 450 451 /** 452 * Sets the endpointUri if it has not been specified yet via some kind of 453 * dependency injection mechanism. This allows dependency injection 454 * frameworks such as Spring or Guice to set the default endpoint URI in 455 * cases where it has not been explicitly configured using the name/context 456 * in which an Endpoint is created. 457 */ 458 public void setEndpointUriIfNotSpecified(String value) { 459 if (endpointUri == null) { 460 setEndpointUri(value); 461 } 462 } 463 464 /** 465 * Sets the URI that created this endpoint. 466 */ 467 protected void setEndpointUri(String endpointUri) { 468 this.endpointUri = endpointUri; 469 } 470 471 public boolean isLenientProperties() { 472 // default should be false for most components 473 return false; 474 } 475 476 public Map<String, Object> getConsumerProperties() { 477 if (consumerProperties == null) { 478 // must create empty if none exists 479 consumerProperties = new HashMap<String, Object>(); 480 } 481 return consumerProperties; 482 } 483 484 public void setConsumerProperties(Map<String, Object> consumerProperties) { 485 // append consumer properties 486 if (consumerProperties != null && !consumerProperties.isEmpty()) { 487 if (this.consumerProperties == null) { 488 this.consumerProperties = new HashMap<String, Object>(consumerProperties); 489 } else { 490 this.consumerProperties.putAll(consumerProperties); 491 } 492 } 493 } 494 495 protected void configureConsumer(Consumer consumer) throws Exception { 496 // inject CamelContext 497 if (consumer instanceof CamelContextAware) { 498 ((CamelContextAware) consumer).setCamelContext(getCamelContext()); 499 } 500 501 if (consumerProperties != null) { 502 // use a defensive copy of the consumer properties as the methods below will remove the used properties 503 // and in case we restart routes, we need access to the original consumer properties again 504 Map<String, Object> copy = new HashMap<String, Object>(consumerProperties); 505 506 // set reference properties first as they use # syntax that fools the regular properties setter 507 EndpointHelper.setReferenceProperties(getCamelContext(), consumer, copy); 508 EndpointHelper.setProperties(getCamelContext(), consumer, copy); 509 510 // special consumer.bridgeErrorHandler option 511 Object bridge = copy.remove("bridgeErrorHandler"); 512 if (bridge != null && "true".equals(bridge)) { 513 if (consumer instanceof DefaultConsumer) { 514 DefaultConsumer defaultConsumer = (DefaultConsumer) consumer; 515 defaultConsumer.setExceptionHandler(new BridgeExceptionHandlerToErrorHandler(defaultConsumer)); 516 } else { 517 throw new IllegalArgumentException("Option consumer.bridgeErrorHandler is only supported by endpoints," 518 + " having their consumer extend DefaultConsumer. The consumer is a " + consumer.getClass().getName() + " class."); 519 } 520 } 521 522 if (!this.isLenientProperties() && copy.size() > 0) { 523 throw new ResolveEndpointFailedException(this.getEndpointUri(), "There are " + copy.size() 524 + " parameters that couldn't be set on the endpoint consumer." 525 + " Check the uri if the parameters are spelt correctly and that they are properties of the endpoint." 526 + " Unknown consumer parameters=[" + copy + "]"); 527 } 528 } 529 } 530 531 protected void configurePollingConsumer(PollingConsumer consumer) throws Exception { 532 configureConsumer(consumer); 533 } 534 535 @Override 536 protected void doStart() throws Exception { 537 // the bridgeErrorHandler/exceptionHandler was originally configured with consumer. prefix, such as consumer.bridgeErrorHandler=true 538 // so if they have been configured on the endpoint then map to the old naming style 539 if (bridgeErrorHandler) { 540 getConsumerProperties().put("bridgeErrorHandler", "true"); 541 } 542 if (exceptionHandler != null) { 543 getConsumerProperties().put("exceptionHandler", exceptionHandler); 544 } 545 } 546 547 @Override 548 protected void doStop() throws Exception { 549 // noop 550 } 551}