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.routeExceptionHandler 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 }