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.net.URI;
020 import java.util.ArrayList;
021 import java.util.List;
022 import java.util.Map;
023 import java.util.concurrent.ExecutorService;
024 import java.util.concurrent.ScheduledExecutorService;
025
026 import org.apache.camel.CamelContext;
027 import org.apache.camel.Component;
028 import org.apache.camel.Endpoint;
029 import org.apache.camel.ResolveEndpointFailedException;
030 import org.apache.camel.util.CamelContextHelper;
031 import org.apache.camel.util.EndpointHelper;
032 import org.apache.camel.util.IntrospectionSupport;
033 import org.apache.camel.util.ObjectHelper;
034 import org.apache.camel.util.URISupport;
035 import org.apache.camel.util.UnsafeUriCharactersEncoder;
036 import org.apache.camel.util.concurrent.ExecutorServiceHelper;
037 import org.apache.commons.logging.Log;
038 import org.apache.commons.logging.LogFactory;
039
040 /**
041 * Default component to use for base for components implementations.
042 *
043 * @version $Revision: 896398 $
044 */
045 public abstract class DefaultComponent extends ServiceSupport implements Component {
046 private static final transient Log LOG = LogFactory.getLog(DefaultComponent.class);
047
048 private static final int DEFAULT_THREADPOOL_SIZE = 10;
049 private CamelContext camelContext;
050 private ExecutorService executorService;
051
052 public DefaultComponent() {
053 }
054
055 public DefaultComponent(CamelContext context) {
056 this.camelContext = context;
057 }
058
059 public Endpoint createEndpoint(String uri) throws Exception {
060 ObjectHelper.notNull(getCamelContext(), "camelContext");
061 //encode URI string to the unsafe URI characters
062 URI u = new URI(UnsafeUriCharactersEncoder.encode(uri));
063 String path = u.getSchemeSpecificPart();
064
065 // lets trim off any query arguments
066 if (path.startsWith("//")) {
067 path = path.substring(2);
068 }
069 int idx = path.indexOf('?');
070 if (idx > 0) {
071 path = path.substring(0, idx);
072 }
073 Map<String, Object> parameters = URISupport.parseParameters(u);
074
075 validateURI(uri, path, parameters);
076
077 if (LOG.isDebugEnabled()) {
078 LOG.debug("Creating endpoint uri=[" + uri + "], path=[" + path + "], parameters=[" + parameters + "]");
079 }
080 Endpoint endpoint = createEndpoint(uri, path, parameters);
081 if (endpoint == null) {
082 return null;
083 }
084
085 if (parameters != null) {
086 endpoint.configureProperties(parameters);
087 if (useIntrospectionOnEndpoint()) {
088 setProperties(endpoint, parameters);
089 }
090
091 // if endpoint is strict (not lenient) and we have unknown parameters configured then
092 // fail if there are parameters that could not be set, then they are probably misspell or not supported at all
093 if (!endpoint.isLenientProperties()) {
094 validateParameters(uri, parameters, null);
095 }
096 }
097
098 afterConfiguration(uri, path, endpoint, parameters);
099 return endpoint;
100 }
101
102 /**
103 * Strategy to do post configuration logic.
104 * <p/>
105 * Can be used to construct an URI based on the remaining parameters. For example the parameters that configures
106 * the endpoint have been removed from the parameters which leaves only the additional parameters left.
107 *
108 * @param endpoint the created endpoint
109 * @param parameters the remaining parameters after the endpoint has been created and parsed the parameters
110 * @throws Exception can be thrown to indicate error creating the endpoint
111 */
112 protected void afterConfiguration(String uri, String remaining, Endpoint endpoint, Map<String, Object> parameters) throws Exception {
113 // noop
114 }
115
116 /**
117 * Strategy for validation of parameters, that was not able to be resolved to any endpoint options.
118 *
119 * @param uri the uri - the uri the end user provided untouched
120 * @param parameters the parameters, an empty map if no parameters given
121 * @param optionPrefix optional prefix to filter the parameters for validation. Use <tt>null</tt> for validate all.
122 * @throws ResolveEndpointFailedException should be thrown if the URI validation failed
123 */
124 protected void validateParameters(String uri, Map<String, Object> parameters, String optionPrefix) {
125 Map<String, Object> param = parameters;
126 if (optionPrefix != null) {
127 param = IntrospectionSupport.extractProperties(parameters, optionPrefix);
128 }
129
130 if (param.size() > 0) {
131 throw new ResolveEndpointFailedException(uri, "There are " + param.size()
132 + " parameters that couldn't be set on the endpoint."
133 + " Check the uri if the parameters are spelt correctly and that they are properties of the endpoint."
134 + " Unknown parameters=[" + param + "]");
135 }
136 }
137
138 /**
139 * Strategy for validation of the uri when creating the endpoint.
140 *
141 * @param uri the uri - the uri the end user provided untouched
142 * @param path the path - part after the scheme
143 * @param parameters the parameters, an empty map if no parameters given
144 * @throws ResolveEndpointFailedException should be thrown if the URI validation failed
145 */
146 protected void validateURI(String uri, String path, Map<String, Object> parameters) {
147 // check for uri containing & but no ? marker
148 if (uri.contains("&") && !uri.contains("?")) {
149 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: no ? marker however the uri "
150 + "has & parameter separators. Check the uri if its missing a ? marker.");
151 }
152
153 // check for uri containing double && markers
154 if (uri.contains("&&")) {
155 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Double && marker found. "
156 + "Check the uri and remove the duplicate & marker.");
157 }
158
159 // if we have a trailing & then that is invalid as well
160 if (uri.endsWith("&")) {
161 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Trailing & marker found. "
162 + "Check the uri and remove the trailing & marker.");
163 }
164 }
165
166 public CamelContext getCamelContext() {
167 return camelContext;
168 }
169
170 public void setCamelContext(CamelContext context) {
171 this.camelContext = context;
172 }
173
174 public synchronized ExecutorService getExecutorService() {
175 if (executorService == null) {
176 executorService = createScheduledExecutorService();
177 }
178 return executorService;
179 }
180
181 public synchronized void setExecutorService(ExecutorService executorService) {
182 this.executorService = executorService;
183 }
184
185 public synchronized ScheduledExecutorService getScheduledExecutorService() {
186 ExecutorService executor = getExecutorService();
187 if (executor instanceof ScheduledExecutorService) {
188 return (ScheduledExecutorService) executor;
189 } else {
190 return createScheduledExecutorService();
191 }
192 }
193
194 /**
195 * A factory method to create a default thread pool and executor
196 */
197 protected ScheduledExecutorService createScheduledExecutorService() {
198 String name = getClass().getSimpleName();
199 return ExecutorServiceHelper.newScheduledThreadPool(DEFAULT_THREADPOOL_SIZE, name, true);
200 }
201
202 protected void doStart() throws Exception {
203 ObjectHelper.notNull(getCamelContext(), "camelContext");
204 }
205
206 protected void doStop() throws Exception {
207 if (executorService != null) {
208 executorService.shutdown();
209 // must null it so we can restart
210 executorService = null;
211 }
212 }
213
214 /**
215 * A factory method allowing derived components to create a new endpoint
216 * from the given URI, remaining path and optional parameters
217 *
218 * @param uri the full URI of the endpoint
219 * @param remaining the remaining part of the URI without the query
220 * parameters or component prefix
221 * @param parameters the optional parameters passed in
222 * @return a newly created endpoint or null if the endpoint cannot be
223 * created based on the inputs
224 */
225 protected abstract Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters)
226 throws Exception;
227
228 /**
229 * Sets the bean properties on the given bean
230 *
231 * @param bean the bean
232 * @param parameters properties to set
233 */
234 protected void setProperties(Object bean, Map<String, Object> parameters) throws Exception {
235 // set reference properties first as they use # syntax that fools the regular properties setter
236 EndpointHelper.setReferenceProperties(getCamelContext(), bean, parameters);
237 EndpointHelper.setProperties(getCamelContext(), bean, parameters);
238 }
239
240 /**
241 * Derived classes may wish to overload this to prevent the default introspection of URI parameters
242 * on the created Endpoint instance
243 */
244 protected boolean useIntrospectionOnEndpoint() {
245 return true;
246 }
247
248
249 // Some helper methods
250 //-------------------------------------------------------------------------
251
252 /**
253 * Converts the given value to the requested type
254 * @deprecated will be removed in Camel 2.3
255 */
256 @Deprecated
257 public <T> T convertTo(Class<T> type, Object value) {
258 return CamelContextHelper.convertTo(getCamelContext(), type, value);
259 }
260
261 /**
262 * Converts the given value to the specified type throwing an {@link IllegalArgumentException}
263 * if the value could not be converted to a non null value
264 * @deprecated will be removed in Camel 2.3
265 */
266 @Deprecated
267 public <T> T mandatoryConvertTo(Class<T> type, Object value) {
268 return CamelContextHelper.mandatoryConvertTo(getCamelContext(), type, value);
269 }
270
271 /**
272 * Creates a new instance of the given type using the {@link org.apache.camel.spi.Injector} on the given
273 * {@link CamelContext}
274 * @deprecated will be removed in Camel 2.3
275 */
276 @Deprecated
277 public <T> T newInstance(Class<T> beanType) {
278 return getCamelContext().getInjector().newInstance(beanType);
279 }
280
281 /**
282 * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the
283 * {@link CamelContext}
284 * @deprecated will be removed in Camel 2.3
285 */
286 @Deprecated
287 public Object lookup(String name) {
288 return getCamelContext().getRegistry().lookup(name);
289 }
290
291 /**
292 * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the
293 * {@link CamelContext}
294 * @deprecated will be removed in Camel 2.3
295 */
296 @Deprecated
297 public <T> T lookup(String name, Class<T> beanType) {
298 return getCamelContext().getRegistry().lookup(name, beanType);
299 }
300
301 /**
302 * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the
303 * {@link CamelContext} or throws exception if not found.
304 * @deprecated will be removed in Camel 2.3
305 */
306 @Deprecated
307 public Object mandatoryLookup(String name) {
308 return CamelContextHelper.mandatoryLookup(getCamelContext(), name);
309 }
310
311 /**
312 * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the
313 * {@link CamelContext} or throws exception if not found.
314 * @deprecated will be removed in Camel 2.3
315 */
316 @Deprecated
317 public <T> T mandatoryLookup(String name, Class<T> beanType) {
318 return CamelContextHelper.mandatoryLookup(getCamelContext(), name, beanType);
319 }
320
321 /**
322 * Gets the parameter and remove it from the parameter map. This method doesn't resolve
323 * reference parameters in the registry.
324 *
325 * @param parameters the parameters
326 * @param key the key
327 * @param type the requested type to convert the value from the parameter
328 * @return the converted value parameter, <tt>null</tt> if parameter does not exists.
329 * @see #resolveAndRemoveReferenceParameter(Map, String, Class)
330 */
331 public <T> T getAndRemoveParameter(Map<String, Object> parameters, String key, Class<T> type) {
332 return getAndRemoveParameter(parameters, key, type, null);
333 }
334
335 /**
336 * Gets the parameter and remove it from the parameter map. This method doesn't resolve
337 * reference parameters in the registry.
338 *
339 * @param parameters the parameters
340 * @param key the key
341 * @param type the requested type to convert the value from the parameter
342 * @param defaultValue use this default value if the parameter does not contain the key
343 * @return the converted value parameter
344 * @see #resolveAndRemoveReferenceParameter(Map, String, Class, Object)
345 */
346 public <T> T getAndRemoveParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) {
347 Object value = parameters.remove(key);
348 if (value == null) {
349 value = defaultValue;
350 }
351 if (value == null) {
352 return null;
353 }
354
355 return CamelContextHelper.convertTo(getCamelContext(), type, value);
356 }
357
358 /**
359 * Resolves a reference parameter in the registry and removes it from the map.
360 *
361 * @param <T> type of object to lookup in the registry.
362 * @param parameters parameter map.
363 * @param key parameter map key.
364 * @param type type of object to lookup in the registry.
365 * @return the referenced object or <code>null</code> if the parameter map
366 * doesn't contain the key.
367 * @throws IllegalArgumentException if a non-null reference was not found in
368 * registry.
369 */
370 public <T> T resolveAndRemoveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type) {
371 return resolveAndRemoveReferenceParameter(parameters, key, type, null);
372 }
373
374 /**
375 * Resolves a reference parameter in the registry and removes it from the map.
376 *
377 * @param <T> type of object to lookup in the registry.
378 * @param parameters parameter map.
379 * @param key parameter map key.
380 * @param type type of object to lookup in the registry.
381 * @param defaultValue default value to use if the parameter map doesn't
382 * contain the key.
383 * @return the referenced object or the default value.
384 * @throws IllegalArgumentException if referenced object was not found in
385 * registry.
386 */
387 public <T> T resolveAndRemoveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) {
388 String value = getAndRemoveParameter(parameters, key, String.class);
389 if (value == null) {
390 return defaultValue;
391 } else {
392 return EndpointHelper.resolveReferenceParameter(getCamelContext(), value.toString(), type);
393 }
394 }
395
396 /**
397 * Resolves a reference list parameter in the registry and removes it from
398 * the map.
399 *
400 * @param parameters
401 * parameter map.
402 * @param key
403 * parameter map key.
404 * @param elementType
405 * result list element type.
406 * @return the list of referenced objects or an empty list if the parameter
407 * map doesn't contain the key.
408 * @throws IllegalArgumentException if any of the referenced objects was
409 * not found in registry.
410 * @see EndpointHelper#resolveReferenceListParameter(CamelContext, String, Class)
411 */
412 public <T> List<T> resolveAndRemoveReferenceListParameter(Map<String, Object> parameters, String key, Class<T> elementType) {
413 return resolveAndRemoveReferenceListParameter(parameters, key, elementType, new ArrayList<T>(0));
414 }
415
416 /**
417 * Resolves a reference list parameter in the registry and removes it from
418 * the map.
419 *
420 * @param parameters
421 * parameter map.
422 * @param key
423 * parameter map key.
424 * @param elementType
425 * result list element type.
426 * @param defaultValue
427 * default value to use if the parameter map doesn't
428 * contain the key.
429 * @return the list of referenced objects or the default value.
430 * @throws IllegalArgumentException if any of the referenced objects was
431 * not found in registry.
432 * @see EndpointHelper#resolveReferenceListParameter(CamelContext, String, Class)
433 */
434 public <T> List<T> resolveAndRemoveReferenceListParameter(Map<String, Object> parameters, String key, Class<T> elementType, List<T> defaultValue) {
435 String value = getAndRemoveParameter(parameters, key, String.class);
436
437 if (value == null) {
438 return defaultValue;
439 } else {
440 return EndpointHelper.resolveReferenceListParameter(getCamelContext(), value.toString(), elementType);
441 }
442 }
443
444 /**
445 * Returns the reminder of the text if it starts with the prefix.
446 * <p/>
447 * Is useable for string parameters that contains commands.
448 *
449 * @param prefix the prefix
450 * @param text the text
451 * @return the reminder, or null if no reminder
452 */
453 protected String ifStartsWithReturnRemainder(String prefix, String text) {
454 if (text.startsWith(prefix)) {
455 String remainder = text.substring(prefix.length());
456 if (remainder.length() > 0) {
457 return remainder;
458 }
459 }
460 return null;
461 }
462
463 }