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

com.paypal.butterfly.core.ExtensionRegistry Maven / Gradle / Ivy

The newest version!
package com.paypal.butterfly.core;

import com.paypal.butterfly.extensions.api.Extension;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfo;
import io.github.classgraph.ScanResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toCollection;

/**
 * Registry of all extensions. This class is used mostly for meta-data
 * purposes, and also to make sure the TransformationTemplate classes
 * are really coming from extensions
 *
 * @author facarvalho
 */
@Component
class ExtensionRegistry {

    private static Logger logger = LoggerFactory.getLogger(ExtensionRegistry.class);

    private List> extensions;

    private void initExtensions() {
        logger.debug("Searching for extensions");
        Set>> extensionClasses = findExtensionClasses();
        logger.debug("Number of extensions found: {}", extensionClasses.size());

        if (extensionClasses.isEmpty()) {
            extensions = Collections.emptyList();
            return;
        }

        logger.debug("Registering extensions");
        registerExtensions(extensionClasses);
        logger.debug("Extensions have been registered");
    }

    private Set>> findExtensionClasses() {
        // Assumption here is that context loader is already a spring-boot ClassLoader and therefore is capable of
        // loading classes from nested jars. If the assumption is not true, we can easily create such a ClassLoader
        // with SpringBootClassLoaderFactory currently used only in tests.
        ClassGraph classGraph = new ClassGraph()
                .enableClassInfo()
                .removeTemporaryFilesAfterScan();

        try (ScanResult scanResult = classGraph.scan()) {
            return scanResult.getSubclasses(Extension.class.getName())
                    .stream()
                    .map(ClassInfo::loadClass)
                    .map(clazz -> asExtensionClass(clazz))
                    .collect(toCollection(() -> new TreeSet<>(comparing(Class::getName))));
        }
    }

    @SuppressWarnings("unchecked")
    private Class> asExtensionClass(Class clazz) {
        return (Class>) clazz;
    }

    private void registerExtensions(Set>> extensionClasses) {
        List> newExtensions = new ArrayList<>();

        for (Class> extensionClass : extensionClasses) {
            try {
                Extension extension = extensionClass.getConstructor().newInstance();
                newExtensions.add(extension);
            } catch (Exception e) {
                logger.error("Cannot register extension class {}", extensionClass, e);
            }
        }

        this.extensions = newExtensions;
    }

    /**
     * Returns an immutable list with all registered extensions
     *
     * @return an immutable list with all registered extensions
     */
    @SuppressWarnings("rawtypes")
    List getExtensions() {
        if (extensions == null) {
            initExtensions();
        }
        return Collections.unmodifiableList(extensions);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy