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