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