org.mule.tck.junit4.ExtensionsFunctionalTestCase.java Source code

Java tutorial

Introduction

Here is the source code for org.mule.tck.junit4.ExtensionsFunctionalTestCase.java

Source

/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package org.mule.tck.junit4;

import static org.mule.api.lifecycle.LifecycleUtils.initialiseIfNeeded;
import static org.mule.util.IOUtils.getResourceAsUrl;
import static org.springframework.util.ReflectionUtils.findMethod;
import org.mule.DefaultMuleContext;
import org.mule.api.MuleContext;
import org.mule.api.config.ConfigurationBuilder;
import org.mule.api.registry.ServiceRegistry;
import org.mule.config.builders.AbstractConfigurationBuilder;
import org.mule.extension.ExtensionManager;
import org.mule.extension.introspection.Extension;
import org.mule.extension.introspection.ExtensionFactory;
import org.mule.extension.introspection.declaration.Describer;
import org.mule.extension.resources.GeneratedResource;
import org.mule.extension.resources.ResourcesGenerator;
import org.mule.extension.resources.spi.GenerableResourceContributor;
import org.mule.module.extension.internal.introspection.AnnotationsBasedDescriber;
import org.mule.module.extension.internal.introspection.DefaultExtensionFactory;
import org.mule.module.extension.internal.manager.DefaultExtensionManager;
import org.mule.module.extension.internal.resources.AbstractResourcesGenerator;
import org.mule.registry.SpiServiceRegistry;
import org.mule.util.ArrayUtils;

import com.google.common.collect.ImmutableList;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.List;

import org.apache.commons.io.FileUtils;

/**
 * Base test class for {@link FunctionalTestCase}s
 * that make use of components generated through the extensions API.
 * <p/>
 * The value added by this class in comparison to a traditional
 * {@link FunctionalTestCase} is that before creating
 * the {@link MuleContext}, it creates a {@link ExtensionManager}
 * and automatically discovers extensions by delegating on
 * {@link ExtensionManager#discoverExtensions(ClassLoader)}.
 * <p/>
 * By default, standard extension discovery will be
 * performed by invoking {@link ExtensionManager#discoverExtensions(ClassLoader)}.
 * Although this behavior suits most use cases, it can be time consuming because of
 * all the classpath scanning and the overhead of initialising extensions that
 * are most likely not used in this tests. As the number of extensions available grows,
 * the problem gets worst. For those cases,  you can override the {@link #getDescribers()}
 * and specify which describers are to be used to initialise the extensions manager. In that way,
 * extensions discovery is skipped and you only initialise what you need.
 * <p/>
 * Once extensions are discovered and described,
 * a {@link ResourcesGenerator} is used to automatically
 * generate any backing resources needed (for example, XSD schemas, spring bundles,
 * service registration files, etc).
 * <p/>
 * In this way, the user experience is greatly simplified when running the test
 * either through an IDE or build tool such as maven or gradle.
 * <p/>
 * Since this class extends {@link FunctionalTestCase}, a new {@link MuleContext}
 * is created per each test. That also means that a new {@link ExtensionManager}
 * is created per test.
 *
 * @since 3.7.0
 */
public abstract class ExtensionsFunctionalTestCase extends FunctionalTestCase {

    private final ServiceRegistry serviceRegistry = new SpiServiceRegistry();
    private final ExtensionFactory extensionFactory = new DefaultExtensionFactory(serviceRegistry);
    private ExtensionManager extensionManager;
    private File generatedResourcesDirectory;

    /**
     * Performs all the logic inherited from the super class, plus invokes
     * {@link #createExtensionsManager()}
     *
     * @throws Exception in case of any failure
     */
    @Override
    protected void doSetUpBeforeMuleContextCreation() throws Exception {
        super.doSetUpBeforeMuleContextCreation();
        createExtensionsManager();
    }

    /**
     * Implement this method to limit the amount of extensions
     * initialised by providing the {@link Describer}s for
     * the extensions that you actually want to use for this test.
     * Returning a {@code null} or empty array will cause the
     * {@link #getAnnotatedExtensionClasses()} method to be considered.
     * Default implementation of this method returns {@code null}
     */
    protected Describer[] getDescribers() {
        return null;
    }

    /**
     * Implement this method to limit the amount of extensions
     * initialised by providing the annotated classes which define
     * the extensions that you actually want to use for this test.
     * Returning a {@code null} or empty array forces the
     * {@link ExtensionManager} to perform a full classpath discovery.
     * Default implementation of this method returns {@code null}.
     * This method will only be considered if {@link #getDescribers()}
     * returns {@code null}
     */
    protected Class<?>[] getAnnotatedExtensionClasses() {
        return null;
    }

