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.impl; 018 019 import java.util.HashMap; 020 import java.util.Map; 021 022 import org.apache.camel.CamelContext; 023 import org.apache.camel.CamelContextAware; 024 import org.apache.camel.Component; 025 import org.apache.camel.Consumer; 026 import org.apache.camel.Endpoint; 027 import org.apache.camel.EndpointConfiguration; 028 import org.apache.camel.Exchange; 029 import org.apache.camel.ExchangePattern; 030 import org.apache.camel.PollingConsumer; 031 import org.apache.camel.ResolveEndpointFailedException; 032 import org.apache.camel.spi.HasId; 033 import org.apache.camel.spi.UriParam; 034 import org.apache.camel.support.ServiceSupport; 035 import org.apache.camel.util.EndpointHelper; 036 import org.apache.camel.util.IntrospectionSupport; 037 import org.apache.camel.util.ObjectHelper; 038 import org.apache.camel.util.URISupport; 039 040 /** 041 * A default endpoint useful for implementation inheritance. 042 * <p/> 043 * Components which leverages <a 044 * href="http://camel.apache.org/asynchronous-routing-engine.html">asynchronous 045 * processing model</a> should check the {@link #isSynchronous()} to determine 046 * if asynchronous processing is allowed. The <tt>synchronous</tt> option on the 047 * endpoint allows Camel end users to dictate whether they want the asynchronous 048 * model or not. The option is default <tt>false</tt> which means asynchronous 049 * processing is allowed. 050 * 051 * @version 052 */ 053 public abstract class DefaultEndpoint extends ServiceSupport implements Endpoint, HasId, CamelContextAware { 054 055 private String endpointUri; 056 private EndpointConfiguration endpointConfiguration; 057 private CamelContext camelContext; 058 private Component component; 059 @UriParam 060 private ExchangePattern exchangePattern = ExchangePattern.InOnly; 061 // option to allow end user to dictate whether async processing should be 062 // used or not (if possible) 063 @UriParam 064 private boolean synchronous; 065 private final String id = EndpointHelper.createEndpointId(); 066 private Map<String, Object> consumerProperties; 067 068 /** 069 * Constructs a fully-initialized DefaultEndpoint instance. This is the 070 * preferred method of constructing an object from Java code (as opposed to 071 * Spring beans, etc.). 072 * 073 * @param endpointUri the full URI used to create this endpoint 074 * @param component the component that created this endpoint 075 */ 076 protected DefaultEndpoint(String endpointUri, Component component) { 077 this.camelContext = component == null ? null : component.getCamelContext(); 078 this.component = component; 079 this.setEndpointUri(endpointUri); 080 } 081 082 /** 083 * Constructs a DefaultEndpoint instance which has <b>not</b> been created 084 * using a {@link Component}. 085 * <p/> 086 * <b>Note:</b> It is preferred to create endpoints using the associated 087 * component. 088 * 089 * @param endpointUri the full URI used to create this endpoint 090 * @param camelContext the Camel Context in which this endpoint is operating 091 */ 092 @Deprecated 093 protected DefaultEndpoint(String endpointUri, CamelContext camelContext) { 094 this(endpointUri); 095 this.camelContext = camelContext; 096 } 097 098 /** 099 * Constructs a partially-initialized DefaultEndpoint instance. 100 * <p/> 101 * <b>Note:</b> It is preferred to create endpoints using the associated 102 * component. 103 * 104 * @param endpointUri the full URI used to create this endpoint 105 */ 106 @Deprecated 107 protected DefaultEndpoint(String endpointUri) { 108 this.setEndpointUri(endpointUri); 109 } 110 111 /** 112 * Constructs a partially-initialized DefaultEndpoint instance. Useful when 113 * creating endpoints manually (e.g., as beans in Spring). 114 * <p/> 115 * Please note that the endpoint URI must be set through properties (or 116 * overriding {@link #createEndpointUri()} if one uses this constructor. 117 * <p/> 118 * <b>Note:</b> It is preferred to create endpoints using the associated 119 * component. 120 */ 121 protected DefaultEndpoint() { 122 } 123 124 public int hashCode() { 125 return getEndpointUri().hashCode() * 37 + 1; 126 } 127 128 @Override 129 public boolean equals(Object object) { 130 if (object instanceof DefaultEndpoint) { 131 DefaultEndpoint that = (DefaultEndpoint)object; 132 return ObjectHelper.equal(this.getEndpointUri(), that.getEndpointUri()); 133 } 134 return false; 135 } 136 137 @Override 138 public String toString() { 139 return String.format("Endpoint[%s]", URISupport.sanitizeUri(getEndpointUri())); 140 } 141 142 /** 143 * Returns a unique String ID which can be used for aliasing without having 144 * to use the whole URI which is not unique 145 */ 146 public String getId() { 147 return id; 148 } 149 150 public String getEndpointUri() { 151 if (endpointUri == null) { 152 endpointUri = createEndpointUri(); 153 if (endpointUri == null) { 154 throw new IllegalArgumentException("endpointUri is not specified and " + getClass().getName() 155 + " does not implement createEndpointUri() to create a default value"); 156 } 157 } 158 return endpointUri; 159 } 160 161 public EndpointConfiguration getEndpointConfiguration() { 162 if (endpointConfiguration == null) { 163 endpointConfiguration = createEndpointConfiguration(getEndpointUri()); 164 } 165 return endpointConfiguration; 166 } 167 168 /** 169 * Sets a custom {@link EndpointConfiguration} 170 * 171 * @param endpointConfiguration a custom endpoint configuration to be used. 172 */ 173 public void setEndpointConfiguration(EndpointConfiguration endpointConfiguration) { 174 this.endpointConfiguration = endpointConfiguration; 175 } 176 177 public String getEndpointKey() { 178 if (isLenientProperties()) { 179 // only use the endpoint uri without parameters as the properties is 180 // lenient 181 String uri = getEndpointUri(); 182 if (uri.indexOf('?') != -1) { 183 return ObjectHelper.before(uri, "?"); 184 } else { 185 return uri; 186 } 187 } else { 188 // use the full endpoint uri 189 return getEndpointUri(); 190 } 191 } 192 193 public CamelContext getCamelContext() { 194 return camelContext; 195 } 196 197 /** 198 * Returns the component that created this endpoint. 199 * 200 * @return the component that created this endpoint, or <tt>null</tt> if 201 * none set 202 */ 203 public Component getComponent() { 204 return component; 205 } 206 207 public void setCamelContext(CamelContext camelContext) { 208 this.camelContext = camelContext; 209 } 210 211 public PollingConsumer createPollingConsumer() throws Exception { 212 // should not configure consumer 213 return new EventDrivenPollingConsumer(this); 214 } 215 216 public Exchange createExchange(Exchange exchange) { 217 return exchange.copy(); 218 } 219 220 public Exchange createExchange() { 221 return createExchange(getExchangePattern()); 222 } 223 224 public Exchange createExchange(ExchangePattern pattern) { 225 return new DefaultExchange(this, pattern); 226 } 227 228 /** 229 * Returns the default exchange pattern to use for createExchange(). 230 * 231 * @see #setExchangePattern(ExchangePattern exchangePattern) 232 */ 233 public ExchangePattern getExchangePattern() { 234 return exchangePattern; 235 } 236 237 /** 238 * Sets the default exchange pattern to use for {@link #createExchange()}. 239 * The default value is {@link ExchangePattern#InOnly} 240 */ 241 public void setExchangePattern(ExchangePattern exchangePattern) { 242 this.exchangePattern = exchangePattern; 243 } 244 245 /** 246 * Returns whether synchronous processing should be strictly used. 247 * 248 * @see #setSynchronous(boolean synchronous) 249 */ 250 public boolean isSynchronous() { 251 return synchronous; 252 } 253 254 /** 255 * Sets whether synchronous processing should be strictly used, or Camel is 256 * allowed to use asynchronous processing (if supported). 257 * 258 * @param synchronous <tt>true</tt> to enforce synchronous processing 259 */ 260 public void setSynchronous(boolean synchronous) { 261 this.synchronous = synchronous; 262 } 263 264 public void configureProperties(Map<String, Object> options) { 265 Map<String, Object> consumerProperties = IntrospectionSupport.extractProperties(options, "consumer."); 266 if (consumerProperties != null && !consumerProperties.isEmpty()) { 267 setConsumerProperties(consumerProperties); 268 } 269 } 270 271 /** 272 * Sets the bean properties on the given bean. 273 * <p/> 274 * This is the same logical implementation as {@link DefaultComponent#setProperties(Object, java.util.Map)} 275 * 276 * @param bean the bean 277 * @param parameters properties to set 278 */ 279 protected void setProperties(Object bean, Map<String, Object> parameters) throws Exception { 280 // set reference properties first as they use # syntax that fools the regular properties setter 281 EndpointHelper.setReferenceProperties(getCamelContext(), bean, parameters); 282 EndpointHelper.setProperties(getCamelContext(), bean, parameters); 283 } 284 285 /** 286 * A factory method to lazily create the endpointUri if none is specified 287 */ 288 protected String createEndpointUri() { 289 return null; 290 } 291 292 /** 293 * A factory method to lazily create the endpoint configuration if none is specified 294 */ 295 protected EndpointConfiguration createEndpointConfiguration(String uri) { 296 // using this factory method to be backwards compatible with the old code 297 if (getComponent() != null) { 298 // prefer to use component endpoint configuration 299 try { 300 return getComponent().createConfiguration(uri); 301 } catch (Exception e) { 302 throw ObjectHelper.wrapRuntimeCamelException(e); 303 } 304 } else if (getCamelContext() != null) { 305 // fallback and use a mapped endpoint configuration 306 return new MappedEndpointConfiguration(getCamelContext(), uri); 307 } 308 // not configuration possible 309 return null; 310 } 311 312 /** 313 * Sets the endpointUri if it has not been specified yet via some kind of 314 * dependency injection mechanism. This allows dependency injection 315 * frameworks such as Spring or Guice to set the default endpoint URI in 316 * cases where it has not been explicitly configured using the name/context 317 * in which an Endpoint is created. 318 */ 319 public void setEndpointUriIfNotSpecified(String value) { 320 if (endpointUri == null) { 321 setEndpointUri(value); 322 } 323 } 324 325 /** 326 * Sets the URI that created this endpoint. 327 */ 328 protected void setEndpointUri(String endpointUri) { 329 this.endpointUri = endpointUri; 330 } 331 332 public boolean isLenientProperties() { 333 // default should be false for most components 334 return false; 335 } 336 337 public Map<String, Object> getConsumerProperties() { 338 if (consumerProperties == null) { 339 // must create empty if none exists 340 consumerProperties = new HashMap<String, Object>(); 341 } 342 return consumerProperties; 343 } 344 345 public void setConsumerProperties(Map<String, Object> consumerProperties) { 346 // append consumer properties 347 if (consumerProperties != null && !consumerProperties.isEmpty()) { 348 if (this.consumerProperties == null) { 349 this.consumerProperties = new HashMap<String, Object>(consumerProperties); 350 } else { 351 this.consumerProperties.putAll(consumerProperties); 352 } 353 } 354 } 355 356 protected void configureConsumer(Consumer consumer) throws Exception { 357 if (consumerProperties != null) { 358 // use a defensive copy of the consumer properties as the methods below will remove the used properties 359 // and in case we restart routes, we need access to the original consumer properties again 360 Map<String, Object> copy = new HashMap<String, Object>(consumerProperties); 361 362 // set reference properties first as they use # syntax that fools the regular properties setter 363 EndpointHelper.setReferenceProperties(getCamelContext(), consumer, copy); 364 EndpointHelper.setProperties(getCamelContext(), consumer, copy); 365 366 // special consumer.bridgeErrorHandler option 367 Object bridge = copy.remove("bridgeErrorHandler"); 368 if (bridge != null && "true".equals(bridge)) { 369 if (consumer instanceof DefaultConsumer) { 370 DefaultConsumer defaultConsumer = (DefaultConsumer) consumer; 371 defaultConsumer.setExceptionHandler(new BridgeExceptionHandlerToErrorHandler(defaultConsumer)); 372 } else { 373 throw new IllegalArgumentException("Option consumer.bridgeErrorHandler is only supported by endpoints," 374 + " having their consumer extend DefaultConsumer. The consumer is a " + consumer.getClass().getName() + " class."); 375 } 376 } 377 378 if (!this.isLenientProperties() && copy.size() > 0) { 379 throw new ResolveEndpointFailedException(this.getEndpointUri(), "There are " + copy.size() 380 + " parameters that couldn't be set on the endpoint consumer." 381 + " Check the uri if the parameters are spelt correctly and that they are properties of the endpoint." 382 + " Unknown consumer parameters=[" + copy + "]"); 383 } 384 } 385 } 386 387 protected void configurePollingConsumer(PollingConsumer consumer) throws Exception { 388 configureConsumer(consumer); 389 } 390 391 @Override 392 protected void doStart() throws Exception { 393 // noop 394 } 395 396 @Override 397 protected void doStop() throws Exception { 398 // noop 399 } 400 }