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 & but no ? marker 200 if (uri.contains("&") && !uri.contains("?")) { 201 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: no ? marker however the uri " 202 + "has & parameter separators. Check the uri if its missing a ? marker."); 203 } 204 205 // check for uri containing double && markers without include by RAW 206 if (uri.contains("&&")) { 207 Pattern pattern = Pattern.compile("RAW(.*&&.*)"); 208 Matcher m = pattern.matcher(uri); 209 // we should skip the RAW part 210 if (!m.find()) { 211 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Double && marker found. " 212 + "Check the uri and remove the duplicate & marker."); 213 } 214 } 215 216 // if we have a trailing & then that is invalid as well 217 if (uri.endsWith("&")) { 218 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Trailing & marker found. " 219 + "Check the uri and remove the trailing & marker."); 220 } 221 } 222 223 public CamelContext getCamelContext() { 224 return camelContext; 225 } 226 227 public void setCamelContext(CamelContext context) { 228 this.camelContext = context; 229 } 230 231 protected void doStart() throws Exception { 232 ObjectHelper.notNull(getCamelContext(), "camelContext"); 233 } 234 235 protected void doStop() throws Exception { 236 // noop 237 } 238 239 /** 240 * A factory method allowing derived components to create a new endpoint 241 * from the given URI, remaining path and optional parameters 242 * 243 * @param uri the full URI of the endpoint 244 * @param remaining the remaining part of the URI without the query 245 * parameters or component prefix 246 * @param parameters the optional parameters passed in 247 * @return a newly created endpoint or null if the endpoint cannot be 248 * created based on the inputs 249 * @throws Exception is thrown if error creating the endpoint 250 */ 251 protected abstract Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) 252 throws Exception; 253 254 /** 255 * Sets the bean properties on the given bean 256 * 257 * @param bean the bean 258 * @param parameters properties to set 259 */ 260 protected void setProperties(Object bean, Map<String, Object> parameters) throws Exception { 261 // set reference properties first as they use # syntax that fools the regular properties setter 262 EndpointHelper.setReferenceProperties(getCamelContext(), bean, parameters); 263 EndpointHelper.setProperties(getCamelContext(), bean, parameters); 264 } 265 266 /** 267 * Derived classes may wish to overload this to prevent the default introspection of URI parameters 268 * on the created Endpoint instance 269 */ 270 protected boolean useIntrospectionOnEndpoint() { 271 return true; 272 } 273 274 /** 275 * Gets the parameter and remove it from the parameter map. This method doesn't resolve 276 * reference parameters in the registry. 277 * 278 * @param parameters the parameters 279 * @param key the key 280 * @param type the requested type to convert the value from the parameter 281 * @return the converted value parameter, <tt>null</tt> if parameter does not exists. 282 * @see #resolveAndRemoveReferenceParameter(Map, String, Class) 283 */ 284 public <T> T getAndRemoveParameter(Map<String, Object> parameters, String key, Class<T> type) { 285 return getAndRemoveParameter(parameters, key, type, null); 286 } 287 288 /** 289 * Gets the parameter and remove it from the parameter map. This method doesn't resolve 290 * reference parameters in the registry. 291 * 292 * @param parameters the parameters 293 * @param key the key 294 * @param type the requested type to convert the value from the parameter 295 * @param defaultValue use this default value if the parameter does not contain the key 296 * @return the converted value parameter 297 * @see #resolveAndRemoveReferenceParameter(Map, String, Class, Object) 298 */ 299 public <T> T getAndRemoveParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) { 300 Object value = parameters.remove(key); 301 if (value == null) { 302 value = defaultValue; 303 } 304 if (value == null) { 305 return null; 306 } 307 308 return CamelContextHelper.convertTo(getCamelContext(), type, value); 309 } 310 311 /** 312 * Gets the parameter and remove it from the parameter map. This method resolves 313 * reference parameters in the registry as well. 314 * 315 * @param parameters the parameters 316 * @param key the key 317 * @param type the requested type to convert the value from the parameter 318 * @return the converted value parameter 319 */ 320 public <T> T getAndRemoveOrResolveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type) { 321 return getAndRemoveOrResolveReferenceParameter(parameters, key, type, null); 322 } 323 324 /** 325 * Gets the parameter and remove it from the parameter map. This method resolves 326 * reference parameters in the registry as well. 327 * 328 * @param parameters the parameters 329 * @param key the key 330 * @param type the requested type to convert the value from the parameter 331 * @param defaultValue use this default value if the parameter does not contain the key 332 * @return the converted value parameter 333 */ 334 public <T> T getAndRemoveOrResolveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) { 335 String value = getAndRemoveParameter(parameters, key, String.class); 336 if (value == null) { 337 return defaultValue; 338 } else if (EndpointHelper.isReferenceParameter(value)) { 339 return EndpointHelper.resolveReferenceParameter(getCamelContext(), value, type); 340 } else { 341 return getCamelContext().getTypeConverter().convertTo(type, value); 342 } 343 } 344 345 /** 346 * Resolves a reference parameter in the registry and removes it from the map. 347 * 348 * @param <T> type of object to lookup in the registry. 349 * @param parameters parameter map. 350 * @param key parameter map key. 351 * @param type type of object to lookup in the registry. 352 * @return the referenced object or <code>null</code> if the parameter map 353 * doesn't contain the key. 354 * @throws IllegalArgumentException if a non-null reference was not found in 355 * registry. 356 */ 357 public <T> T resolveAndRemoveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type) { 358 return resolveAndRemoveReferenceParameter(parameters, key, type, null); 359 } 360 361 /** 362 * Resolves a reference parameter in the registry and removes it from the map. 363 * 364 * @param <T> type of object to lookup in the registry. 365 * @param parameters parameter map. 366 * @param key parameter map key. 367 * @param type type of object to lookup in the registry. 368 * @param defaultValue default value to use if the parameter map doesn't 369 * contain the key. 370 * @return the referenced object or the default value. 371 * @throws IllegalArgumentException if referenced object was not found in 372 * registry. 373 */ 374 public <T> T resolveAndRemoveReferenceParameter(Map<String, Object> parameters, String key, Class<T> type, T defaultValue) { 375 String value = getAndRemoveParameter(parameters, key, String.class); 376 if (value == null) { 377 return defaultValue; 378 } else { 379 return EndpointHelper.resolveReferenceParameter(getCamelContext(), value.toString(), type); 380 } 381 } 382 383 /** 384 * Resolves a reference list parameter in the registry and removes it from 385 * the map. 386 * 387 * @param parameters 388 * parameter map. 389 * @param key 390 * parameter map key. 391 * @param elementType 392 * result list element type. 393 * @return the list of referenced objects or an empty list if the parameter 394 * map doesn't contain the key. 395 * @throws IllegalArgumentException if any of the referenced objects was 396 * not found in registry. 397 * @see EndpointHelper#resolveReferenceListParameter(CamelContext, String, Class) 398 */ 399 public <T> List<T> resolveAndRemoveReferenceListParameter(Map<String, Object> parameters, String key, Class<T> elementType) { 400 return resolveAndRemoveReferenceListParameter(parameters, key, elementType, new ArrayList<T>(0)); 401 } 402 403 /** 404 * Resolves a reference list parameter in the registry and removes it from 405 * the map. 406 * 407 * @param parameters 408 * parameter map. 409 * @param key 410 * parameter map key. 411 * @param elementType 412 * result list element type. 413 * @param defaultValue 414 * default value to use if the parameter map doesn't 415 * contain the key. 416 * @return the list of referenced objects or the default value. 417 * @throws IllegalArgumentException if any of the referenced objects was 418 * not found in registry. 419 * @see EndpointHelper#resolveReferenceListParameter(CamelContext, String, Class) 420 */ 421 public <T> List<T> resolveAndRemoveReferenceListParameter(Map<String, Object> parameters, String key, Class<T> elementType, List<T> defaultValue) { 422 String value = getAndRemoveParameter(parameters, key, String.class); 423 424 if (value == null) { 425 return defaultValue; 426 } else { 427 return EndpointHelper.resolveReferenceListParameter(getCamelContext(), value.toString(), elementType); 428 } 429 } 430 431 /** 432 * Returns the reminder of the text if it starts with the prefix. 433 * <p/> 434 * Is useable for string parameters that contains commands. 435 * 436 * @param prefix the prefix 437 * @param text the text 438 * @return the reminder, or null if no reminder 439 */ 440 protected String ifStartsWithReturnRemainder(String prefix, String text) { 441 if (text.startsWith(prefix)) { 442 String remainder = text.substring(prefix.length()); 443 if (remainder.length() > 0) { 444 return remainder; 445 } 446 } 447 return null; 448 } 449 450}