KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > excalibur > instrument > manager > DefaultInstrumentManager


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

50 package org.apache.excalibur.instrument.manager;
51
52 import java.io.ByteArrayOutputStream JavaDoc;
53 import java.io.File JavaDoc;
54 import java.io.FileInputStream JavaDoc;
55 import java.io.FileOutputStream JavaDoc;
56 import java.io.InputStream JavaDoc;
57 import java.io.OutputStream JavaDoc;
58 import java.util.ArrayList JavaDoc;
59 import java.util.Arrays JavaDoc;
60 import java.util.Comparator JavaDoc;
61 import java.util.HashMap JavaDoc;
62 import java.util.Iterator JavaDoc;
63
64 import org.apache.avalon.framework.activity.Disposable;
65 import org.apache.avalon.framework.activity.Initializable;
66 import org.apache.avalon.framework.configuration.Configurable;
67 import org.apache.avalon.framework.configuration.Configuration;
68 import org.apache.avalon.framework.configuration.ConfigurationException;
69 import org.apache.avalon.framework.configuration.DefaultConfiguration;
70 import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
71 import org.apache.avalon.framework.configuration.DefaultConfigurationSerializer;
72 import org.apache.avalon.framework.container.ContainerUtil;
73 import org.apache.avalon.framework.logger.AbstractLogEnabled;
74 import org.apache.avalon.framework.logger.Logger;
75 import org.apache.avalon.framework.service.ServiceException;
76 import org.apache.excalibur.instrument.AbstractInstrument;
77 import org.apache.excalibur.instrument.CounterInstrument;
78 import org.apache.excalibur.instrument.Instrument;
79 import org.apache.excalibur.instrument.InstrumentManager;
80 import org.apache.excalibur.instrument.Instrumentable;
81 import org.apache.excalibur.instrument.ValueInstrument;
82 import org.apache.excalibur.instrument.manager.interfaces.InstrumentManagerClient;
83 import org.apache.excalibur.instrument.manager.interfaces.NoSuchInstrumentException;
84 import org.apache.excalibur.instrument.manager.interfaces.NoSuchInstrumentSampleException;
85 import org.apache.excalibur.instrument.manager.interfaces.NoSuchInstrumentableException;
86
87 /**
88  *
89  * @author <a HREF="mailto:leif@tanukisoftware.com">Leif Mortenson</a>
90  * @version CVS $Revision: 1.5 $ $Date: 2003/02/25 16:28:16 $
91  * @since 4.1
92  */

