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