/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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; either version 2.1 of * the License, or (at your option) any later version. * * 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.console.manager; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import javax.management.MBeanServer; import javax.management.Notification; import javax.management.NotificationFilter; import javax.management.NotificationListener; import javax.management.ObjectName; import javax.naming.InitialContext; import org.jboss.console.manager.interfaces.ConsolePlugin; import org.jboss.console.manager.interfaces.ManageableResource; import org.jboss.console.manager.interfaces.ResourceTreeNode; import org.jboss.console.manager.interfaces.TreeInfo; import org.jboss.console.manager.interfaces.TreeNode; import org.jboss.console.manager.interfaces.TreeNodeMenuEntry; import org.jboss.console.manager.interfaces.impl.DefaultTreeInfo; import org.jboss.console.manager.interfaces.impl.HttpLinkTreeAction; import org.jboss.console.manager.interfaces.impl.MBeanAction; import org.jboss.console.manager.interfaces.impl.MBeanResource; import org.jboss.console.manager.interfaces.impl.SeparatorTreeNodeMenuEntry; import org.jboss.console.manager.interfaces.impl.SimpleTreeNodeMenuEntryImpl; import org.jboss.console.navtree.RefreshTreeAction; import org.jboss.jmx.adaptor.rmi.RMIRemoteMBeanProxy; import org.jboss.system.Registry; import org.jboss.system.ServiceMBeanSupport; import org.jboss.util.naming.Util; /** * @jmx:mbean extends="org.jboss.system.ServiceMBean" */ public class PluginManager extends ServiceMBeanSupport implements PluginManagerMBean, NotificationListener { // Constants ----------------------------------------------------- public static String PLUGIN_MANAGER_NAME = null; // Attributes ---------------------------------------------------- protected ArrayList plugins = new ArrayList (); protected ManageableResource bootstrapResource = null; public String jndiName = "console/PluginManager"; protected long treeVersion = 0; protected HashMap currentTrees = new HashMap(); protected String mainLogoUrl = "/web-console/images/jboss.gif"; protected String mainLinkUrl = "http://www.jboss.org/forums/"; protected boolean enableShutdown = true; // Static -------------------------------------------------------- // Constructors -------------------------------------------------- public PluginManager () { } // Public -------------------------------------------------------- public void createService () throws Exception { this.bootstrapResource = new MBeanResource (this.getServiceName (), this.getClass ().toString ()); } public void startService () throws Exception { bindProxyInJndi (); PLUGIN_MANAGER_NAME = this.getServiceName().toString(); Registry.bind(PLUGIN_MANAGER_NAME, this); initNotificationReception (); } public void stopService () { Registry.unbind(this.getServiceName().toString()); } /** * send a message * @jmx:managed-operation */ public void registerPlugin (String consolePluginClassName) throws Exception { Class pluginClass = Thread.currentThread ().getContextClassLoader (). loadClass (consolePluginClassName); ConsolePlugin plugin = (ConsolePlugin)pluginClass.newInstance (); this.registerPlugin (plugin); } /** * send a message * @jmx:managed-operation */ public synchronized void registerPlugin (ConsolePlugin plugin) { plugins.add (plugin); regenerateAdminTree(); } /** * send a message * @jmx:managed-operation */ public synchronized void unregisterPlugin (ConsolePlugin plugin) { plugins.remove (plugin); regenerateAdminTree(); } /** * @jmx:managed-operation */ public synchronized void regenerateAdminTree () { // remove all cached trees // currentTrees.clear(); } /** * @jmx:managed-operation */ public synchronized void regenerateAdminTreeForProfile (String profile) { // remove cached tree for profile (if any) // currentTrees.remove(profile); } /** * @jmx:managed-operation */ public synchronized TreeInfo getTreeForProfile (String profile) { TreeInfo currentTree = (TreeInfo)currentTrees.get(profile); if (currentTree == null) { HashSet resourcesToManage = new HashSet (); TreeInfo result = new DefaultTreeInfo (); ArrayList pluginsSubset = getPluginsSubsetForProfile (profile); HashSet resourcesAlreadyScanned = new HashSet (); result.setRootResources (new ManageableResource[] {bootstrapResource}); // Bootstrap tree creation // resourcesToManage.add (bootstrapResource); while (resourcesToManage.size () > 0) { ManageableResource currentResource = (ManageableResource)resourcesToManage.iterator ().next (); // pre-clean resources environment // resourcesToManage.remove (currentResource); resourcesAlreadyScanned.add (currentResource); Iterator iter = getTreesForResource(currentResource, profile, pluginsSubset); while (iter.hasNext ()) { TreeNode subTree = (TreeNode)iter.next (); result.addTreeToResource (currentResource, subTree); HashSet subResources = findSubResources (subTree); if (subResources != null && subResources.size () > 0) { Iterator subsRes = subResources.iterator (); while (subsRes.hasNext ()) { ManageableResource subRes = (ManageableResource)subsRes.next (); if (!resourcesAlreadyScanned.contains (subRes)) resourcesToManage.add (subRes); } } } } this.treeVersion++; result.setTreeVersion (this.treeVersion); try { TreeNodeMenuEntry[] base = new TreeNodeMenuEntry[] { new SimpleTreeNodeMenuEntryImpl ("Update tree", new RefreshTreeAction (false)), new SimpleTreeNodeMenuEntryImpl ("Force update tree", new RefreshTreeAction (true)), }; if (enableShutdown) { result.setRootMenus (new TreeNodeMenuEntry[] { base[0], base[1], new SeparatorTreeNodeMenuEntry (), new SimpleTreeNodeMenuEntryImpl ("Shutdown JBoss instance", new MBeanAction (new ObjectName("jboss.system:type=Server"), "shutdown", new Object[0], new String[0]) ), new SimpleTreeNodeMenuEntryImpl ("Shutdown and Restart JBoss instance", new MBeanAction (new ObjectName("jboss.system:type=Server"), "exit", new Object[] {new Integer (10)}, new String[] {"int"}) ), new SimpleTreeNodeMenuEntryImpl ("HALT and Restart JBoss instance", new MBeanAction (new ObjectName("jboss.system:type=Server"), "halt", new Object[] {new Integer (10)}, new String[] {"int"}) ) } ); } else { result.setRootMenus (base); } result.setHomeAction(new HttpLinkTreeAction (this.mainLinkUrl)); result.setIconUrl (this.mainLogoUrl); } catch (Exception bla) {} currentTree = result; currentTrees.put(profile, currentTree); } return currentTree; } /** * Only return the tree if the actual version is bigger than the known version * @jmx:managed-operation */ public synchronized TreeInfo getUpdateTreeForProfile (String profile, long knownVersion) { TreeInfo currentTree = (TreeInfo)currentTrees.get(profile); if (this.treeVersion > knownVersion || currentTree==null) return getTreeForProfile (profile); else return null; } /** *@jmx:managed-attribute */ public MBeanServer getMBeanServer() { return this.server; } /** *@jmx:managed-attribute */ public ManageableResource getBootstrapResource() { return this.bootstrapResource; } /** *@jmx:managed-attribute */ public String getJndiName() { return jndiName; } /** *@jmx:managed-attribute */ public void setJndiName(String jndiName) { this.jndiName = jndiName; } /** *@jmx:managed-attribute */ public boolean isEnableShutdown() { return enableShutdown; } /** *@jmx:managed-attribute */ public void setEnableShutdown(boolean enableShutdown) { this.enableShutdown = enableShutdown; treeVersion++; } /** *@jmx:managed-attribute */ public String getMainLinkUrl() { return mainLinkUrl; } /** *@jmx:managed-attribute */ public void setMainLinkUrl(String mainLinkUrl) { this.mainLinkUrl = mainLinkUrl; treeVersion++; } /** *@jmx:managed-attribute */ public String getMainLogoUrl() { return mainLogoUrl; } /** *@jmx:managed-attribute */ public void setMainLogoUrl(String mainLogoUrl) { this.mainLogoUrl = mainLogoUrl; treeVersion++; } // Z implementation ---------------------------------------------- // NotificationListener implementation ---------------------------------------------- public void handleNotification (Notification notif, Object handback) { // Very simple implementation: could be optimized to minimize tree regeneration // (local invalidation for example)// // regenerateAdminTree (); } // Y overrides --------------------------------------------------- // Package protected --------------------------------------------- // Protected ----------------------------------------------------- protected Iterator getTreesForResource(ManageableResource res, String profile, ArrayList pluginsSubset) { ArrayList result = new ArrayList (); for (int i = 0; i < pluginsSubset.size(); i++) { ConsolePlugin cp = (ConsolePlugin)pluginsSubset.get(i); TreeNode node = null; try { node = cp.getSubTreeForResource (this, profile, res); } catch (Throwable t) { t.printStackTrace(); } if (node != null) result.add (node); } return result.iterator (); } protected ArrayList getPluginsSubsetForProfile (String profile) { ArrayList result = new ArrayList (); for (int i = 0; i < plugins.size(); i++) { ConsolePlugin cp = (ConsolePlugin)plugins.get(i); String [] set = cp.getSupportedProfiles (); if (java.util.Arrays.asList (set).contains (profile)) result.add (cp); } return result; } protected HashSet findSubResources (TreeNode tree) { HashSet result = new HashSet (); // first add the tree node itself if it is an instance // of an ResourceTreeNode // if (tree instanceof ResourceTreeNode) { result.add (((ResourceTreeNode)tree).getResource ()); } // then add local resources // ResourceTreeNode[] rns = tree.getNodeManagableResources (); if (rns != null && rns.length > 0) { // Then travel to sub-nodes resources... // for (int i=0; i<rns.length; i++) { result.add (rns[i].getResource ()); HashSet subResult = findSubResources (rns[i]); if (subResult != null && subResult.size () > 0) result.addAll (subResult); } } // ..and to other sub-nodes (which are not resources) // TreeNode[] ns = tree.getSubNodes (); if (ns != null && ns.length > 0) { for (int i=0; i<ns.length; i++) { HashSet subResult = findSubResources (ns[i]); if (subResult != null && subResult.size () > 0) result.addAll (subResult); } } return result; } protected void bindProxyInJndi () throws Exception { InitialContext ctx = new InitialContext (); Object proxy = RMIRemoteMBeanProxy.create (PluginManagerMBean.class, this.getServiceName (), this.getServer ()); Util.rebind (ctx, this.jndiName, proxy); } protected void initNotificationReception () throws Exception { ObjectName mbsDelegate = new ObjectName ("JMImplementation:type=MBeanServerDelegate"); NotificationFilter filter = new NotificationFilter () { public boolean isNotificationEnabled (Notification n) { return ( n.getType().equals("JMX.mbean.registered") || n.getType().equals("JMX.mbean.unregistered") ); } }; this.getServer().addNotificationListener(mbsDelegate, this, filter, null); } // Private ------------------------------------------------------- // Inner classes ------------------------------------------------- }