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 }