93 public class DefaultInstrumentManager
94     extends AbstractLogEnabled
95     implements Configurable, Initializable, Disposable, InstrumentManager,
96         Instrumentable, Runnable JavaDoc
97 {
98     public static final String JavaDoc INSTRUMENT_TOTAL_MEMORY = "total-memory";
99     public static final String JavaDoc INSTRUMENT_FREE_MEMORY = "free-memory";
100     public static final String JavaDoc INSTRUMENT_MEMORY = "memory";
101     public static final String JavaDoc INSTRUMENT_ACTIVE_THREAD_COUNT = "active-thread-count";
102     
103     /** The name used to identify this InstrumentManager. */
104     private String JavaDoc m_name;
105
106     /** The description of this InstrumentManager. */
107     private String JavaDoc m_description;
108     
109     /** Configuration for the InstrumentManager */
110     private Configuration m_configuration;
111     
112     /** List of configured connectors. */
113     private ArrayList JavaDoc m_connectors = new ArrayList JavaDoc();
114
115     /** State file. */
116     private File JavaDoc m_stateFile;
117
118     /** Save state interval. */
119     private long m_stateInterval;
120
121     /** Last time that the state was saved. */
122     private long m_lastStateSave;
123
124     /** Semaphore for actions which must be synchronized */
125     private Object JavaDoc m_semaphore = new Object JavaDoc();
126
127     /** HashMap of all of the registered InstrumentableProxies by their keys. */
128     private HashMap JavaDoc m_instrumentableProxies = new HashMap JavaDoc();
129
130     /** Optimized array of the InstrumentableProxies. */
131     private InstrumentableProxy[] m_instrumentableProxyArray;
132
133     /** Optimized array of the InstrumentableDescriptorLocals. */
134     private InstrumentableDescriptorLocal[] m_instrumentableDescriptorArray;
135
136     /** List of leased InstrumentSamples. */
137     private ArrayList JavaDoc m_leasedInstrumentSamples = new ArrayList JavaDoc();
138     
139     /** Optimized array of the leased InstrumentSamples. */
140     private InstrumentSample[] m_leasedInstrumentSampleArray;
141     
142     /**
143      * Thread used to keep the instruments published by the InstrumentManager
144      * up to date.
145      */

146     private Thread JavaDoc m_runner;
147
148     /** Instrumentable Name assigned to this Instrumentable */
149     private String JavaDoc m_instrumentableName = "instrument-manager";
150
151     /** Instrument used to profile the total memory. */
152     private ValueInstrument m_totalMemoryInstrument;
153
154     /** Instrument used to profile the free memory. */
155     private ValueInstrument m_freeMemoryInstrument;
156
157     /** Instrument used to profile the in use memory. */
158     private ValueInstrument m_memoryInstrument;
159
160     /** Instrument used to profile the active thread count of the JVM. */
161     private ValueInstrument m_activeThreadCountInstrument;
162     
163     /** State Version. */
164     private int m_stateVersion;
165
166     /*---------------------------------------------------------------
167      * Constructors
168      *-------------------------------------------------------------*/

169     /**
170      * Creates a new DefaultInstrumentManager.
171      *
172      * @param name The name used to identify this InstrumentManager. Should not
173      * contain any spaces or periods.
174      *
175      * @deprecated Name should be set in the instrument configuration file.
176      */

177     public DefaultInstrumentManager( String JavaDoc name )
178     {
179         this();
180     }
181     
182     /**
183      * Creates a new DefaultInstrumentManager.
184      */

185     public DefaultInstrumentManager()
186     {
187         // Initialize the Instrumentable elements.
188
m_totalMemoryInstrument = new ValueInstrument( INSTRUMENT_TOTAL_MEMORY );
189         m_freeMemoryInstrument = new ValueInstrument( INSTRUMENT_FREE_MEMORY );
190         m_memoryInstrument = new ValueInstrument( INSTRUMENT_MEMORY );
191         m_activeThreadCountInstrument = new ValueInstrument( INSTRUMENT_ACTIVE_THREAD_COUNT );
192     }
193
194     /*---------------------------------------------------------------
195      * Configurable Methods
196      *-------------------------------------------------------------*/

197     /**
198      * Initializes the configured instrumentables.
199      *
200      * @param configuration InstrumentManager configuration.
201      *
202      * @throws ConfigurationException If there are any configuration problems.
203      */

204     public void configure( Configuration configuration )
205         throws ConfigurationException
206     {
207         synchronized( m_semaphore )
208         {
209             m_configuration = configuration;
210
211             // Look for a configured name and description
212
m_name = configuration.getChild( "name" ).getValue( "instrument-manager" );
213             m_description = configuration.getChild( "description" ).getValue( m_name );
214             
215             // Create a logger to use with the connectors
216
Logger connLogger = getLogger().getChildLogger( "connector" );
217             
218             // Configure the connectors
219
Configuration connectorsConf = configuration.getChild( "connectors" );
220             Configuration[] connectorConfs =
221                 connectorsConf.getChildren( "connector" );
222             for( int i = 0; i < connectorConfs.length; i++ )
223             {
224                 Configuration connectorConf = connectorConfs[ i ];
225                 String JavaDoc className = connectorConf.getAttribute( "class" );
226                 // Handle aliases
227
if ( className.equals( "altrmi" ) )
228                 {
229                     className = "org.apache.excalibur.instrument.manager.altrmi."
230                         + "InstrumentManagerAltrmiConnector";
231                 }
232                 
233                 // Look for the connector class and create an instance.
234
try
235                 {
236                     Class JavaDoc clazz = Class.forName( className );
237                     InstrumentManagerConnector connector =
238                         (InstrumentManagerConnector)clazz.newInstance();
239                     
240                     // Initialize the new connector
241
connector.setInstrumentManager( this );
242                     ContainerUtil.enableLogging( connector, connLogger );
243                     ContainerUtil.configure( connector, connectorConf );
244                     ContainerUtil.start( connector );
245                     
246                     m_connectors.add( connector );
247                 }
248                 catch ( Exception JavaDoc e )
249                 {
250                     throw new ConfigurationException( "Unable to create connector because: "
251                         + e );
252                 }
253             }
254             
255             // Configure the instrumentables.
256
Configuration instrumentablesConf = configuration.getChild( "instrumentables" );
257             Configuration[] instrumentableConfs =
258                 instrumentablesConf.getChildren( "instrumentable" );
259             for( int i = 0; i < instrumentableConfs.length; i++ )
260             {
261                 Configuration instrumentableConf = instrumentableConfs[ i ];
262                 String JavaDoc instrumentableName = instrumentableConf.getAttribute( "name" );
263
264                 InstrumentableProxy instrumentableProxy = new InstrumentableProxy(
265                     this, null, instrumentableName, instrumentableName );
266                 instrumentableProxy.enableLogging( getLogger() );
267                 instrumentableProxy.configure( instrumentableConf );
268                 m_instrumentableProxies.put( instrumentableName, instrumentableProxy );
269
270                 // Clear the optimized arrays
271
m_instrumentableProxyArray = null;
272                 m_instrumentableDescriptorArray = null;
273             }
274
275             // Configure the state file.
276
Configuration stateFileConf = configuration.getChild( "state-file" );
277             m_stateInterval = stateFileConf.getAttributeAsLong( "interval", 60000 );
278
279             String JavaDoc stateFile = stateFileConf.getValue( null );
280             if( stateFile != null )
281             {
282                 m_stateFile = new File JavaDoc( stateFile );
283                 if( m_stateFile.exists() )
284                 {
285                     try
286                     {
287                         loadStateFromFile( m_stateFile );
288                     }
289                     catch( Exception JavaDoc e )
290                     {
291                         getLogger().error(
292                             "Unable to load the instrument manager state. The configuration " +
293                             "may have been corruptped. A backup may have been made in the same " +
294                             "directory when it was saved.", e );
295                     }
296                 }
297             }
298         }
299     }
300
301     /*---------------------------------------------------------------
302      * Initializable Methods
303      *-------------------------------------------------------------*/

304     /**
305      * Initializes the InstrumentManager.
306      *
307      * @throws Exception If there were any problems initializing the object.
308      */

309     public void initialize()
310         throws Exception JavaDoc
311     {
312         // Register the InstrumentManager as an Instrumentable.
313
registerInstrumentable( this, getInstrumentableName() );
314
315         if( m_runner == null )
316         {
317             m_runner = new Thread JavaDoc( this, "InstrumentManagerRunner" );
318             m_runner.start();
319         }
320     }
321
322     /*---------------------------------------------------------------
323      * Disposable Methods
324      *-------------------------------------------------------------*/

325     /**
326      * Disposes the InstrumentManager.
327      */

328     public void dispose()
329     {
330         if( m_runner != null )
331         {
332             m_runner = null;
333         }
334         
335         // Shutdown the connectors
336
for ( Iterator JavaDoc iter = m_connectors.iterator(); iter.hasNext(); )
337         {
338             InstrumentManagerConnector connector = (InstrumentManagerConnector)iter.next();
339             try
340             {
341                 ContainerUtil.stop( connector );
342                 ContainerUtil.dispose( connector );
343             }
344             catch ( Exception JavaDoc e )
345             {
346                 getLogger().error( "Encountered an unexpected error shutting down a connector", e );
347             }
348         }
349
350         saveState();
351     }
352
353     /*---------------------------------------------------------------
354      * InstrumentManager Methods
355      *-------------------------------------------------------------*/

356     /**
357      * Instrumentable to be registered with the instrument manager. Should be
358      * called whenever an Instrumentable is created. The '.' character is
359      * used to denote a child Instrumentable and can be used to register the
360      * instrumentable at a specific point in an instrumentable hierarchy.
361      *
362      * @param instrumentable Instrumentable to register with the InstrumentManager.
363      * @param instrumentableName The name to use when registering the Instrumentable.
364      *
365      * @throws Exception If there were any problems registering the Instrumentable.
366      */

367     public void registerInstrumentable( Instrumentable instrumentable, String JavaDoc instrumentableName )
368         throws Exception JavaDoc
369     {
370         getLogger().debug( "Registering Instrumentable: " + instrumentableName );
371
372         synchronized( m_semaphore )
373         {
374             // If the specified instrumentable name contains '.' chars then we need to
375
// make sure we register the instrumentable at the correct location, creating
376
// any parent instrumentables as necessary.
377
int pos = instrumentableName.indexOf( '.' );
378             if ( pos >= 0 )
379             {
380                 String JavaDoc parentName = instrumentableName.substring( 0, pos );
381                 String JavaDoc childName =
382                     instrumentableName.substring( pos + 1 );
383                 InstrumentableProxy instrumentableProxy =
384                     (InstrumentableProxy)m_instrumentableProxies.get( parentName );
385                 if( instrumentableProxy == null )
386                 {
387                     // This is a Instrumentable that has not been seen before.
388
instrumentableProxy = new InstrumentableProxy(
389                         this, null, parentName, parentName );
390                     instrumentableProxy.enableLogging( getLogger() );
391                     // Do not call configure here because there is no configuration
392
// for discovered instrumentables.
393
m_instrumentableProxies.put( parentName, instrumentableProxy );
394     
395                     // Clear the optimized arrays
396
m_instrumentableProxyArray = null;
397                     m_instrumentableDescriptorArray = null;
398     
399                     // Recursively register all the Instruments in this and any child Instrumentables.
400
registerDummyInstrumentableInner(
401                         instrumentable, instrumentableProxy, parentName, childName );
402                 }
403                 else
404                 {
405                     // Additional Instrumentable instance. Possible that new Instruments could be found.
406
registerDummyInstrumentableInner(
407                         instrumentable, instrumentableProxy, parentName, childName );
408                 }
409             } else {
410                 // If the instrumentable does not implement ThreadSafe, then it is possible that
411
// another one of its instance was already registered. If so, then the
412
// Instruments will all be the same. The new instances still need to be
413
// registered however.
414
InstrumentableProxy instrumentableProxy =
415                     (InstrumentableProxy)m_instrumentableProxies.get( instrumentableName );
416                 if( instrumentableProxy == null )
417                 {
418                     // This is a Instrumentable that has not been seen before.
419
instrumentableProxy = new InstrumentableProxy(
420                         this, null, instrumentableName, instrumentableName );
421                     instrumentableProxy.enableLogging( getLogger() );
422                     // Do not call configure here because there is no configuration
423
// for discovered instrumentables.
424
m_instrumentableProxies.put( instrumentableName, instrumentableProxy );
425     
426                     // Clear the optimized arrays
427
m_instrumentableProxyArray = null;
428                     m_instrumentableDescriptorArray = null;
429     
430                     // Recursively register all the Instruments in this and any child Instrumentables.
431
registerInstrumentableInner(
432                         instrumentable, instrumentableProxy, instrumentableName );
433                 }
434                 else
435                 {
436                     // Additional Instrumentable instance. Possible that new Instruments could be found.
437
registerInstrumentableInner(
438                         instrumentable, instrumentableProxy, instrumentableName );
439                 }
440             }
441         }
442         
443         stateChanged();
444     }
445
446     
447     /*---------------------------------------------------------------
448      * Methods
449      *-------------------------------------------------------------*/

450     /**
451      * Returns the name used to identify this InstrumentManager.
452      *
453      * @return The name used to identify this InstrumentManager.
454      */

455     public String JavaDoc getName()
456     {
457         return m_name;
458     }
459     
460     /**
461      * Returns the description of this InstrumentManager.
462      *
463      * @return The description of this InstrumentManager.
464      */

465     public String JavaDoc getDescription()
466     {
467         return m_description;
468     }
469     
470     /**
471      * Returns an InstrumentableDescriptorLocal based on its name or the name
472      * of any of its children.
473      *
474      * @param instrumentableName Name of the Instrumentable being requested.
475      *
476      * @return A Descriptor of the requested Instrumentable.
477      *
478      * @throws NoSuchInstrumentableException If the specified Instrumentable
479      * does not exist.
480      */

481     public InstrumentableDescriptorLocal getInstrumentableDescriptor( String JavaDoc instrumentableName )
482         throws NoSuchInstrumentableException
483     {
484         InstrumentableProxy proxy = getInstrumentableProxy( instrumentableName );
485         if( proxy == null )
486         {
487             throw new NoSuchInstrumentableException(
488                 "No instrumentable can be found using name: " + instrumentableName );
489         }
490
491         return proxy.getDescriptor();
492     }
493
494     /**
495      * Returns an array of Descriptors for the Instrumentables managed by this
496      * InstrumentManager.
497      *
498      * @return An array of Descriptors for the Instrumentables managed by this
499      * InstrumentManager.
500      */

501     public InstrumentableDescriptorLocal[] getInstrumentableDescriptors()
502     {
503         InstrumentableDescriptorLocal[] descriptors = m_instrumentableDescriptorArray;
504         if( descriptors == null )
505         {
506             descriptors = updateInstrumentableDescriptorArray();
507         }
508         return descriptors;
509     }
510     
511     /**
512      * Searches the entire instrument tree an instrumentable with the given
513      * name.
514      *
515      * @param instrumentableName Name of the Instrumentable being requested.
516      *
517      * @return A Descriptor of the requested Instrumentable.
518      *
519      * @throws NoSuchInstrumentableException If the specified Instrumentable does
520      * not exist.
521      */

522     public InstrumentableDescriptorLocal locateInstrumentableDescriptor( String JavaDoc instrumentableName )
523         throws NoSuchInstrumentableException
524     {
525         InstrumentableProxy instrumentableProxy =
526             locateDeepestInstrumentableProxy( instrumentableName );
527         if ( instrumentableProxy != null )
528         {
529             if ( instrumentableProxy.getName().equals( instrumentableName ) )
530             {
531                 // Found what we were looking for
532
return instrumentableProxy.getDescriptor();
533             }
534         }
535         
536         // Unable to locate the requested Instrumentable
537
throw new NoSuchInstrumentableException(
538             "No instrumentable can be found with the name: " + instrumentableName );
539     }
540     
541     /**
542      * Searches the entire instrument tree an instrument with the given name.
543      *
544      * @param instrumentName Name of the Instrument being requested.
545      *
546      * @return A Descriptor of the requested Instrument.
547      *
548      * @throws NoSuchInstrumentException If the specified Instrument does
549      * not exist.
550      */

551     public InstrumentDescriptorLocal locateInstrumentDescriptor( String JavaDoc instrumentName )
552         throws NoSuchInstrumentException
553     {
554         InstrumentableProxy instrumentableProxy =
555             locateDeepestInstrumentableProxy( instrumentName );
556         if ( instrumentableProxy != null )
557         {
558             // Now look for the specified instrument
559
InstrumentProxy instrumentProxy =
560                 instrumentableProxy.getInstrumentProxy( instrumentName );
561             if ( instrumentProxy != null )
562             {
563                 if ( instrumentProxy.getName().equals( instrumentName ) )
564                 {
565                     // Found what we were looking for
566
return instrumentProxy.getDescriptor();
567                 }
568             }
569         }
570         
571         // Unable to locate the requested Instrument
572
throw new NoSuchInstrumentException(
573             "No instrument can be found with the name: " + instrumentName );
574     }
575
576     /**
577      * Searches the entire instrument tree an instrument sample with the given
578      * name.
579      *
580      * @param sampleName Name of the Instrument Sample being requested.
581      *
582      * @return A Descriptor of the requested Instrument Sample.
583      *
584      * @throws NoSuchInstrumentSampleException If the specified Instrument
585      * Sample does not exist.
586      */

587     public InstrumentSampleDescriptorLocal locateInstrumentSampleDescriptor( String JavaDoc sampleName )
588         throws NoSuchInstrumentSampleException
589     {
590         InstrumentableProxy instrumentableProxy =
591             locateDeepestInstrumentableProxy( sampleName );
592         if ( instrumentableProxy != null )
593         {
594             // Now look for the specified instrument
595
InstrumentProxy instrumentProxy =
596                 instrumentableProxy.getInstrumentProxy( sampleName );
597             if ( instrumentProxy != null )
598             {
599                 // Now look for the specified sample
600
InstrumentSample sample = instrumentProxy.getInstrumentSample( sampleName );
601                 if ( sample != null )
602                 {
603                     if ( sample.getName().equals( sampleName ) )
604                     {
605                         // Found what we were looking for
606
return sample.getDescriptor();
607                     }
608                 }
609             }
610         }
611         
612         // Unable to locate the requested Instrument Sample
613
throw new NoSuchInstrumentException(
614             "No instrument sample can be found with the name: " + sampleName );
615     }
616     
617     /**
618      * Returns the stateVersion of the instrument manager. The state version
619      * will be incremented each time any of the configuration of the
620      * instrument manager or any of its children is modified.
621      * Clients can use this value to tell whether or not anything has
622      * changed without having to do an exhaustive comparison.
623      *
624      * @return The state version of the instrument manager.
625      */

626     int getStateVersion()
627     {
628         return m_stateVersion;
629     }
630     
631     /**
632      * Invokes garbage collection.
633      */

634     public void invokeGarbageCollection()
635     {
636         System.gc();
637     }
638     
639     
640
641     /**
642      * Loads the Instrument Manager state from the specified file.
643      *
644      * @param stateFile File to read the instrument manager's state from.
645      *
646      * @throws Exception if there are any problems loading the state.
647      */

648     public void loadStateFromFile( File JavaDoc stateFile )
649         throws Exception JavaDoc
650     {
651         long now = System.currentTimeMillis();
652         getLogger().debug( "Loading Instrument Manager state from: " +
653             stateFile.getAbsolutePath() );
654
655         FileInputStream JavaDoc is = new FileInputStream JavaDoc( stateFile );
656         try
657         {
658             loadStateFromStream( is );
659         }
660         finally
661         {
662             is.close();
663         }
664
665         getLogger().debug( "Loading Instrument Manager state took " +
666                            ( System.currentTimeMillis() - now ) + "ms." );
667     }
668
669     /**
670      * Loads the Instrument Manager state from the specified stream.
671      *
672      * @param is Stream to read the instrument manager's state from.
673      *
674      * @throws Exception if there are any problems loading the state.
675      */

676     public void loadStateFromStream( InputStream JavaDoc is )
677         throws Exception JavaDoc
678     {
679         // Ride on top of the Configuration classes to load the state.
680
DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
681         Configuration stateConfig = builder.build( is );
682
683         loadStateFromConfiguration( stateConfig );
684     }
685
686     /**
687      * Loads the Instrument Manager state from the specified Configuration.
688      *
689      * @param state Configuration object to load the state from.
690      *
691      * @throws ConfigurationException If there were any problems loading the
692      * state.
693      */

694     public void loadStateFromConfiguration( Configuration state )
695         throws ConfigurationException
696     {
697         Configuration[] instrumentableConfs = state.getChildren( "instrumentable" );
698         for( int i = 0; i < instrumentableConfs.length; i++ )
699         {
700             Configuration instrumentableConf = instrumentableConfs[ i ];
701             String JavaDoc instrumentableName = instrumentableConf.getAttribute( "name" );
702             InstrumentableProxy instrumentableProxy = getInstrumentableProxy( instrumentableName );
703             if( instrumentableProxy == null )
704             {
705                 // The Instrumentable was in the state file, but has not yet
706
// been registered. It is possible that it will be registered
707
// at a later time. For now it needs to be created.
708
instrumentableProxy = new InstrumentableProxy(
709                     this, null, instrumentableName, instrumentableName );
710                 instrumentableProxy.enableLogging( getLogger() );
711                 m_instrumentableProxies.put( instrumentableName, instrumentableProxy );
712
713                 // Clear the optimized arrays
714
m_instrumentableProxyArray = null;
715                 m_instrumentableDescriptorArray = null;
716             }
717             
718             instrumentableProxy.loadState( instrumentableConf );
719         }
720         
721         stateChanged();
722     }
723
724     /**
725      * Saves the Instrument Manager's state to the specified file. Any
726      * existing file is backed up before the save takes place and replaced
727      * in the event of an error.
728      *
729      * @param stateFile File to write the Instrument Manager's state to.
730      *
731      * @throws Exception if there are any problems saving the state.
732      */

733     public void saveStateToFile( File JavaDoc stateFile )
734         throws Exception JavaDoc
735     {
736         long now = System.currentTimeMillis();
737         getLogger().debug( "Saving Instrument Manager state to: " + stateFile.getAbsolutePath() );
738
739         // First save the state to an in memory stream to shorten the
740
// period of time needed to write the data to disk. This makes it
741
// less likely that the files will be left in a corrupted state if
742
// the JVM dies at the wrong time.
743
ByteArrayOutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
744         byte[] data;
745         try
746         {
747             saveStateToStream( os );
748             data = os.toByteArray();
749         }
750         finally
751         {
752             os.close();
753         }
754         
755         // If the specified file exists, then rename it before we start writing.
756
// This makes it possible to recover from some errors.
757
File JavaDoc renameFile = null;
758         boolean success = false;
759         if( stateFile.exists() )
760         {
761             renameFile = new File JavaDoc( stateFile.getAbsolutePath() + "." + now + ".backup" );
762             stateFile.renameTo( renameFile );
763         }
764         
765         // Write the data to the new file.
766
FileOutputStream JavaDoc fos = new FileOutputStream JavaDoc( stateFile );
767         try
768         {
769             fos.write( data );
770             success = true;
771         }
772         finally
773         {
774             fos.close();
775             
776             if ( !success )
777             {
778                 // Make sure that part of the file does not exist.
779
stateFile.delete();
780             }
781             
782             // Handle the backup file.
783
if ( renameFile != null )
784             {
785                 if ( success )
786                 {
787                     // No longer need the backup.
788
renameFile.delete();
789                 }
790                 else
791                 {
792                     // Need to replace the backup.
793
renameFile.renameTo( stateFile );
794                 }
795