001/* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2012 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020package org.sonar.server.plugins; 021 022import com.google.common.base.*; 023import com.google.common.collect.Lists; 024import org.apache.commons.io.FileUtils; 025import org.apache.commons.lang.CharEncoding; 026import org.apache.commons.lang.StringUtils; 027import org.slf4j.LoggerFactory; 028 029import javax.annotation.Nullable; 030import java.io.File; 031import java.io.IOException; 032import java.net.URL; 033import java.net.URLDecoder; 034import java.util.Collection; 035import java.util.Enumeration; 036import java.util.jar.JarEntry; 037import java.util.jar.JarFile; 038 039/** 040 * @since 3.0 041 */ 042public final class ClassLoaderUtils { 043 044 private ClassLoaderUtils() { 045 } 046 047 public static File copyResources(ClassLoader classLoader, String rootPath, File toDir) { 048 return copyResources(classLoader, rootPath, toDir, Functions.<String>identity()); 049 } 050 051 public static File copyResources(ClassLoader classLoader, String rootPath, File toDir, Function<String, String> relocationFunction) { 052 Collection<String> relativePaths = listFiles(classLoader, rootPath); 053 for (String relativePath : relativePaths) { 054 URL resource = classLoader.getResource(relativePath); 055 String filename = relocationFunction.apply(relativePath); 056 File toFile = new File(toDir, filename); 057 try { 058 FileUtils.copyURLToFile(resource, toFile); 059 } catch (IOException e) { 060 throw new IllegalStateException("Fail to extract " + relativePath + " to " + toFile.getAbsolutePath()); 061 } 062 } 063 064 return toDir; 065 } 066 067 /** 068 * Finds files within a given directory and its subdirectories 069 * 070 * @param classLoader 071 * @param rootPath the root directory, for example org/sonar/sqale 072 * @return a list of relative paths, for example {"org/sonar/sqale/foo/bar.txt}. Never null. 073 */ 074 public static Collection<String> listFiles(ClassLoader classLoader, String rootPath) { 075 return listResources(classLoader, rootPath, new Predicate<String>() { 076 public boolean apply(@Nullable String path) { 077 return !StringUtils.endsWith(path, "/"); 078 } 079 }); 080 } 081 082 083 public static Collection<String> listResources(ClassLoader classLoader, String rootPath) { 084 return listResources(classLoader, rootPath, Predicates.<String>alwaysTrue()); 085 } 086 087 /** 088 * Finds directories and files within a given directory and its subdirectories. 089 * 090 * @param classLoader 091 * @param rootPath the root directory, for example org/sonar/sqale, or a file in this root directory, for example org/sonar/sqale/index.txt 092 * @param 093 * @return a list of relative paths, for example {"org/sonar/sqale", "org/sonar/sqale/foo", "org/sonar/sqale/foo/bar.txt}. Never null. 094 */ 095 public static Collection<String> listResources(ClassLoader classLoader, String rootPath, Predicate<String> predicate) { 096 String jarPath = null; 097 JarFile jar = null; 098 try { 099 Collection<String> paths = Lists.newArrayList(); 100 rootPath = StringUtils.removeStart(rootPath, "/"); 101 102 URL root = classLoader.getResource(rootPath); 103 if (root != null) { 104 checkJarFile(root); 105 106 // Path of the root directory 107 // Examples : 108 // org/sonar/sqale/index.txt -> rootDirectory is org/sonar/sqale 109 // org/sonar/sqale/ -> rootDirectory is org/sonar/sqale 110 // org/sonar/sqale -> rootDirectory is org/sonar/sqale 111 String rootDirectory = rootPath; 112 if (StringUtils.substringAfterLast(rootPath, "/").indexOf('.') >= 0) { 113 rootDirectory = StringUtils.substringBeforeLast(rootPath, "/"); 114 } 115 jarPath = root.getPath().substring(5, root.getPath().indexOf("!")); //strip out only the JAR file 116 jar = new JarFile(URLDecoder.decode(jarPath, CharEncoding.UTF_8)); 117 Enumeration<JarEntry> entries = jar.entries(); 118 while (entries.hasMoreElements()) { 119 String name = entries.nextElement().getName(); 120 if (name.startsWith(rootDirectory) && predicate.apply(name)) { 121 paths.add(name); 122 } 123 } 124 } 125 return paths; 126 } catch (Exception e) { 127 throw Throwables.propagate(e); 128 } finally { 129 closeJar(jar, jarPath); 130 } 131 } 132 133 private static void closeJar(JarFile jar, String jarPath) { 134 if (jar != null) { 135 try { 136 jar.close(); 137 } catch (Exception e) { 138 LoggerFactory.getLogger(ClassLoaderUtils.class).error("Fail to close JAR file: " + jarPath, e); 139 } 140 } 141 } 142 143 private static void checkJarFile(URL root) { 144 if (!"jar".equals(root.getProtocol())) { 145 throw new IllegalStateException("Unsupported protocol: " + root.getProtocol()); 146 } 147 } 148}