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