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