/** * Abiquo community edition * cloud management application for hybrid clouds * Copyright (C) 2008-2010 - Abiquo Holdings S.L. * * This application is free software; you can redistribute it and/or * modify it under the terms of the GNU LESSER GENERAL PUBLIC * LICENSE as published by the Free Software Foundation under * version 3 of the License * * This software 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 * LESSER GENERAL PUBLIC LICENSE v.3 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package com.abiquo.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class is a helper to load classes in abicloud_server context. Very usefully i plugfins and * extensions. * * @author jdevesa@abiquo.com */ public class AbiClassLoader { /** * The Constant logger. */ private static final Logger logger = LoggerFactory.getLogger(AbiClassLoader.class); /** * Local class Loader */ // private static final URLClassLoader urlLoader = (URLClassLoader) // ClassLoader.getSystemClassLoader(); private static final URLClassLoader urlLoader = (URLClassLoader) AbiClassLoader.class .getClassLoader(); /** * Public method to return all the classes for a given package. It works in two ways: one for * directory (own project) classes and the other one for load classes from Jar. This method is * the only point of access for this class. * <p> * * @param packageName name of the classes to return * @return ArrayList<Class> with all the related classes. */ public static ArrayList<Class< ? extends Object>> loadClassesFromPackage(String requestedPackage) { ArrayList<Class< ? extends Object>> classesToRecover = new ArrayList<Class< ? extends Object>>(); if (requestedPackage == null || requestedPackage.isEmpty()) { return classesToRecover; } /* * Get all the sources where find the classes and browse them. (It is possible that a * package would be inside a directory and, for instance, two packages in the same time. In * this case, we will have three sources. */ try { Enumeration<URL> allSources; String parsedName = requestedPackage.replace('.', File.separatorChar); allSources = urlLoader.findResources(parsedName); while (allSources.hasMoreElements()) { URL urlSource = allSources.nextElement(); String basePath = urlSource.getPath(); /* * The URL source converts the spaces of the local paths to the '%20' string. We * replace to 'space' again. */ basePath = basePath.replace("%20", " "); File rootDirectory = new File(basePath); if (rootDirectory.isDirectory()) { classesToRecover.addAll(loadClassesFromDir(rootDirectory, parsedName)); } else { // Maybe its a jar... ArrayList<Class< ? extends Object>> jarClasses = loadClassesFromJar(urlSource, parsedName); // logger.error(" url is "+ urlSource); if (jarClasses != null) { classesToRecover.addAll(jarClasses); } } } } catch (IOException e) { logger.error(requestedPackage + ": can not find the resources"); } return classesToRecover; } /** * From a given Directory, get all the classes of its files. It works in recursive way. * * @param parentDir directory to be scanned * @param parsedName the parsed(with '/' instead of '.') name of the directory that contains the * file * @return ArrayList containing the existing classes into the directory */ private static ArrayList<Class< ? extends Object>> loadClassesFromDir(File parentDir, String parsedName) { ArrayList<Class< ? extends Object>> childrenToRecover = new ArrayList<Class< ? extends Object>>(); // loop for all its children for (File children : parentDir.listFiles()) { // if it is a directory... if (children.isDirectory()) { // add all its children recursively childrenToRecover.addAll(loadClassesFromDir(children, parsedName + System.getProperty("file.separator") + children.getName())); } else { // Gets the file path an String javaFilePath = parsedName + System.getProperty("file.separator") + children.getName(); // add class if loadClass is not null Class< ? extends Object> tmpClass = loadAbiClass(javaFilePath); if (tmpClass != null) { childrenToRecover.add(tmpClass); } } } return childrenToRecover; } /** * In this case, we load all classes inside a jar. * * @param jarURL entry jar URL (with the template 'jar:file:com.xxx.xxxx.xxx!' ) * @param parsedName the name of the package but with '/' instead of '.' (to browse the package) * @return ArrayList containing the existing classes into the jar */ private static ArrayList<Class< ? extends Object>> loadClassesFromJar(URL jarURL, String parsedName) { /* * First of all, we create a jarconnection in order to find all the desired classes, After * this, we call our loadAbiClass method */ ArrayList<Class< ? extends Object>> childrenJarToRecover = new ArrayList<Class< ? extends Object>>(); /* Create JarConnection and open an Input Stream and browse the jar */ // JarURLConnection jarCon; try { // logger.error(" url is ex_"+ jarURL.toExternalForm()); // logger.error(" url is "+ jarURL.toString()); // jarCon = (JarURLConnection) jarURL.openConnection(); // String jarName = jarCon.getJarFile().getName(); JarInputStream jarFile = new JarInputStream(new FileInputStream(jarURL.toExternalForm())); JarEntry jarEntry; while ((jarEntry = jarFile.getNextJarEntry()) != null) { if ((jarEntry.getName().startsWith(parsedName)) && (jarEntry.getName().endsWith(".class"))) { Class< ? extends Object> tmpClass = loadAbiClass(jarEntry.getName()); if (tmpClass != null) { childrenJarToRecover.add(tmpClass); } } } } catch (IOException e) { logger.error("Exception thrown accessing to jar "); } return childrenJarToRecover; } /** * Load a class from a given path. * * @param path path where the class should be. * @return a Class stored in path. Null otherwise. */ private static Class< ? extends Object> loadAbiClass(String javaFilePath) { Class< ? extends Object> desiredClass; if (javaFilePath.endsWith(".class")) { // Parsing the javaFilePath like com/yyy/xxxxx/zzzzz.class // to com.yyy.xxxxx.zzzzz String parsedFilePath = javaFilePath.replace(File.separatorChar, '.'); parsedFilePath = parsedFilePath.substring(0, parsedFilePath.lastIndexOf('.')); try { desiredClass = urlLoader.loadClass(parsedFilePath); } catch (ClassNotFoundException e) { // invalid .class file logger.debug(javaFilePath + " is an invalid. Discarded"); desiredClass = null; } } else { // maybe a .java or a .xml.. don't want to load! desiredClass = null; } return desiredClass; } }