/* * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2004-2010], VMware, Inc. * This file is part of Hyperic. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. */ package org.hyperic.hq.product; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hyperic.hq.product.pluginxml.PluginData; import org.hyperic.util.PluginLoader; import org.hyperic.util.config.ConfigResponse; import org.hyperic.util.config.ConfigSchema; /** * This class implements common functionality of the GenericPluginManager * interface: * - maintain a registry of plugins * - shutdown() propagated to all plugins */ public abstract class PluginManager { private final String OS; private final String OS_SUFFIX; protected Map<String, GenericPlugin> plugins = Collections.synchronizedMap(new HashMap<String, GenericPlugin>()); private Map pluginInfo = Collections.synchronizedMap(new HashMap()); private Properties props = null; protected Log log = null; private PluginManager parent = null; public PluginManager() { this(new Properties()); } public PluginManager(Properties props) { log = LogFactory.getLog(this.getClass().getName()); this.props = props; OS = HypericOperatingSystem.getInstance().getName(); OS_SUFFIX = " " + OS; } public abstract String getName(); public void init(PluginManager parent) throws PluginException { if (ProductPluginManager.DEBUG_LIFECYCLE) { log.debug("init " + getName()); } this.parent = parent; } public void shutdown() throws PluginException { synchronized (plugins) { for (Map.Entry<String, GenericPlugin> entry : this.plugins.entrySet()) { GenericPlugin plugin = entry.getValue(); try { plugin.shutdown(); } catch (PluginException e) { log.error(plugin.getName() + ".shutdown() failed", e); } } this.plugins.clear(); } if (ProductPluginManager.DEBUG_LIFECYCLE) { log.debug("shutdown"); } } public Properties getProperties() { return this.props; } public String getProperty(String key, String defVal) { return getProperties().getProperty(key, defVal); } public String getProperty(String key) { return getProperty(key, System.getProperty(key)); } public boolean isPropertyEnabled(String key, boolean defVal) { String val = getProperty(key); if (val == null) { return defVal; } return "true".equals(val); } public boolean isPropertyEnabled(String key) { return isPropertyEnabled(key, false); } public PluginManager getParent() { return this.parent; } protected void mergeConfigSchema(PluginManager pm, ConfigSchema schema, TypeInfo info, ConfigResponse config) { try { GenericPlugin plugin = pm.getPlugin(info.getName()); List options = plugin.getConfigSchema(info, config).getOptions(); schema.addOptions(options); } catch (PluginNotFoundException e) { //ok } } //XXX PluginLoader.setClassLoader here? public ConfigSchema getConfigSchema(String plugin, TypeInfo info, ConfigResponse config) throws PluginNotFoundException { return getPlugin(plugin).getConfigSchema(info, config); } public ConfigSchema getConfigSchema(String pluginName, String platformName, String typeName, TypeInfo info, ConfigResponse config) throws PluginNotFoundException { String fullTypeName = typeName.equals(platformName) ? platformName : (typeName + " " + platformName); return getConfigSchema(fullTypeName, info, config); } private String getServicePluginImpl(GenericPlugin plugin) { return plugin.getTypeProperty("SERVICE_" + getName().toUpperCase() + "_PLUGIN"); } private GenericPlugin getPluginExtension(String name) { String platformName = name; boolean isPlatformPlugin = false; if (name.endsWith(OS_SUFFIX)) { //XXX bleh. isPlatformPlugin = true; name = name.substring(0, name.length()-OS_SUFFIX.length()); } PluginData.ServiceExtension ext = PluginData.getServiceExtension(name); if (ext == null) { return null; } ServiceTypeInfo service = ext.service; String serverName = service.getServerName(); GenericPlugin serverPlugin; String msg = " plugin for service '" + name + "' from server '" + serverName + "'"; try { serverPlugin = getPlugin(serverName); log.debug("Created" + msg); } catch (PluginNotFoundException e) { log.debug("PluginNotFound creating" + msg); return null; } String implClass = ext.data.getPlugin(getName(), service.getName()); if (implClass == null) { String defaultImpl = getServicePluginImpl(serverPlugin); //did not configure <plugin type="$type" class"..."/> if (getName().equals(ProductPlugin.TYPE_MEASUREMENT)) { //only default measurement plugin to the server's //very unlikely the server control plugin will work //for a service. if (defaultImpl == null) { implClass = serverPlugin.getClass().getName(); } else { implClass = defaultImpl; } } else if (getName().equals(ProductPlugin.TYPE_CONTROL)) { //allow server types to define the default service impl //<property name="SERVICE.CONTROL" // value="${package}.JBossServiceControlPlugin"/> if (ext.data.getControlActions(service.getName()) != null) { implClass = defaultImpl; } } else { return null; } } if (implClass == null) { return null; } log.debug("Using " + implClass + " " + getName() + " plugin from " + serverPlugin.getName() + " for " + service.getName()); String[] mergeProps = { MeasurementPlugin.PROP_TEMPLATE_CONFIG, }; for (int i=0; i<mergeProps.length; i++) { String key = mergeProps[i]; String value = ext.data.getProperty(key); if (value == null) { value = serverPlugin.data.getProperty(key); if (value != null) { ext.data.setProperty(key, value); } } } //create instance of the plugin using the server type's ClassLoader GenericPlugin plugin = ProductPlugin.getPlugin(serverPlugin, implClass, getName(), service); if (plugin == null) { log.error("Failed to create " + msg); return null; } ProductPluginManager ppm = (ProductPluginManager)getParent(); plugin.data = ext.data; plugin.setName(service.getName()); plugin.setTypeInfo(service); //setPluginInfo from the ProductPlugin String productName = plugin.data.getName(); PluginInfo info = ppm.getPluginInfo(productName); setPluginInfo(plugin.getName(), info); try { ppm.registerTypePlugin(this, info, plugin, service); } catch (PluginExistsException e) { log.debug("PluginExists creating" + msg); return null; } if (isPlatformPlugin) { return this.plugins.get(platformName); } return plugin; } public GenericPlugin getPlugin(String name) throws PluginNotFoundException { GenericPlugin plugin = this.plugins.get(name); if (plugin == null) { if (name!=null && ((plugin = getPluginExtension(name)) != null)) { return plugin; } String msg = getName() + " plugin name=" + name + " not found"; throw new PluginNotFoundException(msg); } return plugin; } public GenericPlugin getPlatformPlugin(String name) throws PluginNotFoundException { return getPlugin(name + OS_SUFFIX); } public GenericPlugin getPlatformPlugin(String os, String name) throws PluginNotFoundException { return getPlugin(name + " " + os); } public void removePlugin(String name) throws PluginException, PluginNotFoundException { if (ProductPluginManager.DEBUG_LIFECYCLE) { log.debug("removePlugin=" + name); } GenericPlugin plugin = getPlugin(name); this.plugins.remove(name); try { plugin.shutdown(); } catch (PluginException e) { throw new PluginException(e.getMessage(), e); } } public boolean isRegistered(String name) { return this.plugins.get(name) != null; } public void registerPlugin(GenericPlugin plugin) throws PluginException, PluginExistsException { registerPlugin(plugin, null); } public void registerPlugin(String name, GenericPlugin plugin) throws PluginException, PluginExistsException { plugin.setName(name); registerPlugin(plugin); } public void registerPlugin(GenericPlugin plugin, ConfigResponse response) throws PluginException, PluginExistsException { String pluginName = plugin.getName(); if ((pluginName == null) || (pluginName.length() < 1)) { String msg = "malformed name '" + pluginName + "' for plugin instance: " + plugin.getClass().getName(); throw new PluginException(msg); } if (this.plugins.get(pluginName) != null) { throw new PluginExistsException("Plugin name=" + pluginName + " already exists"); } //put before init() since init() may register a proxy //who's init() method checks if the proxy is alredy registered this.plugins.put(pluginName, plugin); boolean setClassLoader = PluginLoader.setClassLoader(plugin); try { plugin.init(this); if (response != null) { plugin.configure(response); } } catch (PluginException e) { this.plugins.remove(pluginName); throw e; } finally { if (setClassLoader) { PluginLoader.resetClassLoader(plugin); } } if (ProductPluginManager.DEBUG_LIFECYCLE) { log.debug("registerPlugin=" + pluginName); } } public void updatePlugin(GenericPlugin plugin, ConfigResponse response) throws PluginException, PluginNotFoundException { if (ProductPluginManager.DEBUG_LIFECYCLE) { log.debug("updatePlugin=" + plugin.getName()); } removePlugin(plugin.getName()); try { registerPlugin(plugin, response); } catch (PluginExistsException e) { //this will never happen. throw new PluginException("plugin was not removed?"); } } public GenericPlugin createPlugin(String name, String type, ConfigResponse config) throws PluginException, PluginExistsException, PluginNotFoundException { return createPlugin(name, getPlugin(type), config); } public GenericPlugin createPlugin(String name, GenericPlugin pluginType) throws PluginException, PluginExistsException { return createPlugin(name, pluginType, null); } public GenericPlugin createPlugin(String name, GenericPlugin pluginType, ConfigResponse config) throws PluginException, PluginExistsException { GenericPlugin plugin; try { plugin = pluginType.getClass().newInstance(); } catch (Exception e) { throw new PluginException(e.getMessage(), e); } plugin.data = pluginType.data; plugin.setName(name); plugin.setPluginVersion(pluginType.getPluginVersion()); plugin.setTypeInfo(pluginType.getTypeInfo()); registerPlugin(plugin, config); PluginInfo info = getPluginInfo(pluginType.getName()); if (info != null) { setPluginInfo(name, new PluginInfo(name, info)); } return plugin; } public Map<String, GenericPlugin> getPlugins() { return this.plugins; } /** * @return Map of plugins registered for the given platform. */ public Map getPlatformPlugins(String os) { os = " " + os; HashMap found = new HashMap(); Map plugins = getPlugins(); synchronized (plugins) { for (Iterator it = plugins.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry)it.next(); String name = (String)entry.getKey(); if (name.endsWith(os)) { found.put(name, entry.getValue()); } } } return found; } /** * @return Map of plugins registered for the current platform. */ public Map getPlatformPlugins() { return getPlatformPlugins(OS); } //match all plugins instances from the same jar public List getPlugins(PluginInfo info) { ArrayList found = new ArrayList(); synchronized (pluginInfo) { for (Iterator it = this.pluginInfo.entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry)it.next(); PluginInfo pi = (PluginInfo)entry.getValue(); if (pi.matches(info)) { found.add(pi.name); } } } return found; } public void setPluginInfo(String name, PluginInfo info) { this.pluginInfo.put(name, info); } public PluginInfo getPluginInfo(String name) { return (PluginInfo)this.pluginInfo.get(name); } protected String classNotFoundMessage(NoClassDefFoundError e) { return "Plugin class not found: " + e.getMessage() + " (invalid classpath or corrupt plugin jar)"; } }