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

liquibase.servicelocator.ServiceLocator Maven / Gradle / Ivy

There is a newer version: 3.6.2.5.inovus
Show newest version
package liquibase.servicelocator;

import liquibase.exception.ServiceNotFoundException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.logging.LogService;
import liquibase.logging.LogType;
import liquibase.osgi.OSGiPackageScanClassResolver;
import liquibase.osgi.OSGiResourceAccessor;
import liquibase.osgi.OSGiUtil;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.util.StringUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.jar.Manifest;

/**
 * Entry point to the Liquibase specific ServiceLocator framework.
 *
 * Services (concrete instances of interfaces) are located by scanning nominated
 * packages on the classpath for implementations of the interface.
 */
public class ServiceLocator {

    private static ServiceLocator instance;

    static {
        try {
            Class scanner = Class.forName("Liquibase.ServiceLocator.ClrServiceLocator, Liquibase");
            instance = (ServiceLocator) scanner.newInstance();
        } catch (Exception e) {
            try {
                if (OSGiUtil.isLiquibaseLoadedAsOSGiBundle()) {
                    Bundle liquibaseBundle = FrameworkUtil.getBundle(ServiceLocator.class);
                    instance = new ServiceLocator(new OSGiPackageScanClassResolver(liquibaseBundle), 
                            new OSGiResourceAccessor(liquibaseBundle));
                } else {
                    instance = new ServiceLocator();
                }
            } catch (Throwable e1) {
                LogService.getLog(ServiceLocator.class).severe(LogType.LOG, "Cannot build ServiceLocator", e1);
            }
        }
    }

    private ResourceAccessor resourceAccessor;

    private Map> classesBySuperclass;
    private List packagesToScan;
    private PackageScanClassResolver classResolver;

    protected ServiceLocator() {
        this.classResolver = defaultClassLoader();
        setResourceAccessor(new ClassLoaderResourceAccessor());
    }

    protected ServiceLocator(ResourceAccessor accessor) {
        this.classResolver = defaultClassLoader();
        setResourceAccessor(accessor);
    }

    protected ServiceLocator(PackageScanClassResolver classResolver) {
        this.classResolver = classResolver;
        setResourceAccessor(new ClassLoaderResourceAccessor());
    }

    protected ServiceLocator(PackageScanClassResolver classResolver, ResourceAccessor accessor) {
        this.classResolver = classResolver;
        setResourceAccessor(accessor);
    }

    public static ServiceLocator getInstance() {
        return instance;
    }

    public static synchronized void setInstance(ServiceLocator newInstance) {
        instance = newInstance;
    }

    public static synchronized void reset() {
        instance = new ServiceLocator();
    }

    protected PackageScanClassResolver defaultClassLoader(){
        if (WebSpherePackageScanClassResolver.isWebSphereClassLoader(this.getClass().getClassLoader())) {
            LogService.getLog(getClass()).debug(LogType.LOG, "Using WebSphere Specific Class Resolver");
            return new WebSpherePackageScanClassResolver("liquibase/parser/core/xml/dbchangelog-2.0.xsd");
        } else {
            return new DefaultPackageScanClassResolver();
        }
    }

    public void setResourceAccessor(ResourceAccessor resourceAccessor) {
        this.resourceAccessor = resourceAccessor;
        this.classesBySuperclass = new HashMap<>();

        this.classResolver.setClassLoaders(new HashSet<>(Arrays.asList(new ClassLoader[]{resourceAccessor.toClassLoader()})));

        if (packagesToScan == null) {
            packagesToScan = new ArrayList<>();
            String packagesToScanSystemProp = System.getProperty("liquibase.scan.packages");
            if ((packagesToScanSystemProp != null) &&
                ((packagesToScanSystemProp = StringUtils.trimToNull(packagesToScanSystemProp)) != null)) {
                for (String value : packagesToScanSystemProp.split(",")) {
                    addPackageToScan(value);
                }
            } else {
                Set manifests;
                try {
                    manifests = resourceAccessor.getResourcesAsStream("META-INF/MANIFEST.MF");
                    if (manifests != null) {
                        for (InputStream is : manifests) {
                            Manifest manifest = new Manifest(is);
                            String attributes = StringUtils.trimToNull(manifest.getMainAttributes().getValue("Liquibase-Package"));
                            if (attributes != null) {
                                for (Object value : attributes.split(",")) {
                                    addPackageToScan(value.toString());
                                }
                            }
                            is.close();
                        }
                    }
                } catch (IOException e) {
                    throw new UnexpectedLiquibaseException(e);
                }

                if (packagesToScan.isEmpty()) {
                    addPackageToScan("liquibase.change");
                    addPackageToScan("liquibase.changelog");
                    addPackageToScan("liquibase.database");
                    addPackageToScan("liquibase.parser");
                    addPackageToScan("liquibase.precondition");
                    addPackageToScan("liquibase.datatype");
                    addPackageToScan("liquibase.serializer");
                    addPackageToScan("liquibase.sqlgenerator");
                    addPackageToScan("liquibase.executor");
                    addPackageToScan("liquibase.snapshot");
                    addPackageToScan("liquibase.logging");
                    addPackageToScan("liquibase.diff");
                    addPackageToScan("liquibase.structure");
                    addPackageToScan("liquibase.structurecompare");
                    addPackageToScan("liquibase.lockservice");
                    addPackageToScan("liquibase.sdk.database");
                    addPackageToScan("liquibase.ext");
                }
            }
        }
    }

