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 java.util.LinkedHashSet;
020 import java.util.Map;
021 import java.util.Set;
022 import javax.xml.bind.annotation.XmlAccessType;
023 import javax.xml.bind.annotation.XmlAccessorType;
024 import javax.xml.bind.annotation.XmlRootElement;
025 import javax.xml.bind.annotation.XmlTransient;
026
027 import org.apache.camel.CamelContext;
028 import org.apache.camel.Endpoint;
029 import org.apache.camel.Service;
030 import org.apache.camel.core.xml.CamelJMXAgentDefinition;
031 import org.apache.camel.impl.CamelPostProcessorHelper;
032 import org.apache.camel.impl.DefaultCamelBeanPostProcessor;
033 import org.apache.camel.util.ServiceHelper;
034 import org.slf4j.Logger;
035 import org.slf4j.LoggerFactory;
036 import org.springframework.beans.BeanInstantiationException;
037 import org.springframework.beans.BeansException;
038 import org.springframework.beans.factory.config.BeanPostProcessor;
039 import org.springframework.context.ApplicationContext;
040 import org.springframework.context.ApplicationContextAware;
041
042 /**
043 * Spring specific {@link DefaultCamelBeanPostProcessor} which uses Spring {@link BeanPostProcessor} to post process beans.
044 *
045 * @see DefaultCamelBeanPostProcessor
046 */
047 @XmlRootElement(name = "beanPostProcessor")
048 @XmlAccessorType(XmlAccessType.FIELD)
049 public class CamelBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
050 private static final Logger LOG = LoggerFactory.getLogger(CamelBeanPostProcessor.class);
051 @XmlTransient
052 Set<String> prototypeBeans = new LinkedHashSet<String>();
053 @XmlTransient
054 private CamelContext camelContext;
055 @XmlTransient
056 private ApplicationContext applicationContext;
057 @XmlTransient
058 private String camelId;
059
060 // must use a delegate, as we cannot extend DefaultCamelBeanPostProcessor, as this will cause the
061 // XSD schema generator to include the DefaultCamelBeanPostProcessor as a type, which we do not want to
062 @XmlTransient
063 private final DefaultCamelBeanPostProcessor delegate = new DefaultCamelBeanPostProcessor() {
064 @Override
065 public CamelContext getOrLookupCamelContext() {
066 if (camelContext == null) {
067 if (camelId != null) {
068 LOG.trace("Looking up CamelContext by id: {} from Spring ApplicationContext: {}", camelId, applicationContext);
069 camelContext = applicationContext.getBean(camelId, CamelContext.class);
070 } else {
071 // lookup by type and grab the single CamelContext if exists
072 LOG.trace("Looking up CamelContext by type from Spring ApplicationContext: {}", applicationContext);
073 Map<String, CamelContext> contexts = applicationContext.getBeansOfType(CamelContext.class);
074 if (contexts != null && contexts.size() == 1) {
075 camelContext = contexts.values().iterator().next();
076 }
077 }
078 }
079 return camelContext;
080 }
081
082 @Override
083 public boolean canPostProcessBean(Object bean, String beanName) {
084 // the JMXAgent is a bit strange and causes Spring issues if we let it being
085 // post processed by this one. It does not need it anyway so we are good to go.
086 // We should also avoid to process the null object bean (in Spring 2.5.x)
087 if (bean == null || bean instanceof CamelJMXAgentDefinition) {
088 return false;
089 }
090
091 return super.canPostProcessBean(bean, beanName);
092 }
093
094 @Override
095 public CamelPostProcessorHelper getPostProcessorHelper() {
096 // lets lazily create the post processor
097 if (camelPostProcessorHelper == null) {
098 camelPostProcessorHelper = new CamelPostProcessorHelper() {
099
100 @Override
101 public CamelContext getCamelContext() {
102 // lets lazily lookup the camel context here
103 // as doing this will cause this context to be started immediately
104 // breaking the lifecycle ordering of different camel contexts
105 // so we only want to do this on demand
106 return delegate.getOrLookupCamelContext();
107 }
108
109 @Override
110 protected RuntimeException createProxyInstantiationRuntimeException(Class<?> type, Endpoint endpoint, Exception e) {
111 return new BeanInstantiationException(type, "Could not instantiate proxy of type " + type.getName() + " on endpoint " + endpoint, e);
112 }
113
114 protected boolean isSingleton(Object bean, String beanName) {
115 // no application context has been injected which means the bean
116 // has not been enlisted in Spring application context
117 if (applicationContext == null || beanName == null) {
118 return super.isSingleton(bean, beanName);
119 } else {
120 return applicationContext.isSingleton(beanName);
121 }
122 }
123
124 protected void startService(Service service, Object bean, String beanName) throws Exception {
125 if (isSingleton(bean, beanName)) {
126 getCamelContext().addService(service);
127 } else {
128 // only start service and do not add it to CamelContext
129 ServiceHelper.startService(service);
130 if (prototypeBeans.add(beanName)) {
131 // do not spam the log with WARN so do this only once per bean name
132 CamelBeanPostProcessor.LOG.warn("The bean with id [" + beanName + "] is prototype scoped and cannot stop the injected service when bean is destroyed: "
133 + service + ". You may want to stop the service manually from the bean.");
134 }
135 }
136 }
137 };
138 }
139 return camelPostProcessorHelper;
140 }
141 };
142
143 public CamelBeanPostProcessor() {
144 }
145
146 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
147 try {
148 return delegate.postProcessBeforeInitialization(bean, beanName);
149 } catch (Exception e) {
150 // do not wrap already beans exceptions
151 if (e instanceof BeansException) {
152 throw (BeansException) e;
153 }
154 throw new GenericBeansException("Error post processing bean: " + beanName, e);
155 }
156 }
157
158 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
159 try {
160 return delegate.postProcessAfterInitialization(bean, beanName);
161 } catch (Exception e) {
162 // do not wrap already beans exceptions
163 if (e instanceof BeansException) {
164 throw (BeansException) e;
165 }
166 throw new GenericBeansException("Error post processing bean: " + beanName, e);
167 }
168 }
169
170 // Properties
171 // -------------------------------------------------------------------------
172
173 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
174 this.applicationContext = applicationContext;
175 }
176
177 public CamelContext getCamelContext() {
178 return camelContext;
179 }
180
181 public void setCamelContext(CamelContext camelContext) {
182 this.camelContext = camelContext;
183 }
184
185 public String getCamelId() {
186 return camelId;
187 }
188
189 public void setCamelId(String camelId) {
190 this.camelId = camelId;
191 }
192
193 }