Package org.apache.excalibur.instrument.manager

Source Code of org.apache.excalibur.instrument.manager.DefaultInstrumentManager

/*

============================================================================
                   The Apache Software License, Version 1.1
============================================================================
Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
Redistribution and use in source and binary forms, with or without modifica-
tion, are permitted provided that the following conditions are met:
1. Redistributions of  source code must  retain the above copyright  notice,
    this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any, must
    include  the following  acknowledgment:  "This product includes  software
    developed  by the  Apache Software Foundation  (http://www.apache.org/)."
    Alternately, this  acknowledgment may  appear in the software itself,  if
    and wherever such third-party acknowledgments normally appear.
4. The names "Jakarta", "Avalon", "Excalibur" and "Apache Software Foundation" 
    must not be used to endorse or promote products derived from this  software
    without  prior written permission. For written permission, please contact
    apache@apache.org.
5. Products  derived from this software may not  be called "Apache", nor may
    "Apache" appear  in their name,  without prior written permission  of the
    Apache Software Foundation.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS  FOR A PARTICULAR  PURPOSE ARE  DISCLAIMED.  IN NO  EVENT SHALL  THE
APACHE SOFTWARE  FOUNDATION  OR ITS CONTRIBUTORS  BE LIABLE FOR  ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL,  EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLU-
DING, BUT NOT LIMITED TO, PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE, DATA, OR  PROFITS; OR BUSINESS  INTERRUPTION)  HOWEVER CAUSED AND ON
ANY  THEORY OF LIABILITY,  WHETHER  IN CONTRACT,  STRICT LIABILITY,  OR TORT
(INCLUDING  NEGLIGENCE OR  OTHERWISE) ARISING IN  ANY WAY OUT OF THE  USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
This software  consists of voluntary contributions made  by many individuals
on  behalf of the Apache Software  Foundation. For more  information on the
Apache Software Foundation, please see <http://www.apache.org/>.
*/
package org.apache.excalibur.instrument.manager;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;

import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.framework.configuration.DefaultConfigurationSerializer;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.excalibur.instrument.AbstractInstrument;
import org.apache.excalibur.instrument.CounterInstrument;
import org.apache.excalibur.instrument.Instrument;
import org.apache.excalibur.instrument.InstrumentManager;
import org.apache.excalibur.instrument.Instrumentable;
import org.apache.excalibur.instrument.ValueInstrument;
import org.apache.excalibur.instrument.manager.interfaces.InstrumentManagerClient;
import org.apache.excalibur.instrument.manager.interfaces.NoSuchInstrumentException;
import org.apache.excalibur.instrument.manager.interfaces.NoSuchInstrumentSampleException;
import org.apache.excalibur.instrument.manager.interfaces.NoSuchInstrumentableException;

/**
*
* @author <a href="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
* @version CVS $Revision: 1.5 $ $Date: 2003/02/25 16:28:16 $
* @since 4.1
*/
public class DefaultInstrumentManager
    extends AbstractLogEnabled
    implements Configurable, Initializable, Disposable, InstrumentManager,
        Instrumentable, Runnable
{
    public static final String INSTRUMENT_TOTAL_MEMORY = "total-memory";
    public static final String INSTRUMENT_FREE_MEMORY = "free-memory";
    public static final String INSTRUMENT_MEMORY = "memory";
    public static final String INSTRUMENT_ACTIVE_THREAD_COUNT = "active-thread-count";
   
    /** The name used to identify this InstrumentManager. */
    private String m_name;

    /** The description of this InstrumentManager. */
    private String m_description;
   
    /** Configuration for the InstrumentManager */
    private Configuration m_configuration;
   
    /** List of configured connectors. */
    private ArrayList m_connectors = new ArrayList();

    /** State file. */
    private File m_stateFile;

    /** Save state interval. */
    private long m_stateInterval;

    /** Last time that the state was saved. */
    private long m_lastStateSave;

    /** Semaphore for actions which must be synchronized */
    private Object m_semaphore = new Object();

    /** HashMap of all of the registered InstrumentableProxies by their keys. */
    private HashMap m_instrumentableProxies = new HashMap();

    /** Optimized array of the InstrumentableProxies. */
    private InstrumentableProxy[] m_instrumentableProxyArray;

    /** Optimized array of the InstrumentableDescriptorLocals. */
    private InstrumentableDescriptorLocal[] m_instrumentableDescriptorArray;

    /** List of leased InstrumentSamples. */
    private ArrayList m_leasedInstrumentSamples = new ArrayList();
   
    /** Optimized array of the leased InstrumentSamples. */
    private InstrumentSample[] m_leasedInstrumentSampleArray;
   
    /**
     * Thread used to keep the instruments published by the InstrumentManager
     *  up to date.
     */
    private Thread m_runner;

    /** Instrumentable Name assigned to this Instrumentable */
    private String m_instrumentableName = "instrument-manager";

    /** Instrument used to profile the total memory. */
    private ValueInstrument m_totalMemoryInstrument;

    /** Instrument used to profile the free memory. */
    private ValueInstrument m_freeMemoryInstrument;

    /** Instrument used to profile the in use memory. */
    private ValueInstrument m_memoryInstrument;

    /** Instrument used to profile the active thread count of the JVM. */
    private ValueInstrument m_activeThreadCountInstrument;
   
    /** State Version. */
    private int m_stateVersion;

    /*---------------------------------------------------------------
     * Constructors
     *-------------------------------------------------------------*/
    /**
     * Creates a new DefaultInstrumentManager.
     *
     * @param name The name used to identify this InstrumentManager.  Should not
     *             contain any spaces or periods.
     *
     * @deprecated Name should be set in the instrument configuration file.
     */
    public DefaultInstrumentManager( String name )
    {
        this();
    }
   
    /**
     * Creates a new DefaultInstrumentManager.
     */
    public DefaultInstrumentManager()
    {
        // Initialize the Instrumentable elements.
        m_totalMemoryInstrument = new ValueInstrument( INSTRUMENT_TOTAL_MEMORY );
        m_freeMemoryInstrument = new ValueInstrument( INSTRUMENT_FREE_MEMORY );
        m_memoryInstrument = new ValueInstrument( INSTRUMENT_MEMORY );
        m_activeThreadCountInstrument = new ValueInstrument( INSTRUMENT_ACTIVE_THREAD_COUNT );
    }

    /*---------------------------------------------------------------
     * Configurable Methods
     *-------------------------------------------------------------*/
    /**
     * Initializes the configured instrumentables.
     *
     * @param configuration InstrumentManager configuration.
     *
     * @throws ConfigurationException If there are any configuration problems.
     */
    public void configure( Configuration configuration )
        throws ConfigurationException
    {
        synchronized( m_semaphore )
        {
            m_configuration = configuration;

            // Look for a configured name and description
            m_name = configuration.getChild( "name" ).getValue( "instrument-manager" );
            m_description = configuration.getChild( "description" ).getValue( m_name );
           
            // Create a logger to use with the connectors
            Logger connLogger = getLogger().getChildLogger( "connector" );
           
            // Configure the connectors
            Configuration connectorsConf = configuration.getChild( "connectors" );
            Configuration[] connectorConfs =
                connectorsConf.getChildren( "connector" );
            for( int i = 0; i < connectorConfs.length; i++ )
            {
                Configuration connectorConf = connectorConfs[ i ];
                String className = connectorConf.getAttribute( "class" );
                // Handle aliases
                if ( className.equals( "altrmi" ) )
                {
                    className = "org.apache.excalibur.instrument.manager.altrmi."
                        + "InstrumentManagerAltrmiConnector";
                }
               
                // Look for the connector class and create an instance.
                try
                {
                    Class clazz = Class.forName( className );
                    InstrumentManagerConnector connector =
                        (InstrumentManagerConnector)clazz.newInstance();
                   
                    // Initialize the new connector
                    connector.setInstrumentManager( this );
                    ContainerUtil.enableLogging( connector, connLogger );
                    ContainerUtil.configure( connector, connectorConf );
                    ContainerUtil.start( connector );
                   
                    m_connectors.add( connector );
                }
                catch ( Exception e )
                {
                    throw new ConfigurationException( "Unable to create connector because: "
                        + e );
                }
            }
           
            // Configure the instrumentables.
            Configuration instrumentablesConf =  configuration.getChild( "instrumentables" );
            Configuration[] instrumentableConfs =
                instrumentablesConf.getChildren( "instrumentable" );
            for( int i = 0; i < instrumentableConfs.length; i++ )
            {
                Configuration instrumentableConf = instrumentableConfs[ i ];
                String instrumentableName = instrumentableConf.getAttribute( "name" );

                InstrumentableProxy instrumentableProxy = new InstrumentableProxy(
                    this, null, instrumentableName, instrumentableName );
                instrumentableProxy.enableLogging( getLogger() );
                instrumentableProxy.configure( instrumentableConf );
                m_instrumentableProxies.put( instrumentableName, instrumentableProxy );

                // Clear the optimized arrays
                m_instrumentableProxyArray = null;
                m_instrumentableDescriptorArray = null;
            }

            // Configure the state file.
            Configuration stateFileConf = configuration.getChild( "state-file" );
            m_stateInterval = stateFileConf.getAttributeAsLong( "interval", 60000 );

            String stateFile = stateFileConf.getValue( null );
            if( stateFile != null )
            {
                m_stateFile = new File( stateFile );
                if( m_stateFile.exists() )
                {
                    try
                    {
                        loadStateFromFile( m_stateFile );
                    }
                    catch( Exception e )
                    {
                        getLogger().error(
                            "Unable to load the instrument manager state.  The configuration " +
                            "may have been corruptped.  A backup may have been made in the same " +
                            "directory when it was saved.", e );
                    }
                }
            }
        }
    }

    /*---------------------------------------------------------------
     * Initializable Methods
     *-------------------------------------------------------------*/
    /**
     * Initializes the InstrumentManager.
     *
     * @throws Exception If there were any problems initializing the object.
     */
    public void initialize()
        throws Exception
    {
        // Register the InstrumentManager as an Instrumentable.
        registerInstrumentable( this, getInstrumentableName() );

        if( m_runner == null )
        {
            m_runner = new Thread( this, "InstrumentManagerRunner" );
            m_runner.start();
        }
    }

    /*---------------------------------------------------------------
     * Disposable Methods
     *-------------------------------------------------------------*/
    /**
     * Disposes the InstrumentManager.
     */
    public void dispose()
    {
        if( m_runner != null )
        {
            m_runner = null;
        }
       
        // Shutdown the connectors
        for ( Iterator iter = m_connectors.iterator(); iter.hasNext(); )
        {
            InstrumentManagerConnector connector = (InstrumentManagerConnector)iter.next();
            try
            {
                ContainerUtil.stop( connector );
                ContainerUtil.dispose( connector );
            }
            catch ( Exception e )
            {
                getLogger().error( "Encountered an unexpected error shutting down a connector", e );
            }
        }

        saveState();
    }

    /*---------------------------------------------------------------
     * InstrumentManager Methods
     *-------------------------------------------------------------*/
    /**
     * Instrumentable to be registered with the instrument manager.  Should be
     *  called whenever an Instrumentable is created.  The '.' character is
     *  used to denote a child Instrumentable and can be used to register the
     *  instrumentable at a specific point in an instrumentable hierarchy.
     *
     * @param instrumentable Instrumentable to register with the InstrumentManager.
     * @param instrumentableName The name to use when registering the Instrumentable.
     *
     * @throws Exception If there were any problems registering the Instrumentable.
     */
    public void registerInstrumentable( Instrumentable instrumentable, String instrumentableName )
        throws Exception
    {
        getLogger().debug( "Registering Instrumentable: " + instrumentableName );

        synchronized( m_semaphore )
        {
            // If the specified instrumentable name contains '.' chars then we need to
            //  make sure we register the instrumentable at the correct location, creating
            //  any parent instrumentables as necessary.
            int pos = instrumentableName.indexOf( '.' );
            if ( pos >= 0 )
            {
                String parentName = instrumentableName.substring( 0, pos );
                String childName =
                    instrumentableName.substring( pos + 1 );
                InstrumentableProxy instrumentableProxy =
                    (InstrumentableProxy)m_instrumentableProxies.get( parentName );
                if( instrumentableProxy == null )
                {
                    // This is a Instrumentable that has not been seen before.
                    instrumentableProxy = new InstrumentableProxy(
                        this, null, parentName, parentName );
                    instrumentableProxy.enableLogging( getLogger() );
                    // Do not call configure here because there is no configuration
                    //  for discovered instrumentables.
                    m_instrumentableProxies.put( parentName, instrumentableProxy );
   
                    // Clear the optimized arrays
                    m_instrumentableProxyArray = null;
                    m_instrumentableDescriptorArray = null;
   
                    // Recursively register all the Instruments in this and any child Instrumentables.
                    registerDummyInstrumentableInner(
                        instrumentable, instrumentableProxy, parentName, childName );
                }
                else
                {
                    // Additional Instrumentable instance.  Possible that new Instruments could be found.
                    registerDummyInstrumentableInner(
                        instrumentable, instrumentableProxy, parentName, childName );
                }
            } else {
                // If the instrumentable does not implement ThreadSafe, then it is possible that
                //  another one of its instance was already registered.  If so, then the
                //  Instruments will all be the same.  The new instances still need to be
                //  registered however.
                InstrumentableProxy instrumentableProxy =
                    (InstrumentableProxy)m_instrumentableProxies.get( instrumentableName );
                if( instrumentableProxy == null )
                {
                    // This is a Instrumentable that has not been seen before.
                    instrumentableProxy = new InstrumentableProxy(
                        this, null, instrumentableName, instrumentableName );
                    instrumentableProxy.enableLogging( getLogger() );
                    // Do not call configure here because there is no configuration
                    //  for discovered instrumentables.
                    m_instrumentableProxies.put( instrumentableName, instrumentableProxy );
   
                    // Clear the optimized arrays
                    m_instrumentableProxyArray = null;
                    m_instrumentableDescriptorArray = null;
   
                    // Recursively register all the Instruments in this and any child Instrumentables.
                    registerInstrumentableInner(
                        instrumentable, instrumentableProxy, instrumentableName );
                }
                else
                {
                    // Additional Instrumentable instance.  Possible that new Instruments could be found.
                    registerInstrumentableInner(
                        instrumentable, instrumentableProxy, instrumentableName );
                }
            }
        }
       
        stateChanged();
    }

   
    /*---------------------------------------------------------------
     * Methods
     *-------------------------------------------------------------*/
    /**
     * Returns the name used to identify this InstrumentManager.
     *
     * @return The name used to identify this InstrumentManager.
     */
    public String getName()
    {
        return m_name;
    }
   
    /**
     * Returns the description of this InstrumentManager.
     *
     * @return The description of this InstrumentManager.
     */
    public String getDescription()
    {
        return m_description;
    }
   
    /**
     * Returns an InstrumentableDescriptorLocal based on its name or the name
     *  of any of its children.
     *
     * @param instrumentableName Name of the Instrumentable being requested.
     *
     * @return A Descriptor of the requested Instrumentable.
     *
     * @throws NoSuchInstrumentableException If the specified Instrumentable
     *                                       does not exist.
     */
    public InstrumentableDescriptorLocal getInstrumentableDescriptor( String instrumentableName )
        throws NoSuchInstrumentableException
    {
        InstrumentableProxy proxy = getInstrumentableProxy( instrumentableName );
        if( proxy == null )
        {
            throw new NoSuchInstrumentableException(
                "No instrumentable can be found using name: " + instrumentableName );
        }

        return proxy.getDescriptor();
    }

    /**
     * Returns an array of Descriptors for the Instrumentables managed by this
     *  InstrumentManager.
     *
     * @return An array of Descriptors for the Instrumentables managed by this
     *  InstrumentManager.
     */
    public InstrumentableDescriptorLocal[] getInstrumentableDescriptors()
    {
        InstrumentableDescriptorLocal[] descriptors = m_instrumentableDescriptorArray;
        if( descriptors == null )
        {
            descriptors = updateInstrumentableDescriptorArray();
        }
        return descriptors;
    }
   
    /**
     * Searches the entire instrument tree an instrumentable with the given
     *  name.
     *
     * @param instrumentableName Name of the Instrumentable being requested.
     *
     * @return A Descriptor of the requested Instrumentable.
     *
     * @throws NoSuchInstrumentableException If the specified Instrumentable does
     *                                       not exist.
     */
    public InstrumentableDescriptorLocal locateInstrumentableDescriptor( String instrumentableName )
        throws NoSuchInstrumentableException
    {
        InstrumentableProxy instrumentableProxy =
            locateDeepestInstrumentableProxy( instrumentableName );
        if ( instrumentableProxy != null )
        {
            if ( instrumentableProxy.getName().equals( instrumentableName ) )
            {
                // Found what we were looking for
                return instrumentableProxy.getDescriptor();
            }
        }
       
        // Unable to locate the requested Instrumentable
        throw new NoSuchInstrumentableException(
            "No instrumentable can be found with the name: " + instrumentableName );
    }
   
    /**
     * Searches the entire instrument tree an instrument with the given name.
     *
     * @param instrumentName Name of the Instrument being requested.
     *
     * @return A Descriptor of the requested Instrument.
     *
     * @throws NoSuchInstrumentException If the specified Instrument does
     *                                   not exist.
     */
    public InstrumentDescriptorLocal locateInstrumentDescriptor( String instrumentName )
        throws NoSuchInstrumentException
    {
        InstrumentableProxy instrumentableProxy =
            locateDeepestInstrumentableProxy( instrumentName );
        if ( instrumentableProxy != null )
        {
            // Now look for the specified instrument
            InstrumentProxy instrumentProxy =
                instrumentableProxy.getInstrumentProxy( instrumentName );
            if ( instrumentProxy != null )
            {
                if ( instrumentProxy.getName().equals( instrumentName ) )
                {
                    // Found what we were looking for
                    return instrumentProxy.getDescriptor();
                }
            }
        }
       
        // Unable to locate the requested Instrument
        throw new NoSuchInstrumentException(
            "No instrument can be found with the name: " + instrumentName );
    }

    /**
     * Searches the entire instrument tree an instrument sample with the given
     *  name.
     *
     * @param sampleName Name of the Instrument Sample being requested.
     *
     * @return A Descriptor of the requested Instrument Sample.
     *
     * @throws NoSuchInstrumentSampleException If the specified Instrument
     *                                         Sample does not exist.
     */
    public InstrumentSampleDescriptorLocal locateInstrumentSampleDescriptor( String sampleName )
        throws NoSuchInstrumentSampleException
    {
        InstrumentableProxy instrumentableProxy =
            locateDeepestInstrumentableProxy( sampleName );
        if ( instrumentableProxy != null )
        {
            // Now look for the specified instrument
            InstrumentProxy instrumentProxy =
                instrumentableProxy.getInstrumentProxy( sampleName );
            if ( instrumentProxy != null )
            {
                // Now look for the specified sample
                InstrumentSample sample = instrumentProxy.getInstrumentSample( sampleName );
                if ( sample != null )
                {
                    if ( sample.getName().equals( sampleName ) )
                    {
                        // Found what we were looking for
                        return sample.getDescriptor();
                    }
                }
            }
        }
       
        // Unable to locate the requested Instrument Sample
        throw new NoSuchInstrumentException(
            "No instrument sample can be found with the name: " + sampleName );
    }
   
    /**
     * Returns the stateVersion of the instrument manager.  The state version
     *  will be incremented each time any of the configuration of the
     *  instrument manager or any of its children is modified.
     * Clients can use this value to tell whether or not anything has
     *  changed without having to do an exhaustive comparison.
     *
     * @return The state version of the instrument manager.
     */
    int getStateVersion()
    {
        return m_stateVersion;
    }
   
    /**
     * Invokes garbage collection.
     */
    public void invokeGarbageCollection()
    {
        System.gc();
    }
   
   

    /**
     * Loads the Instrument Manager state from the specified file.
     *
     * @param stateFile File to read the instrument manager's state from.
     *
     * @throws Exception if there are any problems loading the state.
     */
    public void loadStateFromFile( File stateFile )
        throws Exception
    {
        long now = System.currentTimeMillis();
        getLogger().debug( "Loading Instrument Manager state from: " +
            stateFile.getAbsolutePath() );

        FileInputStream is = new FileInputStream( stateFile );
        try
        {
            loadStateFromStream( is );
        }
        finally
        {
            is.close();
        }

        getLogger().debug( "Loading Instrument Manager state took " +
                           ( System.currentTimeMillis() - now ) + "ms." );
    }

    /**
     * Loads the Instrument Manager state from the specified stream.
     *
     * @param is Stream to read the instrument manager's state from.
     *
     * @throws Exception if there are any problems loading the state.
     */
    public void loadStateFromStream( InputStream is )
        throws Exception
    {
        // Ride on top of the Configuration classes to load the state.
        DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
        Configuration stateConfig = builder.build( is );

        loadStateFromConfiguration( stateConfig );
    }

    /**
     * Loads the Instrument Manager state from the specified Configuration.
     *
     * @param state Configuration object to load the state from.
     *
     * @throws ConfigurationException If there were any problems loading the
     *                                state.
     */
    public void loadStateFromConfiguration( Configuration state )
        throws ConfigurationException
    {
        Configuration[] instrumentableConfs = state.getChildren( "instrumentable" );
        for( int i = 0; i < instrumentableConfs.length; i++ )
        {
            Configuration instrumentableConf = instrumentableConfs[ i ];
            String instrumentableName = instrumentableConf.getAttribute( "name" );
            InstrumentableProxy instrumentableProxy = getInstrumentableProxy( instrumentableName );
            if( instrumentableProxy == null )
            {
                // The Instrumentable was in the state file, but has not yet
                //  been registered.  It is possible that it will be registered
                //  at a later time.  For now it needs to be created.
                instrumentableProxy = new InstrumentableProxy(
                    this, null, instrumentableName, instrumentableName );
                instrumentableProxy.enableLogging( getLogger() );
                m_instrumentableProxies.put( instrumentableName, instrumentableProxy );

                // Clear the optimized arrays
                m_instrumentableProxyArray = null;
                m_instrumentableDescriptorArray = null;
            }
           
            instrumentableProxy.loadState( instrumentableConf );
        }
       
        stateChanged();
    }

    /**
     * Saves the Instrument Manager's state to the specified file.  Any
     *  existing file is backed up before the save takes place and replaced
     *  in the event of an error.
     *
     * @param stateFile File to write the Instrument Manager's state to.
     *
     * @throws Exception if there are any problems saving the state.
     */
    public void saveStateToFile( File stateFile )
        throws Exception
    {
        long now = System.currentTimeMillis();
        getLogger().debug( "Saving Instrument Manager state to: " + stateFile.getAbsolutePath() );

        // First save the state to an in memory stream to shorten the
        //  period of time needed to write the data to disk.  This makes it
        //  less likely that the files will be left in a corrupted state if
        //  the JVM dies at the wrong time.
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        byte[] data;
        try
        {
            saveStateToStream( os );
            data = os.toByteArray();
        }
        finally
        {
            os.close();
        }
       
        // If the specified file exists, then rename it before we start writing.
        //  This makes it possible to recover from some errors.
        File renameFile = null;
        boolean success = false;
        if( stateFile.exists() )
        {
            renameFile = new File( stateFile.getAbsolutePath() + "." + now + ".backup" );
            stateFile.renameTo( renameFile );
        }
       
        // Write the data to the new file.
        FileOutputStream fos = new FileOutputStream( stateFile );
        try
        {
            fos.write( data );
            success = true;
        }
        finally
        {
            fos.close();
           
            if ( !success )
            {
                // Make sure that part of the file does not exist.
                stateFile.delete();
            }
           
            // Handle the backup file.
            if ( renameFile != null )
            {
                if ( success )
                {
                    // No longer need the backup.
                    renameFile.delete();
                }
                else
                {
                    // Need to replace the backup.
                    renameFile.renameTo( stateFile );
                }
            }
        }
       
        getLogger().debug( "Saving Instrument Manager state took " +
                           ( System.currentTimeMillis() - now ) + "ms." );
    }

    /**
     * Saves the Instrument Manager's state to the specified output stream.
     *
     * @param os Stream to write the Instrument Manager's state to.
     *
     * @throws Exception if there are any problems saving the state.
     */
    public void saveStateToStream( OutputStream os )
        throws Exception
    {
        Configuration stateConfig = saveStateToConfiguration();

        // Ride on top of the Configuration classes to save the state.
        DefaultConfigurationSerializer serializer = new DefaultConfigurationSerializer();
        serializer.setIndent( true );
        serializer.serialize( os, stateConfig );
    }

    /**
     * Returns the Instrument Manager's state as a Configuration object.
     *
     * @return The Instrument Manager's state as a Configuration object.
     */
    public Configuration saveStateToConfiguration()
    {
        DefaultConfiguration state = new DefaultConfiguration( "instrument-manager-state", "-" );

        InstrumentableProxy[] instrumentableProxies = m_instrumentableProxyArray;
        if( instrumentableProxies == null )
        {
            instrumentableProxies = updateInstrumentableProxyArray();
        }

        for( int i = 0; i < instrumentableProxies.length; i++ )
        {
            Configuration childState = instrumentableProxies[ i ].saveState();
            if ( childState != null )
            {
                state.addChild( childState );
            }
        }

        return state;
    }

    /*---------------------------------------------------------------
     * Instrumentable Methods
     *-------------------------------------------------------------*/
    /**
     * Sets the name for the Instrumentable.  The Instrumentable Name is used
     *  to uniquely identify the Instrumentable during the configuration of
     *  the InstrumentManager and to gain access to an InstrumentableDescriptor
     *  through the InstrumentManager.  The value should be a string which does
     *  not contain spaces or periods.
     * <p>
     * This value may be set by a parent Instrumentable, or by the
     *  InstrumentManager using the value of the 'instrumentable' attribute in
     *  the configuration of the component.
     *
     * @param name The name used to identify a Instrumentable.
     */
    public void setInstrumentableName( String name )
    {
        m_instrumentableName = name;
    }

    /**
     * Gets the name of the Instrumentable.
     *
     * @return The name used to identify a Instrumentable.
     */
    public String getInstrumentableName()
    {
        return m_instrumentableName;
    }

    /**
     * Obtain a reference to all the Instruments that the Instrumentable object
     *  wishes to expose.  All sampling is done directly through the
     *  Instruments as opposed to the Instrumentable interface.
     *
     * @return An array of the Instruments available for profiling.  Should
     *         never be null.  If there are no Instruments, then
     *         EMPTY_INSTRUMENT_ARRAY can be returned.  This should never be
     *         the case though unless there are child Instrumentables with
     *         Instruments.
     */
    public Instrument[] getInstruments()
    {
        return new Instrument[]
        {
            m_totalMemoryInstrument,
            m_freeMemoryInstrument,
            m_memoryInstrument,
            m_activeThreadCountInstrument
        };
    }

    /**
     * Any Object which implements Instrumentable can also make use of other
     *  Instrumentable child objects.  This method is used to tell the
     *  InstrumentManager about them.
     *
     * @return An array of child Instrumentables.  This method should never
     *         return null.  If there are no child Instrumentables, then
     *         EMPTY_INSTRUMENTABLE_ARRAY can be returned.
     */
    public Instrumentable[] getChildInstrumentables()
    {
        return Instrumentable.EMPTY_INSTRUMENTABLE_ARRAY;
    }

    /*---------------------------------------------------------------
     * Runnable Methods
     *-------------------------------------------------------------*/
    public void run()
    {
        while( m_runner != null )
        {
            try
            {
                Thread.sleep( 1000 );

                memoryInstruments();
                threadInstruments();
                testInstrumentSampleLeases();

                // Handle the state file if it is set
                long now = System.currentTimeMillis();
                if( now - m_lastStateSave >= m_stateInterval )
                {
                    saveState();
                }
            }
            catch( Throwable t )
            {
                getLogger().error( "Encountered an unexpected error.", t );
            }
        }
    }

    /*---------------------------------------------------------------
     * Methods
     *-------------------------------------------------------------*/
    /**
     * Saves the state to the current state file if configured.
     */
    private void saveState()
    {
        long now = System.currentTimeMillis();

        // Always set the time even if the save fails so that we don't thrash
        m_lastStateSave = now;

        if( m_stateFile == null )
        {
            return;
        }
       
        try
        {
            saveStateToFile( m_stateFile );
        }
        catch ( Exception e )
        {
            getLogger().warn( "Unable to save the Instrument Manager state", e );
        }
    }

    /**
     * Returns a InstrumentableDescriptor based on its name or the name of any
     *  of its children.
     *
     * @param instrumentableName Name of the Instrumentable being requested.
     *
     * @return A Proxy of the requested Instrumentable or null if not found.
     */
    private InstrumentableProxy getInstrumentableProxy( String instrumentableName )
    {
        String name = instrumentableName;
        while( true )
        {
            InstrumentableProxy proxy = (InstrumentableProxy)m_instrumentableProxies.get( name );
            if( proxy != null )
            {
                return proxy;
            }

            // Assume this is a child name and try looking with the parent name.
            int pos = name.lastIndexOf( '.' );
            if( pos > 0 )
            {
                name = name.substring( 0, pos );
            }
            else
            {
                return null;
            }
        }
    }
   
    /**
     * Given the name of an instrumentable proxy, locate the deepest child
     *  instrumentable given the name.  The name can be the name of an
     *  instrumentable or of any of its children.
     *
     * @param instrumentableName Fully qualified name of the instrumentable
     *                           being requested, or of any of its children.
     *
     * @return The requested instrumentable, or null if not found.
     */
    private InstrumentableProxy locateDeepestInstrumentableProxy( String instrumentableName )
    {
        InstrumentableProxy deepestProxy = null;
        // Start by obtaining a top level instrumentable
        InstrumentableProxy proxy = getInstrumentableProxy( instrumentableName );
       
        // Now attempt to locate a child instrumentable
        while ( proxy != null )
        {
            deepestProxy = proxy;
           
            proxy = deepestProxy.getChildInstrumentableProxy( instrumentableName );
        }
       
        return deepestProxy;
    }

    /**
     * Updates the Memory based Profile Points published by the InstrumentManager.
     */
    private void memoryInstruments()
    {
        // Avoid doing unneeded work if profile points are not being used.
        Runtime runtime = null;
        long totalMemory = -1;
        long freeMemory = -1;

        // Total Memory
        if( m_totalMemoryInstrument.isActive() )
        {
            runtime = Runtime.getRuntime();
            totalMemory = runtime.totalMemory();
            m_totalMemoryInstrument.setValue( (int)totalMemory );
        }

        // Free Memory
        if( m_freeMemoryInstrument.isActive() )
        {
            if( runtime == null )
            {
                runtime = Runtime.getRuntime();
            }
            freeMemory = runtime.freeMemory();
            m_freeMemoryInstrument.setValue( (int)freeMemory );
        }

        // In use Memory
        if( m_memoryInstrument.isActive() )
        {
            if( runtime == null )
            {
                runtime = Runtime.getRuntime();
            }
            if( totalMemory < 0 )
            {
                totalMemory = runtime.totalMemory();
            }
            if( freeMemory < 0 )
            {
                freeMemory = runtime.freeMemory();
            }
            m_memoryInstrument.setValue( (int)( totalMemory - freeMemory ) );
        }
    }

    /**
     * Updates the Thread based Profile Points published by the InstrumentManager.
     */
    private void threadInstruments()
    {
        if( m_activeThreadCountInstrument.isActive() )
        {
            // Get the top level thread group.
            ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
            ThreadGroup parent;
            while( ( parent = threadGroup.getParent() ) != null )
            {
                threadGroup = parent;
            }

            m_activeThreadCountInstrument.setValue( threadGroup.activeCount() );
        }
    }
   
    /**
     * Handles the maintenance of all Instrument Samples which have been leased
     *  by a client.  Any Samples whose leases which have expired are cleaned
     *  up.
     */
    private void testInstrumentSampleLeases()
    {
        long now = System.currentTimeMillis();
       
        InstrumentSample[] samples;
        synchronized( m_leasedInstrumentSamples )
        {
            samples = m_leasedInstrumentSampleArray;
            if ( samples == null )
            {
                m_leasedInstrumentSampleArray =
                    new InstrumentSample[ m_leasedInstrumentSamples.size() ];
                m_leasedInstrumentSamples.toArray( m_leasedInstrumentSampleArray );
                samples = m_leasedInstrumentSampleArray;
            }
        }
       
        for ( int i = 0; i < samples.length; i++ )
        {
            InstrumentSample sample = samples[i];
            long expire = sample.getLeaseExpirationTime();
            if ( now >= expire )
            {
                // The sample lease has expired.
                InstrumentProxy instrument = sample.getInstrumentProxy();
                instrument.removeInstrumentSample( sample );
                sample.expire();
               
                m_leasedInstrumentSamples.remove( sample );
                m_leasedInstrumentSampleArray = null;
            }
        }
    }
   
    /**
     * Registers an InstrumentSample which has been leased so that the
     *  Instrument Manager can efficiently purge it when it has expired.
     *
     * @param instrumentSample Leased InstrumentSample to register.
     */
    void registerLeasedInstrumentSample( InstrumentSample instrumentSample )
    {
        synchronized( m_leasedInstrumentSamples )
        {
            // Make sure that the sample is really leased.
            if ( instrumentSample.getLeaseExpirationTime() <= 0 )
            {
                throw new IllegalStateException( "Got an InstrumentSample that was not leased." );
            }
           
            // Make sure that it is not already in the list.
            if ( m_leasedInstrumentSamples.indexOf( instrumentSample ) < 0 )
            {
                m_leasedInstrumentSamples.add( instrumentSample );
                m_leasedInstrumentSampleArray = null;
            }
        }
    }
   
    /**
     * Updates the cached array of InstrumentableProxies taking
     *  synchronization into account.
     *
     * @return An array of the InstrumentableProxies.
     */
    private InstrumentableProxy[] updateInstrumentableProxyArray()
    {
        synchronized( m_semaphore )
        {
            m_instrumentableProxyArray = new InstrumentableProxy[ m_instrumentableProxies.size() ];
            m_instrumentableProxies.values().toArray( m_instrumentableProxyArray );

            // Sort the array.  This is not a performance problem because this
            //  method is rarely called and doing it here saves cycles in the
            //  client.
            Arrays.sort( m_instrumentableProxyArray, new Comparator()
                {
                    public int compare( Object o1, Object o2 )
                    {
                        return ((InstrumentableProxy)o1).getDescription().
                            compareTo( ((InstrumentableProxy)o2).getDescription() );
                    }
                   
                    public boolean equals( Object obj )
                    {
                        return false;
                    }
                } );
           
            return m_instrumentableProxyArray;
        }
    }

    /**
     * Updates the cached array of InstrumentableDescriptorLocals taking
     *  synchronization into account.
     *
     * @return An array of the InstrumentableDescriptors.
     */
    private InstrumentableDescriptorLocal[] updateInstrumentableDescriptorArray()
    {
        synchronized( m_semaphore )
        {
            if( m_instrumentableProxyArray == null )
            {
                updateInstrumentableProxyArray();
            }

            m_instrumentableDescriptorArray =
                new InstrumentableDescriptorLocal[ m_instrumentableProxyArray.length ];
            for( int i = 0; i < m_instrumentableProxyArray.length; i++ )
            {
                m_instrumentableDescriptorArray[ i ] = m_instrumentableProxyArray[ i ].getDescriptor();
            }

            return m_instrumentableDescriptorArray;
        }
    }

    /**
     * Called as a place holder to handle the registration of instrumentables
     *  that do not really exist.  This makes it possible to register
     *  instrumentables at arbitrary locations in the instrumentable hierarchy.
     *
     * @param instrumentable The instrumentable that was registered below a dummy
     *                       parent.
     * @param instrumentableProxy The proxy assigned to the current placeholder
     *                            instrumentable.
     * @param instrumentableName The name of the current placeholder
     *                           instrumentable.
     * @param childName The name of the child instrumentable to register.  May
     *                  contain further '.' characters.
     */
    private void registerDummyInstrumentableInner( Instrumentable instrumentable,
                                                   InstrumentableProxy instrumentableProxy,
                                                   String instrumentableName,
                                                   String childName )
        throws Exception
    {
        // If the specified instrumentable name contains '.' chars then we need to
        //  make sure we register the instrumentable at the correct location, creating
        //  any parent instrumentables as necessary.
        int pos = childName.indexOf( '.' );
        if ( pos >= 0 )
        {
            String newParentName = childName.substring( 0, pos );
            String newChildName =
                childName.substring( pos + 1 );
           
            String fullChildName = instrumentableName + "." + newParentName;
           
            getLogger().debug( "Registering Child Instrumentable: " + fullChildName );
           
            // See if a proxy exists for the child Instrumentable yet.
            InstrumentableProxy proxy =
                instrumentableProxy.getChildInstrumentableProxy( fullChildName );
            if( proxy == null )
            {
                proxy = new InstrumentableProxy(
                    this, instrumentableProxy, fullChildName, newParentName );
                proxy.enableLogging( getLogger() );
               
                instrumentableProxy.addChildInstrumentableProxy( proxy );
            }
           
            // Recurse to the child
            registerDummyInstrumentableInner( instrumentable, proxy, fullChildName, newChildName );
        }
        else
        {
            // The child does not contain and '.' characters, so we are at the correct location.
            String fullChildName = instrumentableName + "." + childName;
           
            getLogger().debug( "Registering Child Instrumentable: " + fullChildName );
           
            // See if a proxy exists for the child Instrumentable yet.
            InstrumentableProxy proxy =
                instrumentableProxy.getChildInstrumentableProxy( fullChildName );
            if( proxy == null )
            {
                proxy = new InstrumentableProxy(
                    this, instrumentableProxy, fullChildName, childName );
                proxy.enableLogging( getLogger() );
               
                instrumentableProxy.addChildInstrumentableProxy( proxy );
            }
           
            // Recurse to the child
            registerInstrumentableInner( instrumentable, proxy, fullChildName );
        }
    }

    /**
     * Examines a instrumentable and Registers all of its child Instrumentables
     *  and Instruments.
     * <p>
     * Only called when m_semaphore is locked.
     */
    private void registerInstrumentableInner( Instrumentable instrumentable,
                                              InstrumentableProxy instrumentableProxy,
                                              String instrumentableName )
        throws Exception
    {
        // Mark the instrumentable proxy as registered.
        instrumentableProxy.setRegistered();

        // Loop over the Instruments published by this Instrumentable
        Instrument[] instruments = instrumentable.getInstruments();
        for( int i = 0; i < instruments.length; i++ )
        {
            Instrument instrument = instruments[ i ];
            String instrumentName = instrument.getInstrumentName();
            String fullInstrumentName = instrumentableName + "." + instrumentName;

            getLogger().debug( "Registering Instrument: " + fullInstrumentName );

            // See if a proxy exists for the Instrument yet.
            InstrumentProxy proxy = instrumentableProxy.getInstrumentProxy( fullInstrumentName );
            if( proxy == null )
            {
                proxy = new InstrumentProxy(
                    instrumentableProxy, fullInstrumentName, instrumentName );
                proxy.enableLogging( getLogger() );

                // Set the type of the new InstrumentProxy depending on the
                //  class of the actual Instrument.
                if( instrument instanceof CounterInstrument )
                {
                    proxy.setType( InstrumentManagerClient.INSTRUMENT_TYPE_COUNTER );
                }
                else if( instrument instanceof ValueInstrument )
                {
                    proxy.setType( InstrumentManagerClient.INSTRUMENT_TYPE_VALUE );
                }
                else
                {
                    throw new ServiceException( fullInstrumentName, "Encountered an unknown "
                        + "Instrument type for the Instrument with key, "
                        + fullInstrumentName + ": " + instrument.getClass().getName() );
                }

                // Mark the instrument proxy as registered.
                proxy.setRegistered();
               
                // Store a reference to the proxy in the Instrument.
                ( (AbstractInstrument)instrument ).setInstrumentProxy( proxy );

                instrumentableProxy.addInstrumentProxy( proxy );
            }
            else
            {
                // Register the existing proxy with the Instrument.  Make sure that the
                //  type didn't change on us.
                if( instrument instanceof CounterInstrument )
                {
                    switch( proxy.getType() )
                    {
                        case InstrumentManagerClient.INSTRUMENT_TYPE_COUNTER:
                            // Type is the same.
                            // Store a reference to the proxy in the Instrument.
                            ( (AbstractInstrument)instrument ).setInstrumentProxy( proxy );
                            break;

                        case InstrumentManagerClient.INSTRUMENT_TYPE_NONE:
                            // Not yet set.  Created in configuration.
                            proxy.setType( InstrumentManagerClient.INSTRUMENT_TYPE_COUNTER );

                            // Store a reference to the proxy in the Instrument.
                            ( (AbstractInstrument)instrument ).setInstrumentProxy( proxy );
                            break;

                        default:
                            throw new ServiceException( instrumentName,
                                "Instruments of more than one type are assigned to name: "
                                + instrumentName );
                    }
                }
                else if( instrument instanceof ValueInstrument )
                {
                    switch( proxy.getType() )
                    {
                        case InstrumentManagerClient.INSTRUMENT_TYPE_VALUE:
                            // Type is the same.
                            // Store a reference to the proxy in the Instrument.
                            ( (AbstractInstrument)instrument ).setInstrumentProxy( proxy );
                            break;

                        case InstrumentManagerClient.INSTRUMENT_TYPE_NONE:
                            // Not yet set.  Created in configuration.
                            proxy.setType( InstrumentManagerClient.INSTRUMENT_TYPE_VALUE );

                            // Store a reference to the proxy in the Instrument.
                            ( (AbstractInstrument)instrument ).setInstrumentProxy( proxy );
                            break;

                        default:
                            throw new ServiceException( instrumentName,
                                "Instruments of more than one type are assigned to name: "
                                + instrumentName );
                    }
                }
                else
                {
                    throw new ServiceException( instrumentName, "Encountered an unknown Instrument "
                        + "type for the Instrument with name, " + instrumentName + ": "
                        + instrument.getClass().getName() );
                }
               
                // Mark the instrument proxy as registered.
                proxy.setRegistered();
            }
        }

        // Loop over the child Instrumentables published by this Instrumentable
        Instrumentable[] children = instrumentable.getChildInstrumentables();
        for ( int i = 0; i < children.length; i++ )
        {
            Instrumentable child = children[i];
           
            // Make sure that the child instrumentable name is set.
            String childName = child.getInstrumentableName();
            if( childName == null )
            {
                String msg = "The getInstrumentableName() method of a child Instrumentable of " +
                    instrumentableName + " returned null.  Child class: " +
                    child.getClass().getName();
                getLogger().debug( msg );
                throw new ServiceException( instrumentable.getClass().getName(), msg );
            }
           
            String fullChildName = instrumentableName + "." + childName;
           
            getLogger().debug( "Registering Child Instrumentable: " + fullChildName );
           
            // See if a proxy exists for the child Instrumentable yet.
            InstrumentableProxy proxy =
                instrumentableProxy.getChildInstrumentableProxy( fullChildName );
            if( proxy == null )
            {
                proxy = new InstrumentableProxy(
                    this, instrumentableProxy, fullChildName, childName );
                proxy.enableLogging( getLogger() );
               
                instrumentableProxy.addChildInstrumentableProxy( proxy );
            }
           
            // Recurse to the child
            registerInstrumentableInner( child, proxy, fullChildName );
        }
    }
   
    /**
     * Called whenever the state of the instrument manager is changed.
     */
    protected void stateChanged()
    {
        m_stateVersion++;
    }
}
TOP

Related Classes of org.apache.excalibur.instrument.manager.DefaultInstrumentManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.