    public void addPackageToScan(String packageName) {
        packagesToScan.add(packageName);
    }

    public List getPackages() {
        return packagesToScan;
    }

    public Class findClass(Class requiredInterface) throws ServiceNotFoundException {
        Class[] classes = findClasses(requiredInterface);
        if (PrioritizedService.class.isAssignableFrom(requiredInterface)) {
            PrioritizedService returnObject = null;
            for (Class clazz : classes) {
                PrioritizedService newInstance;
                try {
                    newInstance = (PrioritizedService) clazz.newInstance();
                } catch (Exception e) {
                    throw new UnexpectedLiquibaseException(e);
                }

                if ((returnObject == null) || (newInstance.getPriority() > returnObject.getPriority())) {
                    returnObject = newInstance;
                }
            }

            if (returnObject == null) {
                throw new ServiceNotFoundException("Could not find implementation of " + requiredInterface.getName());
            }
            return returnObject.getClass();
        }

        if (classes.length != 1) {
            throw new ServiceNotFoundException("Could not find unique implementation of " + requiredInterface.getName() + ".  Found " + classes.length + " implementations");
        }

        return classes[0];
    }

    public  Class[] findClasses(Class requiredInterface) throws ServiceNotFoundException {
        LogService.getLog(getClass()).debug(LogType.LOG, "ServiceLocator.findClasses for "+requiredInterface.getName());

            try {
                Class.forName(requiredInterface.getName());

                if (!classesBySuperclass.containsKey(requiredInterface)) {
                    classesBySuperclass.put(requiredInterface, findClassesImpl(requiredInterface));
                }
            } catch (Exception e) {
                throw new ServiceNotFoundException(e);
            }

        List classes = classesBySuperclass.get(requiredInterface);
        HashSet uniqueClasses = new HashSet<>(classes);
        return uniqueClasses.toArray(new Class[uniqueClasses.size()]);
    }

    public Object newInstance(Class requiredInterface) throws ServiceNotFoundException {
        try {
            return findClass(requiredInterface).newInstance();
        } catch (Exception e) {
            throw new ServiceNotFoundException(e);
        }
    }

    private List findClassesImpl(Class requiredInterface) throws Exception {
        LogService.getLog(getClass()).debug(LogType.LOG, "ServiceLocator finding classes matching interface " + requiredInterface.getName());

        List classes = new ArrayList<>();

        classResolver.addClassLoader(resourceAccessor.toClassLoader());
        for (Class clazz : classResolver.findImplementations(requiredInterface, packagesToScan.toArray(new String[packagesToScan.size()]))) {
            if ((clazz.getAnnotation(LiquibaseService.class) != null) && clazz.getAnnotation(LiquibaseService.class)
                .skip()) {
                continue;
            }

            if (!Modifier.isAbstract(clazz.getModifiers()) && !Modifier.isInterface(clazz.getModifiers()) && !clazz.isAnonymousClass() &&!clazz.isSynthetic() && Modifier.isPublic(clazz.getModifiers())) {
                try {
                    clazz.getConstructor();
                    LogService.getLog(getClass()).debug(LogType.LOG, clazz.getName() + " matches "+requiredInterface.getName());

                    classes.add(clazz);
                } catch (NoSuchMethodException e) {
                    LogService.getLog(getClass()).info(LogType.LOG, "Can not use " + clazz + " as a Liquibase service because it does not have a " +
                        "no-argument constructor");
                } catch (NoClassDefFoundError e) {
                    String message = "Can not use " + clazz + " as a Liquibase service because " + e.getMessage()
                        .replace("/", ".") + " is not in the classpath";
                    if (e.getMessage().startsWith("org/yaml/snakeyaml")) {
                        LogService.getLog(getClass()).info(LogType.LOG, message);
                    } else {
                        LogService.getLog(getClass()).warning(LogType.LOG, message);
                    }
                }
            }
        }

        return classes;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy