/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is available at http://www.abiquo.com/..... * * The Initial Developer of the Original Code is Soluciones Grid, S.L. (www.abiquo.com), * Consell de Cent 296 principal 2�, 08007 Barcelona, Spain. * No portions of the Code have been created by third parties. * All Rights Reserved. * * Contributor(s): ______________________________________. * * Graphical User Interface of this software may be used under the terms * of the Common Public Attribution License Version 1.0 (the "CPAL License", * available at http://cpal.abiquo.com), in which case the provisions of CPAL * License are applicable instead of those above. In relation of this portions * of the Code, a Legal Notice according to Exhibits A and B of CPAL Licence * should be provided in any distribution of the corresponding Code to Graphical * User Interface. */ package com.abiquo.abicloud.plugin; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; import org.apache.bcel.Repository; import org.apache.bcel.classfile.ClassFormatException; import org.apache.bcel.classfile.ClassParser; import org.apache.bcel.classfile.JavaClass; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.abiquo.abicloud.exception.HypervisorException; import com.abiquo.abicloud.exception.PluginException; import com.abiquo.abicloud.hypervisor.impl.VirtualBoxHypervisor; import com.abiquo.abicloud.hypervisor.impl.VmwareHypervisor; import com.abiquo.abicloud.model.IHypervisor; /** * Maintain Hypervisor's plugins. It loads from package "com.abiquo.abicloud.plugin.impl" (by * default, can add more paths where look up) all the classes implementing IHypervisor interface, * only one implementation class allowed for each HypervisorType. Requires on the implementation * class a default empty constructor (reflection instantiation). TODO: allow URI paths / java * sources (resolving dependences �bcel/maven?) */ public class HypervisorManager { /** The logger object */ private final static Logger logger = LoggerFactory.getLogger(HypervisorManager.class); /** Enumerate all paths where look up for plugin classes */ private List<String> pluginClassPaths; /** * All IHypervisor plugin implementation classes indexed by its hypervisor type */ private Map<String, Class< ? extends IHypervisor>> htHypervisorPlugins; /** * All singleton IHypervisors objects indexed by its hypervisor type / address */ private Map<String, Map<URL, IHypervisor>> htHypervisors; private final static String hypervisorInterface = "com.abiquo.abicloud.model.IHypervisor"; private final static String hypervisorPackage = "com.abiquo.abicloud.hypervisor.impl"; private final static String hypervisorClassPahtProperty = "java.library.path"; // "java.ext.dirs" private final static boolean isRefreshOnCreation = false; /** * Creates a new Plugin Manager and load all IHypervisor implementations located at package * "com.abiquo.abicloud.plugin.impl" */ public HypervisorManager() { htHypervisorPlugins = new Hashtable<String, Class< ? extends IHypervisor>>(); htHypervisors = new Hashtable<String, Map<URL, IHypervisor>>(); pluginClassPaths = new ArrayList<String>(); URL baseClasspath = HypervisorManager.class.getClassLoader().getResource( hypervisorPackage.replace('.', File.separatorChar));// classes pluginClassPaths.add(baseClasspath.getPath()); // TODO java sources // addPluginClassPath("/pro/abicloud/abiCloud_server/src/com/abiquo/abicloud/plugin/impl"); if (isRefreshOnCreation) { logger.info("refresh plugins"); refreshHypervisorPlugins(); logger.info("refresh plugins done"); } else { logger.info("ONLY: adds Virtual Box Hypervisor plugin"); try { addHypervisorClass(VirtualBoxHypervisor.class.asSubclass(IHypervisor.class), false); // do // not // update addHypervisorClass(VmwareHypervisor.class.asSubclass(IHypervisor.class), false); // do // not // update } catch (PluginException e) { logger.error("VirtualBox plugin can not be loaded :" + e.getLocalizedMessage()); } } } /** * Enumerates all the available hypervisors types. No multiple implementations of the same * hypervisor type allowed. * * @return a list containing all IHypervisor.getHypervisorTyper() for each IHypervisor * implementation. getHypervisorType is the IHypervisor identifier, so MUST be different * for each class. * @see IHypervisor.getHypervisorType() */ public String[] getHypervisorTypes() { return htHypervisorPlugins.keySet() .toArray(new String[htHypervisorPlugins.keySet().size()]); } /** * Returns an Hypervisor singleton instance by its type. * * @param type the desired hypervisor type * @return a singleton instance for the IHypervisor implementation for given hypervisor type. * @throws PluginException if there is not an implementation class for this hypervisor type */ public List<IHypervisor> getAllHypervisors(String type) throws HypervisorException { List<IHypervisor> hyper; if (htHypervisors.containsKey(type)) { hyper = new ArrayList<IHypervisor>(htHypervisors.get(type).values()); } else { throw new HypervisorException("There is any Hypervisor for type " + type); } return hyper; } /** * Gets the singleton instance . Or creates a new instance. */ public IHypervisor getHypervisor(String type, URL address) throws PluginException { IHypervisor hyper; Map<URL, IHypervisor> hypersByAdd; if (htHypervisors.containsKey(type)) { hypersByAdd = htHypervisors.get(type); if (hypersByAdd.containsKey(address)) { return htHypervisors.get(type).get(address); } } else { hypersByAdd = new Hashtable<URL, IHypervisor>(); } hyper = instantiateHypervisor(type); hyper.init(address); hyper.connect(address); hypersByAdd.put(address, hyper); htHypervisors.put(type, hypersByAdd); return hyper; } /** * Creates a new Hypervisor plugin. * * @param type the desired hypervisor type * @return new plugin instance for the given hypervisor type. * @throws PluginException if there is not any class implementing the desired hypervisor type or * exist but can not no be instantiated (not default empty constructor ?) * @see IHypervisor.getHypervisorType() */ protected IHypervisor instantiateHypervisor(String type) throws PluginException { Class< ? extends IHypervisor> classHyper; IHypervisor hyper; if (htHypervisorPlugins.containsKey(type)) { classHyper = htHypervisorPlugins.get(type); try { // TODO: required default constructor hyper = classHyper.newInstance(); } catch (Exception e1) // InstantiationException or IllegalAccessException { // TODO: try to find the right constructor throw new PluginException("Failed to instantiate Hypervisor plugin " + "for " + type + " using class " + classHyper.getCanonicalName(), e1); } } else { throw new PluginException("Plugin for hypervisor type " + type + " not loaded "); // TODO try to reload ?? } return hyper; } /** * Cleans up the existing IHypervisors plugin implementation classes and start search on all * given paths (pluginClassPaths) look up for new IHypervisor implementations. */ public void refreshHypervisorPlugins() { htHypervisorPlugins.clear(); logger.info("Loading hypervisors plugins from "); for (String path : pluginClassPaths) { logger.info(" path :" + path); File fPath = new File(path); if (fPath.isDirectory()) { // TODO: if there are subdirectories for (File f : fPath.listFiles()) { try { loadHypervisors(f.getAbsolutePath()); } catch (PluginException e) { logger.error("Failed to load plugin at " + f.getAbsolutePath() + "\n Caused by:" + e.getLocalizedMessage() + "\n" + e.getCause().getLocalizedMessage()); } } } else { try { loadHypervisors(path); } catch (PluginException e) { logger.error("Failed to load plugin at " + path + "\n Caused by:" + e.getLocalizedMessage() + "\n" + e.getCause().getLocalizedMessage()); } } }// for paths } /** * Checks if javaFilePath contain class o source of an IHypervisor implementation, if so, adds * to existing plugin repository indexed by its getHypervisorType. * * @param javaFilePath candidate java file to implement IHypervisor * @throws PluginException if the given class can not no be loaded or is not a java class * (ClassFormatException) */ private void loadHypervisors(String javaFilePath) throws PluginException { JavaClass java_class; if (javaFilePath.endsWith(".class")) { try { java_class = new ClassParser(javaFilePath).parse(); } catch (IOException e1) { final String ex_msg = "IOException while loading class file " + javaFilePath + "\n Caused by " + e1.getCause().getLocalizedMessage(); // log.severe(ex_msg); throw new PluginException(ex_msg, e1); } catch (ClassFormatException e2) { final String ex_msg = "ClassFormatException while loading class file " + javaFilePath + "\n Caused by " + e2.getCause().getLocalizedMessage(); // log.severe(ex_msg); throw new PluginException(ex_msg, e2); } } // dot class else // java or jar { try { java_class = Repository.lookupClass(javaFilePath); // TODO Repository.getRepository().findClass(javaFilePath); } catch (ClassNotFoundException e) { final String ex_msg = "ClassNotFoundException while loading java file " + javaFilePath + "\n Caused by " + e.getCause().getLocalizedMessage(); throw new PluginException(ex_msg, e); } } // dot java // log.fine("Try to load " + java_class.getClassName()); try { if (Repository.implementationOf(java_class, hypervisorInterface)) { Class< ? extends IHypervisor> classHyper = Class.forName(java_class.getClassName()).asSubclass(IHypervisor.class); if (!addHypervisorClass(classHyper, false)) // do not update { throw new PluginException("Hypervisor Type already defined, descarted plugin implementation at class" + classHyper.getCanonicalName(), new Throwable()); } } // else do nothing } catch (ClassNotFoundException e) // must fail before { final String ex_msg = "ClassNotFoundException while loading java file " + javaFilePath + "\n Caused by " + e.getCause().getLocalizedMessage(); throw new PluginException(ex_msg, e); } } /** * @return a list containing all the class paths where plugins are look up. */ public List<String> getPluginClassPaths() { return pluginClassPaths; } /*** * Adds the given path at the end of pluginClassPaths. Remember only one instance allowed for * each hypervisor type, so if some class already implements a plugin before (find on previous * class paths) it will throw a PluginException when try to load from the new path . * * @param path the new path where to look up for IHypervisor implementations. * @param isHighPriority if true, will find first on this class path, otherwise will look up on * it before all others. * @return true if the given path are not already on the list, exist and it could be accessible, * false otherwise. */ public boolean addPluginClassPath(String path, boolean isHighPriority) { if (pluginClassPaths.contains(path)) { return false; } else { File fPath = new File(path); if (!fPath.exists()) { logger.error("Plugin path " + path + " do not exist"); return false; } else if (!fPath.canRead()) { logger.error("Plugin path " + path + " can not be read"); return false; } else { logger.info("Added plugin path " + path); if (isHighPriority) { pluginClassPaths.add(0, path); // TODO Repository.getRepository().getClassPath().getClassPath() System.setProperty("java.ext.dirs", path + File.pathSeparatorChar + System.getProperty(hypervisorClassPahtProperty)); } else { pluginClassPaths.add(path); // TODO Repository.getRepository().getClassPath().getClassPath() System.setProperty("java.ext.dirs", System .getProperty(hypervisorClassPahtProperty) + File.pathSeparatorChar + path); } return true; } }// not contain } /** * Deletes the given IHypervisor class path * * @param path the IHypervisor class path to want to remove. * @return true if success remove an existing IHypervisor class path,false if already do not * exist. */ public boolean removePluginClassPath(String path) { return pluginClassPaths.remove(path); } /** * Gets the current plugin index. * * @return all the Hypervisors plugin class implementations indexed by its hypervisor type. */ public Map<String, Class< ? extends IHypervisor>> getHypervisorMap() { return htHypervisorPlugins; } /** * Adds a new IHypervisor class implementation indexed by its getHypervisorType. * * @param hypervisorClass the IHypervisor implementation class want to add. * @param update require to overwrite already defined implementation class. * @return true if success add the new IHypervisor class, false if update is not required and * some IHypervisor class already defined for the same hypervisor type. * @throws PluginException if the given class can not no be instantiated (not default empty * constructor ?) */ public boolean addHypervisorClass(Class< ? extends IHypervisor> hyperClass, boolean update) throws PluginException { try { String hyperType; hyperType = hyperClass.newInstance().getHypervisorType(); if (htHypervisorPlugins.containsKey(hyperType) && !update) { return false; } else { htHypervisorPlugins.put(hyperType, hyperClass); logger.info("Added an IHypervisor implementation at " + hyperClass.getCanonicalName() + "for hypervisor type " + hyperType); return true; } } catch (InstantiationException e) { final String ex_msg = "ClassNotFoundException while loading java class file " + hyperClass.getCanonicalName(); throw new PluginException(ex_msg, e); } catch (IllegalAccessException e) { final String ex_msg = "ClassNotFoundException while loading java clas file " + hyperClass.getCanonicalName(); throw new PluginException(ex_msg, e); } } /** * Deletes the IHypervisor class implementation for the given type. * * @param hypervisorType the IHypervisor identifier want to remove. * @return true if success remove an existing IHypervisor class, false if do not exist any * IHypervisor class for the given type. */ public boolean removeHypervisorClassFor(String hypervisorType) { if (htHypervisorPlugins.containsKey(hypervisorType)) { htHypervisorPlugins.remove(hypervisorType); return true; } else { return false; } } /** * Tests load hypervisor plugin, reload each 10 seconds. */ public static void main(String args[]) throws Exception { final HypervisorManager pm = new HypervisorManager(); Thread thReload = new Thread() { public void run() { pm.refreshHypervisorPlugins(); for (String sh : pm.getHypervisorTypes()) { System.out.println("Hypervisor " + sh); } try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } run(); } }; thReload.start(); } // ///// TODO DEPENDENCES RESOLVER :: triged if PluginException at class Instantiation /* * public static String[] getClassDependencies(JavaClass.getConstantPool() pool) { String[] * tempArray = new String[pool.getLength()]; int size = 0; StringBuffer buf = new * StringBuffer(); for(int idx = 0; idx < pool.getLength(); idx++) { Constant c = * pool.getConstant(idx); if(c != null && c.getTag() == Constants.CONSTANT_Class) { ConstantUtf8 * c1 = (ConstantUtf8) pool.getConstant(((ConstantClass)c).getNameIndex()); buf.setLength(0); * buf.append(c1.getBytes()); for(int n = 0; n < buf.length(); n++) { if(buf.charAt(n) == '/') { * buf.setCharAt(n, '.'); } } tempArray[size++] = buf.toString(); } } String[] dependencies = * new String[size]; System.arraycopy(tempArray, 0, dependencies, 0, size); return dependencies; * } */ }