Java tutorial
/* * Copyright (C) 2008-2010 Institute for Computational Biomedicine, * Weill Medical College of Cornell University * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package edu.cornell.med.icb.io; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ClassUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.util.Enumeration; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Help with obtaining resources. This will look * @author Marko, Kevin, code from log4j? */ public class ResourceFinder { /** When to search the classpath enum. */ public enum SearchInClasspath { BEFORE_LOCAL, AFTER_LOCAL, NEVER } /** When to search the classpath. Default to search before searching locally. */ public SearchInClasspath searchInClasspath = SearchInClasspath.BEFORE_LOCAL; /** Used to log debug and informational messages. */ private static final Log LOG = LogFactory.getLog(ResourceFinder.class); /** The additional search paths to search. */ private final List<String> searchPaths; /** * No extra paths configuration. */ public ResourceFinder() { this(ArrayUtils.EMPTY_STRING_ARRAY); } /** * Create a resource finder. If the resource isn't found directly at the specified * path, each of searchPathsVal will be prepended to the resource requested to look * in those locations as well. * @param searchPathsVal the additional searchPaths to search for the resource. */ public ResourceFinder(final String... searchPathsVal) { searchPaths = new LinkedList<String>(); if (searchPathsVal != null && searchPathsVal.length > 0) { for (String searchPath : searchPathsVal) { searchPath = FilenameUtils.separatorsToUnix(searchPath); while (searchPath.endsWith("/")) { searchPath = searchPath.substring(0, searchPath.length() - 1); } this.searchPaths.add(searchPath); } } else { // Default to searching "." (current directory) searchPaths.add("."); } } /** * Obtain a URL for a specified resource. Returns null if the resource could not be * obtained. * @param resource the resource name * @return A URL for the specified resource or null */ public URL findResource(final String resource) { URL url; if (searchInClasspath == SearchInClasspath.BEFORE_LOCAL) { url = findResourceInClasspath(resource); if (url != null) { return url; } } url = findResourceInLocal(resource); if (url != null) { return url; } if (searchInClasspath == SearchInClasspath.AFTER_LOCAL) { url = findResourceInClasspath(resource); if (url != null) { return url; } } return null; } private URL findResourceInClasspath(final String resource) { // Find resource using the classpath in the root of the classpath URL url = resourceToUrl(resource); if (url != null) { return url; } // Find a resource using the classpath using searchPaths for (final String searchPath : searchPaths) { url = resourceToUrl(searchPath + "/" + resource); if (url != null) { return url; } } return null; } private URL findResourceInLocal(final String resource) { // Try to find the resource in the searchPaths for (final String searchPath : searchPaths) { final URL url = fileToURL(searchPath + IOUtils.DIR_SEPARATOR + resource); if (url != null) { return url; } } return null; } /** * Try to convert a file to a URL. Returns null if the file doesn't exist or isn't * a file or isn't readable. * @param filename the filename to try to map to URL * @return the URL */ private URL fileToURL(final String filename) { URL url = null; try { final File file = new File(filename); if (LOG.isDebugEnabled()) { LOG.debug("Trying to find [" + filename + "] as file in filesystem"); } if (file.exists() && file.isFile() && file.canRead()) { url = file.toURI().toURL(); } } catch (MalformedURLException e) { // NOPMD // resource is not a URL } catch (IOException e) { // NOPMD // Could not do file getCanonicalPath() } if (url != null && LOG.isDebugEnabled()) { LOG.debug("... fo`und url is [" + url.toString() + "]"); } return url; } /** * Search for a resource using the thread context class loader. If that fails, search * using the class loader that loaded this class. If that still fails, try one last * time with {@link ClassLoader#getSystemResource(String)}. * @param resource The resource to search for * @return A url representing the resource or {@code null} if the resource was not found */ private URL resourceToUrl(final String resource) { URL url; // get the configuration from the classpath of the current thread ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (LOG.isDebugEnabled()) { LOG.debug("Trying to find [" + resource + "] using context class loader " + loader); } url = loader.getResource(resource); if (url == null) { // We couldn't find resource - now try with the class loader that loaded this class loader = ResourceFinder.class.getClassLoader(); // NOPMD if (LOG.isDebugEnabled()) { LOG.debug("Trying to find [" + resource + "] using class loader " + loader); } url = loader.getResource(resource); } if (url == null) { // make a last attempt to get the resource from the system class loader if (LOG.isDebugEnabled()) { LOG.debug("Trying to find [" + resource + "] using system class loader"); } url = ClassLoader.getSystemResource(resource); } if (url != null && LOG.isDebugEnabled()) { LOG.debug("... found url is [" + url.toString() + "]"); } return url; } /** * Return an InputStream for a given resource using the ResourceFinder. * @param resource the resource to get a BufferedReader for * @return the buffered reader or null if the resource could not be located * @throws IOException error obtaining reader after resource was located */ public InputStream inputStreamForResource(final String resource) throws IOException { final URL url = findResource(resource); InputStream is = null; if (url != null) { is = url.openStream(); } return is; } /** * List directory contents for a resource folder that contains the specified * class, this list will include the .class file associated with clazz. * * @param clazz The java class which lives in the directory you want listed * @return Each member item (no path information) * @throws java.net.URISyntaxException bad URI syntax * @throws IOException error reading */ public String[] getResourceListing(final Class clazz) throws URISyntaxException, IOException { final String classPackage = ClassUtils.getPackageName(clazz); final String classPacakgePath = classPackage.replace(".", "/"); return getResourceListing(clazz, classPacakgePath); } /** * List directory contents for a resource folder. Not recursive, does not return * directory entries. Works for regular files and also JARs. * * @param clazz Any java class that lives in the same place as the resources you want. * @param pathVal the path to find files for * @return Each member item (no path information) * @throws URISyntaxException bad URI syntax * @throws IOException error reading */ public String[] getResourceListing(final Class clazz, final String pathVal) throws URISyntaxException, IOException { // Enforce all paths are separated by "/", they do not start with "/" and // the DO end with "/". String path = pathVal.replace("\\", "/"); if (!path.endsWith("/")) { path += "/"; } while (path.startsWith("/")) { path = path.substring(1); } URL dirURL = findResource(path); if (dirURL != null && dirURL.getProtocol().equals("file")) { /* A file path: easy enough */ return new File(dirURL.toURI()).list(); } if (dirURL == null) { /* * In case of a jar file, we can't actually find a directory. * Have to assume the same jar as clazz. */ final String classFilename = clazz.getName().replace(".", "/") + ".class"; dirURL = findResource(classFilename); } if (dirURL.getProtocol().equals("jar")) { /* A JAR path */ String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf('!')); if (jarPath.charAt(2) == ':') { jarPath = jarPath.substring(1); } jarPath = URLDecoder.decode(jarPath, "UTF-8"); final JarFile jar = new JarFile(jarPath); final Enumeration<JarEntry> entries = jar.entries(); final Set<String> result = new HashSet<String>(); while (entries.hasMoreElements()) { final String name = entries.nextElement().getName(); //filter according to the path if (name.startsWith(path)) { final String entry = name.substring(path.length()); if (entry.length() == 0) { // Skip the directory entry for path continue; } final int checkSubdir = entry.indexOf('/'); if (checkSubdir >= 0) { // Skip sub dirs continue; } result.add(entry); } } return result.toArray(new String[result.size()]); } throw new UnsupportedOperationException("Cannot list files for URL " + dirURL); } /** * Get when to search in classpath for the resource (default is SearchInClasspath.BEFORE_LOCAL). * @return the searchInClasspath value */ public SearchInClasspath getSearchInClasspath() { return searchInClasspath; } /** * Set when to search in classpath for the resource. * @param searchInClasspath the new searchInClasspath value */ public void setSearchInClasspath(final SearchInClasspath searchInClasspath) { this.searchInClasspath = searchInClasspath; } }