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.Map;
021 import java.util.concurrent.ScheduledExecutorService;
022 import java.util.concurrent.ScheduledThreadPoolExecutor;
023 import java.util.concurrent.ThreadFactory;
024
025 import org.apache.camel.CamelContext;
026 import org.apache.camel.Component;
027 import org.apache.camel.Endpoint;
028 import org.apache.camel.Exchange;
029 import org.apache.camel.ResolveEndpointFailedException;
030 import org.apache.camel.spi.Injector;
031 import org.apache.camel.spi.Registry;
032 import org.apache.camel.util.CamelContextHelper;
033 import org.apache.camel.util.IntrospectionSupport;
034 import org.apache.camel.util.ObjectHelper;
035 import org.apache.camel.util.URISupport;
036 import org.apache.camel.util.UnsafeUriCharactersEncoder;
037 import org.apache.commons.logging.Log;
038 import org.apache.commons.logging.LogFactory;
039
040
041 /**
042 * Default component to use for base for components implementations.
043 *
044 * @version $Revision: 706013 $
045 */
046 public abstract class DefaultComponent<E extends Exchange> extends ServiceSupport implements Component<E> {
047 private static final transient Log LOG = LogFactory.getLog(DefaultComponent.class);
048
049 private int defaultThreadPoolSize = 5;
050 private CamelContext camelContext;
051 private ScheduledExecutorService executorService;
052
053 public DefaultComponent() {
054 }
055
056 public DefaultComponent(CamelContext context) {
057 this.camelContext = context;
058 }
059
060 public Endpoint<E> createEndpoint(String uri) throws Exception {
061 ObjectHelper.notNull(getCamelContext(), "camelContext");
062 //encode URI string to the unsafe URI characters
063 URI u = new URI(UnsafeUriCharactersEncoder.encode(uri));
064 String path = u.getSchemeSpecificPart();
065
066 // lets trim off any query arguments
067 if (path.startsWith("//")) {
068 path = path.substring(2);
069 }
070 int idx = path.indexOf('?');
071 if (idx > 0) {
072 path = path.substring(0, idx);
073 }
074 Map parameters = URISupport.parseParameters(u);
075
076 validateURI(uri, path, parameters);
077
078 if (LOG.isDebugEnabled()) {
079 LOG.debug("Creating endpoint uri=[" + uri + "], path=[" + path + "], parameters=[" + parameters + "]");
080 }
081 Endpoint<E> endpoint = createEndpoint(uri, path, parameters);
082 if (endpoint == null) {
083 return null;
084 }
085
086 if (parameters != null) {
087 endpoint.configureProperties(parameters);
088 if (useIntrospectionOnEndpoint()) {
089 setProperties(endpoint, parameters);
090 }
091
092 // if endpoint is strict (not lenient) and we have unknown parameters configured then
093 // fail if there are parameters that could not be set, then they are probably miss spelt or not supported at all
094 if (!endpoint.isLenientProperties() && parameters.size() > 0) {
095 throw new ResolveEndpointFailedException(uri, "There are " + parameters.size()
096 + " parameters that couldn't be set on the endpoint."
097 + " Check the uri if the parameters are spelt correctly and that they are properties of the endpoint."
098 + " Unknown parameters=[" + parameters + "]");
099 }
100 }
101
102 return endpoint;
103 }
104
105 /**
106 * Strategy for validation of the uri when creating the endpoint.
107 *
108 * @param uri the uri - the uri the end user provided untouched
109 * @param path the path - part after the scheme
110 * @param parameters the parameters, an empty map if no parameters given
111 * @throws ResolveEndpointFailedException should be thrown if the URI validation failed
112 */
113 protected void validateURI(String uri, String path, Map parameters) throws ResolveEndpointFailedException {
114 // check for uri containing & but no ? marker
115 if (uri.contains("&") && !uri.contains("?")) {
116 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: no ? marker however the uri "
117 + "has & parameter separators. Check the uri if its missing a ? marker.");
118 }
119
120 // check for uri containing double && markers
121 if (uri.contains("&&")) {
122 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Double && marker found. "
123 + "Check the uri and remove the duplicate & marker.");
124 }
125 }
126
127 public CamelContext getCamelContext() {
128 return camelContext;
129 }
130
131 public void setCamelContext(CamelContext context) {
132 this.camelContext = context;
133 }
134
135 public ScheduledExecutorService getExecutorService() {
136 if (executorService == null) {
137 executorService = createExecutorService();
138 }
139 return executorService;
140 }
141
142 public void setExecutorService(ScheduledExecutorService executorService) {
143 this.executorService = executorService;
144 }
145
146 /**
147 * A factory method to create a default thread pool and executor
148 */
149 protected ScheduledExecutorService createExecutorService() {
150 return new ScheduledThreadPoolExecutor(defaultThreadPoolSize, new ThreadFactory() {
151 int counter;
152
153 public synchronized Thread newThread(Runnable runnable) {
154 Thread thread = new Thread(runnable);
155 thread.setName("Thread: " + (++counter) + " " + DefaultComponent.this.toString());
156 return thread;
157 }
158 });
159 }
160
161 protected void doStart() throws Exception {
162 ObjectHelper.notNull(getCamelContext(), "camelContext");
163 }
164
165 protected void doStop() throws Exception {
166 if (executorService != null) {
167 executorService.shutdown();
168 }
169 }
170
171 /**
172 * A factory method allowing derived components to create a new endpoint
173 * from the given URI, remaining path and optional parameters
174 *
175 * @param uri the full URI of the endpoint
176 * @param remaining the remaining part of the URI without the query
177 * parameters or component prefix
178 * @param parameters the optional parameters passed in
179 * @return a newly created endpoint or null if the endpoint cannot be
180 * created based on the inputs
181 */
182 protected abstract Endpoint<E> createEndpoint(String uri, String remaining, Map parameters)
183 throws Exception;
184
185 /**
186 * Sets the bean properties on the given bean
187 */
188 protected void setProperties(Object bean, Map parameters) throws Exception {
189 IntrospectionSupport.setProperties(getCamelContext().getTypeConverter(), bean, parameters);
190 }
191
192 /**
193 * Derived classes may wish to overload this to prevent the default introspection of URI parameters
194 * on the created Endpoint instance
195 */
196 protected boolean useIntrospectionOnEndpoint() {
197 return true;
198 }
199
200
201 // Some helper methods
202 //-------------------------------------------------------------------------
203
204 /**
205 * Converts the given value to the requested type
206 */
207 public <T> T convertTo(Class<T> type, Object value) {
208 return CamelContextHelper.convertTo(getCamelContext(), type, value);
209 }
210
211 /**
212 * Converts the given value to the specified type throwing an {@link IllegalArgumentException}
213 * if the value could not be converted to a non null value
214 */
215 public <T> T mandatoryConvertTo(Class<T> type, Object value) {
216 return CamelContextHelper.mandatoryConvertTo(getCamelContext(), type, value);
217 }
218
219 /**
220 * Creates a new instance of the given type using the {@link Injector} on the given
221 * {@link CamelContext}
222 */
223 public <T> T newInstance(Class<T> beanType) {
224 return getCamelContext().getInjector().newInstance(beanType);
225 }
226
227 /**
228 * Look up the given named bean in the {@link Registry} on the
229 * {@link CamelContext}
230 */
231 public Object lookup(String name) {
232 return getCamelContext().getRegistry().lookup(name);
233 }
234
235 /**
236 * Look up the given named bean of the given type in the {@link Registry} on the
237 * {@link CamelContext}
238 */
239 public <T> T lookup(String name, Class<T> beanType) {
240 return getCamelContext().getRegistry().lookup(name, beanType);
241 }
242
243 /**
244 * Look up the given named bean in the {@link Registry} on the
245 * {@link CamelContext} or throws
246 */
247 public Object mandatoryLookup(String name) {
248 return CamelContextHelper.mandatoryLookup(getCamelContext(), name);
249 }
250
251 /**
252 * Look up the given named bean of the given type in the {@link Registry} on the
253 * {@link CamelContext}
254 */
255 public <T> T mandatoryLookup(String name, Class<T> beanType) {
256 return CamelContextHelper.mandatoryLookup(getCamelContext(), name, beanType);
257 }
258
259 /**
260 * Gets the parameter and remove it from the parameter map.
261 *
262 * @param parameters the parameters
263 * @param key the key
264 * @param type the requested type to convert the value from the parameter
265 * @return the converted value parameter, <tt>null</tt> if parameter does not exists.
266 */
267 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type) {
268 return getAndRemoveParameter(parameters, key, type, null);
269 }
270
271 /**
272 * Gets the parameter and remove it from the parameter map.
273 *
274 * @param parameters the parameters
275 * @param key the key
276 * @param type the requested type to convert the value from the parameter
277 * @param defaultValue use this default value if the parameter does not contain the key
278 * @return the converted value parameter
279 */
280 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type, T defaultValue) {
281 Object value = parameters.remove(key);
282 if (value == null) {
283 value = defaultValue;
284 }
285 if (value == null) {
286 return null;
287 }
288 return convertTo(type, value);
289 }
290
291 }