All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.camel.main.injection.AnnotationDependencyInjection Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.camel.main.injection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.inject.Singleton;

import org.apache.camel.BindToRegistry;
import org.apache.camel.CamelConfiguration;
import org.apache.camel.CamelContext;
import org.apache.camel.Configuration;
import org.apache.camel.Converter;
import org.apache.camel.LoggingLevel;
import org.apache.camel.NoSuchBeanException;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.TypeConverterExists;
import org.apache.camel.impl.engine.CamelPostProcessorHelper;
import org.apache.camel.spi.CamelBeanPostProcessor;
import org.apache.camel.spi.CamelBeanPostProcessorInjector;
import org.apache.camel.spi.CompilePostProcessor;
import org.apache.camel.spi.Registry;
import org.apache.camel.spi.TypeConverterRegistry;
import org.apache.camel.support.PluginHelper;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.util.ReflectionHelper;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * To enable camel/spring/quarkus based annotations for dependency injection when loading DSLs.
 */
public final class AnnotationDependencyInjection {

    private AnnotationDependencyInjection() {
    }

    public static void initAnnotationBasedDependencyInjection(CamelContext context) {
        Registry registry = context.getRegistry();
        CamelBeanPostProcessor cbbp = PluginHelper.getBeanPostProcessor(context);

        // camel / common
        registry.bind("CamelTypeConverterCompilePostProcessor", new TypeConverterCompilePostProcessor());
        registry.bind("CamelBindToRegistryCompilePostProcessor", new BindToRegistryCompilePostProcessor());
        // spring
        registry.bind("SpringAnnotationCompilePostProcessor", new SpringAnnotationCompilePostProcessor());
        cbbp.addCamelBeanPostProjectInjector(new SpringBeanPostProcessorInjector(context));
        // quarkus
        registry.bind("QuarkusAnnotationCompilePostProcessor", new QuarkusAnnotationCompilePostProcessor());
        cbbp.addCamelBeanPostProjectInjector(new QuarkusBeanPostProcessorInjector(context));
    }

    private static class TypeConverterCompilePostProcessor implements CompilePostProcessor {

        @Override
        public void postCompile(CamelContext camelContext, String name, Class clazz, byte[] byteCode, Object instance)
                throws Exception {
            if (clazz.isAnnotationPresent(Converter.class)) {
                TypeConverterRegistry tcr = camelContext.getTypeConverterRegistry();
                TypeConverterExists exists = tcr.getTypeConverterExists();
                LoggingLevel level = tcr.getTypeConverterExistsLoggingLevel();
                // force type converter to override as we could be re-loading
                tcr.setTypeConverterExists(TypeConverterExists.Override);
                tcr.setTypeConverterExistsLoggingLevel(LoggingLevel.OFF);
                try {
                    tcr.addTypeConverters(clazz);
                } finally {
                    tcr.setTypeConverterExists(exists);
                    tcr.setTypeConverterExistsLoggingLevel(level);
                }
            }
        }

    }

    private static class BindToRegistryCompilePostProcessor implements CompilePostProcessor {

        @Override
        public void postCompile(CamelContext camelContext, String name, Class clazz, byte[] byteCode, Object instance)
                throws Exception {
            if (instance == null) {
                return;
            }

            BindToRegistry bir = instance.getClass().getAnnotation(BindToRegistry.class);
            Configuration cfg = instance.getClass().getAnnotation(Configuration.class);
            if (bir != null || cfg != null || instance instanceof CamelConfiguration) {
                CamelBeanPostProcessor bpp = PluginHelper.getBeanPostProcessor(camelContext);
                if (bir != null && ObjectHelper.isNotEmpty(bir.value())) {
                    name = bir.value();
                } else if (cfg != null && ObjectHelper.isNotEmpty(cfg.value())) {
                    name = cfg.value();
                }
                // to support hot reloading of beans then we need to enable unbind mode in bean post processor
                bpp.setUnbindEnabled(true);
                try {
                    // this class uses camels own annotations so the bind to registry happens
                    // automatic by the bean post processor
                    bpp.postProcessBeforeInitialization(instance, name);
                    bpp.postProcessAfterInitialization(instance, name);
                } finally {
                    bpp.setUnbindEnabled(false);
                }
                if (instance instanceof CamelConfiguration) {
                    ((CamelConfiguration) instance).configure(camelContext);
                }
            }
        }

    }

    private static class SpringAnnotationCompilePostProcessor implements CompilePostProcessor {

        @Override
        public void postCompile(CamelContext camelContext, String name, Class clazz, byte[] byteCode, Object instance)
                throws Exception {
            if (instance == null) {
                return;
            }
            // @Component and @Service are the same
            Component comp = clazz.getAnnotation(Component.class);
            Service service = clazz.getAnnotation(Service.class);
            if (comp != null || service != null) {
                if (comp != null && ObjectHelper.isNotEmpty(comp.value())) {
                    name = comp.value();
                } else if (service != null && ObjectHelper.isNotEmpty(service.value())) {
                    name = service.value();
                }
                bindBean(camelContext, name, instance, true);
            }
        }
    }

