All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.excalibur.instrument.manager.impl.DefaultInstrumentManagerImpl Maven / Gradle / Ivy

The newest version!
/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed  under the  License is distributed on an "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 * implied.
 * 
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.excalibur.instrument.manager.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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.DefaultConfigurationBuilder;
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.DefaultInstrumentManager;
import org.apache.excalibur.instrument.manager.DefaultInstrumentManagerConnector;
import org.apache.excalibur.instrument.manager.InstrumentableDescriptor;
import org.apache.excalibur.instrument.manager.InstrumentDescriptor;
import org.apache.excalibur.instrument.manager.InstrumentSampleDescriptor;
import org.apache.excalibur.instrument.manager.InstrumentSampleUtils;
import org.apache.excalibur.instrument.manager.NoSuchInstrumentException;
import org.apache.excalibur.instrument.manager.NoSuchInstrumentSampleException;
import org.apache.excalibur.instrument.manager.NoSuchInstrumentableException;

/**
 *
 * @author Avalon Development Team
 * @version CVS $Revision: 1.4 $ $Date: 2004/02/28 11:47:25 $
 * @since 4.1
 */
public class DefaultInstrumentManagerImpl
    extends AbstractLogEnabled
    implements Configurable, Initializable, Disposable, DefaultInstrumentManager,
        Instrumentable, Runnable
{
    /** The name used to identify this InstrumentManager. */
    private String m_name;

    /** The description of this InstrumentManager. */
    private String m_description;
    
    /** The maximum number of leased samples which will be allowed.  This is
     *   important to prevent denial of service attacks using connectors. */
    private int m_maxLeasedSamples;
    
    /** The maximum size of a leased sample.  This is important to prevent
     *   denial of service attacks using connectors. */
    private int m_maxLeasedSampleSize;
    
    /** The maximum amount of time that a lease will be granted for.  This is
     *   important to prevent denial of service attacks using connectors. */
    private long m_maxLeasedSampleLease;
    
    /** List of configured connectors. */
    private List 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 Map m_instrumentableProxies = new HashMap();

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

    /** Optimized array of the InstrumentableDescriptors. */
    private InstrumentableDescriptor[] m_instrumentableDescriptorArray;

    /** List of leased InstrumentSamples. */
    private List m_leasedInstrumentSamples = new ArrayList();
    
    /** Optimized array of the leased InstrumentSamples. */
    private InstrumentSample[] m_leasedInstrumentSampleArray;
    
    /** Logger dedicated to logging translations. */
    private Logger m_translationLogger;
    
    /** Map of all registered translations. */
    private Map m_nameTranslations = new HashMap();
    
    /** Optimized array of the registered translations. */
    private String[][] m_nameTranslationArray;
    
    /**
     * 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;
    
    /** Instrument to track the number of times registerInstrumentable is called. */
    private CounterInstrument m_registrationsInstrument;

    /** The Instrumentable count. */
    private int m_instrumentableCount;
    
    /** Instrument used to track the number of Instrumentables in the system. */
    private ValueInstrument m_instrumentablesInstrument;

    /** The Instrument count. */
    private int m_instrumentCount;

    /** Instrument used to track the number of Instruments in the system. */
    private ValueInstrument m_instrumentsInstrument;

    /** The Permanent Instrument Sample count. */
    private int m_permanentSampleCount;

    /** The Leased Instrument Sample count. */
    private int m_leasedSampleCount;

    /** Instrument used to track the number of Instrument samples in the system. */
    private ValueInstrument m_samplesInstrument;

    /** Instrument used to track the number of Leased Instrument samples in the system. */
    private ValueInstrument m_leasedSamplesInstrument;
    
    /** Instrument used to track the number of lease requests. */
    private CounterInstrument m_leaseRequestsInstrument;
    
    /** Instrument used to track the number of state saves performed. */
    private CounterInstrument m_stateSavesInstrument;
    
    /** Instrument used to track the time it takes to save the state. */
    private ValueInstrument m_stateSaveTimeInstrument;
    
    /** State Version. */
    private int m_stateVersion;

    /*---------------------------------------------------------------
     * Constructors
     *-------------------------------------------------------------*/
    /**
     * Creates a new DefaultInstrumentManagerImpl.
     *
     * @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 DefaultInstrumentManagerImpl( String name )
    {
        this();
    }
    
    /**
     * Creates a new DefaultInstrumentManagerImpl.
     */
    public DefaultInstrumentManagerImpl()
    {
        // Initialize the Instrumentable elements.
        m_totalMemoryInstrument = new ValueInstrument( "total-memory" );
        m_freeMemoryInstrument = new ValueInstrument( "free-memory" );
        m_memoryInstrument = new ValueInstrument( "memory" );
        m_activeThreadCountInstrument = new ValueInstrument( "active-thread-count" );
        m_registrationsInstrument = new CounterInstrument( "instrumentable-registrations" );
        m_instrumentablesInstrument = new ValueInstrument( "instrumentables" );
        m_instrumentsInstrument = new ValueInstrument( "instruments" );
        m_samplesInstrument = new ValueInstrument( "samples" );
        m_leasedSamplesInstrument = new ValueInstrument( "leased-samples" );
        m_leaseRequestsInstrument = new CounterInstrument( "lease-requests" );
        m_stateSavesInstrument = new CounterInstrument( "state-saves" );
        m_stateSaveTimeInstrument = new ValueInstrument( "state-save-time" );
    }

    /*---------------------------------------------------------------
     * 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
    {
        m_translationLogger = getLogger().getChildLogger( "translation" );
        
        // Register the InstrumentManager as an Instrumentable.  This must be done before
        //  the configuration and state file is loaded or our own instruments will not yet
        //  have proxies.
        try
        {
            registerInstrumentable( this, getInstrumentableName() );
        }
        catch ( Exception e )
        {
            // Should never happen
            throw new ConfigurationException(
                "Unable to register the InstrumentManager's own instruments.", e );
        }

        synchronized( m_semaphore )
        {
            // Look for a configured name and description
            m_name = configuration.getChild( "name" ).getValue( "instrument-manager" );
            m_description = configuration.getChild( "description" ).getValue( m_name );
            
            // Get configuration values which limit the leases that can be made.
            m_maxLeasedSamples =
                configuration.getChild( "max-leased-samples" ).getValueAsInteger( 256 );
            m_maxLeasedSampleSize =
                configuration.getChild( "max-leased-sample-size" ).getValueAsInteger( 2048 );
            m_maxLeasedSampleLease = 1000L *
                configuration.getChild( "max-leased-sample-lease" ).getValueAsInteger( 86400 );
            
            // Configure any translations
            Configuration translationsConf = configuration.getChild( "translations" );
            Configuration[] translationConfs = translationsConf.getChildren( "translation" );
            for( int i = 0; i < translationConfs.length; i++ )
            {
                Configuration translationConf = translationConfs[i];
                String source = translationConf.getAttribute( "source" );
                String target = translationConf.getAttribute( "target" );
                try
                {
                    registerNameTranslationInner( source, target );
                }
                catch ( IllegalArgumentException e )
                {
                    throw new ConfigurationException( e.getMessage(), translationConf );
                }
            }
            
            // 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" );

                // See if the instrumentable already exists.
                InstrumentableProxy instrumentableProxy =
                    (InstrumentableProxy)m_instrumentableProxies.get( instrumentableName );
                if ( instrumentableProxy == null )
                {
                    instrumentableProxy = new InstrumentableProxy(
                        this, null, instrumentableName, instrumentableName );
                    instrumentableProxy.enableLogging( getLogger() );
                    incrementInstrumentableCount();
                    m_instrumentableProxies.put( instrumentableName, instrumentableProxy );
    
                    // Clear the optimized arrays
                    m_instrumentableProxyArray = null;
                    m_instrumentableDescriptorArray = null;
                }
                // Always configure
                instrumentableProxy.configure( instrumentableConf );
            }

            // 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 )
                    {
                        String msg = "Unable to load the instrument manager state.  The "
                            + "configuration may have been corrupted.  A backup may have been "
                            + "made in the same directory when it was saved.";
                        
                        if ( getLogger().isDebugEnabled() )
                        {
                            getLogger().error( msg, e );
                        }
                        else
                        {
                            getLogger().error( msg + " : " + e.toString() );
                        }
                    }
                }
            }
            
            // 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( "http" ) )
                {
                    // Don't use InstrumentManagerAltrmiConnector.class.getName() because
                    //  the class is optional for the build.
                    className = "org.apache.excalibur.instrument.manager.http."
                        + "InstrumentManagerHTTPConnector";
                }
                
                // Look for the connector class and create an instance.
                try
                {
                    Class clazz = Class.forName( className );
                    DefaultInstrumentManagerConnector connector =
                        (DefaultInstrumentManagerConnector)clazz.newInstance();
                    
                    // Initialize the new connector
                    connector.setInstrumentManager( this );
                    ContainerUtil.enableLogging( connector, connLogger );
                    ContainerUtil.configure( connector, connectorConf );
                    ContainerUtil.start( connector );
                    if ( connector instanceof Instrumentable )
                    {
                        Instrumentable inst = (Instrumentable)connector;
                        registerInstrumentable( inst,
                            m_instrumentableName + ".connector." + inst.getInstrumentableName() );
                    }
                    
                    m_connectors.add( connector );
                }
                catch ( Exception e )
                {
                    String msg = "Unable to create connector because: " + e;
                    
                    // Was the optional flag set?
                    if ( connectorConf.getAttributeAsBoolean( "optional", true ) )
                    {
                        getLogger().warn( msg );
                    }
                    else
                    {
                        throw new ConfigurationException( msg );
                    }
                }
            }
        }
    }

    /*---------------------------------------------------------------
     * Initializable Methods
     *-------------------------------------------------------------*/
    /**
     * Initializes the InstrumentManager.
     *
     * @throws Exception If there were any problems initializing the object.
     */
    public void initialize()
        throws Exception
    {
        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(); )
        {
            DefaultInstrumentManagerConnector connector =
                (DefaultInstrumentManagerConnector)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 );
        
        m_registrationsInstrument.increment();

        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() );
                    incrementInstrumentableCount();
                    // 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() );
                    incrementInstrumentableCount();
                    // 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();
    }

    /*---------------------------------------------------------------
     * DefaultInstrumentManager Methods
     *-------------------------------------------------------------*/
    /**
     * Returns the name used to identify this DefaultInstrumentManager.
     *
     * @return The name used to identify this DefaultInstrumentManager.
     */
    public String getName()
    {
        return m_name;
    }
    
    /**
     * Returns the description of this DefaultInstrumentManager.
     *
     * @return The description of this DefaultInstrumentManager.
     */
    public String getDescription()
    {
        return m_description;
    }
    
    /**
     * Registers a name translation that will be applied to all named based
     *  lookups of instrumentables, instruments, and samples.   The more
     *  translations that are registered, the greater the impact on name
     *  based lookups will be.
     * 

* General operation of the instrument manager will not be affected as * collection on sample data is always done using direct object * references. *

* Translations can be registered for exact name matches, or for * the bases of names. Any translation which ends in a '.' will * imply a translation to any name beginning with that name base. * If the source ends with a '.' then the target must as well. * * @param source The source name or name base of the translation. * @param target The target name or name base of the translation. * * @throws IllegalArgumentException If the one but not both of the source * and target parameters end in '.'. */ public void registerNameTranslation( String source, String target ) throws IllegalArgumentException { synchronized( m_semaphore ) { registerNameTranslationInner( source, target ); } } /** * 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 Descriptor of the requested Instrumentable. * * @throws NoSuchInstrumentableException If the specified Instrumentable * does not exist. */ public InstrumentableDescriptor 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 * DefaultInstrumentManager. * * @return An array of InstrumentableDescriptors. */ public InstrumentableDescriptor[] getInstrumentableDescriptors() { InstrumentableDescriptor[] descriptors = m_instrumentableDescriptorArray; if( descriptors == null ) { descriptors = updateInstrumentableDescriptorArray(); } return descriptors; } /** * Searches the entire instrument tree for 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 InstrumentableDescriptor 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 for 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 InstrumentDescriptor 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 for 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 InstrumentSampleDescriptor 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 NoSuchInstrumentSampleException( "No instrument sample can be found with the name: " + sampleName ); } /** * Returns the stateVersion of the DefaultInstrumeManager. 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. */ public int getStateVersion() { return m_stateVersion; } /** * Invokes garbage collection. */ public void invokeGarbageCollection() { System.gc(); } /** * Returns the current number of leased samples. * * @return The current number of leased samples. */ public int getLeaseSampleCount() { return m_leasedSampleCount; } /** * Returns the maximum number of leased samples that will be approved. * * @return The maximum number of leased samples. */ public int getMaxLeasedSamples() { return m_maxLeasedSamples; } /** * Returns the maximum size of a leased sample. * * @return The maximum size of a leased sample. */ public int getMaxLeasedSampleSize() { return m_maxLeasedSampleSize; } /** * Returns the maximum number of milliseconds that a lease will be granted * for. * * @return The maximum lease length. */ public long getMaxLeasedSampleLease() { return m_maxLeasedSampleLease; } /*--------------------------------------------------------------- * 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. *

* 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, m_registrationsInstrument, m_instrumentablesInstrument, m_instrumentsInstrument, m_samplesInstrument, m_leasedSamplesInstrument, m_leaseRequestsInstrument, m_stateSavesInstrument, m_stateSaveTimeInstrument }; } /** * 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 ); } } } /*--------------------------------------------------------------- * State File Methods *-------------------------------------------------------------*/ /** * 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, stateFile.getCanonicalPath() ); } 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 { loadStateFromStream( is, null ); } /** * Loads the Instrument Manager state from the specified stream. * * @param is Stream to read the instrument manager's state from. * @param location The location of the stream. Used to improve the * usefulness of any exceptions thrown. * * @throws Exception if there are any problems loading the state. */ private void loadStateFromStream( InputStream is, String location ) throws Exception { // Ride on top of the Configuration classes to load the state. DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(); Configuration stateConfig; if ( location == null ) { stateConfig = builder.build( is ); } else { stateConfig = builder.build( is, location ); } 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 { loadStateFromConfiguration( state, null ); } private void loadStateFromConfiguration( Configuration state, String parentName ) throws ConfigurationException { // When loading state, the only thing that we are really interrested in are the samples. // Don't bother looking anything up until one is found. Doing it this way is also // critical to make name translations work correctly. // Drill down into Instrumentables and Instruments by recursing into this method. // This is not used by state files saved with version 2.2 or newer, but older // versions of the instrument manager saved their states in a tree structure. Configuration[] instrumentableConfs = state.getChildren( "instrumentable" ); for( int i = 0; i < instrumentableConfs.length; i++ ) { loadStateFromConfiguration( instrumentableConfs[i], instrumentableConfs[i].getAttribute( "name", null ) ); } Configuration[] instrumentConfs = state.getChildren( "instrument" ); for( int i = 0; i < instrumentConfs.length; i++ ) { loadStateFromConfiguration( instrumentConfs[i], instrumentConfs[i].getAttribute( "name", null ) ); } // Look for any samples. Configuration[] sampleConfs = state.getChildren( "sample" ); for( int i = 0; i < sampleConfs.length; i++ ) { Configuration sampleConf = sampleConfs[i]; // Obtain and translate the sample name. String sampleName = sampleConf.getAttribute( "name", null ); if ( sampleName == null ) { // The sample name was missing. This can happen for very old state files. // Build the name. if ( parentName == null ) { throw new ConfigurationException( "Unable to resolve sample name.", sampleConf ); } int sampleType = InstrumentSampleUtils.resolveInstrumentSampleType( sampleConf.getAttribute( "type" ) ); long sampleInterval = sampleConf.getAttributeAsLong( "interval" ); int sampleSize = sampleConf.getAttributeAsInteger( "size" ); sampleName = InstrumentSampleUtils.generateFullInstrumentSampleName( parentName, sampleType, sampleInterval, sampleSize ); } // Translate the resulting name. sampleName = getTranslatedName( sampleName ); // Before we do anything, decide how we want to handle the sample. long now = System.currentTimeMillis(); long leaseExpirationTime = sampleConf.getAttributeAsLong( "lease-expiration", 0 ); if ( leaseExpirationTime == 0 ) { // This is the saved state of a permanent sample. We only want to load it // if the instrument and sample exists. If it does not exist then it means // that the user changed the configuration so the sample is no longer // permanent. // Look for the existing instrument proxy. InstrumentProxy instrumentProxy = getInstrumentProxyForSample( sampleName, false ); if ( instrumentProxy == null ) { // The instrument did not exist, so we want to skip this state. getLogger().info( "Skipping old permantent sample from state due to missing instrument: " + sampleName ); } else { // The instrument exists, but the sample may not. That is decided within // the instrument. InstrumentSample sample = instrumentProxy.loadSampleState( sampleConf ); if ( sample == null ) { getLogger().info( "Skipping old permantent sample from state: " + sampleName ); } else { if ( getLogger().isDebugEnabled() ) { getLogger().debug( "Load permanent sample state: " + sampleName ); } } } } else if ( leaseExpirationTime > now ) { // This is a leased sample that has not yet expired. Even if the sample // or even its instrument does not yet exist, we want to go ahead and // create it. It is possible for instruments and samples to be created // a while after the application has been started. // Get the instrument. Will never return null. InstrumentProxy instrumentProxy = getInstrumentProxyForSample( sampleName, true ); // Load the sample state. instrumentProxy.loadSampleState( sampleConf ); if ( getLogger().isDebugEnabled() ) { getLogger().debug( "Load leased sample state : " + sampleName ); } } else { // This sample has expired since the state was saved. Do nothing. if ( getLogger().isDebugEnabled() ) { getLogger().debug( "Skip expired sample state: " + sampleName ); } } } // If anything was actually loaded then stateChanged() will be called from the samples. } /** * 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() ); // To make corruption as unlikely as possible, write the state file to a // temporary file. Only overwrite the previous state file when complete. File tempFile = new File( stateFile.getAbsolutePath() + "." + now + ".temp" ); boolean success = false; FileOutputStream fos = new FileOutputStream( tempFile ); try { saveStateToStream( fos ); success = true; } finally { fos.close(); File renameFile = null; try { if ( success ) { // Rename the old state file first of all. if ( stateFile.exists() ) { renameFile = new File( stateFile.getAbsolutePath() + "." + now + ".backup" ); if ( !stateFile.renameTo( renameFile ) ) { throw new IOException( "Unable to rename the old instrument state file from '" + stateFile.getAbsolutePath() + "' to '" + renameFile.getAbsolutePath() + "'" ); } } // Now rename the new temp state file to the final name. if ( !tempFile.renameTo( stateFile ) ) { if ( renameFile != null ) { // Attempt to restore the old state file. if ( !renameFile.renameTo( stateFile ) ) { // Failed for some reason. getLogger().error( "Unable to save the instrument state. The last known state " + "file is backed up as: " + renameFile.getAbsolutePath() ); // Clear the rename file so it does not get deleted. renameFile = null; } } throw new IOException( "Unable to rename the new instrument state file from '" + tempFile.getAbsolutePath() + "' to '" + stateFile.getAbsolutePath() + "'" ); } else { // Temp fle renamed, so clear its name. tempFile = null; } } } finally { // Delete the temp file if it still exists. if ( ( tempFile != null ) && tempFile.exists() ) { if ( !tempFile.delete() ) { getLogger().warn( "Unable to delete temporary state file: " + tempFile.getAbsolutePath() ); } } // Delete the rename file if it still exists. if ( ( renameFile != null ) && renameFile.exists() ) { if ( !renameFile.delete() ) { getLogger().warn( "Unable to delete temporary state file: " + renameFile.getAbsolutePath() ); } } } } 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 { // We used to create a big Configuration object and then write it to an output // stream. While that worked, on applications with lots of samples, it // could result in very large Configuration objects that would cause a large // spike in memory usage when the state was saved. // The new method writes directly to the file thus avoiding the need for any // significant amount of memory. PrintWriter out = new PrintWriter( new OutputStreamWriter( os, "UTF-8" ) ); // Output the XML headers and main node. out.println( "" ); out.println( "" ); InstrumentableProxy[] instrumentableProxies = m_instrumentableProxyArray; if( instrumentableProxies == null ) { instrumentableProxies = updateInstrumentableProxyArray(); } for( int i = 0; i < instrumentableProxies.length; i++ ) { instrumentableProxies[i].writeState( out ); } // Close off the main node. out.println( "" ); // We don't want to close the writer here or it will close the underlying stream // Do the next best thing by flushing to make sure that nothing is left unflushed // in writer buffers. out.flush(); } /*--------------------------------------------------------------- * Package Methods *-------------------------------------------------------------*/ /** * 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; } } } /** * Called whenever the state of the instrument manager is changed. */ void stateChanged() { m_stateVersion++; } /** * Called to increment the number of Instrumentables registered. */ void incrementInstrumentableCount() { int count; synchronized( m_semaphore ) { count = ++m_instrumentableCount; } m_instrumentablesInstrument.setValue( count ); } /** * Called to increment the number of Instruments registered. */ void incrementInstrumentCount() { int count; synchronized( m_semaphore ) { count = ++m_instrumentCount; } m_instrumentsInstrument.setValue( count ); } /** * Called to increment the number of Permanent Instrument Samples registered. */ void incrementPermanentSampleCount() { int count; synchronized( m_semaphore ) { count = ++m_permanentSampleCount + m_leasedSampleCount; } m_samplesInstrument.setValue( count ); } /** * Called to increment the number of Leased Instrument Samples registered. */ void incrementLeasedSampleCount() { int count; int leasedCount; synchronized( m_semaphore ) { leasedCount = ++m_leasedSampleCount; count = m_permanentSampleCount + m_leasedSampleCount; } m_samplesInstrument.setValue( count ); m_leasedSamplesInstrument.setValue( leasedCount ); } /** * Called to decrement the number of Leased Instrument Samples registered. */ void decrementLeasedSampleCount() { int count; int leasedCount; synchronized( m_semaphore ) { leasedCount = --m_leasedSampleCount; count = m_permanentSampleCount + m_leasedSampleCount; } m_samplesInstrument.setValue( count ); m_leasedSamplesInstrument.setValue( leasedCount ); } /** * Increment the lease requests. */ void incrementLeaseRequests() { m_leaseRequestsInstrument.increment(); } /*--------------------------------------------------------------- * Private 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 ) { String msg = "Unable to save the Instrument Manager state"; if ( getLogger().isDebugEnabled() ) { getLogger().warn( msg, e ); } else { getLogger().warn( msg + " : " + e.toString() ); } } m_stateSavesInstrument.increment(); m_stateSaveTimeInstrument.setValue( (int)( System.currentTimeMillis() - now ) ); } /** * Updates the cached array of registered name translations taking * synchronization into account. * * @return An array of the String[2]s representing the registered name * translations. */ private String[][] updateNameTranslationArray() { synchronized( m_semaphore ) { String[][] nameTranslations = new String[m_nameTranslations.size()][2]; int i = 0; for ( Iterator iter = m_nameTranslations.entrySet().iterator(); iter.hasNext(); i++ ) { Map.Entry entry = (Map.Entry)iter.next(); nameTranslations[i][0] = (String)entry.getKey(); nameTranslations[i][1] = (String)entry.getValue(); } // Once we are done modifying this array, set it to the variable accessable outside // of synchronization. m_nameTranslationArray = nameTranslations; return nameTranslations; } } /** * Translates a item name depending on a set of configured translations. * If the name does not exist as a translation then the requested name will * be returned unmodified. * * @param name Requested name. * * @return target name. */ String getTranslatedName( String name ) { String[][] nameTranslations = m_nameTranslationArray; if( nameTranslations == null ) { nameTranslations = updateNameTranslationArray(); } for ( int i = 0; i < nameTranslations.length; i++ ) { String[] nameTranslation = nameTranslations[i]; if ( name.startsWith( nameTranslation[0] ) ) { // Match if ( name.equals( nameTranslation[0] ) ) { // Exact match String newName = nameTranslation[1]; if ( m_translationLogger.isDebugEnabled() ) { m_translationLogger.debug( "Translate \"" + name + "\" to \"" + newName + "\"" ); } return newName; } else if ( nameTranslation[0].endsWith( "." ) ) { // Beginning match String newName = nameTranslation[1] + name.substring( nameTranslation[0].length() ); if ( m_translationLogger.isDebugEnabled() ) { m_translationLogger.debug( "Translate \"" + name + "\" to \"" + newName + "\"" ); } return newName; } else { // The name happened to match the beginning of this name, but it was not meant // as a base translation. } } } // No match. return name; } /** */ private InstrumentableProxy getInstrumentableProxy( String instrumentableName, boolean create ) { //getLogger().debug( "getInstrumentableProxy( " + instrumentableName + ", " + create + " )" ); // The instrumable name may begin with the name of a parent Instrumentable int pos = instrumentableName.lastIndexOf( '.' ); if ( pos <= 0 ) { // This is a root level instrumentable. Look for it within the Instrument Manager. InstrumentableProxy instrumentableProxy; synchronized( m_semaphore ) { instrumentableProxy = (InstrumentableProxy)m_instrumentableProxies.get( instrumentableName ); if ( ( instrumentableProxy == null ) && create ) { //getLogger().debug( " New Instrumentable" ); // Not found, create it. instrumentableProxy = new InstrumentableProxy( this, null, instrumentableName, instrumentableName ); instrumentableProxy.enableLogging( getLogger() ); incrementInstrumentableCount(); m_instrumentableProxies.put( instrumentableName, instrumentableProxy ); // Clear the optimized arrays m_instrumentableProxyArray = null; m_instrumentableDescriptorArray = null; } } //getLogger().debug( " -> " + instrumentableProxy ); return instrumentableProxy; } else { String parentInstrumentableName = instrumentableName.substring( 0, pos ); // See if the parent Instrumentable exists. InstrumentableProxy parentInstrumentableProxy = getInstrumentableProxy( parentInstrumentableName, create ); if ( parentInstrumentableProxy == null ) { // Parent Instrumentable did not exist, so the Instrumentable did not exist. // Will not get here if create is true. return null; } // Locate the Instrumentable within the parent Instrumentable. return parentInstrumentableProxy.getChildInstrumentableProxy( instrumentableName, create ); } } /** * @throws IllegalArgumentException If the specified instrumentName is invalid. */ private InstrumentProxy getInstrumentProxy( String instrumentName, boolean create ) throws IllegalArgumentException { //getLogger().debug( "getInstrumentProxy( " + instrumentName + ", " + create + " )" ); // The instrument name must always begin with the name of an Instrumentable int pos = instrumentName.lastIndexOf( '.' ); if ( pos <= 0 ) { throw new IllegalArgumentException( "\"" + instrumentName + "\" is not a valid instrument name." ); } String instrumentableName = instrumentName.substring( 0, pos ); // See if the Instrumentable exists. InstrumentableProxy instrumentableProxy = getInstrumentableProxy( instrumentableName, create ); if ( instrumentableProxy == null ) { // Instrumentable did not exist, so the instrument did not exist. Will not get here // if create is true. return null; } // Locate the instrument within the Instrumentable. return instrumentableProxy.getInstrumentProxy( instrumentName, create ); } /** * @throws IllegalArgumentException If the specified sampleName is invalid. */ private InstrumentProxy getInstrumentProxyForSample( String sampleName, boolean create ) throws IllegalArgumentException { //getLogger().debug( "getInstrumentProxyForSample( " + sampleName + ", " + create + " )" ); // The sample name must always begin with the name of an Instrument int pos = sampleName.lastIndexOf( '.' ); if ( pos <= 0 ) { throw new IllegalArgumentException( "\"" + sampleName + "\" is not a valid instrument sample name." ); } String instrumentName = sampleName.substring( 0, pos ); // Lookup the Instrument. return getInstrumentProxy( instrumentName, create ); } /** * 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; } } } /** * Updates the cached array of InstrumentableProxies taking * synchronization into account. * * @return An array of the InstrumentableProxies. */ private InstrumentableProxy[] updateInstrumentableProxyArray() { synchronized( m_semaphore ) { InstrumentableProxy[] instrumentableProxyArray = new InstrumentableProxy[ m_instrumentableProxies.size() ]; m_instrumentableProxies.values().toArray( 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( 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; } } ); // Once we are done modifying this array, set it to the variable accessable outside // of synchronization. m_instrumentableProxyArray = instrumentableProxyArray; return instrumentableProxyArray; } } /** * Updates the cached array of InstrumentableDescriptors taking * synchronization into account. * * @return An array of the InstrumentableDescriptors. */ private InstrumentableDescriptor[] updateInstrumentableDescriptorArray() { synchronized( m_semaphore ) { // Get the proxy array. This is done in synchronization so it is not possible that it // will be reset before we obtain the descriptor array. They are both set to null // at the same time when there is a change. InstrumentableProxy[] instrumentableProxyArray = m_instrumentableProxyArray; if ( instrumentableProxyArray == null ) { instrumentableProxyArray = updateInstrumentableProxyArray(); } InstrumentableDescriptor[] instrumentableDescriptorArray = new InstrumentableDescriptor[ instrumentableProxyArray.length ]; for( int i = 0; i < instrumentableProxyArray.length; i++ ) { instrumentableDescriptorArray[ i ] = instrumentableProxyArray[ i ].getDescriptor(); } // Once we are done modifying this array, set it to the variable accessable outside // of synchronization. m_instrumentableDescriptorArray = instrumentableDescriptorArray; return instrumentableDescriptorArray; } } /** * Registers a name translation that will be applied to all named based * lookups of instrumentables, instruments, and samples. The more * translations that are registered, the greater the impact on name * based lookups will be. *

* General operation of the instrument manager will not be affected as * collection on sample data is always done using direct object * references. *

* Translations can be registered for translations of sample names up to * and including the name of the instrument. This means that all source * and target names must end in a '.'. *

* This method should only be called when m_semaphore is synchronized. * * @param source The source name or name base of the translation. * @param target The target name or name base of the translation. * * @throws IllegalArgumentException If either the source or target does * not end in a '.' or is invalid. */ private void registerNameTranslationInner( String source, String target ) throws IllegalArgumentException { if ( !source.endsWith( "." ) ) { throw new IllegalArgumentException( "The translation source must end with a '.'." ); } if ( !target.endsWith( "." ) ) { throw new IllegalArgumentException( "The translation target must end with a '.'." ); } // No component of the source or target name may be 0 length. if ( source.startsWith( "." ) || ( source.indexOf( ".." ) >= 0 ) ) { throw new IllegalArgumentException( "The translation source is invalid: \"" + source + "\"." ); } if ( target.startsWith( "." ) || ( target.indexOf( ".." ) >= 0 ) ) { throw new IllegalArgumentException( "The translation target is invalid: \"" + target + "\"." ); } m_nameTranslations.put( source, target ); m_nameTranslationArray = null; } /** * 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() ); incrementInstrumentableCount(); 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() ); incrementInstrumentableCount(); instrumentableProxy.addChildInstrumentableProxy( proxy ); } // Recurse to the child registerInstrumentableInner( instrumentable, proxy, fullChildName ); } } /** * Examines a instrumentable and Registers all of its child Instrumentables * and Instruments. *

* 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() ); incrementInstrumentCount(); // Set the type of the new InstrumentProxy depending on the // class of the actual Instrument. if( instrument instanceof CounterInstrument ) { proxy.setType( DefaultInstrumentManager.INSTRUMENT_TYPE_COUNTER ); } else if( instrument instanceof ValueInstrument ) { proxy.setType( DefaultInstrumentManager.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 DefaultInstrumentManager.INSTRUMENT_TYPE_COUNTER: // Type is the same. // Store a reference to the proxy in the Instrument. ( (AbstractInstrument)instrument ).setInstrumentProxy( proxy ); break; case DefaultInstrumentManager.INSTRUMENT_TYPE_NONE: // Not yet set. Created in configuration. proxy.setType( DefaultInstrumentManager.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 DefaultInstrumentManager.INSTRUMENT_TYPE_VALUE: // Type is the same. // Store a reference to the proxy in the Instrument. ( (AbstractInstrument)instrument ).setInstrumentProxy( proxy ); break; case DefaultInstrumentManager.INSTRUMENT_TYPE_NONE: // Not yet set. Created in configuration. proxy.setType( DefaultInstrumentManager.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() ); incrementInstrumentableCount(); instrumentableProxy.addChildInstrumentableProxy( proxy ); } // Recurse to the child registerInstrumentableInner( child, proxy, fullChildName ); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy