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

org.hrodberaht.injection.plugin.junit.plugins.SpringExtensionPlugin Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
/*
 * Copyright (c) 2017 org.hrodberaht
 *
 * Licensed 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.hrodberaht.injection.plugin.junit.plugins;

import org.hrodberaht.injection.core.internal.annotation.DefaultInjectionPointFinder;
import org.hrodberaht.injection.core.register.InjectionRegister;
import org.hrodberaht.injection.core.register.RegistrationModuleAnnotation;
import org.hrodberaht.injection.core.spi.ContainerConfigBuilder;
import org.hrodberaht.injection.plugin.junit.api.Plugin;
import org.hrodberaht.injection.plugin.junit.api.PluginContext;
import org.hrodberaht.injection.plugin.junit.api.annotation.InjectionPluginInjectionFinder;
import org.hrodberaht.injection.plugin.junit.api.annotation.InjectionPluginInjectionRegister;
import org.hrodberaht.injection.plugin.junit.api.annotation.RunnerPluginBeforeContainerCreation;
import org.hrodberaht.injection.plugin.junit.api.resource.ResourceProvider;
import org.hrodberaht.injection.plugin.junit.api.resource.ResourceProviderSupport;
import org.hrodberaht.injection.plugin.junit.plugins.common.PluginLifeCycledResource;
import org.hrodberaht.injection.plugin.junit.spring.beans.config.ContainerAllSpringConfig;
import org.hrodberaht.injection.plugin.junit.spring.beans.config.ContainerSpringConfig;
import org.hrodberaht.injection.plugin.junit.spring.injector.SpringBeanInjector;
import org.hrodberaht.injection.plugin.junit.spring.injector.SpringInject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

/**
 * The SpringExtensionPlugin will start a Spring Container and use it internally as bridge.
 */
public class SpringExtensionPlugin implements Plugin {

    private static final Logger LOG = LoggerFactory.getLogger(SpringExtensionPlugin.class);

    private PluginLifeCycledResource pluginLifeCycledResource = new PluginLifeCycledResource<>(SpringRunner.class);

    private LifeCycle lifeCycle = LifeCycle.TEST_CONFIG;
    private Builder builder = new Builder();
    private InjectionRegister injectionRegister;
    private SpringRunner springRunner;

    @Override
    public LifeCycle getLifeCycle() {
        LOG.info("using lifeCycle:{} for plugin {}", lifeCycle, this);
        return lifeCycle;
    }

    public SpringExtensionPlugin springConfig(String... config) {
        builder.springConfigFiles = config;
        return this;
    }

    public SpringExtensionPlugin springConfig(Class... config) {
        builder.springConfigsClasses = config;
        return this;
    }

    public SpringExtensionPlugin lifeCycle(LifeCycle lifeCycle) {
        this.lifeCycle = lifeCycle;
        return this;
    }

    public SpringExtensionPlugin with(Plugin plugin) {
        if (plugin instanceof ResourceProviderSupport) {
            for (ResourceProvider resourceProvider : ((ResourceProviderSupport) plugin).resources()) {
                LOG.info("Adding resource {} - {}", resourceProvider.getName(), resourceProvider.getType());
                builder.resourceProviders.add(resourceProvider);
            }
        } else {
            LOG.warn("Plugin {} does not implement ResourceProviderSupport to exponse resources", plugin.getClass().getName());
        }
        builder.hasJpaPlugin = hasJpaPLugin(plugin);
        return this;
    }

    public SpringExtensionPlugin withResources(ResourceProviderSupport resourceProviderSupport) {
        builder.resourceProviders.addAll(resourceProviderSupport.resources());
        return this;
    }

