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