diff --git a/pom.xml b/pom.xml index eef5a5033..08cb8ae45 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,18 @@ 1.1.1 + + org.avaje + avaje-classpath-scanner-api + 2.1 + + + + org.avaje + avaje-classpath-scanner + 2.1.1 + + org.slf4j slf4j-api diff --git a/src/main/java/com/avaje/ebeaninternal/api/ClassPathSearchService.java b/src/main/java/com/avaje/ebeaninternal/api/ClassPathSearchService.java deleted file mode 100644 index 28af3e606..000000000 --- a/src/main/java/com/avaje/ebeaninternal/api/ClassPathSearchService.java +++ /dev/null @@ -1,46 +0,0 @@ -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 9759c38a8..df8b2cfa8 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClassPathSearch.java @@ -1,7 +1,7 @@ package com.avaje.ebeaninternal.server.core; -import com.avaje.ebeaninternal.api.ClassPathSearchService; -import com.avaje.ebeaninternal.server.util.ClassPathSearchFilter; +import com.avaje.ebean.config.ServerConfig; +import org.avaje.classpath.scanner.ClassPathScanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,118 +10,54 @@ import java.util.*; /** * Searches for interesting classes such as Entities, Embedded and ScalarTypes. */ -public class BootupClassPathSearch { +class BootupClassPathSearch { private static final Logger logger = LoggerFactory.getLogger(BootupClassPathSearch.class); - private final Object monitor = new Object(); - - private final ClassLoader classLoader; - private final List packages; - private final List jars; - - private List classPathSearchServices; - - private BootupClasses bootupClasses; - - private final String classPathReaderClassName; + private final List scanners; /** - * Construct and search for interesting classes. + * Search the classPath for the classes we are interested in returning + * them as BootupClasses. */ - public BootupClassPathSearch(ClassLoader classLoader, List packages, List jars, String classPathReaderClassName) { - this.classLoader = (classLoader == null) ? getClass().getClassLoader() : classLoader; - this.packages = packages; - this.jars = jars; - this.classPathReaderClassName = classPathReaderClassName; + public static BootupClasses search(ServerConfig serverConfig) { - loadAndInitializeClassPathSearchServices(); + return new BootupClassPathSearch(serverConfig).getBootupClasses(); } - public BootupClasses getBootupClasses() { - synchronized (monitor) { - - if (bootupClasses == null) { - bootupClasses = search(); - } - - return bootupClasses; - } + private BootupClassPathSearch(ServerConfig serverConfig) { + this.packages = serverConfig.getPackages(); + this.scanners = ClassPathScanners.find(serverConfig); } /** * Search the classPath for the classes we are interested in. */ - private BootupClasses search() { - synchronized (monitor) { - try { + private BootupClasses getBootupClasses() { - BootupClasses bc = new BootupClasses(); + try { + BootupClasses bc = new BootupClasses(); - long st = System.currentTimeMillis(); - - ClassPathSearchFilter filter = createFilter(); - - Set foundJars = new HashSet(); - Set foundPkgs = new HashSet(); - - for (ClassPathSearchService finder : this.classPathSearchServices) { - finder.init(classLoader, filter, bc, classPathReaderClassName); - finder.findClasses(); - foundJars.addAll(finder.getJarHits()); - foundPkgs.addAll(finder.getPackageHits()); + long st = System.currentTimeMillis(); + for (ClassPathScanner finder : this.scanners) { + if (packages != null && packages.size() > 0) { + for (String packageName : packages) { + finder.scanForClasses(packageName, bc); + } + } else { + // scan locally + finder.scanForClasses("", bc); } - - long searchTime = System.currentTimeMillis() - st; - - logger.info("Classpath search hits in jars {} pkgs {} searchTime [{}]", foundJars, foundPkgs, searchTime); - return bc; - - } catch (Exception ex) { - String msg = "Error in classpath search (looking for entities etc)"; - throw new RuntimeException(msg, ex); - } - } - } - - private ClassPathSearchFilter createFilter() { - - ClassPathSearchFilter filter = new ClassPathSearchFilter(); - filter.addDefaultExcludePackages(); - - 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); - } + long searchTime = System.currentTimeMillis() - st; + logger.info("Classpath search entities[{}] searchTime [{}]", bc.getEntities().size(), searchTime); + return bc; - 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); - } - - 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); + } catch (Exception ex) { + throw new RuntimeException("Error in classpath search (looking for entities etc)", ex); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java index 7a1aa902a..3b6e40e7e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/BootupClasses.java @@ -17,6 +17,7 @@ import com.avaje.ebean.event.readaudit.ReadAuditLogger; import com.avaje.ebean.event.readaudit.ReadAuditPrepare; import com.avaje.ebeaninternal.server.type.ScalarType; import com.avaje.ebeaninternal.server.util.ClassPathSearchMatcher; +import org.avaje.classpath.scanner.ClassFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +34,7 @@ import java.util.List; * Interesting classes for a EbeanServer such as Embeddable, Entity, * ScalarTypes, Finders, Listeners and Controllers. */ -public class BootupClasses implements ClassPathSearchMatcher { +public class BootupClasses implements ClassPathSearchMatcher, ClassFilter { private static final Logger logger = LoggerFactory.getLogger(BootupClasses.class); @@ -370,6 +371,7 @@ public class BootupClasses implements ClassPathSearchMatcher { return compoundTypeList; } + @Override public boolean isMatch(Class cls) { if (isEmbeddable(cls)) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/ClassPathScanners.java b/src/main/java/com/avaje/ebeaninternal/server/core/ClassPathScanners.java new file mode 100644 index 000000000..6464fe709 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/core/ClassPathScanners.java @@ -0,0 +1,31 @@ +package com.avaje.ebeaninternal.server.core; + +import com.avaje.ebean.config.ServerConfig; +import org.avaje.classpath.scanner.ClassPathScanner; +import org.avaje.classpath.scanner.ClassPathScannerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +/** + * Utility to finds and return the list of ClassPathScanner services. + */ +public class ClassPathScanners { + + /** + * Return the list of ClassPathScanner services using serverConfig service loader. + */ + public static List find(ServerConfig serverConfig) { + + List scanners = new ArrayList(); + + ServiceLoader scannerLoader = serverConfig.serviceLoad(ClassPathScannerFactory.class); + for (ClassPathScannerFactory factory : scannerLoader) { + ClassPathScanner scanner = factory.createScanner(serverConfig.getClassLoadConfig().getClassLoader()); + scanners.add(scanner); + } + + return scanners; + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java index 42954eea9..049766a1e 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultContainer.java @@ -225,8 +225,7 @@ public class DefaultContainer implements SpiContainer { return new BootupClasses(serverConfig.getClasses()); } - BootupClassPathSearch search = new BootupClassPathSearch(null, serverConfig.getPackages(), serverConfig.getJars(), serverConfig.getClassPathReaderClassName()); - return search.getBootupClasses(); + return BootupClassPathSearch.search(serverConfig); } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/util/ClassPathSearch.java b/src/main/java/com/avaje/ebeaninternal/server/util/ClassPathSearch.java deleted file mode 100644 index 46bf855b2..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/util/ClassPathSearch.java +++ /dev/null @@ -1,566 +0,0 @@ -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; -import org.springframework.boot.loader.jar.JarEntryData; - -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLDecoder; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.jar.Attributes; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -/** - * Can search the class path for classes using a ClassPathSearchMatcher. A - * ClassPathSearch should only be used once in a single threaded manor. It is - * not safe for multithreaded use. - *

- * For example, used to find all the Entity beans and ScalarTypes for Ebean. - *

- */ -public class ClassPathSearch implements ClassPathSearchService { - - private static final Logger logger = LoggerFactory.getLogger(ClassPathSearch.class); - - private ClassLoader classLoader; - - private final List classPath = new ArrayList(); - - private ClassPathSearchFilter filter; - - private ClassPathSearchMatcher matcher; - - private final ArrayList> matchList = new ArrayList>(); - - private final HashSet jarHits = new HashSet(); - - private final HashSet packageHits = new HashSet(); - - private ClassPathReader classPathReader = new DefaultClassPathReader(); - - private final ArrayList scannedUris = new ArrayList(); - - public ClassPathSearch() { - // Default Construct - } - - @Override - public void init(ClassLoader classLoader, ClassPathSearchFilter filter, ClassPathSearchMatcher matcher, String classPathReaderClassName) { - this.classLoader = classLoader; - this.filter = filter; - this.matcher = matcher; - initClassPaths(classPathReaderClassName); - } - - private void initClassPaths(String classPathReaderCN) { - - try { - - if (classPathReaderCN != null) { - // use a user defined classPathReader - logger.info("Using [" + classPathReaderCN + "] to read the searchable class path"); - classPathReader = (ClassPathReader) ClassUtil.newInstance(classPathReaderCN); - } - - Object[] rawClassPaths = classPathReader.readPath(classLoader); - - if (rawClassPaths == null || rawClassPaths.length == 0) { - logger.warn("ClassPath is EMPTY using ClassPathReader [" + classPathReader + "]"); - return; - } - - 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); - } - } - - } catch (Exception e) { - throw new RuntimeException("Error trying to read the classpath entries", e); - } - } - - /** - * Return the set of jars that contained classes that matched. - */ - @Override - public Set getJarHits() { - return jarHits; - } - - /** - * Return the set of packages that contained classes that matched. - */ - @Override - public Set getPackageHits() { - return packageHits; - } - - /** - * 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(""); - } - } - - /** - * Searches the class path for all matching classes. - */ - @Override - public List> findClasses() throws IOException { - - if (classPath.isEmpty()) { - // returning an empty list - return matchList; - } - - int classPathSize = classPath.size(); - for (int i = 0; i < classPathSize; i++) { - - ClassPathElement element = getClassPathElement(classPath.get(i)); - - if (element.isDirectory()) { - scanDirectory(element); - - } else if (element.isJarOrWar()) { - // search name including the ! offset if it is there - if (classPathSize == 1 || filter.isSearchJar(element.getJarNameWithOffset(), element.getJarOffset())) { - scanJar(element); - } - - } else { - logger.error("Error: expected classPath entry [" + element + "] to be a directory or a .jar file but it is not either of those?"); - } - } - - if (matchList.isEmpty()) { - logger.warn("No Entities found in ClassPath using ClassPathReader [" + classPathReader + "] Classpath Searched[" + classPath + "]"); - } - - return matchList; - } - - private ClassPathElement getClassPathElement(Object classPathEntry) throws MalformedURLException { - - URL fileUrl; - - 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, 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 { - - JarFile module = null; - try { - // our resource is a jar - File file = classPathEntry.classPath; - module = new JarFile(file); - - logger.trace("scanJar file:{}", file); - - List classPathFromManifest = getClassPathFromManifest(file, module.getManifest()); - for (URI uri : classPathFromManifest) { - scanUri(uri); - } - - searchFiles(module.entries(), classPathEntry.getJarName(), classPathEntry.jarOffset, file); - - } catch (MalformedURLException ex) { - throw new IOException("Bad classpath error: ", ex); - - } finally { - if (module != null) { - try { - // close the jar if it was used - module.close(); - } catch (IOException e) { - logger.error("Error closing jar", e); - } - } - } - } - - private List getDirectoryFiles(File classPath) { - - // list of file names (latter checked as Classes) - ArrayList fileNameList = new ArrayList(); - - 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)); - } - } - - } else { - // get a recursive listing of this classPath - recursivelyListDir(fileNameList, classPath, new StringBuilder()); - } - - return fileNameList; - } - - /** - * Searches through the Java Archive (jar or war file) looking for classes - * that match our requirements. - * @param entries - all of the entries 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. - * @param module the containing jar/war file (used for spring boot embedded jar scanning) - */ - private void searchFiles(Enumeration entries, String jarFileName, String jarOffset, File module) { - - if (entries == null) { - return; - } - - logger.debug("searchFiles jarFileName:{} jarOffset:{}", jarFileName, jarOffset); - - // 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 ("/".equals(jarOffset)) { - // root level for runnable jar (spring boot etc) - jarOffset = null; - - } else if (jarOffset != null) { - if (jarOffset.startsWith("/")) { - jarOffset = jarOffset.substring(1); - } - - if (!jarOffset.endsWith("/")) { - jarOffset += "/"; - } - } - - while (entries.hasMoreElements()) { - - Object element = entries.nextElement(); - String entryName = element.toString(); - - if (isEntryEmbeddedJar(module, entryName)) { - scanSpringBootEmbeddedJar(jarFileName, module, entryName); - } - - if (isEntryClass(jarOffset, entryName)) { - // check if it an 'interesting' class - entity etc - registerScannedClass(jarFileName, jarOffset, entryName); - } - } - } - - /** - * Return true if this is an embedded jar that should be scanned. - */ - private boolean isEntryEmbeddedJar(File module, String entryName) { - return entryName.endsWith(".jar") && module != null && filter.isSearchJar(entryName, null); - } - - /** - * Return true if this is a class that should be checked (for entity, interesting interface etc). - */ - private boolean isEntryClass(String jarOffset, String entryName) { - return entryName.endsWith(".class") && (jarOffset == null || entryName.startsWith(jarOffset)); - } - - private void scanSpringBootEmbeddedJar(String jarFileName, File module, String fileName) { - // spring boot embedded jar - logger.debug("spring boot embedded:{} : module:{}", fileName, module.getAbsoluteFile()); - try { - org.springframework.boot.loader.jar.JarFile jarFile = new org.springframework.boot.loader.jar.JarFile(module); - org.springframework.boot.loader.jar.JarFile jarEntryFile = jarFile.getNestedJarFile(jarFile.getJarEntryData(fileName)); - Iterator iterator = jarEntryFile.iterator(); - while (iterator.hasNext()) { - JarEntryData jarEntryData = iterator.next(); - if (jarEntryData.getName().toString().endsWith(".class")) { - logger.debug("... spring boot class entry:{}", jarEntryData.getName().toString()); - registerScannedClass(jarFileName, null, jarEntryData.getName().toString()); - } - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private void registerScannedClass(String jarFileName, String jarOffset, String fileName) { - - 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 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 (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()); - } - } - } - - private void recursivelyListDir(List fileNameList, File dir, StringBuilder relativePath) { - - if (!dir.isDirectory()) { - // add class fileName to the list - fileNameList.add(relativePath.toString()); - - } 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()); - - recursivelyListDir(fileNameList, files[i], relativePath); - - // 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) { - - 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 classpath return that. - */ - private static List getClassPathFromManifest(File jarFile, Manifest manifest) { - - if (manifest == null) { - return Collections.emptyList(); - } - List list = new ArrayList(); - String classpathAttribute = manifest.getMainAttributes().getValue(Attributes.Name.CLASS_PATH.toString()); - - 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; - } - - 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(); - } - } - - private static File decodePath(File classPath) { - - try { - String charsetName = Charset.defaultCharset().name(); - - // URL Decode the path replacing %20 to space characters. - String path = URLDecoder.decode(classPath.getAbsolutePath(), charsetName); - return new File(path); - - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - } - - /** - * Element that has both underlying file and ! jarOffset. - */ - private static class ClassPathElement { - - private final File classPath; - private final String jarOffset; - - ClassPathElement(String path) { - this(new File(path)); - } - - ClassPathElement(File file) { - this(file, null); - } - - ClassPathElement(File file, String jarOffset) { - classPath = decodePath(file); - this.jarOffset = jarOffset; - } - - public String toString() { - return classPath.getAbsolutePath(); - } - - boolean isDirectory() { - return classPath.isDirectory(); - } - - 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; - } - - String getJarOffset() { - return 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 deleted file mode 100644 index fc9764dae..000000000 --- a/src/main/resources/META-INF/services/com.avaje.ebeaninternal.api.ClassPathSearchService +++ /dev/null @@ -1 +0,0 @@ -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 deleted file mode 100644 index 6037bd6fd..000000000 --- a/src/test/java/com/avaje/ebeaninternal/server/util/ClassPathSearchTests.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.avaje.ebeaninternal.server.util; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; - -import org.apache.commons.io.IOUtils; -import org.junit.Assert; -import org.junit.Test; - -/** - * This ensures that the ClassPathSearch supports normal file:file.jar files as well as jar/war files - * with bang paths. Bang paths typically look like this as a url: - * - * jar:file:/path/to/file.war!/WEB-INF/classes - * - * author: Richard Vowles - http://plus.google.com/RichardVowles - */ -public class ClassPathSearchTests { - - private static final String WEB_INF_CLASSES = "WEB-INF/classes/"; - - private void runAsserts(List> found, URLClassLoader cl, ClassPathSearch search, - URL jar, URL jarBang) throws ClassNotFoundException { - - assert found.size() == 2; - assert found.contains(cl.loadClass(SimpleJarBangClass.class.getName())); - assert found.contains(cl.loadClass(SimpleJarClass.class.getName())); - - assert search.getJarHits().contains(new File(jar.getFile()).getName()); - assert search.getJarHits().contains(new File(jarBang.getFile().split("!")[0]).getName()); - } - - @Test - public void ensureClassPathFindsClassesInBangPaths() throws IOException, ClassNotFoundException { - URL jar = setupJar(); - URL jarBang = setupJarBang(); - - URLClassLoader cl = new URLClassLoader(new URL[] { jar, jarBang}); - - ClassPathSearchFilter filter = new ClassPathSearchFilter(); - filter.includePackage("com.avaje.ebeaninternal.server"); - filter.includeJar("WEB-INF"); - filter.setDefaultJarMatch(false); - - ClassPathSearch search = new ClassPathSearch(); - search.init(cl, filter, new ClassPathSearchMatcher() { - @Override - public boolean isMatch(Class cls) { - return true; - } - }, null); - - List> found = search.findClasses(); - - Assert.assertEquals(1, found.size()); - Assert.assertTrue(found.contains(cl.loadClass(SimpleJarBangClass.class.getName()))); - - Assert.assertTrue(search.getJarHits().contains(new File(jarBang.getFile().split("!")[0]).getName())); - - filter = new ClassPathSearchFilter(); - filter.includePackage("com.avaje.ebeaninternal.server"); - filter.includeJar("bang"); - - search = new ClassPathSearch(); - search.init(cl, filter, new ClassPathSearchMatcher() { - @Override - public boolean isMatch(Class cls) { - return true; - } - },null); - - runAsserts(search.findClasses(), cl, search, jar, jarBang); - } - - private URL createJar(Class clazz, File jarFile, String offset) throws IOException { - FileOutputStream stream = new FileOutputStream(jarFile); - JarOutputStream jarOutputStream = new JarOutputStream(stream); - String clazzPath = clazz.getPackage().getName().replace(".", "/") + "/" + clazz.getSimpleName() + ".class"; - JarEntry entry = new JarEntry(offset + clazzPath); - jarOutputStream.putNextEntry(entry); - InputStream classStream = getClass().getResourceAsStream("/" + clazzPath); - IOUtils.copy(classStream, jarOutputStream); - - if (offset != null) { // copy the same file in, with a different offset - entry = new JarEntry("random/" + clazzPath); - jarOutputStream.putNextEntry(entry); - classStream = getClass().getResourceAsStream("/" + clazzPath); - IOUtils.copy(classStream, jarOutputStream); - } - - jarOutputStream.close(); - stream.close(); - - if (offset.length() > 0) { - return new URL("jar:" + jarFile.toURI().toString() + "!/" + offset); - } else { - return jarFile.toURI().toURL(); - } - } - - private URL setupJarBang() throws IOException { - File jarFile = File.createTempFile("bang", ".war"); - return createJar(SimpleJarBangClass.class, jarFile, WEB_INF_CLASSES); - } - - private URL setupJar() throws IOException { - File jarFile = File.createTempFile("nobang", ".jar"); - return createJar(SimpleJarClass.class, jarFile, ""); - } -} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml index e7e24a770..c49f3fd5b 100644 --- a/src/test/resources/logback-test.xml +++ b/src/test/resources/logback-test.xml @@ -82,6 +82,7 @@ +