Java tutorial
/* * RHQ Management Platform * Copyright (C) 2005-2008 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, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also 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 and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.pc; import java.beans.Introspector; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.security.auth.login.Configuration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.clientapi.agent.bundle.BundleAgentService; import org.rhq.core.clientapi.agent.configuration.ConfigurationAgentService; import org.rhq.core.clientapi.agent.content.ContentAgentService; import org.rhq.core.clientapi.agent.discovery.DiscoveryAgentService; import org.rhq.core.clientapi.agent.inventory.ResourceFactoryAgentService; import org.rhq.core.clientapi.agent.measurement.MeasurementAgentService; import org.rhq.core.clientapi.agent.operation.OperationAgentService; import org.rhq.core.clientapi.agent.support.SupportAgentService; import org.rhq.core.pc.agent.AgentRegistrar; import org.rhq.core.pc.agent.AgentService; import org.rhq.core.pc.agent.AgentServiceLifecycleListener; import org.rhq.core.pc.agent.AgentServiceStreamRemoter; import org.rhq.core.pc.bundle.BundleManager; import org.rhq.core.pc.configuration.ConfigurationManager; import org.rhq.core.pc.configuration.ConfigurationManagerInitializer; import org.rhq.core.pc.content.ContentManager; import org.rhq.core.pc.drift.DriftManager; import org.rhq.core.pc.event.EventManager; import org.rhq.core.pc.inventory.InventoryManager; import org.rhq.core.pc.inventory.ResourceContainer; import org.rhq.core.pc.inventory.ResourceFactoryManager; import org.rhq.core.pc.measurement.MeasurementManager; import org.rhq.core.pc.operation.OperationManager; import org.rhq.core.pc.plugin.PluginComponentFactory; import org.rhq.core.pc.plugin.PluginManager; import org.rhq.core.pc.support.SupportManager; import org.rhq.core.pluginapi.util.FileUtils; /** * This is the embeddable container that houses all plugins and the infrastructure that binds them together. It contains * all the managers such as {@link PluginManager} and {@link InventoryManager}. * * <p>This container is controlled by its lifecycle methods ({@link #initialize()} and {@link #shutdown()}. Prior to * initialization, this container's configuration should be set via * {@link #setConfiguration(PluginContainerConfiguration)}. If this is not done, a default configuration will be * created.</p> * * @author John Mazzitelli * @author Greg Hinkle */ public class PluginContainer implements ContainerService { private static final PluginContainer INSTANCE = new PluginContainer(); private final Log log = LogFactory.getLog(PluginContainer.class); private static final class NullRebootRequestListener implements RebootRequestListener { @Override public void reboot() { } } /** * Invoked by the plugin container immediately after it is initialized */ public static interface InitializationListener { /** * Notifies the listener that the plugin container has been initialized. This method executes in the same * thread in which {@link PluginContainer#initialize()} is executing. */ void initialized(); } /** * Invoked by the plugin container immediately after it is shutdown */ public static interface ShutdownListener { /** * Notifies the listener that the plugin container has been shutdown. This method executes in the same * thread in which {@link PluginContainer#shutdown()} is executing. */ void shutdown(); } // our management interface private PluginContainerMBeanImpl mbean; private PluginContainerConfiguration configuration; private String version; private boolean started = false; private PluginManager pluginManager; private PluginComponentFactory pluginComponentFactory; private InventoryManager inventoryManager; private MeasurementManager measurementManager; private ConfigurationManager configurationManager; private OperationManager operationManager; private ResourceFactoryManager resourceFactoryManager; private ContentManager contentManager; private EventManager eventManager; private SupportManager supportManager; private BundleManager bundleManager; private DriftManager driftManager; private Collection<AgentServiceLifecycleListener> agentServiceListeners = new LinkedHashSet<AgentServiceLifecycleListener>(); private AgentServiceStreamRemoter agentServiceStreamRemoter = null; private AgentRegistrar agentRegistrar = null; private RebootRequestListener rebootListener = new NullRebootRequestListener(); // this is to prevent race conditions on startup between components from all the different managers private ReadWriteLock rwLock = new ReentrantReadWriteLock(); private Map<String, InitializationListener> initListeners = new HashMap<String, InitializationListener>(); private Map<String, ShutdownListener> shutdownListeners = new HashMap<String, ShutdownListener>(); private Object initListenersLock = new Object(); private Object shutdownListenersLock = new Object(); /** * Returns the singleton instance. * * @return the plugin container */ public static PluginContainer getInstance() { return INSTANCE; } private PluginContainer() { // for why we need to do this, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6727821 try { Configuration.getConfiguration(); } catch (Throwable t) { } } /** * Sets this plugin container's configuration which also provides configuration settings for all the internal * services. * * @param configuration */ public void setConfiguration(PluginContainerConfiguration configuration) { this.configuration = configuration; } /** * Adds a listener that will be notified whenever an {@link AgentService} hosted within this plugin container is * started or stopped. * * @param listener */ public void addAgentServiceLifecycleListener(AgentServiceLifecycleListener listener) { agentServiceListeners.add(listener); } /** * Removes the given listener such that it will no longer receive {@link AgentService} notifications. * * @param listener */ public void removeAgentServiceLifecycleListener(AgentServiceLifecycleListener listener) { agentServiceListeners.remove(listener); } /** * Returns a remoter object that can be used to remote streams. If <code>null</code>, the plugin container will not * be able to remote streams to external clients, as in the case when the plugin container is not running inside an * agent (i.e. embedded mode). * * @return remoter the object that will prepare a stream to be accessed by remote clients (may be <code>null</code>) */ public AgentServiceStreamRemoter getAgentServiceStreamRemoter() { return agentServiceStreamRemoter; } /** * Adds a remoter object that is responsible for remoting streams. If <code>null</code>, the plugin container will * not be able to remote streams to external clients, as in the case when the plugin container is not running inside * an agent (i.e. embedded mode). * * @param streamRemoter */ public void setAgentServiceStreamRemoter(AgentServiceStreamRemoter streamRemoter) { agentServiceStreamRemoter = streamRemoter; } /** * Sets the given registrar as the object responsible for registering the plugin container with a remote server. If * <code>null</code>, the plugin container will not be considered running in an agent containing needing to be * registered with a remote server. * * @return the object that can be used to register this plugin container (may be <code>null</code>) */ public AgentRegistrar getAgentRegistrar() { return agentRegistrar; } /** * Sets the given registrar as the object responsible for registering the plugin container with a remote server. If * <code>null</code>, the plugin container will not be considered running in an agent containing needing to be * registered with a remote server. * * @param registrar */ public void setAgentRegistrar(AgentRegistrar registrar) { agentRegistrar = registrar; } /** * If the plugin container has been initialized and plugins have started work, this returns <code>true</code>. * * @return <code>true</code> if the plugin container was initialized and started; <code>false</code> otherwise */ public boolean isStarted() { Lock lock = obtainReadLock(); try { return started; } finally { releaseLock(lock); } } /** * Initializes the plugin container which initializes all internal managers. Once initialized, all plugins will be * activated, metrics will begin getting collected and automatic discovery will start. * * <p>Note that if no configuration was {@link #setConfiguration(PluginContainerConfiguration) set} prior to this * method being called, a default configuration will be created and used.</p> * * <p>If the plugin container has already been initialized, this method does nothing and returns.</p> */ public void initialize() { Lock lock = obtainWriteLock(); try { if (!started) { version = PluginContainer.class.getPackage().getImplementationVersion(); log.info("Initializing Plugin Container" + ((version != null) ? (" v" + version) : "") + "..."); if (configuration == null) { configuration = new PluginContainerConfiguration(); } purgeTmpDirectoryContents(); if (configuration.isStartManagementBean()) { mbean = new PluginContainerMBeanImpl(this); mbean.register(); } ResourceContainer.initialize(); pluginManager = new PluginManager(); pluginComponentFactory = new PluginComponentFactory(); inventoryManager = new InventoryManager(); measurementManager = new MeasurementManager(); configurationManager = new ConfigurationManager(); operationManager = new OperationManager(); resourceFactoryManager = new ResourceFactoryManager(); contentManager = new ContentManager(); eventManager = new EventManager(); supportManager = new SupportManager(); bundleManager = new BundleManager(); driftManager = new DriftManager(); startContainerService(pluginManager); startContainerService(pluginComponentFactory); startContainerService(inventoryManager); startContainerService(measurementManager); startContainerService(configurationManager); startContainerService(operationManager); startContainerService(resourceFactoryManager); startContainerService(contentManager); startContainerService(eventManager); startContainerService(supportManager); startContainerService(bundleManager); startContainerService(driftManager); started = true; log.info("Plugin Container initialized."); } } finally { releaseLock(lock); } synchronized (initListenersLock) { if (started) { for (InitializationListener listener : initListeners.values()) { listener.initialized(); } } } return; } /** * Shuts down the plugin container and all its internal services. If the plugin container has already been shutdown, * this method does nothing and returns. */ public void shutdown() { Lock lock = obtainWriteLock(); try { if (started) { log.info("Plugin container is being shutdown..."); if (mbean != null) { mbean.unregister(); mbean = null; } boolean isInsideAgent = configuration.isInsideAgent(); driftManager.shutdown(); bundleManager.shutdown(); supportManager.shutdown(); eventManager.shutdown(); contentManager.shutdown(); resourceFactoryManager.shutdown(); operationManager.shutdown(); configurationManager.shutdown(); measurementManager.shutdown(); inventoryManager.shutdown(); pluginComponentFactory.shutdown(); pluginManager.shutdown(); agentServiceListeners.clear(); agentServiceListeners = new LinkedHashSet<AgentServiceLifecycleListener>(); agentServiceStreamRemoter = null; agentRegistrar = null; purgeTmpDirectoryContents(); ResourceContainer.shutdown(); driftManager = null; bundleManager = null; supportManager = null; eventManager = null; contentManager = null; resourceFactoryManager = null; operationManager = null; configurationManager = null; measurementManager = null; inventoryManager = null; pluginComponentFactory = null; pluginManager = null; configuration = null; started = false; log.info("Plugin container is now shutdown."); // we typically do not want to do this if embedded somewhere other than the agent VM if (isInsideAgent) { cleanMemory(); } } } finally { releaseLock(lock); } synchronized (shutdownListenersLock) { if (!started) { for (ShutdownListener listener : shutdownListeners.values()) { listener.shutdown(); } } } return; } /** * Does things that help the garbage collector clean up the memory. * Only call this after the plugin container has been shutdown. */ private void cleanMemory() { Introspector.flushCaches(); LogFactory.releaseAll(); // for why we need to do this, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6727821 try { Configuration.setConfiguration(null); } catch (Throwable t) { } System.gc(); } private void purgeTmpDirectoryContents() { try { FileUtils.purge(configuration.getTemporaryDirectory(), false); } catch (IOException e) { log.warn("Failed to purge contents of temporary directory - cause: " + e); } } private void startContainerService(ContainerService containerService) { log.debug("Starting and configuring container service: " + containerService.getClass().getSimpleName()); containerService.setConfiguration(configuration); AgentService agentService = null; if (containerService instanceof AgentService) { agentService = (AgentService) containerService; agentService.setAgentServiceStreamRemoter(agentServiceStreamRemoter); for (AgentServiceLifecycleListener agentServiceListener : agentServiceListeners) { agentService.addLifecycleListener(agentServiceListener); } if (containerService instanceof ConfigurationManager) { ConfigurationManagerInitializer initializer = new ConfigurationManagerInitializer(); initializer.initialize((ConfigurationManager) containerService); } } containerService.initialize(); if (agentService != null) { agentService.notifyLifecycleListenersOfNewState(AgentService.LifecycleState.STARTED); } } private Lock obtainReadLock() { // try to obtain the lock, but if we can't get the lock in 60 seconds, // keep going. The PC is usually fine within seconds after its initializes, // so not getting this lock within 60 seconds probably isn't detrimental. // But if there is a deadlock, blocking forever here would be detrimental, // so we do not do it. We'll just log a warning and let the thread keep going. Lock readLock = rwLock.readLock(); try { if (!readLock.tryLock(60L, TimeUnit.SECONDS)) { String msg = "There may be a deadlock in the plugin container."; //noinspection ThrowableInstanceNeverThrown log.warn(msg, new Throwable(msg)); readLock = null; } } catch (InterruptedException e) { readLock = null; } return readLock; } private Lock obtainWriteLock() { // try to obtain the lock, but if we can't get the lock in 60 seconds, // keep going. The PC is usually fine within seconds after its initializes, // so not getting this lock within 60 seconds probably isn't detrimental. // But if there is a deadlock, blocking forever here would be detrimental, // so we do not do it. We'll just log a warning and let the thread keep going. Lock writeLock = rwLock.writeLock(); try { if (!writeLock.tryLock(60L, TimeUnit.SECONDS)) { String msg = "There may be a deadlock in the plugin container."; //noinspection ThrowableInstanceNeverThrown log.warn(msg, new Throwable(msg)); writeLock = null; } } catch (InterruptedException e) { writeLock = null; } return writeLock; } private void releaseLock(Lock lock) { if (lock != null) { lock.unlock(); } } // The methods below return the actual manager implementation objects. // Only those objects inside the plugin container should be calling these getXXXManager() methods. public PluginManager getPluginManager() { Lock lock = obtainReadLock(); try { return pluginManager; } finally { releaseLock(lock); } } public PluginComponentFactory getPluginComponentFactory() { Lock lock = obtainReadLock(); try { return pluginComponentFactory; } finally { releaseLock(lock); } } public InventoryManager getInventoryManager() { Lock lock = obtainReadLock(); try { return inventoryManager; } finally { releaseLock(lock); } } public ConfigurationManager getConfigurationManager() { Lock lock = obtainReadLock(); try { return configurationManager; } finally { releaseLock(lock); } } public MeasurementManager getMeasurementManager() { Lock lock = obtainReadLock(); try { return measurementManager; } finally { releaseLock(lock); } } public OperationManager getOperationManager() { Lock lock = obtainReadLock(); try { return operationManager; } finally { releaseLock(lock); } } public ResourceFactoryManager getResourceFactoryManager() { Lock lock = obtainReadLock(); try { return resourceFactoryManager; } finally { releaseLock(lock); } } public ContentManager getContentManager() { Lock lock = obtainReadLock(); try { return contentManager; } finally { releaseLock(lock); } } public EventManager getEventManager() { Lock lock = obtainReadLock(); try { return eventManager; } finally { releaseLock(lock); } } public SupportManager getSupportManager() { Lock lock = obtainReadLock(); try { return supportManager; } finally { releaseLock(lock); } } public BundleManager getBundleManager() { Lock lock = obtainReadLock(); try { return bundleManager; } finally { releaseLock(lock); } } public DriftManager getDriftManager() { Lock lock = obtainReadLock(); try { return driftManager; } finally { releaseLock(lock); } } // The methods below return the manager implementations wrapped in their remote client interfaces. // External clients to the plugin container should probably use these rather than the getXXXManager() methods. public DiscoveryAgentService getDiscoveryAgentService() { return getInventoryManager(); } public ConfigurationAgentService getConfigurationAgentService() { return getConfigurationManager(); } public MeasurementAgentService getMeasurementAgentService() { return getMeasurementManager(); } public OperationAgentService getOperationAgentService() { return getOperationManager(); } public ResourceFactoryAgentService getResourceFactoryAgentService() { return getResourceFactoryManager(); } public ContentAgentService getContentAgentService() { return getContentManager(); } public SupportAgentService getSupportAgentService() { return getSupportManager(); } public BundleAgentService getBundleAgentService() { return getBundleManager(); } public boolean isInsideAgent() { return (this.configuration != null && this.configuration.isInsideAgent()); } public void setRebootRequestListener(RebootRequestListener listener) { rebootListener = listener; } public void notifyRebootRequestListener() { rebootListener.reboot(); } /** * Add the callback listener to notify when the plugin container is initialized. If this method is invoked and * the PC is already initialized, then <code>listener</code> will be invoked immediately. * * @param name associated with the listener * @param listener The callback object to notify. If a listener with the supplied name is registered, it * will be replaced with the newly supplied listner. */ public void addInitializationListener(String name, InitializationListener listener) { synchronized (initListenersLock) { initListeners.put(name, listener); if (started) { listener.initialized(); } } } /** * Add the callback listener to notify when the plugin container is shutdown. Unlike * {@link #addInitializationListener(String, InitializationListener)} the <code>listener</code> will * not be invoked immediately if the PC is already shutdown. It will only be invoked on future shutdowns. * * @param name associated with the listener * @param listener The callback object to notify. If a listener with the supplied name is registered, it * will be replaced with the newly supplied listner. */ public void addShutdownListener(String name, ShutdownListener listener) { synchronized (shutdownListenersLock) { shutdownListeners.put(name, listener); } } }