    private boolean hasJpaPLugin(Plugin plugin) {
        try {
            Class.forName("org.hrodberaht.injection.plugin.junit.plugins.JpaPlugin");
            return plugin instanceof JpaPlugin;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

    @RunnerPluginBeforeContainerCreation
    protected void beforeContainerCreation(PluginContext pluginContext) {
        LOG.info("beforeContainerCreation for {}", this);
        springRunner = pluginLifeCycledResource.create(lifeCycle, pluginContext, () -> new SpringRunner(this));
        injectionRegister.register(new RegistrationModuleAnnotation() {
            @Override
            public void registrations() {
                register(ApplicationContext.class).withFactoryInstance(springRunner.context);
            }
        });
    }

    @InjectionPluginInjectionRegister
    protected void setInjectionRegister(InjectionRegister injectionRegister) {
        this.injectionRegister = injectionRegister;
    }

    @InjectionPluginInjectionFinder
    protected DefaultInjectionPointFinder getInjectionFinder(ContainerConfigBuilder containerConfigBuilder) {
        return new SpringInjectionPointFinder(this, containerConfigBuilder);
    }

    private static class SpringRunner {
        // Basic Injection
        private final SpringBeanInjector springBeanInjector;
        private final SpringExtensionPlugin springExtensionPlugin;
        // Spring
        private final ApplicationContext context;
        private final DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory();

        private SpringRunner(SpringExtensionPlugin springExtensionPlugin) {
            LOG.info("SpringRunner - Creating SpringApplication", this.getClass());
            Builder builder = springExtensionPlugin.builder;
            this.springExtensionPlugin = springExtensionPlugin;

            resourcesAsSpringBeans();

            if (builder.springConfigsClasses != null) {
                context = loadConfig(builder.springConfigsClasses);
            } else if (builder.springConfigFiles != null) {
                context = loadConfig(builder.springConfigFiles);
            } else {
                context = null;
            }
            if (context != null) {
                springBeanInjector = new SpringBeanInjector(context);
            } else {
                springBeanInjector = null;
            }
        }

        private void resourcesAsSpringBeans() {
            LOG.info("resourcesAsSpringBeans for {}", this);
            springExtensionPlugin.builder.resourceProviders.forEach(pluginResource -> {
                Object instance = getInstance(pluginResource);
                if (pluginResource.getName() != null) {
                    LOG.info("spring registerSingleton for {} using instance {}", pluginResource.getName(), instance);
                    parentBeanFactory.registerSingleton(pluginResource.getName(), instance);
                } else if (pluginResource.getType() != null) {
                    LOG.info("spring registerResolvableDependency for {} using instance {}", pluginResource.getType(), instance);
                    parentBeanFactory.registerResolvableDependency(pluginResource.getType(), instance);
                } else {
                    LOG.warn("No name or type was provided for {}", pluginResource);
                }
            });
        }

        private Object getInstance(ResourceProvider resourceProvider) {
            return resourceProvider.getInstance();
        }

        private Class getContainerSpringConfigClass() {
            return springExtensionPlugin.builder.hasJpaPlugin ? ContainerAllSpringConfig.class : ContainerSpringConfig.class;
        }

        private ClassPathXmlApplicationContext loadConfig(String... springConfigs) {
            LOG.info("SpringContainerConfigBase - Creating SpringApplication XML for {}", this.getClass());
            validateEmptyContext(context);
            // TODO: we have no "jpa enabled" spring bean XML file ...
            String testSpringConfig = "/META-INF/container-spring-config.xml";
            String[] config = new String[]{testSpringConfig};
            if (springConfigs != null) {
                Stream stringStream = Stream.concat(Stream.of(springConfigs), Stream.of(testSpringConfig));
                config = stringStream.toArray(String[]::new);
            }
            return new ClassPathXmlApplicationContext(config);
        }

        private AnnotationConfigApplicationContext loadConfig(Class... springConfigs) {
            LOG.info("SpringContainerConfigBase - Creating SpringApplication Java for {}", this.getClass());
            validateEmptyContext(context);
            Class[] config = new Class[]{getContainerSpringConfigClass()};
            if (springConfigs != null) {
                Stream stringStream = Stream.concat(Stream.of(springConfigs), Stream.of(getContainerSpringConfigClass()));
                config = stringStream.toArray(Class[]::new);
            }
            AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(parentBeanFactory);
            applicationContext.register(config);
            applicationContext.refresh();
            return applicationContext;
        }

        private void validateEmptyContext(ApplicationContext context) {
            if (context != null) {
                throw new IllegalStateException("Context is already loaded, can only be loaded once");
            }
        }

    }

    private static class Builder {
        private String[] springConfigFiles = null;
        private Class[] springConfigsClasses = null;
        private List resourceProviders = new ArrayList<>();
        private boolean hasJpaPlugin = false;

    }

    private static class SpringInjectionPointFinder extends DefaultInjectionPointFinder {
        private final SpringExtensionPlugin springExtensionPlugin;

        private SpringInjectionPointFinder(SpringExtensionPlugin springExtensionPlugin, ContainerConfigBuilder containerConfigBuilder) {
            super(containerConfigBuilder);
            this.springExtensionPlugin = springExtensionPlugin;
        }

        @Override
        public Object extendedInjection(Object instance) {
            super.extendedInjection(instance);
            if (springExtensionPlugin.springRunner.springBeanInjector != null) {
                springExtensionPlugin.springRunner.springBeanInjector.inject(instance, springExtensionPlugin.injectionRegister.getContainer());
            }
            return instance;
        }

        @Override
        protected boolean hasInjectAnnotationOnField(Field field) {
            return field.isAnnotationPresent(SpringInject.class) || super.hasInjectAnnotationOnField(field);
        }

        @Override
        protected boolean hasInjectAnnotationOnMethod(Method method) {
            return method.isAnnotationPresent(SpringInject.class) || super.hasInjectAnnotationOnMethod(method);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy