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 // set reference properties first as they use # syntax that fools the regular properties setter 256 EndpointHelper.setReferenceProperties(getCamelContext(), bean, parameters); 257 EndpointHelper.setProperties(getCamelContext(), bean, parameters); 258 } 259 260 /** 261 * Derived classes may wish to overload this to prevent the default introspection of URI parameters 262 * on the created Endpoint instance 263 */ 264 protected boolean useIntrospectionOnEndpoint() { 265 return true; 266 } 267 268 /** 269 * Gets the parameter and remove it from the parameter map. This method doesn't resolve 270 * reference parameters in the registry. 271 * 272 * @param parameters the parameters 273 * @param key the key 274 * @param type the requested type to convert the value from the parameter 275 * @return the converted value parameter, <tt>null</tt> if parameter does not exists. 276 * @see #resolveAndRemoveReferenceParameter(Map, String, Class) 277 */ 278 public <T> T getAndRemoveParameter(Map<String, Object> parameters, String key, Class<T> type) { 279 return getAndRemoveParameter(parameters, key, type, null); 280 } 281 282 /** 283 * Gets the parameter and remove it from the parameter map. This method doesn't resolve 284 * reference parameters in the registry. 285 * 286 * @param parameters the parameters 287 * @param key the key 288 * @param type the requested type to convert the value from the parameter 289 * @param defaultValue use this default value if the parameter does not contain the key 290 * @return the converted value parameter 291 * @see #resolveAndRemoveReferenceParameter(Map, String, Class, Object) 292 */ 293 public <T> T getAndRemoveParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) { 294 Object value = parameters.remove(key); 295 if (value == null) { 296 value = defaultValue; 297 } 298 if (value == null) { 299 return null; 300 } 301 302 return CamelContextHelper.convertTo(getCamelContext(), type, value); 303 } 304 305 /** 306 * Gets the parameter and remove it from the parameter map. This method resolves 307 * reference parameters in the registry as well. 308 * 309 * @param parameters the parameters 310 * @param key the key 311 * @param type the requested type to convert the value from the parameter 312 * @return the converted value parameter 313 */ 314 public <T> T getAndRemoveOrResolveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type) { 315 return getAndRemoveOrResolveReferenceParameter(parameters, key, type, null); 316 } 317 318 /** 319 * Gets the parameter and remove it from the parameter map. This method resolves 320 * reference parameters in the registry as well. 321 * 322 * @param parameters the parameters 323 * @param key the key 324 * @param type the requested type to convert the value from the parameter 325 * @param defaultValue use this default value if the parameter does not contain the key 326 * @return the converted value parameter 327 */ 328 public <T> T getAndRemoveOrResolveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) { 329 String value = getAndRemoveParameter(parameters, key, String.class); 330 if (value == null) { 331 return defaultValue; 332 } else if (EndpointHelper.isReferenceParameter(value)) { 333 return EndpointHelper.resolveReferenceParameter(getCamelContext(), value, type); 334 } else { 335 return getCamelContext().getTypeConverter().convertTo(type, value); 336 } 337 } 338 339 /** 340 * Resolves a reference parameter in the registry and removes it from the map. 341 * 342 * @param <T> type of object to lookup in the registry. 343 * @param parameters parameter map. 344 * @param key parameter map key. 345 * @param type type of object to lookup in the registry. 346 * @return the referenced object or <code>null</code> if the parameter map 347 * doesn't contain the key. 348 * @throws IllegalArgumentException if a non-null reference was not found in 349 * registry. 350 */ 351 public <T> T resolveAndRemoveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type) { 352 return resolveAndRemoveReferenceParameter(parameters, key, type, null); 353 } 354 355 /** 356 * Resolves a reference parameter in the registry and removes it from the map. 357 * 358 * @param <T> type of object to lookup in the registry. 359 * @param parameters parameter map. 360 * @param key parameter map key. 361 * @param type type of object to lookup in the registry. 362 * @param defaultValue default value to use if the parameter map doesn't 363 * contain the key. 364 * @return the referenced object or the default value. 365 * @throws IllegalArgumentException if referenced object was not found in 366 * registry. 367 */ 368 public <T> T resolveAndRemoveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) { 369 String value = getAndRemoveParameter(parameters, key, String.class); 370 if (value == null) { 371 return defaultValue; 372 } else { 373 return EndpointHelper.resolveReferenceParameter(getCamelContext(), value.toString(), type); 374 } 375 } 376 377 /** 378 * Resolves a reference list parameter in the registry and removes it from 379 * the map. 380 * 381 * @param parameters 382 * parameter map. 383 * @param key 384 * parameter map key. 385 * @param elementType 386 * result list element type. 387 * @return the list of referenced objects or an empty list if the parameter 388 * map doesn't contain the key. 389 * @throws IllegalArgumentException if any of the referenced objects was 390 * not found in registry. 391 * @see EndpointHelper#resolveReferenceListParameter(CamelContext, String, Class) 392 */ 393 public <T> List<T> resolveAndRemoveReferenceListParameter(Map<String, Object> parameters, String key, Class<T> elementType) { 394 return resolveAndRemoveReferenceListParameter(parameters, key, elementType, new ArrayList<T>(0)); 395 } 396 397 /** 398 * Resolves a reference list parameter in the registry and removes it from 399 * the map. 400 * 401 * @param parameters 402 * parameter map. 403 * @param key 404 * parameter map key. 405 * @param elementType 406 * result list element type. 407 * @param defaultValue 408 * default value to use if the parameter map doesn't 409 * contain the key. 410 * @return the list of referenced objects or the default value. 411 * @throws IllegalArgumentException if any of the referenced objects was 412 * not found in registry. 413 * @see EndpointHelper#resolveReferenceListParameter(CamelContext, String, Class) 414 */ 415 public <T> List<T> resolveAndRemoveReferenceListParameter(Map<String, Object> parameters, String key, Class<T> elementType, List<T> defaultValue) { 416 String value = getAndRemoveParameter(parameters, key, String.class); 417 418 if (value == null) { 419 return defaultValue; 420 } else { 421 return EndpointHelper.resolveReferenceListParameter(getCamelContext(), value.toString(), elementType); 422 } 423 } 424 425 /** 426 * Returns the reminder of the text if it starts with the prefix. 427 * <p/> 428 * Is useable for string parameters that contains commands. 429 * 430 * @param prefix the prefix 431 * @param text the text 432 * @return the reminder, or null if no reminder 433 */ 434 protected String ifStartsWithReturnRemainder(String prefix, String text) { 435 if (text.startsWith(prefix)) { 436 String remainder = text.substring(prefix.length()); 437 if (remainder.length() > 0) { 438 return remainder; 439 } 440 } 441 return null; 442 } 443 444}