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.Iterator; 021 import java.util.Map; 022 import java.util.concurrent.ExecutorService; 023 import java.util.concurrent.ScheduledExecutorService; 024 025 import org.apache.camel.CamelContext; 026 import org.apache.camel.Component; 027 import org.apache.camel.Endpoint; 028 import org.apache.camel.ResolveEndpointFailedException; 029 import org.apache.camel.util.CamelContextHelper; 030 import org.apache.camel.util.IntrospectionSupport; 031 import org.apache.camel.util.ObjectHelper; 032 import org.apache.camel.util.URISupport; 033 import org.apache.camel.util.UnsafeUriCharactersEncoder; 034 import org.apache.camel.util.concurrent.ExecutorServiceHelper; 035 import org.apache.commons.logging.Log; 036 import org.apache.commons.logging.LogFactory; 037 038 039 /** 040 * Default component to use for base for components implementations. 041 * 042 * @version $Revision: 777808 $ 043 */ 044 public abstract class DefaultComponent extends ServiceSupport implements Component { 045 private static final transient Log LOG = LogFactory.getLog(DefaultComponent.class); 046 047 private static final int DEFAULT_THREADPOOL_SIZE = 5; 048 private CamelContext camelContext; 049 private ExecutorService executorService; 050 051 public DefaultComponent() { 052 } 053 054 public DefaultComponent(CamelContext context) { 055 this.camelContext = context; 056 } 057 058 public Endpoint createEndpoint(String uri) throws Exception { 059 ObjectHelper.notNull(getCamelContext(), "camelContext"); 060 //encode URI string to the unsafe URI characters 061 URI u = new URI(UnsafeUriCharactersEncoder.encode(uri)); 062 String path = u.getSchemeSpecificPart(); 063 064 // lets trim off any query arguments 065 if (path.startsWith("//")) { 066 path = path.substring(2); 067 } 068 int idx = path.indexOf('?'); 069 if (idx > 0) { 070 path = path.substring(0, idx); 071 } 072 Map parameters = URISupport.parseParameters(u); 073 074 validateURI(uri, path, parameters); 075 076 if (LOG.isDebugEnabled()) { 077 LOG.debug("Creating endpoint uri=[" + uri + "], path=[" + path + "], parameters=[" + parameters + "]"); 078 } 079 Endpoint endpoint = createEndpoint(uri, path, parameters); 080 if (endpoint == null) { 081 return null; 082 } 083 084 if (parameters != null) { 085 endpoint.configureProperties(parameters); 086 if (useIntrospectionOnEndpoint()) { 087 setProperties(endpoint, parameters); 088 } 089 090 // if endpoint is strict (not lenient) and we have unknown parameters configured then 091 // fail if there are parameters that could not be set, then they are probably miss spelt or not supported at all 092 if (!endpoint.isLenientProperties()) { 093 validateParameters(uri, parameters, null); 094 } 095 } 096 097 return endpoint; 098 } 099 100 /** 101 * Strategy for validation of parameters, that was not able to be resolved to any endpoint options. 102 * 103 * @param uri the uri - the uri the end user provided untouched 104 * @param parameters the parameters, an empty map if no parameters given 105 * @param optionPrefix optional prefix to filter the parameters for validation. Use <tt>null</tt> for validate all. 106 * @throws ResolveEndpointFailedException should be thrown if the URI validation failed 107 */ 108 protected void validateParameters(String uri, Map parameters, String optionPrefix) { 109 Map param = parameters; 110 if (optionPrefix != null) { 111 param = IntrospectionSupport.extractProperties(parameters, optionPrefix); 112 } 113 114 if (param.size() > 0) { 115 throw new ResolveEndpointFailedException(uri, "There are " + param.size() 116 + " parameters that couldn't be set on the endpoint." 117 + " Check the uri if the parameters are spelt correctly and that they are properties of the endpoint." 118 + " Unknown parameters=[" + param + "]"); 119 } 120 } 121 122 /** 123 * Strategy for validation of the uri when creating the endpoint. 124 * 125 * @param uri the uri - the uri the end user provided untouched 126 * @param path the path - part after the scheme 127 * @param parameters the parameters, an empty map if no parameters given 128 * @throws ResolveEndpointFailedException should be thrown if the URI validation failed 129 */ 130 protected void validateURI(String uri, String path, Map parameters) { 131 // check for uri containing & but no ? marker 132 if (uri.contains("&") && !uri.contains("?")) { 133 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: no ? marker however the uri " 134 + "has & parameter separators. Check the uri if its missing a ? marker."); 135 } 136 137 // check for uri containing double && markers 138 if (uri.contains("&&")) { 139 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Double && marker found. " 140 + "Check the uri and remove the duplicate & marker."); 141 } 142 } 143 144 public CamelContext getCamelContext() { 145 return camelContext; 146 } 147 148 public void setCamelContext(CamelContext context) { 149 this.camelContext = context; 150 } 151 152 public synchronized ExecutorService getExecutorService() { 153 if (executorService == null) { 154 executorService = createScheduledExecutorService(); 155 } 156 return executorService; 157 } 158 159 public synchronized void setExecutorService(ExecutorService executorService) { 160 this.executorService = executorService; 161 } 162 163 public synchronized ScheduledExecutorService getScheduledExecutorService() { 164 ExecutorService executor = getExecutorService(); 165 if (executor instanceof ScheduledExecutorService) { 166 return (ScheduledExecutorService) executor; 167 } else { 168 return createScheduledExecutorService(); 169 } 170 } 171 172 /** 173 * A factory method to create a default thread pool and executor 174 */ 175 protected ScheduledExecutorService createScheduledExecutorService() { 176 String name = getClass().getSimpleName(); 177 return ExecutorServiceHelper.newScheduledThreadPool(DEFAULT_THREADPOOL_SIZE, name, true); 178 } 179 180 protected void doStart() throws Exception { 181 ObjectHelper.notNull(getCamelContext(), "camelContext"); 182 } 183 184 protected void doStop() throws Exception { 185 if (executorService != null) { 186 executorService.shutdown(); 187 } 188 } 189 190 /** 191 * A factory method allowing derived components to create a new endpoint 192 * from the given URI, remaining path and optional parameters 193 * 194 * @param uri the full URI of the endpoint 195 * @param remaining the remaining part of the URI without the query 196 * parameters or component prefix 197 * @param parameters the optional parameters passed in 198 * @return a newly created endpoint or null if the endpoint cannot be 199 * created based on the inputs 200 */ 201 protected abstract Endpoint createEndpoint(String uri, String remaining, Map parameters) 202 throws Exception; 203 204 /** 205 * Sets the bean properties on the given bean 206 * 207 * @param bean the bean 208 * @param parameters properties to set 209 */ 210 protected void setProperties(Object bean, Map parameters) throws Exception { 211 // set reference properties first as they use # syntax that fools the regular properties setter 212 setReferenceProperties(bean, parameters); 213 IntrospectionSupport.setProperties(getCamelContext().getTypeConverter(), bean, parameters); 214 } 215 216 /** 217 * Sets the reference properties on the given bean 218 * <p/> 219 * This is convention over configuration, setting all reference parameters (using {@link #isReferenceParameter(String)} 220 * by looking it up in registry and setting it on the bean if possible. 221 */ 222 protected void setReferenceProperties(Object bean, Map parameters) throws Exception { 223 Iterator it = parameters.keySet().iterator(); 224 while (it.hasNext()) { 225 Object key = it.next(); 226 String value = (String) parameters.get(key); 227 if (isReferenceParameter(value)) { 228 Object ref = lookup(value.substring(1)); 229 String name = key.toString(); 230 if (ref != null) { 231 boolean hit = IntrospectionSupport.setProperty(getCamelContext().getTypeConverter(), bean, name, ref); 232 if (hit) { 233 if (LOG.isDebugEnabled()) { 234 LOG.debug("Configued property: " + name + " on bean: " + bean + " with value: " + ref); 235 } 236 // must remove as its a valid option and we could configure it 237 it.remove(); 238 } 239 } 240 } 241 } 242 } 243 244 /** 245 * Is the given parameter a reference parameter (starting with a # char) 246 */ 247 protected boolean isReferenceParameter(String parameter) { 248 return parameter != null && parameter.startsWith("#"); 249 } 250 251 /** 252 * Derived classes may wish to overload this to prevent the default introspection of URI parameters 253 * on the created Endpoint instance 254 */ 255 protected boolean useIntrospectionOnEndpoint() { 256 return true; 257 } 258 259 260 // Some helper methods 261 //------------------------------------------------------------------------- 262 263 /** 264 * Converts the given value to the requested type 265 */ 266 public <T> T convertTo(Class<T> type, Object value) { 267 return CamelContextHelper.convertTo(getCamelContext(), type, value); 268 } 269 270 /** 271 * Converts the given value to the specified type throwing an {@link IllegalArgumentException} 272 * if the value could not be converted to a non null value 273 */ 274 public <T> T mandatoryConvertTo(Class<T> type, Object value) { 275 return CamelContextHelper.mandatoryConvertTo(getCamelContext(), type, value); 276 } 277 278 /** 279 * Creates a new instance of the given type using the {@link org.apache.camel.spi.Injector} on the given 280 * {@link CamelContext} 281 */ 282 public <T> T newInstance(Class<T> beanType) { 283 return getCamelContext().getInjector().newInstance(beanType); 284 } 285 286 /** 287 * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the 288 * {@link CamelContext} 289 */ 290 public Object lookup(String name) { 291 return getCamelContext().getRegistry().lookup(name); 292 } 293 294 /** 295 * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the 296 * {@link CamelContext} 297 */ 298 public <T> T lookup(String name, Class<T> beanType) { 299 return getCamelContext().getRegistry().lookup(name, beanType); 300 } 301 302 /** 303 * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the 304 * {@link CamelContext} or throws exception if not found. 305 */ 306 public Object mandatoryLookup(String name) { 307 return CamelContextHelper.mandatoryLookup(getCamelContext(), name); 308 } 309 310 /** 311 * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the 312 * {@link CamelContext} or throws exception if not found. 313 */ 314 public <T> T mandatoryLookup(String name, Class<T> beanType) { 315 return CamelContextHelper.mandatoryLookup(getCamelContext(), name, beanType); 316 } 317 318 /** 319 * Gets the parameter and remove it from the parameter map. 320 * 321 * @param parameters the parameters 322 * @param key the key 323 * @param type the requested type to convert the value from the parameter 324 * @return the converted value parameter, <tt>null</tt> if parameter does not exists. 325 */ 326 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type) { 327 return getAndRemoveParameter(parameters, key, type, null); 328 } 329 330 /** 331 * Gets the parameter and remove it from the parameter map. 332 * 333 * @param parameters the parameters 334 * @param key the key 335 * @param type the requested type to convert the value from the parameter 336 * @param defaultValue use this default value if the parameter does not contain the key 337 * @return the converted value parameter 338 */ 339 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type, T defaultValue) { 340 Object value = parameters.remove(key); 341 if (value == null) { 342 value = defaultValue; 343 } 344 if (value == null) { 345 return null; 346 } 347 return convertTo(type, value); 348 } 349 350 /** 351 * Returns the reminder of the text if it starts with the prefix. 352 * <p/> 353 * Is useable for string parameters that contains commands. 354 * 355 * @param prefix the prefix 356 * @param text the text 357 * @return the reminder, or null if no reminder 358 */ 359 protected String ifStartsWithReturnRemainder(String prefix, String text) { 360 if (text.startsWith(prefix)) { 361 String remainder = text.substring(prefix.length()); 362 if (remainder.length() > 0) { 363 return remainder; 364 } 365 } 366 return null; 367 } 368 369 }