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: 41314 $ 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 120 public CamelContext getCamelContext() { 121 return camelContext; 122 } 123 124 public void setCamelContext(CamelContext context) { 125 this.camelContext = context; 126 } 127 128 public ScheduledExecutorService getExecutorService() { 129 if (executorService == null) { 130 executorService = createExecutorService(); 131 } 132 return executorService; 133 } 134 135 public void setExecutorService(ScheduledExecutorService executorService) { 136 this.executorService = executorService; 137 } 138 139 /** 140 * A factory method to create a default thread pool and executor 141 */ 142 protected ScheduledExecutorService createExecutorService() { 143 return new ScheduledThreadPoolExecutor(defaultThreadPoolSize, new ThreadFactory() { 144 int counter; 145 146 public synchronized Thread newThread(Runnable runnable) { 147 Thread thread = new Thread(runnable); 148 thread.setName("Thread: " + (++counter) + " " + DefaultComponent.this.toString()); 149 return thread; 150 } 151 }); 152 } 153 154 protected void doStart() throws Exception { 155 } 156 157 protected void doStop() throws Exception { 158 if (executorService != null) { 159 executorService.shutdown(); 160 } 161 } 162 163 /** 164 * A factory method allowing derived components to create a new endpoint 165 * from the given URI, remaining path and optional parameters 166 * 167 * @param uri the full URI of the endpoint 168 * @param remaining the remaining part of the URI without the query 169 * parameters or component prefix 170 * @param parameters the optional parameters passed in 171 * @return a newly created endpoint or null if the endpoint cannot be 172 * created based on the inputs 173 */ 174 protected abstract Endpoint<E> createEndpoint(String uri, String remaining, Map parameters) 175 throws Exception; 176 177 /** 178 * Sets the bean properties on the given bean 179 */ 180 protected void setProperties(Object bean, Map parameters) throws Exception { 181 IntrospectionSupport.setProperties(getCamelContext().getTypeConverter(), bean, parameters); 182 } 183 184 /** 185 * Derived classes may wish to overload this to prevent the default introspection of URI parameters 186 * on the created Endpoint instance 187 */ 188 protected boolean useIntrospectionOnEndpoint() { 189 return true; 190 } 191 192 193 // Some helper methods 194 //------------------------------------------------------------------------- 195 196 /** 197 * Converts the given value to the requested type 198 */ 199 public <T> T convertTo(Class<T> type, Object value) { 200 return CamelContextHelper.convertTo(getCamelContext(), type, value); 201 } 202 203 /** 204 * Converts the given value to the specified type throwing an {@link IllegalArgumentException} 205 * if the value could not be converted to a non null value 206 */ 207 public <T> T mandatoryConvertTo(Class<T> type, Object value) { 208 return CamelContextHelper.mandatoryConvertTo(getCamelContext(), type, value); 209 } 210 211 /** 212 * Creates a new instance of the given type using the {@link Injector} on the given 213 * {@link CamelContext} 214 */ 215 public <T> T newInstance(Class<T> beanType) { 216 return getCamelContext().getInjector().newInstance(beanType); 217 } 218 219 /** 220 * Look up the given named bean in the {@link Registry} on the 221 * {@link CamelContext} 222 */ 223 public Object lookup(String name) { 224 return getCamelContext().getRegistry().lookup(name); 225 } 226 227 /** 228 * Look up the given named bean of the given type in the {@link Registry} on the 229 * {@link CamelContext} 230 */ 231 public <T> T lookup(String name, Class<T> beanType) { 232 return getCamelContext().getRegistry().lookup(name, beanType); 233 } 234 235 /** 236 * Look up the given named bean in the {@link Registry} on the 237 * {@link CamelContext} or throws 238 */ 239 public Object mandatoryLookup(String name) { 240 return CamelContextHelper.mandatoryLookup(getCamelContext(), name); 241 } 242 243 /** 244 * Look up the given named bean of the given type in the {@link Registry} on the 245 * {@link CamelContext} 246 */ 247 public <T> T mandatoryLookup(String name, Class<T> beanType) { 248 return CamelContextHelper.mandatoryLookup(getCamelContext(), name, beanType); 249 } 250 251 /** 252 * Gets the parameter and remove it from the parameter map. 253 * 254 * @param parameters the parameters 255 * @param key the key 256 * @param type the requested type to convert the value from the parameter 257 * @return the converted value parameter, <tt>null</tt> if parameter does not exists. 258 */ 259 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type) { 260 return getAndRemoveParameter(parameters, key, type, null); 261 } 262 263 /** 264 * Gets the parameter and remove it from the parameter map. 265 * 266 * @param parameters the parameters 267 * @param key the key 268 * @param type the requested type to convert the value from the parameter 269 * @param defaultValue use this default value if the parameter does not contain the key 270 * @return the converted value parameter 271 */ 272 public <T> T getAndRemoveParameter(Map parameters, String key, Class<T> type, T defaultValue) { 273 Object value = parameters.remove(key); 274 if (value == null) { 275 value = defaultValue; 276 } 277 if (value == null) { 278 return null; 279 } 280 return convertTo(type, value); 281 } 282 283 }