From 2fa7c57357dc60e6499e126d88cdc27c558a6fad Mon Sep 17 00:00:00 2001 From: Kefeng Deng Date: Tue, 28 Jul 2015 21:31:49 +1200 Subject: [PATCH] Introduced a service interface to allow multiple ClassPathSearchs with its own customized search way --- .../api/ClassPathSearchService.java | 46 ++ .../server/core/BootupClassPathSearch.java | 82 +- .../server/util/ClassPathSearch.java | 770 +++++++++--------- ...e.ebeaninternal.api.ClassPathSearchService | 1 + .../server/util/ClassPathSearchTests.java | 6 +- 5 files changed, 489 insertions(+), 416 deletions(-) create mode 100644 src/main/java/com/avaje/ebeaninternal/api/ClassPathSearchService.java create mode 100644 src/main/resources/META-INF/services/com.avaje.ebeaninternal.api.ClassPathSearchService diff --git a/src/main/java/com/avaje/ebeaninternal/api/ClassPathSearchService.java b/src/main/java/com/avaje/ebeaninternal/api/ClassPathSearchService.java new file mode 100644 index 000000000..7f73d823f --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/api/ClassPathSearchService.java @@ -0,0 +1,46 @@ +package com.avaje.ebeaninternal.api; + +import com.avaje.ebeaninternal.server.util.ClassPathSearchFilter; +import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +/** + * This interface allows us to have more than one ClassPathSearch + * to scan the resources by a customized way. + * + * @author Kefeng Deng (deng@51any.com) + */ +public interface ClassPathSearchService { + + /** + * Initialize this ClassPathSearchService with given parameters + * + * @param classLoader is current classLoader + * @param filter is filter + * @param matcher + * @param classPathReaderClassName + */ + void init(ClassLoader classLoader, ClassPathSearchFilter filter, ClassPathSearchMatcher matcher, String classPathReaderClassName); + + /** + * Searches the class path for all matching classes. + * + * @return a collection of all matching classes + * @throws IOException if a resource is un-reachable + */ + List> findClasses() throws IOException; + + /** + * Return the set of jars that contained classes that matched. + */ + Set getJarHits(); + + /** + * Return the set of packages that contained classes that matched. + */ + Set getPackageHits(); + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java index cb23d286f..6ed9f13dc 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java @@ -1,12 +1,11 @@ package com.avaje.ebeaninternal.server.core; -import com.avaje.ebeaninternal.server.util.ClassPathSearch; +import com.avaje.ebeaninternal.api.ClassPathSearchService; import com.avaje.ebeaninternal.server.util.ClassPathSearchFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; -import java.util.Set; +import java.util.*; /** * Searches for interesting classes such as Entities, Embedded and ScalarTypes. @@ -21,11 +20,13 @@ public class BootupClassPathSearch { private final List packages; - private final List jars; - + private final List jars; + + private List classPathSearchServices; + private BootupClasses bootupClasses; - private final String classPathReaderClassName; + private final String classPathReaderClassName; /** * Construct and search for interesting classes. @@ -34,16 +35,18 @@ public class BootupClassPathSearch { this.classLoader = (classLoader == null) ? getClass().getClassLoader() : classLoader; this.packages = packages; this.jars = jars; - this.classPathReaderClassName = classPathReaderClassName; + this.classPathReaderClassName = classPathReaderClassName; + + loadAndInitializeClassPathSearchServices(); } public BootupClasses getBootupClasses() { synchronized (monitor) { - - if (bootupClasses == null){ + + if (bootupClasses == null) { bootupClasses = search(); } - + return bootupClasses; } } @@ -54,24 +57,26 @@ public class BootupClassPathSearch { private BootupClasses search() { synchronized (monitor) { try { - + BootupClasses bc = new BootupClasses(); long st = System.currentTimeMillis(); ClassPathSearchFilter filter = createFilter(); - ClassPathSearch finder = new ClassPathSearch(classLoader, filter, bc, classPathReaderClassName); + Set foundJars = new HashSet(); + Set foundPkgs = new HashSet(); - finder.findClasses(); - Set jars = finder.getJarHits(); - Set pkgs = finder.getPackageHits(); + for (ClassPathSearchService finder : this.classPathSearchServices) { + finder.init(classLoader, filter, bc, classPathReaderClassName); + finder.findClasses(); + foundJars.addAll(finder.getJarHits()); + foundPkgs.addAll(finder.getPackageHits()); + } long searchTime = System.currentTimeMillis() - st; - String msg = "Classpath search hits in jars" + jars + " pkgs" + pkgs + " searchTime[" + searchTime+ "]"; - logger.info(msg); - + logger.info("Classpath search hits in jars {} pkgs {} searchTime [{}]", foundJars, foundPkgs, searchTime); return bc; } catch (Exception ex) { @@ -86,24 +91,37 @@ public class BootupClassPathSearch { ClassPathSearchFilter filter = new ClassPathSearchFilter(); filter.addDefaultExcludePackages(); - if (packages != null && packages.size() > 0) { - for (String packageName : packages) { - filter.includePackage(packageName); - } + if (packages != null && packages.size() > 0) { + for (String packageName : packages) { + filter.includePackage(packageName); + } - // if they specified include packages, they don't want by default to include everything - filter.setDefaultPackageMatch(false); - } + // if they specified include packages, they don't want by default to include everything + filter.setDefaultPackageMatch(false); + } - if (jars != null && jars.size() > 0) { - for (String jarName : jars) { - filter.includeJar(jarName); - } + if (jars != null && jars.size() > 0) { + for (String jarName : jars) { + filter.includeJar(jarName); + } - // if they specified jars to specifically include, they don't want everything included - filter.setDefaultJarMatch(false); - } + // if they specified jars to specifically include, they don't want everything included + filter.setDefaultJarMatch(false); + } return filter; } + + /** + * Load and initialize all ClassPathSearchServices + */ + private void loadAndInitializeClassPathSearchServices() { + if (this.classPathSearchServices == null) { + this.classPathSearchServices = new ArrayList(); + } + + for (ClassPathSearchService searchService : ServiceLoader.load(ClassPathSearchService.class, classLoader)) { + this.classPathSearchServices.add(searchService); + } + } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/util/ClassPathSearch.java b/src/main/java/com/avaje/ebeaninternal/server/util/ClassPathSearch.java index 93d515380..866a574fe 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/util/ClassPathSearch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/util/ClassPathSearch.java @@ -1,5 +1,6 @@ package com.avaje.ebeaninternal.server.util; +import com.avaje.ebeaninternal.api.ClassPathSearchService; import com.avaje.ebeaninternal.api.ClassUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,474 +23,479 @@ import java.util.jar.Manifest; * For example, used to find all the Entity beans and ScalarTypes for Ebean. *

*/ -public class ClassPathSearch { +public class ClassPathSearch implements ClassPathSearchService { - private static final Logger logger = LoggerFactory.getLogger(ClassPathSearch.class); + private static final Logger logger = LoggerFactory.getLogger(ClassPathSearch.class); - private ClassLoader classLoader; + private ClassLoader classLoader; - private List classPath = new ArrayList(); - - private ClassPathSearchFilter filter; + private List classPath = new ArrayList(); - private ClassPathSearchMatcher matcher; + private ClassPathSearchFilter filter; - private ArrayList> matchList = new ArrayList>(); + private ClassPathSearchMatcher matcher; - private HashSet jarHits = new HashSet(); + private ArrayList> matchList = new ArrayList>(); - private HashSet packageHits = new HashSet(); + private HashSet jarHits = new HashSet(); - private ClassPathReader classPathReader = new DefaultClassPathReader(); + private HashSet packageHits = new HashSet(); - private ArrayList scannedUris = new ArrayList(); + private ClassPathReader classPathReader = new DefaultClassPathReader(); - public ClassPathSearch(ClassLoader classLoader, ClassPathSearchFilter filter, ClassPathSearchMatcher matcher, String classPathReaderClassName) { - this.classLoader = classLoader; - this.filter = filter; - this.matcher = matcher; - initClassPaths(classPathReaderClassName); - } + private ArrayList scannedUris = new ArrayList(); - private void initClassPaths(String classPathReaderCN) { + public ClassPathSearch() { + // Default Construct + } - try { + @Override + public void init(ClassLoader classLoader, ClassPathSearchFilter filter, ClassPathSearchMatcher matcher, String classPathReaderClassName) { + this.classLoader = classLoader; + this.filter = filter; + this.matcher = matcher; + initClassPaths(classPathReaderClassName); + } - if (classPathReaderCN != null) { - // use a user defined classPathReader - logger.info("Using [" + classPathReaderCN + "] to read the searchable class path"); - classPathReader = (ClassPathReader) ClassUtil.newInstance(classPathReaderCN, this.getClass()); - } + private void initClassPaths(String classPathReaderCN) { - Object[] rawClassPaths = classPathReader.readPath(classLoader); + try { - if (rawClassPaths == null || rawClassPaths.length == 0) { - logger.warn("ClassPath is EMPTY using ClassPathReader [" + classPathReader + "]"); - return; - } + if (classPathReaderCN != null) { + // use a user defined classPathReader + logger.info("Using [" + classPathReaderCN + "] to read the searchable class path"); + classPathReader = (ClassPathReader) ClassUtil.newInstance(classPathReaderCN, this.getClass()); + } - for (int i = 0; i < rawClassPaths.length; i++) { - // check for a jarfile with a manifest classpath (e.g. maven surefire) - List classPathFromManifest = getClassPathFromManifest(rawClassPaths[i]); - if (classPathFromManifest.isEmpty()) { - classPath.add(rawClassPaths[i]); - } else { - classPath.addAll(classPathFromManifest); - } - } - - if (rawClassPaths.length == 1) { - // look to add an 'outer' jar when it contains a manifest classpath - if (!classPath.contains(rawClassPaths[0])) { - classPath.add(rawClassPaths[0]); - } - } - - if (logger.isDebugEnabled()) { - for (Object entry : classPath) { - logger.debug("Classpath Entry: {}",entry); - } - } + Object[] rawClassPaths = classPathReader.readPath(classLoader); - } catch (Exception e) { - throw new RuntimeException("Error trying to read the classpath entries", e); - } - } + if (rawClassPaths == null || rawClassPaths.length == 0) { + logger.warn("ClassPath is EMPTY using ClassPathReader [" + classPathReader + "]"); + return; + } - /** - * Return the set of jars that contained classes that matched. - */ - public Set getJarHits() { - return jarHits; - } + for (int i = 0; i < rawClassPaths.length; i++) { + // check for a jarfile with a manifest classpath (e.g. maven surefire) + List classPathFromManifest = getClassPathFromManifest(rawClassPaths[i]); + if (classPathFromManifest.isEmpty()) { + classPath.add(rawClassPaths[i]); + } else { + classPath.addAll(classPathFromManifest); + } + } - /** - * Return the set of packages that contained classes that matched. - */ - public Set getPackageHits() { - return packageHits; - } + if (rawClassPaths.length == 1) { + // look to add an 'outer' jar when it contains a manifest classpath + if (!classPath.contains(rawClassPaths[0])) { + classPath.add(rawClassPaths[0]); + } + } - /** - * Register where matching classes where found. - *

- * Could use this info to speed up future searches. - *

- */ - private void registerHit(String jarFileName, Class cls) { - if (jarFileName != null) { - jarHits.add(jarFileName); - } - Package pkg = cls.getPackage(); - if (pkg != null) { - packageHits.add(pkg.getName()); - } else { - packageHits.add(""); - } - } + if (logger.isDebugEnabled()) { + for (Object entry : classPath) { + logger.debug("Classpath Entry: {}", entry); + } + } - /** - * Searches the class path for all matching classes. - */ - public List> findClasses() throws IOException { + } catch (Exception e) { + throw new RuntimeException("Error trying to read the classpath entries", e); + } + } - if (classPath.isEmpty()) { - // returning an empty list - return matchList; - } + /** + * Return the set of jars that contained classes that matched. + */ + @Override + public Set getJarHits() { + return jarHits; + } - int classPathSize = classPath.size(); - for (int i = 0; i < classPathSize; i++) { + /** + * Return the set of packages that contained classes that matched. + */ + @Override + public Set getPackageHits() { + return packageHits; + } - ClassPathElement element = getClassPathElement(classPath.get(i)); + /** + * Register where matching classes where found. + *

+ * Could use this info to speed up future searches. + *

+ */ + private void registerHit(String jarFileName, Class cls) { + if (jarFileName != null) { + jarHits.add(jarFileName); + } + Package pkg = cls.getPackage(); + if (pkg != null) { + packageHits.add(pkg.getName()); + } else { + packageHits.add(""); + } + } - if (element.isDirectory()) { - scanDirectory(element); + /** + * Searches the class path for all matching classes. + */ + @Override + public List> findClasses() throws IOException { - } else if (element.isJarOrWar()) { - // search name including the ! offset if it is there - if (classPathSize == 1 || filter.isSearchJar(element.getJarNameWithOffset())) { - scanJar(element); - } + if (classPath.isEmpty()) { + // returning an empty list + return matchList; + } - } else { - logger.error("Error: expected classPath entry [" + element+ "] to be a directory or a .jar file but it is not either of those?"); - } - } + int classPathSize = classPath.size(); + for (int i = 0; i < classPathSize; i++) { - if (matchList.isEmpty()) { - logger.warn("No Entities found in ClassPath using ClassPathReader [" + classPathReader + "] Classpath Searched[" + classPath + "]"); - } + ClassPathElement element = getClassPathElement(classPath.get(i)); - return matchList; - } + if (element.isDirectory()) { + scanDirectory(element); - private ClassPathElement getClassPathElement(Object classPathEntry) throws MalformedURLException { + } else if (element.isJarOrWar()) { + // search name including the ! offset if it is there + if (classPathSize == 1 || filter.isSearchJar(element.getJarNameWithOffset())) { + scanJar(element); + } - URL fileUrl = null; - - if (URI.class.isInstance(classPathEntry)) { - fileUrl = ((URI)classPathEntry).toURL(); - - } else if (!URL.class.isInstance(classPathEntry)) { - // assumed to be a file path - return new ClassPathElement(classPathEntry.toString()); - - } else { - fileUrl = (URL) classPathEntry; - } - - if (!fileUrl.getPath().contains("!")) { - return new ClassPathElement(new File(fileUrl.getFile())); - } + } else { + logger.error("Error: expected classPath entry [" + element + "] to be a directory or a .jar file but it is not either of those?"); + } + } - // jar:file:..../file.war!/WEB-INF/classes typically - String[] parts = fileUrl.getPath().split("!"); - String fileName = parts[0]; - String jarOffset = parts[1]; - if (fileName.startsWith("file:")) { - fileName = fileName.substring("file:".length()); - } - return new ClassPathElement(new File(fileName), jarOffset); - } + if (matchList.isEmpty()) { + logger.warn("No Entities found in ClassPath using ClassPathReader [" + classPathReader + "] Classpath Searched[" + classPath + "]"); + } - private void scanDirectory(ClassPathElement classPathEntry) { - scanDirectory(classPathEntry.classPath); - } + return matchList; + } - private void scanDirectory(File directory) { - List directoryFiles = getDirectoryFiles(directory); - searchFiles(Collections.enumeration(directoryFiles), null, null); - } + private ClassPathElement getClassPathElement(Object classPathEntry) throws MalformedURLException { - private void scanUri(URI uri) throws IOException { + URL fileUrl = null; - if (uri.getScheme().equals("file") && scannedUris.add(uri)) { - File file = new File(uri); - if (file.exists()) { - if (file.isDirectory()) { - scanDirectory(file); - } else { - scanJar(new ClassPathElement(file)); - } - } - } - } + if (URI.class.isInstance(classPathEntry)) { + fileUrl = ((URI) classPathEntry).toURL(); + + } else if (!URL.class.isInstance(classPathEntry)) { + // assumed to be a file path + return new ClassPathElement(classPathEntry.toString()); + + } else { + fileUrl = (URL) classPathEntry; + } + + if (!fileUrl.getPath().contains("!")) { + return new ClassPathElement(new File(fileUrl.getFile())); + } + + // jar:file:..../file.war!/WEB-INF/classes typically + String[] parts = fileUrl.getPath().split("!"); + String fileName = parts[0]; + String jarOffset = parts[1]; + if (fileName.startsWith("file:")) { + fileName = fileName.substring("file:".length()); + } + return new ClassPathElement(new File(fileName), jarOffset); + } + + private void scanDirectory(ClassPathElement classPathEntry) { + scanDirectory(classPathEntry.classPath); + } + + private void scanDirectory(File directory) { + List directoryFiles = getDirectoryFiles(directory); + searchFiles(Collections.enumeration(directoryFiles), null, null); + } + + private void scanUri(URI uri) throws IOException { + + if (uri.getScheme().equals("file") && scannedUris.add(uri)) { + File file = new File(uri); + if (file.exists()) { + if (file.isDirectory()) { + scanDirectory(file); + } else { + scanJar(new ClassPathElement(file)); + } + } + } + } - private void scanJar(ClassPathElement classPathEntry) throws IOException { + private void scanJar(ClassPathElement classPathEntry) throws IOException { - JarFile module = null; - try { - // our resource is a jar - File file = classPathEntry.classPath; - module = new JarFile(file); + JarFile module = null; + try { + // our resource is a jar + File file = classPathEntry.classPath; + module = new JarFile(file); - List classPathFromManifest = getClassPathFromManifest(file, module.getManifest()); - for (URI uri : classPathFromManifest) { - scanUri(uri); - } - - searchFiles(module.entries(), classPathEntry.getJarName(), classPathEntry.jarOffset); + List classPathFromManifest = getClassPathFromManifest(file, module.getManifest()); + for (URI uri : classPathFromManifest) { + scanUri(uri); + } - } catch (MalformedURLException ex) { - throw new IOException("Bad classpath error: ", ex); + searchFiles(module.entries(), classPathEntry.getJarName(), classPathEntry.jarOffset); - } finally { - if (module != null) { - try { - // close the jar if it was used - module.close(); - } catch (IOException e) { - throw new IOException("Error closing jar", e); - } - } - } - } + } catch (MalformedURLException ex) { + throw new IOException("Bad classpath error: ", ex); - private List getDirectoryFiles(File classPath) { + } finally { + if (module != null) { + try { + // close the jar if it was used + module.close(); + } catch (IOException e) { + throw new IOException("Error closing jar", e); + } + } + } + } - // list of file names (latter checked as Classes) - ArrayList fileNameList = new ArrayList(); + private List getDirectoryFiles(File classPath) { - Set includePkgs = filter.getIncludePackages(); - if (includePkgs.size() > 0) { - // just search the relevant directories based on the - // list of included packages - for (String pkg : includePkgs) { - String relativePath = pkg.replace('.', '/'); - File dir = new File(classPath, relativePath); - if (dir.exists()) { - recursivelyListDir(fileNameList, dir, new StringBuilder(relativePath)); - } - } + // list of file names (latter checked as Classes) + ArrayList fileNameList = new ArrayList(); - } else { - // get a recursive listing of this classPath - recursivelyListDir(fileNameList, classPath, new StringBuilder()); - } + Set includePkgs = filter.getIncludePackages(); + if (includePkgs.size() > 0) { + // just search the relevant directories based on the + // list of included packages + for (String pkg : includePkgs) { + String relativePath = pkg.replace('.', '/'); + File dir = new File(classPath, relativePath); + if (dir.exists()) { + recursivelyListDir(fileNameList, dir, new StringBuilder(relativePath)); + } + } - return fileNameList; - } + } else { + // get a recursive listing of this classPath + recursivelyListDir(fileNameList, classPath, new StringBuilder()); + } - /** - * Searches through the Java Archive (jar or war file) looking for classes - * that match our requirements. - * - * @param files - * - all of the files in the Java Archive, this is an enumeration - * provided by the Jar file - * @param jarFileName - * - the name of the java archive - * @param jarOffset - * - an offset inside the archive to chop off the name of the class - - * this is used when we have bang path offsets (e.g. - * file:///myfile.war!/WEB-INF/classes) - */ - private void searchFiles(Enumeration files, String jarFileName, String jarOffset) { + return fileNameList; + } - if (files == null) { - return; - } - /* + /** + * Searches through the Java Archive (jar or war file) looking for classes + * that match our requirements. + * + * @param files - all of the files in the Java Archive, this is an enumeration + * provided by the Jar file + * @param jarFileName - the name of the java archive + * @param jarOffset - an offset inside the archive to chop off the name of the class - + * this is used when we have bang path offsets (e.g. + * file:///myfile.war!/WEB-INF/classes) + */ + private void searchFiles(Enumeration files, String jarFileName, String jarOffset) { + + if (files == null) { + return; + } + /* * Strips the first character off as all entries in a jar file have no / * prefix. We want to come out with a name like WEB-INF/classes/ to ensure * we filter the contents of the war/jar file by this. */ - if (jarOffset != null) { - if (jarOffset.startsWith("/")) { - jarOffset = jarOffset.substring(1); - } + if (jarOffset != null) { + if (jarOffset.startsWith("/")) { + jarOffset = jarOffset.substring(1); + } - if (!jarOffset.endsWith("/")) { - jarOffset += "/"; - } - } + if (!jarOffset.endsWith("/")) { + jarOffset += "/"; + } + } - while (files.hasMoreElements()) { + while (files.hasMoreElements()) { - String fileName = files.nextElement().toString(); + String fileName = files.nextElement().toString(); - // we only want the class files - if (fileName.endsWith(".class") && (jarOffset == null || fileName.startsWith(jarOffset))) { + // we only want the class files + if (fileName.endsWith(".class") && (jarOffset == null || fileName.startsWith(jarOffset))) { - if (jarOffset != null) { - // we got through here only if there is an offset and we - // matched it, so strip it off the file - // as we are trying to find the className - fileName = fileName.substring(jarOffset.length()); - } + if (jarOffset != null) { + // we got through here only if there is an offset and we + // matched it, so strip it off the file + // as we are trying to find the className + fileName = fileName.substring(jarOffset.length()); + } - String className = fileName.replace('/', '.').substring(0, fileName.length() - 6); - int lastPeriod = className.lastIndexOf("."); + String className = fileName.replace('/', '.').substring(0, fileName.length() - 6); + int lastPeriod = className.lastIndexOf("."); - String pckgName; - if (lastPeriod > 0) { - pckgName = className.substring(0, lastPeriod); - } else { - pckgName = ""; - } + String pckgName; + if (lastPeriod > 0) { + pckgName = className.substring(0, lastPeriod); + } else { + pckgName = ""; + } - if (filter.isSearchPackage(pckgName)) { - // get the class for our class name - try { - Class theClass = Class.forName(className, false, classLoader); + if (filter.isSearchPackage(pckgName)) { + // get the class for our class name + try { + Class theClass = Class.forName(className, false, classLoader); - if (matcher.isMatch(theClass)) { - matchList.add(theClass); - registerHit(jarFileName, theClass); - } + if (matcher.isMatch(theClass)) { + matchList.add(theClass); + registerHit(jarFileName, theClass); + } - } catch (ClassNotFoundException e) { - // expected to get this hence trace - logger.trace("Error searching classpath" + e.getMessage()); - } catch (NoClassDefFoundError e) { - // expected to get this hence trace - logger.trace("Error searching classpath" + e.getMessage()); - } - } - } - } - } + } catch (ClassNotFoundException e) { + // expected to get this hence trace + logger.trace("Error searching classpath" + e.getMessage()); + } catch (NoClassDefFoundError e) { + // expected to get this hence trace + logger.trace("Error searching classpath" + e.getMessage()); + } + } + } + } + } - private void recursivelyListDir(List fileNameList, File dir, StringBuilder relativePath) { + private void recursivelyListDir(List fileNameList, File dir, StringBuilder relativePath) { - if (!dir.isDirectory()) { - // add class fileName to the list - fileNameList.add(relativePath.toString()); + if (!dir.isDirectory()) { + // add class fileName to the list + fileNameList.add(relativePath.toString()); - } else { + } else { - File[] files = dir.listFiles(); - for (int i = 0; i < files.length; i++) { - // store our original relative path string length - int prevLen = relativePath.length(); - relativePath.append(prevLen == 0 ? "" : "/").append(files[i].getName()); + File[] files = dir.listFiles(); + for (int i = 0; i < files.length; i++) { + // store our original relative path string length + int prevLen = relativePath.length(); + relativePath.append(prevLen == 0 ? "" : "/").append(files[i].getName()); - recursivelyListDir(fileNameList, files[i], relativePath); + recursivelyListDir(fileNameList, files[i], relativePath); - // delete sub directory from our relative path - relativePath.delete(prevLen, relativePath.length()); - } - } - } + // delete sub directory from our relative path + relativePath.delete(prevLen, relativePath.length()); + } + } + } - /** - * If URL and actually a jarfile with manifest return the derived classpath. - */ - private static List getClassPathFromManifest(Object classPathElement) { + /** + * If URL and actually a jarfile with manifest return the derived classpath. + */ + private static List getClassPathFromManifest(Object classPathElement) { - try { - if (classPathElement instanceof URL) { - File file = new File(((URL)classPathElement).getFile()); - if (file.isDirectory()) { - return Collections.emptyList(); - } - JarFile jarFile = new JarFile(file); - try { - return getClassPathFromManifest(file, jarFile.getManifest()); - } finally { - jarFile.close(); - } - } - return Collections.emptyList(); - - } catch (IOException e) { - return Collections.emptyList(); - } - } - - /** - * If a jarfile with a manifest claspath return that. - */ - private static List getClassPathFromManifest(File jarFile, Manifest manifest) { + try { + if (classPathElement instanceof URL) { + File file = new File(((URL) classPathElement).getFile()); + if (file.isDirectory()) { + return Collections.emptyList(); + } + JarFile jarFile = new JarFile(file); + try { + return getClassPathFromManifest(file, jarFile.getManifest()); + } finally { + jarFile.close(); + } + } + return Collections.emptyList(); - if (manifest == null) { - return Collections.emptyList(); - } - List list = new ArrayList(); - String classpathAttribute = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString()); + } catch (IOException e) { + return Collections.emptyList(); + } + } - if (classpathAttribute != null) { - String[] split = classpathAttribute.split(" "); - for (String path : split) { - try { - path = path.trim(); - if (path.length() > 0) { - URI uri = getClassPathEntry(jarFile, path); - list.add(uri); - } - } catch (URISyntaxException e) { - // Ignore bad entry - logger.warn("Invalid Class-Path entry: " + path); - } - } - } - return list; - } + /** + * If a jarfile with a manifest claspath return that. + */ + private static List getClassPathFromManifest(File jarFile, Manifest manifest) { - private static URI getClassPathEntry(File jarFile, String path) throws URISyntaxException { - URI uri = new URI(path); - if (uri.isAbsolute()) { - return uri; - } else { - return new File(jarFile.getParentFile(), path.replace('/', File.separatorChar)).toURI(); - } - } + if (manifest == null) { + return Collections.emptyList(); + } + List list = new ArrayList(); + String classpathAttribute = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString()); - private static File decodePath(File classPath) { + if (classpathAttribute != null) { + String[] split = classpathAttribute.split(" "); + for (String path : split) { + try { + path = path.trim(); + if (path.length() > 0) { + URI uri = getClassPathEntry(jarFile, path); + list.add(uri); + } + } catch (URISyntaxException e) { + // Ignore bad entry + logger.warn("Invalid Class-Path entry: " + path); + } + } + } + return list; + } - try { - String charsetName = Charset.defaultCharset().name(); + private static URI getClassPathEntry(File jarFile, String path) throws URISyntaxException { + URI uri = new URI(path); + if (uri.isAbsolute()) { + return uri; + } else { + return new File(jarFile.getParentFile(), path.replace('/', File.separatorChar)).toURI(); + } + } - // URL Decode the path replacing %20 to space characters. - String path = URLDecoder.decode(classPath.getAbsolutePath(), charsetName); - return new File(path); + private static File decodePath(File classPath) { - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } + try { + String charsetName = Charset.defaultCharset().name(); - /** - * Element that has both underlying file and ! jarOffset. - */ - private static class ClassPathElement { + // URL Decode the path replacing %20 to space characters. + String path = URLDecoder.decode(classPath.getAbsolutePath(), charsetName); + return new File(path); - private final File classPath; - private final String jarOffset; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } - ClassPathElement(String path) { - this(new File(path)); - } + /** + * Element that has both underlying file and ! jarOffset. + */ + private static class ClassPathElement { - ClassPathElement(File file) { - this(file, null); - } + private final File classPath; + private final String jarOffset; - ClassPathElement(File file, String jarOffset) { - classPath = decodePath(file); - this.jarOffset = jarOffset; - } + ClassPathElement(String path) { + this(new File(path)); + } - public String toString() { - return classPath.getAbsolutePath(); - } + ClassPathElement(File file) { + this(file, null); + } - boolean isDirectory() { - return classPath.isDirectory(); - } + ClassPathElement(File file, String jarOffset) { + classPath = decodePath(file); + this.jarOffset = jarOffset; + } - boolean isJarOrWar() { - return classPath.getName().endsWith(".jar") || classPath.getName().endsWith(".war"); - } + public String toString() { + return classPath.getAbsolutePath(); + } - String getJarName() { - return classPath.getName(); - } + boolean isDirectory() { + return classPath.isDirectory(); + } - String getJarNameWithOffset() { - return (jarOffset == null) ? classPath.getName() : classPath.getName() + "!" + jarOffset; - } - } + boolean isJarOrWar() { + return classPath.getName().endsWith(".jar") || classPath.getName().endsWith(".war"); + } + + String getJarName() { + return classPath.getName(); + } + + String getJarNameWithOffset() { + return (jarOffset == null) ? classPath.getName() : classPath.getName() + "!" + jarOffset; + } + } } \ No newline at end of file diff --git a/src/main/resources/META-INF/services/com.avaje.ebeaninternal.api.ClassPathSearchService b/src/main/resources/META-INF/services/com.avaje.ebeaninternal.api.ClassPathSearchService new file mode 100644 index 000000000..fc9764dae --- /dev/null +++ b/src/main/resources/META-INF/services/com.avaje.ebeaninternal.api.ClassPathSearchService @@ -0,0 +1 @@ +com.avaje.ebeaninternal.server.util.ClassPathSearch \ No newline at end of file diff --git a/src/test/java/com/avaje/ebeaninternal/server/util/ClassPathSearchTests.java b/src/test/java/com/avaje/ebeaninternal/server/util/ClassPathSearchTests.java index 5c8d570ac..6037bd6fd 100644 --- a/src/test/java/com/avaje/ebeaninternal/server/util/ClassPathSearchTests.java +++ b/src/test/java/com/avaje/ebeaninternal/server/util/ClassPathSearchTests.java @@ -49,7 +49,8 @@ public class ClassPathSearchTests { filter.includeJar("WEB-INF"); filter.setDefaultJarMatch(false); - ClassPathSearch search = new ClassPathSearch(cl, filter, new ClassPathSearchMatcher() { + ClassPathSearch search = new ClassPathSearch(); + search.init(cl, filter, new ClassPathSearchMatcher() { @Override public boolean isMatch(Class cls) { return true; @@ -67,7 +68,8 @@ public class ClassPathSearchTests { filter.includePackage("com.avaje.ebeaninternal.server"); filter.includeJar("bang"); - search = new ClassPathSearch(cl, filter, new ClassPathSearchMatcher() { + search = new ClassPathSearch(); + search.init(cl, filter, new ClassPathSearchMatcher() { @Override public boolean isMatch(Class cls) { return true;