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.io.IOException;
020 import java.util.ArrayList;
021 import java.util.Collection;
022 import java.util.HashMap;
023 import java.util.List;
024 import java.util.Map;
025 import java.util.concurrent.Callable;
026
027 import javax.naming.Context;
028
029 import org.apache.camel.CamelContext;
030 import org.apache.camel.Component;
031 import org.apache.camel.Endpoint;
032 import org.apache.camel.Exchange;
033 import org.apache.camel.LifecycleStrategy;
034 import org.apache.camel.Processor;
035 import org.apache.camel.ResolveEndpointFailedException;
036 import org.apache.camel.Route;
037 import org.apache.camel.RuntimeCamelException;
038 import org.apache.camel.Service;
039 import org.apache.camel.TypeConverter;
040 import org.apache.camel.builder.RouteBuilder;
041 import org.apache.camel.impl.converter.DefaultTypeConverter;
042 import org.apache.camel.spi.ComponentResolver;
043 import org.apache.camel.spi.ExchangeConverter;
044 import org.apache.camel.spi.Injector;
045 import org.apache.camel.spi.Language;
046 import org.apache.camel.spi.LanguageResolver;
047 import org.apache.camel.spi.Registry;
048 import org.apache.camel.util.FactoryFinder;
049 import org.apache.camel.util.NoFactoryAvailableException;
050 import org.apache.camel.util.ObjectHelper;
051
052 import static org.apache.camel.util.ServiceHelper.startServices;
053 import static org.apache.camel.util.ServiceHelper.stopServices;
054
055 /**
056 * Represents the context used to configure routes and the policies to use.
057 *
058 * @version $Revision: 520517 $
059 * @org.apache.xbean.XBean element="container" rootElement="true"
060 */
061 public class DefaultCamelContext extends ServiceSupport implements CamelContext, Service {
062 private Map<String, Endpoint> endpoints = new HashMap<String, Endpoint>();
063 private Map<String, Component> components = new HashMap<String, Component>();
064 private List<Route> routes;
065 private List<Service> servicesToClose = new ArrayList<Service>();
066 private TypeConverter typeConverter;
067 private ExchangeConverter exchangeConverter;
068 private Injector injector;
069 private ComponentResolver componentResolver;
070 private boolean autoCreateComponents = true;
071 private LanguageResolver languageResolver = new DefaultLanguageResolver();
072 private Registry registry;
073 private LifecycleStrategy lifecycleStrategy = new DefaultLifecycleStrategy();
074
075 public DefaultCamelContext() {
076 }
077
078 /**
079 * Creates the {@link CamelContext} using the given JNDI context as the
080 * registry
081 *
082 * @param jndiContext
083 */
084 public DefaultCamelContext(Context jndiContext) {
085 this(new JndiRegistry(jndiContext));
086 }
087
088 /**
089 * Creates the {@link CamelContext} using the given registry
090 */
091 public DefaultCamelContext(Registry registry) {
092 this.registry = registry;
093 }
094
095 /**
096 * Adds a component to the container.
097 */
098 public void addComponent(String componentName, final Component component) {
099 if (component == null) {
100 throw new IllegalArgumentException("Component cannot be null");
101 }
102 synchronized (components) {
103 if (components.containsKey(componentName)) {
104 throw new IllegalArgumentException("Component previously added: " + componentName);
105 }
106 component.setCamelContext(this);
107 components.put(componentName, component);
108 }
109 }
110
111 public Component getComponent(String name) {
112 // synchronize the look up and auto create so that 2 threads can't
113 // concurrently auto create the same component.
114 synchronized (components) {
115 Component component = components.get(name);
116 if (component == null && autoCreateComponents) {
117 try {
118 component = getComponentResolver().resolveComponent(name, this);
119 if (component != null) {
120 addComponent(name, component);
121 if (isStarted()) {
122 // If the component is looked up after the context
123 // is started,
124 // lets start it up.
125 startServices(component);
126 }
127 }
128 } catch (Exception e) {
129 throw new RuntimeCamelException("Could not auto create component: " + name, e);
130 }
131 }
132 return component;
133 }
134 }
135
136 public <T extends Component> T getComponent(String name, Class<T> componentType) {
137 Component component = getComponent(name);
138 if (componentType.isInstance(component)) {
139 return componentType.cast(component);
140 } else {
141 throw new IllegalArgumentException("The component is not of type: " + componentType + " but is: "
142 + component);
143 }
144 }
145
146 /**
147 * Removes a previously added component.
148 *
149 * @param componentName
150 * @return the previously added component or null if it had not been
151 * previously added.
152 */
153 public Component removeComponent(String componentName) {
154 synchronized (components) {
155 return components.remove(componentName);
156 }
157 }
158
159 /**
160 * Gets the a previously added component by name or lazily creates the
161 * component using the factory Callback.
162 *
163 * @param componentName
164 * @param factory used to create a new component instance if the component
165 * was not previously added.
166 * @return
167 */
168 public Component getOrCreateComponent(String componentName, Callable<Component> factory) {
169 synchronized (components) {
170 Component component = components.get(componentName);
171 if (component == null) {
172 try {
173 component = factory.call();
174 if (component == null) {
175 throw new RuntimeCamelException("Factory failed to create the " + componentName
176 + " component, it returned null.");
177 }
178 components.put(componentName, component);
179 component.setCamelContext(this);
180 } catch (Exception e) {
181 throw new RuntimeCamelException("Factory failed to create the " + componentName
182 + " component", e);
183 }
184 }
185 return component;
186 }
187 }
188
189 // Endpoint Management Methods
190 // -----------------------------------------------------------------------
191
192 public Collection<Endpoint> getSingletonEndpoints() {
193 synchronized (endpoints) {
194 return new ArrayList<Endpoint>(endpoints.values());
195 }
196 }
197
198 public Endpoint addSingletonEndpoint(String uri, Endpoint endpoint) throws Exception {
199 Endpoint oldEndpoint;
200 synchronized (endpoints) {
201 startServices(endpoint);
202 oldEndpoint = endpoints.remove(uri);
203 endpoints.put(uri, endpoint);
204 stopServices(oldEndpoint);
205 }
206 return oldEndpoint;
207 }
208
209 public Endpoint removeSingletonEndpoint(String uri) throws Exception {
210 Endpoint oldEndpoint;
211 synchronized (endpoints) {
212 oldEndpoint = endpoints.remove(uri);
213 stopServices(oldEndpoint);
214 }
215 return oldEndpoint;
216 }
217
218 /**
219 * Resolves the given URI to an endpoint
220 */
221 public Endpoint getEndpoint(String uri) {
222 Endpoint answer;
223 synchronized (endpoints) {
224 answer = endpoints.get(uri);
225 if (answer == null) {
226 try {
227
228 // Use the URI prefix to find the component.
229 String splitURI[] = ObjectHelper.splitOnCharacter(uri, ":", 2);
230 if (splitURI[1] != null) {
231 String scheme = splitURI[0];
232 Component component = getComponent(scheme);
233
234 // Ask the component to resolve the endpoint.
235 if (component != null) {
236 // Have the component create the endpoint if it can.
237 answer = component.createEndpoint(uri);
238 }
239 }
240 if (answer == null) {
241 answer = createEndpoint(uri);
242 }
243
244 // If it's a singleton then auto register it.
245 if (answer != null && answer.isSingleton()) {
246 startServices(answer);
247 endpoints.put(uri, answer);
248 lifecycleStrategy.onEndpointAdd(answer);
249 }
250 } catch (Exception e) {
251 throw new ResolveEndpointFailedException(uri, e);
252 }
253 }
254 }
255 return answer;
256 }
257
258 public <T extends Endpoint> T getEndpoint(String name, Class<T> endpointType) {
259 Endpoint endpoint = getEndpoint(name);
260 if (endpointType.isInstance(endpoint)) {
261 return endpointType.cast(endpoint);
262 } else {
263 throw new IllegalArgumentException("The endpoint is not of type: " + endpointType + " but is: "
264 + endpoint);
265 }
266 }
267
268 // Route Management Methods
269 // -----------------------------------------------------------------------
270 public List<Route> getRoutes() {
271 return routes;
272 }
273
274 public void setRoutes(List<Route> routes) {
275 this.routes = routes;
276 }
277
278 public void addRoutes(Collection<Route> routes) throws Exception {
279 if (this.routes == null) {
280 this.routes = new ArrayList<Route>(routes);
281 } else {
282 this.routes.addAll(routes);
283 }
284 lifecycleStrategy.onRoutesAdd(routes);
285 if (isStarted()) {
286 startRoutes(routes);
287 }
288 }
289
290 public void addRoutes(RouteBuilder builder) throws Exception {
291 // lets now add the routes from the builder
292 builder.setContext(this);
293 addRoutes(builder.getRouteList());
294 }
295
296 // Helper methods
297 // -----------------------------------------------------------------------
298
299 /**
300 * Resolves a language for creating expressions
301 */
302 public Language resolveLanguage(String language) {
303 return getLanguageResolver().resolveLanguage(language, this);
304 }
305
306 // Properties
307 // -----------------------------------------------------------------------
308 public ExchangeConverter getExchangeConverter() {
309 if (exchangeConverter == null) {
310 exchangeConverter = createExchangeConverter();
311 }
312 return exchangeConverter;
313 }
314
315 public void setExchangeConverter(ExchangeConverter exchangeConverter) {
316 this.exchangeConverter = exchangeConverter;
317 }
318
319 public TypeConverter getTypeConverter() {
320 if (typeConverter == null) {
321 typeConverter = createTypeConverter();
322 }
323 return typeConverter;
324 }
325
326 public void setTypeConverter(TypeConverter typeConverter) {
327 this.typeConverter = typeConverter;
328 }
329
330 public Injector getInjector() {
331 if (injector == null) {
332 injector = createInjector();
333 }
334 return injector;
335 }
336
337 public void setInjector(Injector injector) {
338 this.injector = injector;
339 }
340
341 public ComponentResolver getComponentResolver() {
342 if (componentResolver == null) {
343 componentResolver = createComponentResolver();
344 }
345 return componentResolver;
346 }
347
348 public void setComponentResolver(ComponentResolver componentResolver) {
349 this.componentResolver = componentResolver;
350 }
351
352 public LanguageResolver getLanguageResolver() {
353 return languageResolver;
354 }
355
356 public void setLanguageResolver(LanguageResolver languageResolver) {
357 this.languageResolver = languageResolver;
358 }
359
360 public boolean isAutoCreateComponents() {
361 return autoCreateComponents;
362 }
363
364 public void setAutoCreateComponents(boolean autoCreateComponents) {
365 this.autoCreateComponents = autoCreateComponents;
366 }
367
368 public Registry getRegistry() {
369 if (registry == null) {
370 registry = createRegistry();
371 }
372 return registry;
373 }
374
375 public void setRegistry(Registry registry) {
376 this.registry = registry;
377 }
378
379 public LifecycleStrategy getLifecycleStrategy() {
380 return lifecycleStrategy;
381 }
382
383 public void setLifecycleStrategy(LifecycleStrategy lifecycleStrategy) {
384 this.lifecycleStrategy = lifecycleStrategy;
385 }
386
387 // Implementation methods
388 // -----------------------------------------------------------------------
389
390 protected void doStart() throws Exception {
391 forceLazyInitialization();
392 if (components != null) {
393 for (Component component : components.values()) {
394 startServices(component);
395 }
396 }
397 startRoutes(routes);
398 }
399
400 protected void doStop() throws Exception {
401 stopServices(servicesToClose);
402 if (components != null) {
403 for (Component component : components.values()) {
404 stopServices(component);
405 }
406 }
407 }
408
409 protected void startRoutes(Collection<Route> routeList) throws Exception {
410 if (routeList != null) {
411 for (Route<Exchange> route : routeList) {
412 List<Service> services = route.getServicesForRoute();
413 servicesToClose.addAll(services);
414 startServices(services);
415 }
416 }
417 }
418
419 /**
420 * Lets force some lazy initialization to occur upfront before we start any
421 * components and create routes
422 */
423 protected void forceLazyInitialization() {
424 getExchangeConverter();
425 getInjector();
426 getLanguageResolver();
427 getTypeConverter();
428 }
429
430 /**
431 * Lazily create a default implementation
432 */
433 protected ExchangeConverter createExchangeConverter() {
434 return new DefaultExchangeConverter();
435 }
436
437 /**
438 * Lazily create a default implementation
439 */
440 protected TypeConverter createTypeConverter() {
441 return new DefaultTypeConverter(getInjector());
442 }
443
444 /**
445 * Lazily create a default implementation
446 */
447 protected Injector createInjector() {
448 FactoryFinder finder = new FactoryFinder();
449 try {
450 return (Injector)finder.newInstance("Injector");
451 } catch (NoFactoryAvailableException e) {
452 // lets use the default
453 return new ReflectionInjector();
454 } catch (IllegalAccessException e) {
455 throw new RuntimeCamelException(e);
456 } catch (InstantiationException e) {
457 throw new RuntimeCamelException(e);
458 } catch (IOException e) {
459 throw new RuntimeCamelException(e);
460 } catch (ClassNotFoundException e) {
461 throw new RuntimeCamelException(e);
462 }
463 }
464
465 /**
466 * Lazily create a default implementation
467 */
468 protected ComponentResolver createComponentResolver() {
469 return new DefaultComponentResolver();
470 }
471
472 /**
473 * Lazily create a default implementation
474 */
475 protected Registry createRegistry() {
476 return new JndiRegistry();
477 }
478
479 /**
480 * A pluggable strategy to allow an endpoint to be created without requiring
481 * a component to be its factory, such as for looking up the URI inside some
482 * {@link Registry}
483 *
484 * @param uri the uri for the endpoint to be created
485 * @return the newly created endpoint or null if it could not be resolved
486 */
487 protected Endpoint createEndpoint(String uri) {
488 Object value = getRegistry().lookup(uri);
489 if (value instanceof Endpoint) {
490 return (Endpoint)value;
491 } else if (value instanceof Processor) {
492 return new ProcessorEndpoint(uri, this, (Processor)value);
493 } else if (value != null) {
494 return convertBeanToEndpoint(uri, value);
495 }
496 return null;
497 }
498
499 /**
500 * Attempt to convert the bean from a {@link Registry} to an endpoint using
501 * some kind of transformation or wrapper
502 *
503 * @param uri the uri for the endpoint (and name in the registry)
504 * @param bean the bean to be converted to an endpoint, which will be not
505 * null
506 * @return a new endpoint
507 */
508 protected Endpoint convertBeanToEndpoint(String uri, Object bean) {
509 throw new IllegalArgumentException("uri: " + uri + " bean: " + bean
510 + " could not be converted to an Endpoint");
511 }
512
513 }