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.spring;
018
019 import org.apache.camel.Endpoint;
020 import org.apache.camel.component.bean.BeanProcessor;
021 import org.apache.camel.component.event.EventComponent;
022 import org.apache.camel.component.event.EventEndpoint;
023 import org.apache.camel.impl.DefaultCamelContext;
024 import org.apache.camel.impl.ProcessorEndpoint;
025 import org.apache.camel.spi.Injector;
026 import org.apache.camel.spi.Registry;
027 import org.apache.camel.spring.spi.ApplicationContextRegistry;
028 import org.apache.camel.spring.spi.SpringInjector;
029 import org.slf4j.Logger;
030 import org.slf4j.LoggerFactory;
031 import org.springframework.beans.BeansException;
032 import org.springframework.beans.factory.DisposableBean;
033 import org.springframework.beans.factory.InitializingBean;
034 import org.springframework.context.ApplicationContext;
035 import org.springframework.context.ApplicationContextAware;
036 import org.springframework.context.ApplicationEvent;
037 import org.springframework.context.ConfigurableApplicationContext;
038 import org.springframework.context.event.ContextRefreshedEvent;
039 import org.springframework.context.event.ContextStoppedEvent;
040 import org.springframework.context.support.ClassPathXmlApplicationContext;
041
042 import static org.apache.camel.util.ObjectHelper.wrapRuntimeCamelException;
043
044 /**
045 * A Spring aware implementation of {@link org.apache.camel.CamelContext} which
046 * will automatically register itself with Springs lifecycle methods plus allows
047 * spring to be used to customize a any <a
048 * href="http://camel.apache.org/type-converter.html">Type Converters</a>
049 * as well as supporting accessing components and beans via the Spring
050 * {@link ApplicationContext}
051 *
052 * @version
053 */
054 public class SpringCamelContext extends DefaultCamelContext implements InitializingBean, DisposableBean,
055 ApplicationContextAware {
056
057 private static final transient Logger LOG = LoggerFactory.getLogger(SpringCamelContext.class);
058 private static final ThreadLocal<Boolean> NO_START = new ThreadLocal<Boolean>();
059 private ApplicationContext applicationContext;
060 private EventEndpoint eventEndpoint;
061
062 public SpringCamelContext() {
063 }
064
065 public SpringCamelContext(ApplicationContext applicationContext) {
066 setApplicationContext(applicationContext);
067 }
068
069
070 public static void setNoStart(boolean b) {
071 if (b) {
072 NO_START.set(b);
073 } else {
074 NO_START.remove();
075 }
076 }
077
078 public static SpringCamelContext springCamelContext(ApplicationContext applicationContext) throws Exception {
079 return springCamelContext(applicationContext, true);
080 }
081
082 public static SpringCamelContext springCamelContext(ApplicationContext applicationContext, boolean maybeStart) throws Exception {
083 // lets try and look up a configured camel context in the context
084 String[] names = applicationContext.getBeanNamesForType(SpringCamelContext.class);
085 if (names.length == 1) {
086 return (SpringCamelContext)applicationContext.getBean(names[0], SpringCamelContext.class);
087 }
088 SpringCamelContext answer = new SpringCamelContext();
089 answer.setApplicationContext(applicationContext);
090 if (maybeStart) {
091 answer.afterPropertiesSet();
092 }
093 return answer;
094 }
095
096 public static SpringCamelContext springCamelContext(String configLocations) throws Exception {
097 return springCamelContext(new ClassPathXmlApplicationContext(configLocations));
098 }
099
100 public void afterPropertiesSet() throws Exception {
101 maybeStart();
102 }
103
104 public void destroy() throws Exception {
105 stop();
106 }
107
108 public void onApplicationEvent(ApplicationEvent event) {
109 LOG.debug("onApplicationEvent: {}", event);
110
111 if (event instanceof ContextRefreshedEvent) {
112 // now lets start the CamelContext so that all its possible
113 // dependencies are initialized
114 try {
115 maybeStart();
116 } catch (Exception e) {
117 throw wrapRuntimeCamelException(e);
118 }
119 } else if (event instanceof ContextStoppedEvent) {
120 try {
121 maybeStop();
122 } catch (Exception e) {
123 throw wrapRuntimeCamelException(e);
124 }
125 }
126
127 if (eventEndpoint != null) {
128 eventEndpoint.onApplicationEvent(event);
129 } else {
130 LOG.info("No spring-event endpoint enabled to handle event: {}", event);
131 }
132 }
133
134 // Properties
135 // -----------------------------------------------------------------------
136
137 public ApplicationContext getApplicationContext() {
138 return applicationContext;
139 }
140
141 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
142 this.applicationContext = applicationContext;
143 ClassLoader cl;
144
145 // set the application context classloader
146 if (applicationContext != null && applicationContext.getClassLoader() != null) {
147 cl = applicationContext.getClassLoader();
148 } else {
149 LOG.warn("Cannot find the class loader from application context, using the thread context class loader instead");
150 cl = Thread.currentThread().getContextClassLoader();
151 }
152 LOG.debug("Set the application context classloader to: {}", cl);
153 this.setApplicationContextClassLoader(cl);
154
155 if (applicationContext instanceof ConfigurableApplicationContext) {
156 // only add if not already added
157 if (hasComponent("spring-event") == null) {
158 addComponent("spring-event", new EventComponent(applicationContext));
159 }
160 }
161 }
162
163 public EventEndpoint getEventEndpoint() {
164 return eventEndpoint;
165 }
166
167 public void setEventEndpoint(EventEndpoint eventEndpoint) {
168 this.eventEndpoint = eventEndpoint;
169 }
170
171 // Implementation methods
172 // -----------------------------------------------------------------------
173
174 @Override
175 protected void doStart() throws Exception {
176 super.doStart();
177 if (eventEndpoint == null) {
178 eventEndpoint = createEventEndpoint();
179 }
180 }
181
182 @Override
183 protected Injector createInjector() {
184 if (applicationContext instanceof ConfigurableApplicationContext) {
185 return new SpringInjector((ConfigurableApplicationContext)applicationContext);
186 } else {
187 LOG.warn("Cannot use SpringInjector as applicationContext is not a ConfigurableApplicationContext as its: "
188 + applicationContext);
189 return super.createInjector();
190 }
191 }
192
193 protected EventEndpoint createEventEndpoint() {
194 return getEndpoint("spring-event:default", EventEndpoint.class);
195 }
196
197 protected Endpoint convertBeanToEndpoint(String uri, Object bean) {
198 // We will use the type convert to build the endpoint first
199 Endpoint endpoint = getTypeConverter().convertTo(Endpoint.class, bean);
200 if (endpoint != null) {
201 endpoint.setCamelContext(this);
202 return endpoint;
203 }
204
205 return new ProcessorEndpoint(uri, this, new BeanProcessor(bean, this));
206 }
207
208 @Override
209 protected Registry createRegistry() {
210 return new ApplicationContextRegistry(getApplicationContext());
211 }
212
213 private void maybeStart() throws Exception {
214 // for example from unit testing we want to start Camel later and not when Spring framework
215 // publish a ContextRefreshedEvent
216 String maybeStart = System.getProperty("maybeStartCamelContext", "true");
217
218 if ("true".equals(maybeStart)
219 && NO_START.get() == null) {
220 if (!isStarted() && !isStarting()) {
221 start();
222 } else {
223 // ignore as Camel is already started
224 LOG.trace("Ignoring maybeStart() as Apache Camel is already started");
225 }
226 } else {
227 LOG.trace("Ignoring maybeStart() as System property maybeStartCamelContext is false");
228 }
229 }
230
231 private void maybeStop() throws Exception {
232 if (!isStopping() && !isStopped()) {
233 stop();
234 } else {
235 // ignore as Camel is already stopped
236 LOG.trace("Ignoring maybeStop() as Apache Camel is already stopped");
237 }
238 }
239
240 @Override
241 public String toString() {
242 StringBuilder sb = new StringBuilder();
243 sb.append("SpringCamelContext(").append(getName()).append(")");
244 if (applicationContext != null) {
245 sb.append(" with spring id ").append(applicationContext.getId());
246 }
247 return sb.toString();
248 }
249
250 }