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.ScheduledExecutorService; 022 import java.util.concurrent.ScheduledThreadPoolExecutor; 023 import java.util.concurrent.ThreadFactory; 024 025 import org.apache.camel.CamelContext; 026 import org.apache.camel.Component; 027 import org.apache.camel.Endpoint; 028 import org.apache.camel.Exchange; 029 import org.apache.camel.ResolveEndpointFailedException; 030 import org.apache.camel.spi.Injector; 031 import org.apache.camel.spi.Registry; 032 import org.apache.camel.util.CamelContextHelper; 033 import org.apache.camel.util.IntrospectionSupport; 034 import org.apache.camel.util.ObjectHelper; 035 import org.apache.camel.util.URISupport; 036 import org.apache.camel.util.UnsafeUriCharactersEncoder; 037 import org.apache.commons.logging.Log; 038 import org.apache.commons.logging.LogFactory; 039 040 041 /** 042 * Default component to use for base for components implementations. 043 * 044 * @version $Revision: 676850 $ 045 */ 046 public abstract class DefaultComponent<E extends Exchange> extends ServiceSupport implements Component<E> { 047 private static final transient Log LOG = LogFactory.getLog(DefaultComponent.class); 048 049 private int defaultThreadPoolSize = 5; 050 private CamelContext camelContext; 051 private ScheduledExecutorService executorService; 052 053 public DefaultComponent() { 054 } 055 056 public DefaultComponent(CamelContext context) { 057 this.camelContext = context; 058 } 059 060 public Endpoint<E> createEndpoint(String uri) throws Exception { 061 ObjectHelper.notNull(getCamelContext(), "camelContext"); 062 //encode URI string to the unsafe URI characters 063 URI u = new URI(UnsafeUriCharactersEncoder.encode(uri)); 064 String path = u.getSchemeSpecificPart(); 065 066 // lets trim off any query arguments 067 if (path.startsWith("//")) { 068 path = path.substring(2); 069 } 070 int idx = path.indexOf('?'); 071 if (idx > 0) { 072 path = path.substring(0, idx); 073 } 074 Map parameters = URISupport.parseParameters(u); 075 076 validateURI(uri, path, parameters); 077 078 if (LOG.isDebugEnabled()) { 079 LOG.debug("Creating endpoint uri=[" + uri + "], path=[" + path + "], parameters=[" + parameters + "]"); 080 } 081 Endpoint<E> endpoint = createEndpoint(uri, path, parameters); 082 if (endpoint == null) { 083 return null; 084 } 085 086 if (parameters != null) { 087 endpoint.configureProperties(parameters); 088 if (useIntrospectionOnEndpoint()) { 089 setProperties(endpoint, parameters); 090 } 091 092 // fail if there are parameters that could not be set, then they are probably miss spelt or not supported at all 093 if (parameters.size() > 0) { 094 throw new ResolveEndpointFailedException(uri, "There are " + parameters.size() 095 + " parameters that couldn't be set on the endpoint." 096 + " Check the uri if the parameters are spelt correctly and that they are properties of the endpoint." 097 + " Unknown parameters=[" + parameters + "]"); 098 } 099 } 100 101 return endpoint; 102 } 103 104 /** 105 * Strategy for validation of the uri when creating the endpoint. 106 * 107 * @param uri the uri - the uri the end user provided untouched 108 * @param path the path - part after the scheme 109 * @param parameters the parameters, an empty map if no parameters given 110 * @throws ResolveEndpointFailedException should be thrown if the URI validation failed 111 */ 112 protected void validateURI(String uri, String path, Map parameters) throws ResolveEndpointFailedException { 113 // check for uri containing & but no ? marker 114 if (uri.contains("&") && !uri.contains("?")) { 115 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: no ? marker however the uri " 116 + "has & parameter separators. Check the uri if its missing a ? marker."); 117 } 118 119 // check for uri containing double && markers 120 if (uri.contains("&&")) { 121 throw new ResolveEndpointFailedException(uri, "Invalid uri syntax: Double && marker found. " 122 + "Check the uri and remove the duplicate & marker."); 123 } 124 } 125 126 public CamelContext getCamelContext() { 127 return camelContext; 128 } 129 130 public void setCamelContext(CamelContext context) { 131 this.camelContext = context; 132 } 133 134 public ScheduledExecutorService getExecutorService() { 135 if (executorService == null) { 136 executorService = createExecutorService(); 137 } 138 return executorService; 139 } 140 141 public void setExecutorService(ScheduledExecutorService executorService) { 142 this.executorService = executorService; 143 } 144 145 /** 146 * A factory method to create a default thread pool and executor 147 */ 148 protected ScheduledExecutorService createExecutorService() { 149 return new ScheduledThreadPoolExecutor(defaultThreadPoolSize, new ThreadFactory() { 150 int counter; 151 152 public synchronized Thread newThread(Runnable runnable) { 153 Thread thread = new Thread(runnable); 154 thread.setName("Thread: " + (++counter) + " " + DefaultComponent.this.toString()); 155 return thread; 156 } 157 }); 158 } 159 160 protected void doStart() throws Exception { 161 } 162 163 protected void doStop() throws Exception { 164 if (executorService != null) { 165 executorService.shutdown(); 166 } 167 } 168 169 /** 170 * A factory method allowing derived components to create a new endpoint 171 * from the given URI, remaining path and optional parameters 172 * 173 * @param uri the full URI of the endpoint 174 * @param remaining the remaining part of the URI without the query 175 * parameters or component prefix 176 * @param parameters the optional parameters passed in 177 * @return a newly created endpoint or null if the endpoint cannot be 178 * created based on the inputs 179 */ 180 protected abstract Endpoint<E> createEndpoint(String uri, String remaining, Map parameters) 181 throws Exception; 182 183 /** 184 * Sets the bean properties on the given bean 185 */ 186 protected void setProperties(Object bean, Map parameters) throws Exception { 187 IntrospectionSupport.setProperties(getCamelContext().getTypeConverter(), bean, parameters); 188 } 189 190 /** 191 * Derived classes may wish to overload this to prevent the default introspection of URI parameters 192 * on the created Endpoint instance 193 */ 194 protected boolean useIntrospectionOnEndpoint() { 195 return true; 196 } 197 198 199 // Some helper methods 200 //------------------------------------------------------------------------- 201 202 /** 203 * Converts the given value to the requested type 204 */ 205 public <T> T convertTo(Class<T> type, Object value) { 206 return CamelContextHelper.convertTo(getCamelContext(), type, value); 207 } 208 209 /** 210 * Converts the given value to the specified type throwing an {@link IllegalArgumentException} 211 * if the value could not be converted to a non null value 212 */ 213 public <T> T mandatoryConvertTo(Class<T> type, Object value) { 214 return CamelContextHelper.mandatoryConvertTo(getCamelContext(), type, value); 215 } 216 217 /** 218 * Creates a new instance of the given type using the {@link Injector} on the given 219 * {@link CamelContext} 220 */ 221 public <T> T newInstance(Class<T> beanType) { 222 return getCamelContext().getInjector().newInstance(beanType); 223 } 224 225 /** 226 * Look up the given named bean in the {@link Registry} on the 227 * {@link CamelContext} 228 */ 229 public Object lookup(String name) { 230 return getCamelContext().getRegistry().lookup(name); 231 } 232 233 /** 234 * Look up the given named bean of the given type in the {@link Registry} on the 235 * {@link CamelContext} 236 */ 237 public <T> T lookup(String name, Class<T> beanType) { 238 return getCamelContext().getRegistry().lookup(name, beanType); 239 } 240 241 /** 242 * Look up the given named bean in the {@link Registry} on the 243 * {@link CamelContext} or throws 244 */ 245 public Object mandatoryLookup(String name) { 246 return CamelContextHelper.mandatoryLookup(getCamelContext(), name); 247 } 248 249 /** 250 * Look up the given named bean of the given type in the {@link Registry} on the 251 * {@link CamelContext} 252 */ 253 public <T> T mandatoryLookup(String name, Class<T> beanType) { 254 return CamelContextHelper.mandatoryLookup(getCamelContext(), name, beanType); 255 } 256 257 /** 258 * Gets the parameter and remove it from the parameter map. 259 * 260 * @param parameters the parameters 261 * @param key the key 262 * @param type the requested type to convert the value from the parameter 263 * @return the converted value parameter, <tt>null</tt> if parameter does not exists. 264 */ 265 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type) { 266 return getAndRemoveParameter(parameters, key, type, null); 267 } 268 269 /** 270 * Gets the parameter and remove it from the parameter map. 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 * @param defaultValue use this default value if the parameter does not contain the key 276 * @return the converted value parameter 277 */ 278 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type, T defaultValue) { 279 Object value = parameters.remove(key); 280 if (value == null) { 281 value = defaultValue; 282 } 283 if (value == null) { 284 return null; 285 } 286 return convertTo(type, value); 287 } 288 289 }