/* * Copyright (c) 2014 Red Hat, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ovirt.engine.api.common.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class PackageExplorer { /** * The logger used by this class. */ private static final Logger log = LoggerFactory.getLogger(PackageExplorer.class); /** * Discover classes under target package. * * @param packageName the fully qualified name of the package * @return the list of class names found */ public static List<String> discoverClasses(String packageName) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); List<String> classNames = new ArrayList<>(); try { Enumeration<URL> resources = classLoader.getResources(toPath(packageName)); List<File> dirs = new ArrayList<>(); List<JarInputStream> jars = new ArrayList<>(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); if (isJar(resource)) { jars.add(new JarInputStream(new FileInputStream(URLDecoder.decode(getJarName(resource), "UTF-8")))); } else if (containsJar(resource)) { jars.add(getContainingResource(classLoader, resource)); } else { dirs.add(new File(URLDecoder.decode(resource.getFile(), "UTF-8"))); } } walkJars(classNames, packageName, jars); walkDirs(classNames, packageName, dirs); } catch (IOException exception) { log.error( "Error while trying to find scan classpath for package \"{}\".", packageName, exception ); } return classNames; } private static JarInputStream getContainingResource(ClassLoader classLoader, URL resource) throws IOException { JarInputStream ret = null; Enumeration<URL> globals = classLoader.getResources("/"); while (globals.hasMoreElements()) { URL global = globals.nextElement(); if (resource.toString().startsWith(global.toString())) { ret = (JarInputStream) global.openStream(); break; } } return ret; } private static void walkJars(List<String> classNames, String packageName, List<JarInputStream> jars) throws IOException { for (JarInputStream jarFile : jars) { try { JarEntry entry; while ((entry = jarFile.getNextJarEntry()) != null) { String name = toPackage(entry.getName()); if (name.startsWith(packageName) && isClass(name)) { classNames.add(trimClass(name)); } } } finally { if (jarFile != null) { jarFile.close(); } } } } private static void walkDirs(List<String> classNames, String packageName, List<File> dirs) throws IOException { for (File directory : dirs) { if (directory.exists()) { classNames.addAll(getClassesUnder(directory, packageName)); } } } private static List<String> getClassesUnder(File directory, String packageName) { List<String> classes = new ArrayList<>(); if (!directory.exists()) { return classes; } File[] files = directory.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { classes.addAll(getClassesUnder(file, in(packageName, file.getName()))); } else if (isClass(file.getName())) { classes.add(in(packageName, trimClass(file.getName()))); } } } return classes; } private static String toPath(String packageName) { return packageName.replace('.', '/'); } private static String toPackage(String path) { return path.replaceAll("/", "\\."); } private static String in(String packageName, String entry) { return packageName + '.' + entry; } private static String getJarName(URL resource) { return resource.getPath().split("!")[0].substring("file:".length()); } private static boolean isJar(URL resource) { return resource.getProtocol().equals("jar"); } private static boolean containsJar(URL resource) { return resource.getPath().indexOf(".jar") != -1; } private static boolean isClass(String s) { return s.endsWith(".class"); } private static String trimClass(String s) { return s.substring(0, s.length() - 6); } }