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
024 import org.apache.camel.CamelContext;
025 import org.apache.camel.Component;
026 import org.apache.camel.Endpoint;
027 import org.apache.camel.ResolveEndpointFailedException;
028 import org.apache.camel.util.CamelContextHelper;
029 import org.apache.camel.util.EndpointHelper;
030 import org.apache.camel.util.IntrospectionSupport;
031 import org.apache.camel.util.ObjectHelper;
032 import org.apache.camel.util.URISupport;
033 import org.apache.camel.util.UnsafeUriCharactersEncoder;
034 import org.apache.commons.logging.Log;
035 import org.apache.commons.logging.LogFactory;
036
037 /**
038 * Default component to use for base for components implementations.
039 *
040 * @version $Revision: 18297 $
041 */
042 public abstract class DefaultComponent extends ServiceSupport implements Component {
043 private static final transient Log LOG = LogFactory.getLog(DefaultComponent.class);
044
045 private CamelContext camelContext;
046
047 public DefaultComponent() {
048 }
049
050 public DefaultComponent(CamelContext context) {
051 this.camelContext = context;
052 }
053
054 public Endpoint createEndpoint(String uri) throws Exception {
055 ObjectHelper.notNull(getCamelContext(), "camelContext");
056 //encode URI string to the unsafe URI characters
057 URI u = new URI(UnsafeUriCharactersEncoder.encode(uri));
058 String path = u.getSchemeSpecificPart();
059
060 // lets trim off any query arguments
061 if (path.startsWith("//")) {
062 path = path.substring(2);
063 }
064 int idx = path.indexOf('?');
065 if (idx > 0) {
066 path = path.substring(0, idx);
067 }
068 Map<String, Object> parameters = URISupport.parseParameters(u);
069
070 validateURI(uri, path, parameters);
071
072 if (LOG.isDebugEnabled()) {
073 LOG.debug("Creating endpoint uri=[" + uri + "], path=[" + path + "], parameters=[" + parameters + "]");
074 }
075 Endpoint endpoint = createEndpoint(uri, path, parameters);
076 if (endpoint == null) {
077 return null;
078 }
079
080 if (parameters != null) {
081 endpoint.configureProperties(parameters);
082 if (useIntrospectionOnEndpoint()) {
083 setProperties(endpoint, parameters);
084 }
085
086 // if endpoint is strict (not lenient) and we have unknown parameters configured then
087 // fail if there are parameters that could not be set, then they are probably misspell or not supported at all
088 if (!endpoint.isLenientProperties()) {
089 validateParameters(uri, parameters, null);
090 }
091 }
092
093 afterConfiguration(uri, path, endpoint, parameters);
094 return endpoint;
095 }
096
097 /**
098 * Strategy to do post configuration logic.
099 * <p/>
100 * Can be used to construct an URI based on the remaining parameters. For example the parameters that configures
101 * the endpoint have been removed from the parameters which leaves only the additional parameters left.
102 *
103 * @param endpoint the created endpoint
104 * @param parameters the remaining parameters after the endpoint has been created and parsed the parameters
105 * @throws Exception can be thrown to indicate error creating the endpoint
106 */
107 protected void afterConfiguration(String uri, String remaining, Endpoint endpoint, Map<String, Object> parameters) throws Exception {
108 // noop
109 }
110
111 /**
112 * Strategy for validation of parameters, that was not able to be resolved to any endpoint options.
113 *
114 * @param uri the uri - the uri the end user provided untouched
115 * @param parameters the parameters, an empty map if no parameters given
116 * @param optionPrefix optional prefix to filter the parameters for validation. Use <tt>null</tt> for validate all.
117 * @throws ResolveEndpointFailedException should be thrown if the URI validation failed
118 */
119 protected void validateParameters(String uri, Map<String, Object> parameters, String optionPrefix) {
120 Map<String, Object> param = parameters;
121 if (optionPrefix != null) {
122 param = IntrospectionSupport.extractProperties(parameters, optionPrefix);
123 }
124
125 if (param.size() > 0) {
126 throw new ResolveEndpointFailedException(uri, "There are " + param.size()
127 + " parameters that couldn't be set on the endpoint."
128 + " Check the uri if the parameters are spelt correctly and that they are properties of the endpoint."
129 + " Unknown parameters=[" + param + "]");
130 }
131 }
132
133 /**
134 * Strategy for validation of the uri when creating the endpoint.
135 *
136 * @param uri the uri - the uri the end user provided untouched
137 * @param path the path - part after the scheme
138 * @param parameters the parameters, an empty map if no parameters given
139 * @throws ResolveEndpointFailedException should be thrown if the URI validation failed
140 */
141 protected void validateURI(String uri, String path, Map<String, Object> parameters) {
142 // check for uri containing & but no ? marker
143 if (uri.contains("&") && !uri.contains("?")) {
144 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: no ? marker however the uri "
145 + "has & parameter separators. Check the uri if its missing a ? marker.");
146 }
147
148 // check for uri containing double && markers
149 if (uri.contains("&&")) {
150 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Double && marker found. "
151 + "Check the uri and remove the duplicate & marker.");
152 }
153
154 // if we have a trailing & then that is invalid as well
155 if (uri.endsWith("&")) {
156 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Trailing & marker found. "
157 + "Check the uri and remove the trailing & marker.");
158 }
159 }
160
161 public CamelContext getCamelContext() {
162 return camelContext;
163 }
164
165 public void setCamelContext(CamelContext context) {
166 this.camelContext = context;
167 }
168
169 protected void doStart() throws Exception {
170 ObjectHelper.notNull(getCamelContext(), "camelContext");
171 }
172
173 protected void doStop() throws Exception {
174 // noop
175 }
176
177 /**
178 * A factory method allowing derived components to create a new endpoint
179 * from the given URI, remaining path and optional parameters
180 *
181 * @param uri the full URI of the endpoint
182 * @param remaining the remaining part of the URI without the query
183 * parameters or component prefix
184 * @param parameters the optional parameters passed in
185 * @return a newly created endpoint or null if the endpoint cannot be
186 * created based on the inputs
187 */
188 protected abstract Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters)
189 throws Exception;
190
191 /**
192 * Sets the bean properties on the given bean
193 *
194 * @param bean the bean
195 * @param parameters properties to set
196 */
197 protected void setProperties(Object bean, Map<String, Object> parameters) throws Exception {
198 // set reference properties first as they use # syntax that fools the regular properties setter
199 EndpointHelper.setReferenceProperties(getCamelContext(), bean, parameters);
200 EndpointHelper.setProperties(getCamelContext(), bean, parameters);
201 }
202
203 /**
204 * Derived classes may wish to overload this to prevent the default introspection of URI parameters
205 * on the created Endpoint instance
206 */
207 protected boolean useIntrospectionOnEndpoint() {
208 return true;
209 }
210
211 /**
212 * Gets the parameter and remove it from the parameter map. This method doesn't resolve
213 * reference parameters in the registry.
214 *
215 * @param parameters the parameters
216 * @param key the key
217 * @param type the requested type to convert the value from the parameter
218 * @return the converted value parameter, <tt>null</tt> if parameter does not exists.
219 * @see #resolveAndRemoveReferenceParameter(Map, String, Class)
220 */
221 public <T> T getAndRemoveParameter(Map<String, Object> parameters, String key, Class<T> type) {
222 return getAndRemoveParameter(parameters, key, type, null);
223 }
224
225 /**
226 * Gets the parameter and remove it from the parameter map. This method doesn't resolve
227 * reference parameters in the registry.
228 *
229 * @param parameters the parameters
230 * @param key the key
231 * @param type the requested type to convert the value from the parameter
232 * @param defaultValue use this default value if the parameter does not contain the key
233 * @return the converted value parameter
234 * @see #resolveAndRemoveReferenceParameter(Map, String, Class, Object)
235 */
236 public <T> T getAndRemoveParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) {
237 Object value = parameters.remove(key);
238 if (value == null) {
239 value = defaultValue;
240 }
241 if (value == null) {
242 return null;
243 }
244
245 return CamelContextHelper.convertTo(getCamelContext(), type, value);
246 }
247
248 /**
249 * Resolves a reference parameter in the registry and removes it from the map.
250 *
251 * @param <T> type of object to lookup in the registry.
252 * @param parameters parameter map.
253 * @param key parameter map key.
254 * @param type type of object to lookup in the registry.
255 * @return the referenced object or <code>null</code> if the parameter map
256 * doesn't contain the key.
257 * @throws IllegalArgumentException if a non-null reference was not found in
258 * registry.
259 */
260 public <T> T resolveAndRemoveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type) {
261 return resolveAndRemoveReferenceParameter(parameters, key, type, null);
262 }
263
264 /**
265 * Resolves a reference parameter in the registry and removes it from the map.
266 *
267 * @param <T> type of object to lookup in the registry.
268 * @param parameters parameter map.
269 * @param key parameter map key.
270 * @param type type of object to lookup in the registry.
271 * @param defaultValue default value to use if the parameter map doesn't
272 * contain the key.
273 * @return the referenced object or the default value.
274 * @throws IllegalArgumentException if referenced object was not found in
275 * registry.
276 */
277 public <T> T resolveAndRemoveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) {
278 String value = getAndRemoveParameter(parameters, key, String.class);
279 if (value == null) {
280 return defaultValue;
281 } else {
282 return EndpointHelper.resolveReferenceParameter(getCamelContext(), value.toString(), type);
283 }
284 }
285
286 /**
287 * Resolves a reference list parameter in the registry and removes it from
288 * the map.
289 *
290 * @param parameters
291 * parameter map.
292 * @param key
293 * parameter map key.
294 * @param elementType
295 * result list element type.
296 * @return the list of referenced objects or an empty list if the parameter
297 * map doesn't contain the key.
298 * @throws IllegalArgumentException if any of the referenced objects was
299 * not found in registry.
300 * @see EndpointHelper#resolveReferenceListParameter(CamelContext, String, Class)
301 */
302 public <T> List<T> resolveAndRemoveReferenceListParameter(Map<String, Object> parameters, String key, Class<T> elementType) {
303 return resolveAndRemoveReferenceListParameter(parameters, key, elementType, new ArrayList<T>(0));
304 }
305
306 /**
307 * Resolves a reference list parameter in the registry and removes it from
308 * the map.
309 *
310 * @param parameters
311 * parameter map.
312 * @param key
313 * parameter map key.
314 * @param elementType
315 * result list element type.
316 * @param defaultValue
317 * default value to use if the parameter map doesn't
318 * contain the key.
319 * @return the list of referenced objects or the default value.
320 * @throws IllegalArgumentException if any of the referenced objects was
321 * not found in registry.
322 * @see EndpointHelper#resolveReferenceListParameter(CamelContext, String, Class)
323 */
324 public <T> List<T> resolveAndRemoveReferenceListParameter(Map<String, Object> parameters, String key, Class<T> elementType, List<T> defaultValue) {
325 String value = getAndRemoveParameter(parameters, key, String.class);
326
327 if (value == null) {
328 return defaultValue;
329 } else {
330 return EndpointHelper.resolveReferenceListParameter(getCamelContext(), value.toString(), elementType);
331 }
332 }
333
334 /**
335 * Returns the reminder of the text if it starts with the prefix.
336 * <p/>
337 * Is useable for string parameters that contains commands.
338 *
339 * @param prefix the prefix
340 * @param text the text
341 * @return the reminder, or null if no reminder
342 */
343 protected String ifStartsWithReturnRemainder(String prefix, String text) {
344 if (text.startsWith(prefix)) {
345 String remainder = text.substring(prefix.length());
346 if (remainder.length() > 0) {
347 return remainder;
348 }
349 }
350 return null;
351 }
352
353 }