/* * Copyright (c) 2010-2017 Evolveum * * 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 com.evolveum.midpoint.util; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Collection; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; /** * @author Peter Prochazka * @author Radovan Semancik */ public class ClassPathUtil { public static Trace LOGGER = TraceManager.getTrace(ClassPathUtil.class); public static Set<Class> listClasses(Package pkg) { return listClasses(pkg.getName()); } public static Set<Class> listClasses(String packageName) { Set<Class> classes = new HashSet<Class>(); searchClasses(packageName, c -> classes.add(c)); return classes; } /** * This is not entirely reliable method. * Maybe it would be better to rely on Spring ClassPathScanningCandidateComponentProvider */ public static void searchClasses(String packageName, Consumer<Class> consumer) { String path = packageName.replace('.', '/'); Enumeration<URL> resources = null; // HACK this is not available use LOGGER ClassLoader classLoader = LOGGER.getClass().getClassLoader(); LOGGER.trace("Classloader: {} : {}", classLoader, classLoader.getClass()); try { resources = classLoader.getResources(path); } catch (IOException e) { LOGGER.error("Classloader scaning error for " + path, e); } while (resources.hasMoreElements()) { URL candidateUrl = resources.nextElement(); LOGGER.trace("Candidates from: " + candidateUrl); // test if it is a directory or JAR String protocol = candidateUrl.getProtocol(); if ("file".contentEquals(protocol)) { getFromDirectory(candidateUrl, packageName, consumer); } else if ("jar".contentEquals(protocol) || "zip".contentEquals(protocol)) { getFromJar(candidateUrl, packageName, consumer); } else { LOGGER.warn("Unsupported protocol for candidate URL {}", candidateUrl); } } } /** * Extract specified source on class path to file system dst * * @param src * source * @param dst * destination * @return successful extraction */ public static Boolean extractFileFromClassPath(String src, String dst) { InputStream is = ClassPathUtil.class.getClassLoader().getResourceAsStream(src); if (null == is) { LOGGER.error("Unable to find file {} for extraction to {}", src, dst); return false; } return copyFile(is, src, dst); } public static boolean copyFile(InputStream srcStream, String srcName, String dstPath) { OutputStream dstStream = null; try { dstStream = new FileOutputStream(dstPath); } catch (FileNotFoundException e) { LOGGER.error("Unable to open destination file " + dstPath + ":", e); return false; } return copyFile(srcStream, srcName, dstStream, dstPath); } public static boolean copyFile(InputStream srcStream, String srcName, File dstFile) { OutputStream dstStream = null; try { dstStream = new FileOutputStream(dstFile); } catch (FileNotFoundException e) { LOGGER.error("Unable to open destination file " + dstFile + ":", e); return false; } return copyFile(srcStream, srcName, dstStream, dstFile.toString()); } public static boolean copyFile(InputStream srcStream, String srcName, OutputStream dstStream, String dstName) { byte buf[] = new byte[655360]; int len; try { while ((len = srcStream.read(buf)) > 0) { try { dstStream.write(buf, 0, len); } catch (IOException e) { LOGGER.error("Unable to write file " + dstName + ":", e); return false; } } } catch (IOException e) { LOGGER.error("Unable to read file " + srcName + " from classpath", e); return false; } try { dstStream.close(); } catch (IOException e) { LOGGER.error("Unable to close file " + dstName + ":", e); return false; } try { srcStream.close(); } catch (IOException e) { LOGGER.error("This never happend:", e); return false; } return true; } /** * Extracts all files in a directory on a classPath (system resource) to * a directory on a file system. */ public static boolean extractFilesFromClassPath(String srcPath, String dstPath, boolean overwrite) throws URISyntaxException, IOException { URL src = ClassPathUtil.class.getClassLoader().getResource(srcPath); if (src == null) { LOGGER.debug("No resource for {}", srcPath); return false; } URI srcUrl = src.toURI(); // URL srcUrl = ClassLoader.getSystemResource(srcPath); LOGGER.trace("URL: {}", srcUrl); if (srcUrl.getPath().contains("!/")) { URI srcFileUri = new URI(srcUrl.getPath().split("!/")[0]); // e.g. file:/C:/Documents%20and%20Settings/user/.m2/repository/com/evolveum/midpoint/infra/test-util/2.1-SNAPSHOT/test-util-2.1-SNAPSHOT.jar File srcFile = new File(srcFileUri); JarFile jar = new JarFile(srcFile); Enumeration<JarEntry> entries = jar.entries(); JarEntry jarEntry; while (entries.hasMoreElements()) { jarEntry = entries.nextElement(); // skip other files if (!jarEntry.getName().contains(srcPath)) { LOGGER.trace("Not relevant: ", jarEntry.getName()); continue; } // prepare destination file String filepath = jarEntry.getName().substring(srcPath.length()); File dstFile = new File(dstPath, filepath); if (!overwrite && dstFile.exists()) { LOGGER.debug("Skipping file {}: exists", dstFile); continue; } if (jarEntry.isDirectory()) { dstFile.mkdirs(); continue; } InputStream is = ClassLoader.getSystemResourceAsStream(jarEntry.getName()); LOGGER.debug("Copying {} from {} to {} ", jarEntry.getName(), srcFile, dstFile); copyFile(is, jarEntry.getName(), dstFile); } jar.close(); } else { try { File file = new File(srcUrl); File[] files = file.listFiles(); for (File subFile : files) { File dstFile = new File(dstPath, subFile.getName()); if (subFile.isDirectory()) { LOGGER.debug("Copying directory {} to {} ", subFile, dstFile); MiscUtil.copyDirectory(subFile, dstFile); } else { LOGGER.debug("Copying file {} to {} ", subFile, dstFile); MiscUtil.copyFile(subFile, dstFile); } } } catch (Exception ex) { throw new IOException(ex); } } return true; } /** * Get clasess from JAR * * @param srcUrl * @param packageName * @return */ @SuppressWarnings("rawtypes") private static void getFromJar(URL srcUrl, String packageName, Consumer<Class> consumer) { // sample: // file:/C:/.m2/repository/test-util/1.9-SNAPSHOT/test-util-1.9-SNAPSHOT.jar!/test-data/opendj.template // output: // file/C:/.m2/repository/test-util/1.9-SNAPSHOT/test-util-1.9-SNAPSHOT.jar String srcName = srcUrl.getPath().split("!/")[0]; //solution to make it work in Weblogic, because we have to make the path absolute, that means with scheme, that means basically with prefix file:/ //in Tomcat the form is jar:file:/ //in Weblogic the form is only zip:/ LOGGER.trace("srcUrl.getProtocol(): {}", srcUrl.getProtocol()); if ("zip".equals(srcUrl.getProtocol())) { srcName = "file:" + srcName; } LOGGER.trace("srcName: {}", srcName); // Probably hepls fix error in windows with URI File jarTmp = null; try { jarTmp = new File(new URI(srcName)); } catch (URISyntaxException ex) { LOGGER.error("Error converting jar " + srcName + " name to URI:", ex); return; } if (!jarTmp.isFile()) { LOGGER.error("Is {} not a file.", srcName); } JarFile jar = null; try { jar = new JarFile(jarTmp); } catch (IOException ex) { LOGGER.error("Error during open JAR " + srcName, ex); return; } String path = packageName.replace('.', '/'); Enumeration<JarEntry> entries = jar.entries(); LOGGER.trace("PATH:" + path); JarEntry e; while (entries.hasMoreElements()) { e = entries.nextElement(); // get name and replace inner class String name = e.getName().replace('$', '/'); // NOTICE: inner class are seperated and anonymous are part of the // listing !! // skip other files in other packas and skip non class files if (!name.contains(path) || !name.contains(".class")) { continue; } // Skip all that are not in package if (name.matches(path + "/.+/.*.class")) { continue; } LOGGER.trace("JAR Candidate: {}", name); try {// to create class // Convert name back to package Class clazz = Class.forName(name.replace('/', '.').replace(".class", "")); consumer.accept(clazz); } catch (ClassNotFoundException ex) { LOGGER.error("Error during loading class {} from {}. ", name, jar.getName()); } } try { jar.close(); } catch (IOException ex) { LOGGER.error("Error during close JAR {} " + srcName, ex); return; } } /** * get classes from directory * * @param candidateUrl * @param packageName * @return */ @SuppressWarnings("rawtypes") private static void getFromDirectory(URL candidateUrl, String packageName, Consumer<Class> consumer) { // Directory preparation File dir = null; try { dir = new File(candidateUrl.toURI()); } catch (URISyntaxException e) { LOGGER.error("NEVER HAPPEND -- Wrong URI: " + candidateUrl.getPath(), e); return; } // Skip if it is directory if (!dir.isDirectory()) { LOGGER.warn(" Skip: {} is not a directory", candidateUrl.getPath()); return; } // List directory String[] dirList = dir.list(); for (int i = 0; i < dirList.length; i++) { // skip directories if (!dirList[i].contains(".class")) { continue; } try {// to create class LOGGER.trace("DIR Candidate: {}", dirList[i]); Class<?> clazz = Class.forName(packageName + "." + dirList[i].replace(".class", "")); consumer.accept(clazz); } catch (ClassNotFoundException e) { LOGGER.error("Error during loading class {} from {}. ", dirList[i], dir.getAbsolutePath()); } } } }