    /**
     * Adds a {@link ConfigurationBuilder} that sets the {@link #extensionManager}
     * into the {@link #muleContext}. This {@link ConfigurationBuilder} is set
     * as the first element of the {@code builders} {@link List}
     *
     * @param builders the list of {@link ConfigurationBuilder}s that will be used to initialise the {@link #muleContext}
     */
    @Override
    protected final void addBuilders(List<ConfigurationBuilder> builders) {
        super.addBuilders(builders);
        builders.add(0, new AbstractConfigurationBuilder() {
            @Override
            protected void doConfigure(MuleContext muleContext) throws Exception {
                ((DefaultMuleContext) muleContext).setExtensionManager(extensionManager);
                initialiseIfNeeded(extensionManager, muleContext);
            }
        });
    }

    private List<GenerableResourceContributor> getGenerableResourceContributors() {
        return ImmutableList.copyOf(serviceRegistry.lookupProviders(GenerableResourceContributor.class));
    }

    private void createExtensionsManager() throws Exception {
        extensionManager = new DefaultExtensionManager();

        Describer[] describers = getDescribers();
        if (ArrayUtils.isEmpty(describers)) {
            Class<?>[] annotatedClasses = getAnnotatedExtensionClasses();
            if (!ArrayUtils.isEmpty(annotatedClasses)) {
                describers = new Describer[annotatedClasses.length];
                int i = 0;
                for (Class<?> annotatedClass : annotatedClasses) {
                    describers[i++] = new AnnotationsBasedDescriber(annotatedClass);
                }
            }
        }

        if (ArrayUtils.isEmpty(describers)) {
            extensionManager.discoverExtensions(getClass().getClassLoader());
        } else {
            loadExtensionsFromDescribers(extensionManager, describers);
        }

        generatedResourcesDirectory = getGenerationTargetDirectory();

        ResourcesGenerator generator = new ExtensionsTestInfrastructureResourcesGenerator(serviceRegistry,
                generatedResourcesDirectory);

        List<GenerableResourceContributor> resourceContributors = getGenerableResourceContributors();
        for (Extension extension : extensionManager.getExtensions()) {
            for (GenerableResourceContributor contributor : resourceContributors) {
                contributor.contribute(extension, generator);
            }
        }

        generateResourcesAndAddToClasspath(generator);
    }

    private void loadExtensionsFromDescribers(ExtensionManager extensionManager, Describer[] describers) {
        for (Describer describer : describers) {
            extensionManager.registerExtension(extensionFactory.createFrom(describer.describe()));
        }
    }

    private void generateResourcesAndAddToClasspath(ResourcesGenerator generator) throws Exception {
        ClassLoader cl = getClass().getClassLoader();
        Method method = findMethod(cl.getClass(), "addURL", URL.class);
        method.setAccessible(true);

        for (GeneratedResource resource : generator.dumpAll()) {
            URL generatedResourceURL = new File(generatedResourcesDirectory, resource.getFilePath()).toURI()
                    .toURL();
            method.invoke(cl, generatedResourceURL);
        }
    }

    private File getGenerationTargetDirectory() {
        URL url = getResourceAsUrl(getEffectiveConfigFile(), getClass(), true, true);
        File targetDirectory = new File(FileUtils.toFile(url).getParentFile(), "META-INF");

        if (!targetDirectory.exists() && !targetDirectory.mkdir()) {
            throw new RuntimeException("Could not create target directory " + targetDirectory.getAbsolutePath());
        }

        return targetDirectory;
    }

    private String getEffectiveConfigFile() {
        String configFile = getConfigFile();
        if (configFile != null) {
            return configFile;
        }

        configFile = getConfigFileFromSplittable(getConfigurationResources());
        if (configFile != null) {
            return configFile;
        }

        configFile = getConfigFileFromSplittable(getConfigResources());
        if (configFile != null) {
            return configFile;
        }

        String[] configFiles = getConfigFiles();
        if (!ArrayUtils.isEmpty(configFiles)) {
            return configFiles[0].trim();
        }

        throw new IllegalArgumentException("No valid config file was specified");
    }

    private String getConfigFileFromSplittable(String configFile) {
        if (configFile != null) {
            return configFile.split(",")[0].trim();
        }

        return null;
    }

    private class ExtensionsTestInfrastructureResourcesGenerator extends AbstractResourcesGenerator {

        private File targetDirectory;

        private ExtensionsTestInfrastructureResourcesGenerator(ServiceRegistry serviceRegistry,
                File targetDirectory) {
            super(serviceRegistry);
            this.targetDirectory = targetDirectory;
        }

        @Override
        protected void write(GeneratedResource resource) {
            File targetFile = new File(targetDirectory, resource.getFilePath());
            try {
                FileUtils.write(targetFile, resource.getContentBuilder().toString());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}