liquibase.servicelocator.ServiceLocator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of liquibase-core Show documentation
Show all versions of liquibase-core Show documentation
Liquibase is a tool for managing and executing database changes.
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;
}
}