io.gravitee.management.repository.plugins.RepositoryPluginHandler.java Source code

Java tutorial

Introduction

Here is the source code for io.gravitee.management.repository.plugins.RepositoryPluginHandler.java

Source

/**
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * 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 io.gravitee.management.repository.plugins;

import io.gravitee.management.repository.proxy.AbstractProxy;
import io.gravitee.plugin.core.api.*;
import io.gravitee.plugin.core.internal.AnnotationBasedPluginContextConfigurer;
import io.gravitee.repository.Repository;
import io.gravitee.repository.Scope;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author David BRASSELY (david at gravitee.io)
 * @author GraviteeSource Team
 */
public class RepositoryPluginHandler implements PluginHandler, InitializingBean {

    private final static Logger LOGGER = LoggerFactory.getLogger(RepositoryPluginHandler.class);

    @Resource
    private Environment environment;

    @Resource
    private PluginContextFactory pluginContextFactory;

    @Resource(name = "pluginClassLoaderFactory")
    private PluginClassLoaderFactory pluginClassLoaderFactory;

    @Resource
    private ApplicationContext applicationContext;

    private final Map<Scope, Repository> repositories = new HashMap<>();
    private final Map<Scope, String> repositoryTypeByScope = new HashMap<>();
    private final Map<String, Collection<Scope>> scopeByRepositoryType = new HashMap<>();

    @Override
    public void afterPropertiesSet() throws Exception {
        // The gateway need 2 repositories :
        // 1_ Management
        lookForRepositoryType(Scope.MANAGEMENT);
        // 2_ Analytics
        lookForRepositoryType(Scope.ANALYTICS);
    }

    @Override
    public boolean canHandle(Plugin plugin) {
        return plugin.type() == PluginType.REPOSITORY;
    }

    @Override
    public void handle(Plugin plugin) {
        try {
            ClassLoader classloader = pluginClassLoaderFactory.getOrCreateClassLoader(plugin,
                    this.getClass().getClassLoader());

            final Class<?> repositoryClass = classloader.loadClass(plugin.clazz());
            LOGGER.info("Register a new repository: {} [{}]", plugin.id(), plugin.clazz());

            Assert.isAssignable(Repository.class, repositoryClass);

            Repository repository = createInstance((Class<Repository>) repositoryClass);
            Collection<Scope> scopes = scopeByRepositoryType.getOrDefault(repository.type(),
                    Collections.EMPTY_LIST);

            for (Scope scope : scopes) {
                if (!repositories.containsKey(scope)) {
                    // Not yet loaded, let's mount the repository in application context
                    try {
                        ApplicationContext applicationContext = pluginContextFactory
                                .create(new AnnotationBasedPluginContextConfigurer(plugin) {
                                    @Override
                                    public Set<Class<?>> configurations() {
                                        return Collections.singleton(repository.configuration(scope));
                                    }
                                });

                        registerRepositoryDefinitions(repository, applicationContext);
                        repositories.put(scope, repository);
                    } catch (Exception iae) {
                        LOGGER.error("Unexpected error while creating context for repository instance", iae);
                        pluginContextFactory.remove(plugin);
                    }

                } else {
                    LOGGER.warn("Repository scope {} already loaded by {}", scope, repositories.get(scope));
                }
            }
        } catch (Exception iae) {
            LOGGER.error("Unexpected error while create repository instance", iae);
        }
    }

    private void registerRepositoryDefinitions(Repository repository, ApplicationContext repoApplicationContext) {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) ((ConfigurableApplicationContext) applicationContext)
                .getBeanFactory();

        String[] beanNames = repoApplicationContext.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Object repositoryClassInstance = repoApplicationContext.getBean(beanName);
            Class<?> repositoryObjectClass = repositoryClassInstance.getClass();
            if ((beanName.endsWith("Repository")
                    || (beanName.endsWith("Manager") && !beanName.endsWith("TransactionManager")))
                    && !repository.getClass().equals(repositoryClassInstance.getClass())) {
                if (repositoryObjectClass.getInterfaces().length > 0) {
                    Class<?> repositoryItfClass = repositoryObjectClass.getInterfaces()[0];
                    LOGGER.debug("Set proxy target for {} [{}]", beanName, repositoryItfClass);
                    try {
                        Object proxyRepository = beanFactory.getBean(repositoryItfClass);
                        if (proxyRepository instanceof AbstractProxy) {
                            AbstractProxy proxy = (AbstractProxy) proxyRepository;
                            proxy.setTarget(repositoryClassInstance);
                        }
                    } catch (NoSuchBeanDefinitionException nsbde) {
                        LOGGER.debug("Unable to proxify {} [{}]", beanName, repositoryItfClass);
                    }
                }
            } else if (beanName.endsWith("TransactionManager")) {
                beanFactory.registerSingleton(beanName, repositoryClassInstance);
            }
        }
    }

    private String lookForRepositoryType(Scope scope) throws Exception {
        String repositoryType = environment.getProperty(scope.getName() + ".type");
        LOGGER.info("Loading repository for scope {}: {}", scope, repositoryType);

        if (repositoryType == null || repositoryType.isEmpty()) {
            LOGGER.error("No repository type defined in configuration for {}", scope.getName());
            throw new IllegalStateException("No repository type defined in configuration for " + scope.getName());
        }

        repositoryTypeByScope.put(scope, repositoryType);
        Collection<Scope> scopes = scopeByRepositoryType.getOrDefault(repositoryType, new ArrayList<>());
        scopes.add(scope);
        scopeByRepositoryType.put(repositoryType, scopes);
        return repositoryType;
    }

    private <T> T createInstance(Class<T> clazz) throws Exception {
        try {
            return clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException ex) {
            LOGGER.error("Unable to instantiate class: {}", ex);
            throw ex;
        }
    }
}