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