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