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