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.Map; 021 import java.util.concurrent.ExecutorService; 022 import java.util.concurrent.ScheduledExecutorService; 023 024 import org.apache.camel.CamelContext; 025 import org.apache.camel.Component; 026 import org.apache.camel.Endpoint; 027 import org.apache.camel.ResolveEndpointFailedException; 028 import org.apache.camel.util.CamelContextHelper; 029 import org.apache.camel.util.EndpointHelper; 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: 789117 $ 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 EndpointHelper.setReferenceProperties(getCamelContext(), bean, parameters); 213 EndpointHelper.setProperties(getCamelContext(), bean, parameters); 214 } 215 216 /** 217 * Derived classes may wish to overload this to prevent the default introspection of URI parameters 218 * on the created Endpoint instance 219 */ 220 protected boolean useIntrospectionOnEndpoint() { 221 return true; 222 } 223 224 225 // Some helper methods 226 //------------------------------------------------------------------------- 227 228 /** 229 * Converts the given value to the requested type 230 */ 231 public <T> T convertTo(Class<T> type, Object value) { 232 return CamelContextHelper.convertTo(getCamelContext(), type, value); 233 } 234 235 /** 236 * Converts the given value to the specified type throwing an {@link IllegalArgumentException} 237 * if the value could not be converted to a non null value 238 */ 239 public <T> T mandatoryConvertTo(Class<T> type, Object value) { 240 return CamelContextHelper.mandatoryConvertTo(getCamelContext(), type, value); 241 } 242 243 /** 244 * Creates a new instance of the given type using the {@link org.apache.camel.spi.Injector} on the given 245 * {@link CamelContext} 246 */ 247 public <T> T newInstance(Class<T> beanType) { 248 return getCamelContext().getInjector().newInstance(beanType); 249 } 250 251 /** 252 * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the 253 * {@link CamelContext} 254 */ 255 public Object lookup(String name) { 256 return getCamelContext().getRegistry().lookup(name); 257 } 258 259 /** 260 * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the 261 * {@link CamelContext} 262 */ 263 public <T> T lookup(String name, Class<T> beanType) { 264 return getCamelContext().getRegistry().lookup(name, beanType); 265 } 266 267 /** 268 * Look up the given named bean in the {@link org.apache.camel.spi.Registry} on the 269 * {@link CamelContext} or throws exception if not found. 270 */ 271 public Object mandatoryLookup(String name) { 272 return CamelContextHelper.mandatoryLookup(getCamelContext(), name); 273 } 274 275 /** 276 * Look up the given named bean of the given type in the {@link org.apache.camel.spi.Registry} on the 277 * {@link CamelContext} or throws exception if not found. 278 */ 279 public <T> T mandatoryLookup(String name, Class<T> beanType) { 280 return CamelContextHelper.mandatoryLookup(getCamelContext(), name, beanType); 281 } 282 283 /** 284 * Gets the parameter and remove it from the parameter map. 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 * @return the converted value parameter, <tt>null</tt> if parameter does not exists. 290 */ 291 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type) { 292 return getAndRemoveParameter(parameters, key, type, null); 293 } 294 295 /** 296 * Gets the parameter and remove it from the parameter map. 297 * 298 * @param parameters the parameters 299 * @param key the key 300 * @param type the requested type to convert the value from the parameter 301 * @param defaultValue use this default value if the parameter does not contain the key 302 * @return the converted value parameter 303 */ 304 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type, T defaultValue) { 305 Object value = parameters.remove(key); 306 if (value == null) { 307 value = defaultValue; 308 } 309 if (value == null) { 310 return null; 311 } 312 return convertTo(type, value); 313 } 314 315 /** 316 * Returns the reminder of the text if it starts with the prefix. 317 * <p/> 318 * Is useable for string parameters that contains commands. 319 * 320 * @param prefix the prefix 321 * @param text the text 322 * @return the reminder, or null if no reminder 323 */ 324 protected String ifStartsWithReturnRemainder(String prefix, String text) { 325 if (text.startsWith(prefix)) { 326 String remainder = text.substring(prefix.length()); 327 if (remainder.length() > 0) { 328 return remainder; 329 } 330 } 331 return null; 332 } 333 334 }