Coverage Report - liquibase.servicelocator.ServiceLocator
 
Classes in this File Line Coverage Branch Coverage Complexity
ServiceLocator
70%
71/101
66%
28/42
4.091
 
 1  
 package liquibase.servicelocator;
 2  
 
 3  
 import java.io.IOException;
 4  
 import java.io.InputStream;
 5  
 import java.lang.reflect.Modifier;
 6  
 import java.net.URL;
 7  
 import java.util.ArrayList;
 8  
 import java.util.Arrays;
 9  
 import java.util.Enumeration;
 10  
 import java.util.HashMap;
 11  
 import java.util.HashSet;
 12  
 import java.util.List;
 13  
 import java.util.Map;
 14  
 import java.util.jar.Manifest;
 15  
 
 16  
 import liquibase.exception.ServiceNotFoundException;
 17  
 import liquibase.exception.UnexpectedLiquibaseException;
 18  
 import liquibase.logging.Logger;
 19  
 import liquibase.logging.core.DefaultLogger;
 20  
 import liquibase.resource.ClassLoaderResourceAccessor;
 21  
 import liquibase.resource.ResourceAccessor;
 22  
 import liquibase.util.StringUtils;
 23  
 
 24  
 public class ServiceLocator {
 25  
 
 26  
     private static ServiceLocator instance;
 27  
 
 28  
     static {
 29  
         try {
 30  1
             Class<?> scanner = Class.forName("Liquibase.ServiceLocator.ClrServiceLocator, Liquibase");
 31  0
             instance = (ServiceLocator) scanner.newInstance();
 32  1
         } catch (Exception e) {
 33  1
             instance = new ServiceLocator();
 34  0
         }
 35  1
     }
 36  
 
 37  
     private ResourceAccessor resourceAccessor;
 38  
 
 39  
     private Map<Class, List<Class>> classesBySuperclass;
 40  
     private List<String> packagesToScan;
 41  5
     private Logger logger = new DefaultLogger(); // cannot look up regular logger because you get a stackoverflow since
 42  
     // we are in the servicelocator
 43  
     private PackageScanClassResolver classResolver;
 44  
 
 45  5
     private ServiceLocator() {
 46  5
         setResourceAccessor(new ClassLoaderResourceAccessor());
 47  5
     }
 48  
 
 49  0
     private ServiceLocator(ResourceAccessor accessor) {
 50  0
         setResourceAccessor(accessor);
 51  0
     }
 52  
 
 53  
     public static ServiceLocator getInstance() {
 54  67
         return instance;
 55  
     }
 56  
 
 57  
     public void setResourceAccessor(ResourceAccessor resourceAccessor) {
 58  9
         this.resourceAccessor = resourceAccessor;
 59  9
         this.classesBySuperclass = new HashMap<Class, List<Class>>();
 60  
 
 61  9
         if (WebSpherePackageScanClassResolver.isWebSphereClassLoader(this.getClass().getClassLoader())) {
 62  0
             logger.debug("Using WebSphere Specific Class Resolver");
 63  0
             this.classResolver = new WebSpherePackageScanClassResolver("liquibase/parser/core/xml/dbchangelog-2.0.xsd");
 64  
         } else {
 65  9
             this.classResolver = new DefaultPackageScanClassResolver();
 66  
         }
 67  9
         this.classResolver.setClassLoaders(new HashSet<ClassLoader>(Arrays.asList(new ClassLoader[] { resourceAccessor
 68  
                 .toClassLoader() })));
 69  
 
 70  9
         packagesToScan = new ArrayList<String>();
 71  9
         String packagesToScanSystemProp = System.getProperty("liquibase.scan.packages");
 72  9
         if ((packagesToScanSystemProp != null)
 73  
                 && ((packagesToScanSystemProp = StringUtils.trimToNull(packagesToScanSystemProp)) != null)) {
 74  0
             for (String value : packagesToScanSystemProp.split(",")) {
 75  0
                 addPackageToScan(value);
 76  
             }
 77  
         } else {
 78  9
             Enumeration<URL> manifests = null;
 79  
             try {
 80  9
                 manifests = resourceAccessor.getResources("META-INF/MANIFEST.MF");
 81  171
                 while (manifests.hasMoreElements()) {
 82  162
                     URL url = manifests.nextElement();
 83  162
                     InputStream is = url.openStream();
 84  162
                     Manifest manifest = new Manifest(is);
 85  162
                     String attributes = StringUtils.trimToNull(manifest.getMainAttributes().getValue(
 86  
                             "Liquibase-Package"));
 87  162
                     if (attributes != null) {
 88  99
                         for (Object value : attributes.split(",")) {
 89  90
                             addPackageToScan(value.toString());
 90  
                         }
 91  
                     }
 92  162
                     is.close();
 93  162
                 }
 94  0
             } catch (IOException e) {
 95  0
                 throw new UnexpectedLiquibaseException(e);
 96  9
             }
 97  
 
 98  9
             if (packagesToScan.size() == 0) {
 99  0
                 addPackageToScan("liquibase.change");
 100  0
                 addPackageToScan("liquibase.database");
 101  0
                 addPackageToScan("liquibase.parser");
 102  0
                 addPackageToScan("liquibase.precondition");
 103  0
                 addPackageToScan("liquibase.serializer");
 104  0
                 addPackageToScan("liquibase.sqlgenerator");
 105  0
                 addPackageToScan("liquibase.executor");
 106  0
                 addPackageToScan("liquibase.snapshot");
 107  0
                 addPackageToScan("liquibase.logging");
 108  0
                 addPackageToScan("liquibase.ext");
 109  
             }
 110  
         }
 111  9
     }
 112  
 
 113  
     public void addPackageToScan(String packageName) {
 114  90
         packagesToScan.add(packageName);
 115  90
     }
 116  
 
 117  
     public Class findClass(Class requiredInterface) throws ServiceNotFoundException {
 118  7
         Class[] classes = findClasses(requiredInterface);
 119  7
         if (PrioritizedService.class.isAssignableFrom(requiredInterface)) {
 120  1
             PrioritizedService returnObject = null;
 121  2
             for (Class clazz : classes) {
 122  
                 PrioritizedService newInstance;
 123  
                 try {
 124  1
                     newInstance = (PrioritizedService) clazz.newInstance();
 125  0
                 } catch (Exception e) {
 126  0
                     throw new UnexpectedLiquibaseException(e);
 127  1
                 }
 128  
 
 129  1
                 if (returnObject == null || newInstance.getPriority() > returnObject.getPriority()) {
 130  1
                     returnObject = newInstance;
 131  
                 }
 132  
             }
 133  
 
 134  1
             if (returnObject == null) {
 135  0
                 throw new ServiceNotFoundException("Could not find implementation of " + requiredInterface.getName());
 136  
             }
 137  1
             return returnObject.getClass();
 138  
         }
 139  
 
 140  6
         if (classes.length != 1) {
 141  0
             throw new ServiceNotFoundException("Could not find unique implementation of " + requiredInterface.getName()
 142  
                     + ".  Found " + classes.length + " implementations");
 143  
         }
 144  
 
 145  6
         return classes[0];
 146  
     }
 147  
 
 148  
     public Class[] findClasses(Class requiredInterface) throws ServiceNotFoundException {
 149  63
         logger.debug("ServiceLocator.findClasses for " + requiredInterface.getName());
 150  
 
 151  
         try {
 152  63
             Class.forName(requiredInterface.getName());
 153  
 
 154  63
             if (!classesBySuperclass.containsKey(requiredInterface)) {
 155  12
                 classesBySuperclass.put(requiredInterface, findClassesImpl(requiredInterface));
 156  
             }
 157  0
         } catch (Exception e) {
 158  0
             throw new ServiceNotFoundException(e);
 159  63
         }
 160  
 
 161  63
         List<Class> classes = classesBySuperclass.get(requiredInterface);
 162  63
         HashSet<Class> uniqueClasses = new HashSet<Class>(classes);
 163  63
         return uniqueClasses.toArray(new Class[uniqueClasses.size()]);
 164  
     }
 165  
 
 166  
     public Object newInstance(Class requiredInterface) throws ServiceNotFoundException {
 167  
         try {
 168  7
             return findClass(requiredInterface).newInstance();
 169  0
         } catch (Exception e) {
 170  0
             throw new ServiceNotFoundException(e);
 171  
         }
 172  
     }
 173  
 
 174  
     private List<Class> findClassesImpl(Class requiredInterface) throws Exception {
 175  12
         logger.debug("ServiceLocator finding classes matching interface " + requiredInterface.getName());
 176  
 
 177  12
         List<Class> classes = new ArrayList<Class>();
 178  
 
 179  12
         classResolver.addClassLoader(resourceAccessor.toClassLoader());
 180  12
         for (Class<?> clazz : classResolver.findImplementations(requiredInterface,
 181  
                 packagesToScan.toArray(new String[packagesToScan.size()]))) {
 182  363
             if (clazz.getAnnotation(LiquibaseService.class) != null
 183  
                     && clazz.getAnnotation(LiquibaseService.class).skip()) {
 184  5
                 continue;
 185  
             }
 186  
 
 187  358
             if (!Modifier.isAbstract(clazz.getModifiers()) && !Modifier.isInterface(clazz.getModifiers())
 188  
                     && Modifier.isPublic(clazz.getModifiers())) {
 189  
                 try {
 190  323
                     clazz.getConstructor();
 191  322
                     logger.debug(clazz.getName() + " matches " + requiredInterface.getName());
 192  
 
 193  322
                     classes.add(clazz);
 194  1
                 } catch (NoSuchMethodException e) {
 195  1
                     logger.info("Can not use " + clazz
 196  
                             + " as a Liquibase service because it does not have a no-argument constructor");
 197  680
                 }
 198  
             }
 199  
         }
 200  
 
 201  12
         return classes;
 202  
     }
 203  
 
 204  
     public static void reset() {
 205  4
         instance = new ServiceLocator();
 206  4
     }
 207  
 
 208  
     protected Logger getLogger() {
 209  0
         return logger;
 210  
     }
 211  
 }