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