/* * RHQ Management Platform * Copyright (C) 2005-2010 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.plugin.pc.bundle; import java.io.File; import org.rhq.core.domain.configuration.definition.ConfigurationDefinition; import org.rhq.core.domain.configuration.definition.PropertyDefinition; import org.rhq.core.domain.configuration.definition.PropertyDefinitionSimple; import org.rhq.enterprise.server.bundle.BundleDistributionInfo; import org.rhq.enterprise.server.bundle.RecipeParseResults; import org.rhq.enterprise.server.plugin.pc.ServerPluginComponent; import org.rhq.enterprise.server.plugin.pc.ServerPluginEnvironment; import org.rhq.enterprise.server.plugin.pc.ServerPluginManager; import org.rhq.enterprise.server.xmlschema.generated.serverplugin.bundle.BundlePluginDescriptorType; import org.rhq.enterprise.server.xmlschema.generated.serverplugin.bundle.BundleType; /** * This loads in all bundle server plugins that can be found. You can obtain a loaded plugin's * {@link ServerPluginEnvironment environment}, including its classloader, from this object as well. * * @author John Mazzitelli */ public class BundleServerPluginManager extends ServerPluginManager { public BundleServerPluginManager(BundleServerPluginContainer pc) { super(pc); } // TODO override methods like initialize, shutdown, loadPlugin, etc. for custom bundle functionality @Override public void initialize() throws Exception { super.initialize(); } @Override protected void loadPlugin(ServerPluginEnvironment env, boolean enabled) throws Exception { if (enabled) { // validate some things about this plugin that are specific for bundle functionality BundlePluginDescriptorType descriptor = (BundlePluginDescriptorType) env.getPluginDescriptor(); BundleType bt = descriptor.getBundle(); if (bt == null || bt.getType() == null || bt.getType().length() == 0) { // if the xml parser did its job, this will probably never happen, but just in case, make sure there is // a non-null, valid bundle type name - we have other code that expects this to be true throw new Exception("The bundle plugin [" + env.getPluginKey().getPluginName() + "] did not specify a valid bundle type in its descriptor"); } ServerPluginComponent component = createServerPluginComponent(env); if (!(component instanceof BundleServerPluginFacet)) { throw new Exception("The bundle plugin [" + env.getPluginKey().getPluginName() + "] has an invalid component [" + component + "]. It does not implement [" + BundleServerPluginFacet.class + "]"); } } super.loadPlugin(env, enabled); } /** * Given the {@link BundleType#getName() name of a bundle type}, this will parse the given recipe by asking the * bundle plugin that can parse a recipe of that bundle type. * * @param bundleTypeName essentially identifies the kind of recipe that is to be parsed * @param recipe the recipe to parse * * @return the results of the parse * * @throws Exception if the recipe could not be parsed successfully */ public RecipeParseResults parseRecipe(String bundleTypeName, String recipe) throws Exception { if (bundleTypeName == null) { throw new IllegalArgumentException("bundleTypeName == null"); } if (recipe == null) { throw new IllegalArgumentException("recipe == null"); } // find the plugin environment for the bundle plugin of the given type ServerPluginEnvironment pluginEnv = null; for (ServerPluginEnvironment env : getPluginEnvironments()) { BundlePluginDescriptorType descriptor = (BundlePluginDescriptorType) env.getPluginDescriptor(); if (bundleTypeName.equals(descriptor.getBundle().getType())) { pluginEnv = env; break; } } if (pluginEnv == null) { throw new IllegalArgumentException("Bundle type [" + bundleTypeName + "] is not known to the system"); } // get the facet and call the parse method in the appropriate classloader String pluginName = pluginEnv.getPluginKey().getPluginName(); ServerPluginComponent component = getServerPluginComponent(pluginName); BundleServerPluginFacet facet = (BundleServerPluginFacet) component; // we know this cast will work because our loadPlugin ensured so getLog().debug("Bundle server plugin [" + pluginName + "] is parsing a bundle recipe"); ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(pluginEnv.getPluginClassLoader()); RecipeParseResults results = facet.parseRecipe(recipe); ensureDisplayNameIsSet(results); return results; } finally { Thread.currentThread().setContextClassLoader(originalContextClassLoader); } } /** * Given just a recipe, this will attempt to parse the given recipe by asking all the * bundle plugins to see if any can parse it successfully. If the recipe cannot be * parsed by any plugin, an exception is thrown, otherwise, results are returned. * * @param recipe the recipe to parse * * @return the results of the parse, which also includes the bundle type * * @throws Exception if the recipe could not be parsed successfully */ public BundleDistributionInfo parseRecipe(String recipe) throws Exception { if (recipe == null) { throw new IllegalArgumentException("recipe == null"); } BundleDistributionInfo info = null; for (ServerPluginEnvironment env : getPluginEnvironments()) { BundlePluginDescriptorType descriptor = (BundlePluginDescriptorType) env.getPluginDescriptor(); // get the facet and see if this plugin can deal with the recipe String pluginName = env.getPluginKey().getPluginName(); ServerPluginComponent component = getServerPluginComponent(pluginName); BundleServerPluginFacet facet = (BundleServerPluginFacet) component; // we know this cast will work because our loadPlugin ensured so getLog().debug("Bundle server plugin [" + pluginName + "] is parsing a recipe"); ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(env.getPluginClassLoader()); try { RecipeParseResults results = facet.parseRecipe(recipe); info = new BundleDistributionInfo(recipe, results, null); info.setBundleTypeName(descriptor.getBundle().getType()); break; } catch (UnknownRecipeException ure) { // the recipe is not a type that the plugin can handle, go on to the next info = null; } } finally { Thread.currentThread().setContextClassLoader(originalContextClassLoader); } } if (null == info) { throw new IllegalArgumentException("Invalid recipe not recognized by any deployed server bundle plugin."); } ensureDisplayNameIsSet(info.getRecipeParseResults()); return info; } /** * Given an bundle distribution file, this will find the appropriate server side plugin that can process it * and will ask that plugin to crack open the bundle distribution file and return information about it. * * An bundle distribution file is a zip file that contains a recipe and 0, 1 or more bundle files. * * @param distributionFile * @return the information gleened by cracking open the bundle distribution file and examining its contents * @throws Exception if the bundle distribution file could not be processed successfully */ public BundleDistributionInfo processBundleDistributionFile(File distributionFile) throws Exception { if (null == distributionFile) { throw new IllegalArgumentException("bundleDistributionFile == null"); } // find the bundle plugin that can handle this distribution and get the distro info BundleDistributionInfo info = null; for (ServerPluginEnvironment env : getPluginEnvironments()) { BundlePluginDescriptorType descriptor = (BundlePluginDescriptorType) env.getPluginDescriptor(); // get the facet and see if this plugin can deal with the distro String pluginName = env.getPluginKey().getPluginName(); ServerPluginComponent component = getServerPluginComponent(pluginName); BundleServerPluginFacet facet = (BundleServerPluginFacet) component; // we know this cast will work because our loadPlugin ensured so getLog().debug( "Bundle server plugin [" + pluginName + "] is parsing a distribution file [" + distributionFile + "]"); ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(env.getPluginClassLoader()); try { info = facet.processBundleDistributionFile(distributionFile); info.setBundleTypeName(descriptor.getBundle().getType()); break; } catch (UnknownRecipeException ure) { // the recipe is not a type that the plugin can handle, go on to the next info = null; } } finally { Thread.currentThread().setContextClassLoader(originalContextClassLoader); } } if (null == info) { throw new IllegalArgumentException( "Invalid bundle distribution file. BundleType/Recipe not recognized by any deployed server bundle plugin."); } ensureDisplayNameIsSet(info.getRecipeParseResults()); return info; } private void ensureDisplayNameIsSet(RecipeParseResults recipeParseResults) { if (recipeParseResults != null && recipeParseResults.getConfigurationDefinition() != null) { ConfigurationDefinition configDef = recipeParseResults.getConfigurationDefinition(); for (PropertyDefinition propDef : configDef.getPropertyDefinitions().values()) { if (propDef instanceof PropertyDefinitionSimple) { if (propDef.getDisplayName() == null) { propDef.setDisplayName(propDef.getName()); } } } } return; } }