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: 4.29.2
Show newest version
package liquibase.servicelocator;

import liquibase.exception.ServiceNotFoundException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.logging.Logger;
import liquibase.logging.core.DefaultLogger;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.util.StringUtils;

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

public class ServiceLocator {

    private static ServiceLocator instance;

    static {
        try {
            Class scanner = Class.forName("Liquibase.ServiceLocator.ClrServiceLocator, Liquibase");
            instance = (ServiceLocator) scanner.newInstance();
        } catch (Exception e) {
            instance = new ServiceLocator();
        }
    }

    private ResourceAccessor resourceAccessor;

    private Map> classesBySuperclass;
    private List packagesToScan;
    private Logger logger = new DefaultLogger(); //cannot look up regular logger because you get a stackoverflow since we are in the servicelocator
    private PackageScanClassResolver classResolver;

    private ServiceLocator() {
        setResourceAccessor(new ClassLoaderResourceAccessor());
    }

    private ServiceLocator(ResourceAccessor accessor) {
        setResourceAccessor(accessor);
    }

    public static ServiceLocator getInstance() {
        return instance;
    }

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

        if (WebSpherePackageScanClassResolver.isWebSphereClassLoader(this.getClass().getClassLoader())) {
            logger.debug("Using WebSphere Specific Class Resolver");
            this.classResolver = new WebSpherePackageScanClassResolver("liquibase/parser/core/xml/dbchangelog-2.0.xsd");
        } else {
            this.classResolver = new DefaultPackageScanClassResolver();
        }
        this.classResolver.setClassLoaders(new HashSet(Arrays.asList(new ClassLoader[] {resourceAccessor.toClassLoader()})));

        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 {
	        Enumeration manifests = null;
	        try {
	            manifests = resourceAccessor.getResources("META-INF/MANIFEST.MF");
	            while (manifests.hasMoreElements()) {
	                URL url = manifests.nextElement();
	                InputStream is = url.openStream();
	                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.size() == 0) {
                addPackageToScan("liquibase.change");
                addPackageToScan("liquibase.database");
                addPackageToScan("liquibase.parser");
                addPackageToScan("liquibase.precondition");
                addPackageToScan("liquibase.serializer");
                addPackageToScan("liquibase.sqlgenerator");
                addPackageToScan("liquibase.executor");
                addPackageToScan("liquibase.snapshot");
                addPackageToScan("liquibase.logging");
                addPackageToScan("liquibase.ext");
            }
        }
    }

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

    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 {
        logger.debug("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 {
        logger.debug("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()) && Modifier.isPublic(clazz.getModifiers())) {
                try {
                    clazz.getConstructor();
                    logger.debug(clazz.getName() + " matches "+requiredInterface.getName());

                    classes.add(clazz);
                } catch (NoSuchMethodException e) {
                    logger.info("Can not use "+clazz+" as a Liquibase service because it does not have a no-argument constructor" );
                }
            }
        }

        return classes;
    }

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

    protected Logger getLogger() {
        return logger;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy