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 */ 017package org.apache.camel.spring; 018 019import java.util.LinkedHashSet; 020import java.util.Map; 021import java.util.Set; 022import javax.xml.bind.annotation.XmlAccessType; 023import javax.xml.bind.annotation.XmlAccessorType; 024import javax.xml.bind.annotation.XmlRootElement; 025import javax.xml.bind.annotation.XmlTransient; 026 027import org.apache.camel.CamelContext; 028import org.apache.camel.Endpoint; 029import org.apache.camel.Service; 030import org.apache.camel.core.xml.CamelJMXAgentDefinition; 031import org.apache.camel.impl.CamelPostProcessorHelper; 032import org.apache.camel.impl.DefaultCamelBeanPostProcessor; 033import org.apache.camel.util.ServiceHelper; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036import org.springframework.beans.BeanInstantiationException; 037import org.springframework.beans.BeansException; 038import org.springframework.beans.factory.config.BeanPostProcessor; 039import org.springframework.context.ApplicationContext; 040import 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) 049public 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}