    private static class SpringBeanPostProcessorInjector implements CamelBeanPostProcessorInjector {

        private final CamelContext context;
        private final CamelPostProcessorHelper helper;

        public SpringBeanPostProcessorInjector(CamelContext context) {
            this.context = context;
            this.helper = new CamelPostProcessorHelper(context);
        }

        @Override
        public void onFieldInject(Field field, Object bean, String beanName) {
            Autowired autowired = field.getAnnotation(Autowired.class);
            if (autowired != null) {
                String name = null;
                Qualifier qualifier = field.getAnnotation(Qualifier.class);
                if (qualifier != null) {
                    name = qualifier.value();
                }

                try {
                    ReflectionHelper.setField(field, bean,
                            helper.getInjectionBeanValue(field.getType(), name));
                } catch (NoSuchBeanException e) {
                    if (autowired.required()) {
                        throw e;
                    }
                    // not required so ignore
                }
            }
            Value value = field.getAnnotation(Value.class);
            if (value != null) {
                ReflectionHelper.setField(field, bean,
                        helper.getInjectionPropertyValue(field.getType(), value.value(), null, null, bean, beanName));
            }
        }

        @Override
        public void onMethodInject(Method method, Object bean, String beanName) {
            Bean bi = method.getAnnotation(Bean.class);
            if (bi != null) {
                Object instance = helper.getInjectionBeanMethodValue(context, method, bean, beanName);
                if (instance != null) {
                    String name = method.getName();
                    if (bi.name().length > 0) {
                        name = bi.name()[0];
                    }
                    bindBean(context, name, instance, false);
                }
            }
        }
    }

    private static class QuarkusAnnotationCompilePostProcessor implements CompilePostProcessor {

        @Override
        public void postCompile(CamelContext camelContext, String name, Class clazz, byte[] byteCode, Object instance)
                throws Exception {
            if (instance == null) {
                return;
            }
            // @ApplicationScoped and @Singleton are considered the same
            ApplicationScoped as = clazz.getAnnotation(ApplicationScoped.class);
            Singleton ss = clazz.getAnnotation(Singleton.class);
            if (as != null || ss != null) {
                Named named = clazz.getAnnotation(Named.class);
                if (named != null) {
                    name = named.value();
                }
                bindBean(camelContext, name, instance, true);
            }
        }
    }

    private static class QuarkusBeanPostProcessorInjector implements CamelBeanPostProcessorInjector {

        private final CamelContext context;
        private final CamelPostProcessorHelper helper;

        public QuarkusBeanPostProcessorInjector(CamelContext context) {
            this.context = context;
            this.helper = new CamelPostProcessorHelper(context);
        }

        @Override
        public void onFieldInject(Field field, Object bean, String beanName) {
            Inject inject = field.getAnnotation(Inject.class);
            if (inject != null) {
                String name = null;
                Named named = field.getAnnotation(Named.class);
                if (named != null) {
                    name = named.value();
                }

                ReflectionHelper.setField(field, bean,
                        helper.getInjectionBeanValue(field.getType(), name));
            }
            ConfigProperty cp = field.getAnnotation(ConfigProperty.class);
            if (cp != null) {
                ReflectionHelper.setField(field, bean,
                        helper.getInjectionPropertyValue(field.getType(), cp.name(), cp.defaultValue(), null, bean, beanName));
            }
        }

        @Override
        public void onMethodInject(Method method, Object bean, String beanName) {
            Produces produces = method.getAnnotation(Produces.class);
            Named bi = method.getAnnotation(Named.class);
            if (produces != null || bi != null) {
                Object instance = helper.getInjectionBeanMethodValue(context, method, bean, beanName);
                if (instance != null) {
                    String name = method.getName();
                    if (bi != null && !bi.value().isBlank()) {
                        name = bi.value();
                    }
                    bindBean(context, name, instance, false);
                }
            }
        }
    }

    private static void bindBean(CamelContext context, String name, Object instance, boolean postProcess) {
        // to support hot reloading of beans then we need to enable unbind mode in bean post processor
        Registry registry = context.getRegistry();
        CamelBeanPostProcessor bpp = PluginHelper.getBeanPostProcessor(context);
        bpp.setUnbindEnabled(true);
        try {
            // re-bind the bean to the registry
            registry.unbind(name);
            registry.bind(name, instance);
            if (postProcess) {
                bpp.postProcessBeforeInitialization(instance, name);
                bpp.postProcessAfterInitialization(instance, name);
            }
        } catch (Exception e) {
            throw RuntimeCamelException.wrapRuntimeException(e);
        } finally {
            bpp.setUnbindEnabled(false);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy