KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > excalibur > instrument > manager > impl > AbstractInstrumentSample


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

19
20 package org.apache.excalibur.instrument.manager.impl;
21
22 import java.io.PrintWriter JavaDoc;
23 import java.util.StringTokenizer JavaDoc;
24 import java.util.Calendar JavaDoc;
25
26 import org.apache.avalon.framework.configuration.Configuration;
27 import org.apache.avalon.framework.configuration.ConfigurationException;
28 import org.apache.avalon.framework.logger.AbstractLogEnabled;
29
30 import org.apache.excalibur.instrument.manager.InstrumentSampleDescriptor;
31 import org.apache.excalibur.instrument.manager.InstrumentSampleListener;
32 import org.apache.excalibur.instrument.manager.InstrumentSampleSnapshot;
33 import org.apache.excalibur.instrument.manager.InstrumentSampleUtils;
34
35 /**
36  * An AbstractInstrumentSample contains all of the functionality common to all
37  * InstrumentSamples.
38  *
39  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
40  */

41 abstract class AbstractInstrumentSample
42     extends AbstractLogEnabled
43     implements InstrumentSample
44 {
45     /** Stores the time-zone offset for this JVM. */
46     private static long m_zoneOffset;
47     
48     /** The InstrumentProxy which owns the InstrumentSample. */
49     private InstrumentProxy m_instrumentProxy;
50
51     /** Configured flag. */
52     private boolean m_configured;
53
54     /** The name of the new InstrumentSample. */
55     private String JavaDoc m_name;
56
57     /** The sample interval of the new InstrumentSample. */
58     private long m_interval;
59
60     /** The number of samples to store as history. */
61     private int m_size;
62
63     /** The description of the new InstrumentSample. */
64     private String JavaDoc m_description;
65
66     /** The Descriptor for the InstrumentSample. */
67     private InstrumentSampleDescriptor m_descriptor;
68
69     /**
70      * The maximum amount of time between updates before history will be
71      * wiped clean.
72      */

73     private long m_maxAge;
74
75     /** The UNIX time of the beginning of the sample. */
76     protected long m_time;
77
78     /** The time that the current lease expires. */
79     private long m_leaseExpirationTime;
80
81     /** True if the lease has expired. */
82     private boolean m_expired;
83
84     /** The Index into the history arrays. */
85     private int m_historyIndex;
86
87     /** The Old half of the history array. */
88     private int[] m_historyOld;
89
90     /** The New half of the history array. */
91     private int[] m_historyNew;
92
93     /** Array of registered InstrumentSampleListeners. */
94     private InstrumentSampleListener[] m_listeners;
95
96     /** State Version. */
97     private int m_stateVersion;
98
99     /*---------------------------------------------------------------
100      * Static Initializer
101      *-------------------------------------------------------------*/

102     static
103     {
104         Calendar JavaDoc now = Calendar.getInstance();
105         m_zoneOffset = now.get( Calendar.ZONE_OFFSET );
106     }
107     
108     /*---------------------------------------------------------------
109      * Constructors
110      *-------------------------------------------------------------*/

111     /**
112      * Creates a new AbstractInstrumentSample
113      *
114      * @param instrumentProxy The InstrumentProxy which owns the
115      * InstrumentSample.
116      * @param name The name of the new InstrumentSample.
117      * @param interval The sample interval of the new InstrumentSample.
118      * @param size The number of samples to store as history. Assumes that
119      * size is at least 1.
120      * @param description The description of the new InstrumentSample.
121      * @param lease The length of the lease in milliseconds.
122      */

123     protected AbstractInstrumentSample( InstrumentProxy instrumentProxy,
124                                         String JavaDoc name,
125                                         long interval,
126                                         int size,
127                                         String JavaDoc description,
128                                         long lease )
129     {
130         m_instrumentProxy = instrumentProxy;
131
132         if( interval < 1 )
133         {
134             throw new IllegalArgumentException JavaDoc( "interval must be at least 1." );
135         }
136         if( size < 1 )
137         {
138             throw new IllegalArgumentException JavaDoc( "size must be at least 1." );
139         }
140
141         m_name = name;
142         m_interval = interval;
143         m_size = size;
144         m_description = description;
145         if( lease > 0 )
146         {
147             m_leaseExpirationTime = System.currentTimeMillis() + lease;
148         }
149         else
150         {
151             // Permanent lease.
152
m_leaseExpirationTime = 0;
153         }
154
155         // Calculate the maxAge
156
m_maxAge = m_size * m_interval;
157
158         init( 0 );
159
160         // Create the descriptor
161
m_descriptor = new InstrumentSampleDescriptorImpl( this );
162     }
163
164     /*---------------------------------------------------------------
165      * InstrumentSample Methods
166      *-------------------------------------------------------------*/

167     /**
168      * Returns the InstrumentProxy which owns the InstrumentSample.
169      *
170      * @return The InstrumentProxy which owns the InstrumentSample.
171      */

172     public InstrumentProxy getInstrumentProxy()
173     {
174         return m_instrumentProxy;
175     }
176
177     /**
178      * Returns true if the Instrument was configured in the instrumentables
179      * section of the configuration.
180      *
181      * @return True if configured.
182      */

183     public boolean isConfigured()
184     {
185         return m_configured;
186     }
187
188     /**
189      * Returns the name of the sample.
190      *
191      * @return The name of the sample.
192      */

193     public final String JavaDoc getName()
194     {
195         return m_name;
196     }
197
198     /**
199      * Returns the sample interval. The period of each sample in millisends.
200      *
201      * @return The sample interval.
202      */

203     public final long getInterval()
204     {
205         return m_interval;
206     }
207
208     /**
209      * Returns the number of samples in the sample history.
210      *
211      * @return The size of the sample history.
212      */

213     public final int getSize()
214     {
215         return m_size;
216     }
217
218     /**
219      * Returns the description of the sample.
220      *
221      * @return The description of the sample.
222      */

223     public final String JavaDoc getDescription()
224     {
225         return m_description;
226     }
227
228     /**
229      * Returns a Descriptor for the InstrumentSample.
230      *
231      * @return A Descriptor for the InstrumentSample.
232      */

233     public InstrumentSampleDescriptor getDescriptor()
234     {
235         return m_descriptor;
236     }
237
238     /**
239      * Obtain the value of the sample. All samples are integers, so the profiled
240      * objects must measure quantity (numbers of items), rate (items/period), time in
241      * milliseconds, etc.
242      *
243      * @return The sample value.
244      */

245     public final int getValue()
246     {
247         boolean update;
248         int value;
249         long time;
250
251         synchronized( this )
252         {
253             long now = System.currentTimeMillis();
254             update = update( now, false );
255             value = getValueInner();
256             time = m_time;
257         }
258
259         if( update )
260         {
261             updateListeners( value, time );
262         }
263         return value;
264     }
265
266     /**
267      * Obtain the UNIX time of the beginning of the sample.
268      *
269      * @return The UNIX time of the beginning of the sample.
270      */

271     public final long getTime()
272     {
273         boolean update;
274         int value;
275         long time;
276
277         synchronized( this )
278         {
279             long now = System.currentTimeMillis();
280             update = update( now, false );
281             value = getValueInner();
282             time = m_time;
283         }
284
285         if( update )
286         {
287             updateListeners( value, time );
288         }
289         return time;
290     }
291
292     /**
293      * Returns the time that the current lease expires. Permanent samples will
294      * return a value of 0.
295      *
296      * @return The time that the current lease expires.
297      */

298     public long getLeaseExpirationTime()
299     {
300         return m_leaseExpirationTime;
301     }
302
303     /**
304      * Extends the lease to be lease milliseconds from the current time.
305      * Ignored if the lease has already expired.
306      *
307      * @param lease The length of the lease in milliseconds.
308      *
309      * @return The new lease expiration time. Returns 0 if the sample is
310      * permanent.
311      */

312     public long extendLease( long lease )
313     {
314         DefaultInstrumentManagerImpl manager =
315             m_instrumentProxy.getInstrumentableProxy().getInstrumentManager();
316         
317         // Make sure that the requested lease is valid.
318
lease = Math.max( 1, Math.min( lease, manager.getMaxLeasedSampleLease() ) );
319         
320         // Since the lease already exists, we know that we can always renew it.
321

322         manager.incrementLeaseRequests();
323         
324         synchronized( this )
325         {
326             // Only extend the lease if it is not permanent.
327
if( ( m_leaseExpirationTime > 0 ) && ( !m_expired ) )
328             {
329                 long newLeaseExpirationTime = System.currentTimeMillis() + lease;
330                 if( newLeaseExpirationTime > m_leaseExpirationTime )
331                 {
332                     m_leaseExpirationTime = newLeaseExpirationTime;
333                     stateChanged();
334                 }
335             }
336
337             return m_leaseExpirationTime;
338         }
339     }
340
341     /**
342      * Tells the sample that its lease has expired. No new references to
343      * the sample will be made available, but clients which already have
344      * access to the sample may continue to use it.
345      */

346     public void expire()
347     {
348         // Update to the time that we expire at.
349
update( m_leaseExpirationTime, false );
350
351         m_expired = true;
352     }
353
354     /**
355      * Obtains a static snapshot of the InstrumentSample.
356      *
357      * @return A static snapshot of the InstrumentSample.
358      */

359     public final InstrumentSampleSnapshot getSnapshot()
360     {
361         synchronized( this )
362         {
363             long time = System.currentTimeMillis();
364             update( time, false );
365
366             return new InstrumentSampleSnapshot(
367                 m_name,
368                 m_interval,
369                 m_size,
370                 m_time,
371                 getHistorySnapshot(),
372                 m_stateVersion );
373         }
374     }
375
376     /**
377      * Returns the stateVersion of the sample. The state version will be
378      * incremented each time any of the configuration of the sample is
379      * modified.
380      * Clients can use this value to tell whether or not anything has
381      * changed without having to do an exhaustive comparison.
382      *
383      * @return The state version of the sample.
384      */

385     public int getStateVersion()
386     {
387         return m_stateVersion;
388     }
389
390     /**
391      * Registers a InstrumentSampleListener with a InstrumentSample given a name.
392      *
393      * @param listener The listener which should start receiving updates from the
394      * InstrumentSample.
395      */

396     public void addInstrumentSampleListener( InstrumentSampleListener listener )
397     {
398         if( getLogger().isDebugEnabled() )
399         {
400             getLogger().debug( "An InstrumentSampleListener was added to sample, " + m_name
401                 + " : " + listener.getClass().getName() );
402         }
403
404         synchronized( this )
405         {
406             // Store the listeners in an array. This makes it possible to
407
// avoid synchronization while propagating events. Never change
408
// the contents of the listener array once it has been set to the
409
// m_listeners field.
410
InstrumentSampleListener[] oldListeners = m_listeners;
411             InstrumentSampleListener[] newListeners;
412             if( oldListeners == null )
413             {
414                 newListeners = new InstrumentSampleListener[]{listener};
415             }
416             else
417             {
418                 newListeners = new InstrumentSampleListener[ oldListeners.length + 1 ];
419                 System.arraycopy( oldListeners, 0, newListeners, 0, oldListeners.length );
420                 newListeners[ oldListeners.length ] = listener;
421             }
422
423             // Update the m_listeners field.
424
m_listeners = newListeners;
425         }
426     }
427
428     /**
429      * Unregisters a InstrumentSampleListener from a InstrumentSample given a name.
430      *
431      * @param listener The listener which should stop receiving updates from the
432      * InstrumentSample.
433      */

434     public void removeInstrumentSampleListener( InstrumentSampleListener listener )
435     {
436         if( getLogger().isDebugEnabled() )
437         {
438             getLogger().debug( "An InstrumentSampleListener was removed from sample, " + m_name
439                 + " : " + listener.getClass().getName() );
440         }
441
442         synchronized( this )
443         {
444             // Store the listeners in an array. This makes it possible to
445
// avoid synchronization while propagating events. Never change
446
// the contents of the listener array once it has been set to the
447
// m_listeners field.
448
InstrumentSampleListener[] oldListeners = m_listeners;
449             InstrumentSampleListener[] newListeners;
450             if( oldListeners == null )
451             {
452                 // Means that this method should not have been called, but
453
// don't throw an error.
454
newListeners = null;
455             }
456             else if( oldListeners.length == 1 )
457             {
458                 if( oldListeners[ 0 ] == listener )
459                 {
460                     newListeners = null;
461                 }
462                 else
463                 {
464                     // The listener was not in the list.
465
newListeners = oldListeners;
466                 }
467             }
468             else
469             {
470                 // Look for the listener in the array.
471
int pos = -1;
472                 for( int i = 0; i < oldListeners.length; i++ )
473                 {
474                     if( oldListeners[ i ] == listener )
475                     {
476                         pos = i;
477                         break;
478                     }
479                 }
480
481                 if( pos < 0 )
482                 {
483                     // The listener was not in the list.
484
newListeners = oldListeners;
485                 }
486                 else
487                 {
488                     newListeners = new InstrumentSampleListener[ oldListeners.length - 1 ];
489                     if( pos > 0 )
490                     {
491                         // Copy the head of the array
492
System.arraycopy( oldListeners, 0, newListeners, 0, pos );
493                     }
494                     if( pos < oldListeners.length - 1 )
495                     {
496                         // Copy the tail of the array
497
System.arraycopy( oldListeners, pos + 1,
498                                           newListeners, pos, oldListeners.length - 1 - pos );
499                     }
500                 }
501             }
502
503             // Update the m_listeners field.
504
m_listeners = newListeners;
505         }
506     }
507
508     /**
509      * Notifies any listeners of a change.
510      * <p>
511      * Make sure that this is called OUTSIDE of a synchrinization block.
512      *
513      * @param value The new value.
514      * @param time The time that the new value was set.
515      */

516     protected void updateListeners( int value, long time )
517     {
518         // Get a local reference to the listeners, so that synchronization can be avoided.
519
InstrumentSampleListener[] listeners = m_listeners;
520         if( listeners != null )
521         {
522             for( int i = 0; i < listeners.length; i++ )
523             {
524                 listeners[ i ].setValue( getName(), value, time );
525             }
526         }
527     }
528     
529     /**
530      * Writes the current state to a PrintWriter as XML.
531      *
532      * @param out The PrintWriter to which the state should be written.
533      */

534     public void writeState( PrintWriter JavaDoc out )
535     {
536         // If this sample is not configured and its lease time is 0, then it
537
// is an artifact of a previous state file, so it should not be saved.
538
if( ( !isConfigured() ) && ( getLeaseExpirationTime() <= 0 ) )
539         {
540             return;
541         }
542
543         boolean update;
544         int value;
545         long time;
546
547         synchronized( this )
548         {
549             
550             // Always update the sample so its state will be correct when saved.
551
long now = System.currentTimeMillis();
552             update = update( now, false );
553             value = getValueInner();
554             time = m_time;
555             
556             String JavaDoc history = getHistoryList();
557             
558             if( ( getLeaseExpirationTime() == 0 ) && ( history == null ) )
559             {
560                 // This is a permanent sample with no data points, there is no point therefor
561
// in writing it to the state file.
562
// Do nothing. We may have updated though so handle that below.
563
}
564             else
565             {
566                 // Open the node.
567
out.print( "<sample name=\"" );
568                 out.print( XMLUtil.getXMLSafeString( m_name ) );
569                 out.print( "\" type=\"" );
570                 out.print( InstrumentSampleUtils.getInstrumentSampleTypeName( getType() ) );
571                 out.print( "\" interval=\"" );
572                 out.print( m_interval );
573                 out.print( "\" size=\"" );
574                 out.print( m_size );
575                 out.print( "\" time=\"" );
576                 out.print( m_time );
577                 out.print( "\"" );
578                 if( getLeaseExpirationTime() != 0 )
579                 {
580                     out.print( " lease-expiration=\"" );
581                     out.print( getLeaseExpirationTime() );
582                     out.print( "\"" );
583                     
584                     // If the sample is permanent then its description will be set in the configuration
585
// file and does not need to be saved here as well.
586
out.print( " description=\"" );
587                     out.print( XMLUtil.getXMLSafeString( m_description ) );
588                     out.print( "\"" );
589                 }
590     
591                 // Let subclasses add additional attributes.
592
writeStateAttributes( out );
593                 
594                 if ( history == null )
595                 {
596                     // No history. Childless node.
597
out.println( "/>" );
598                 }
599                 else
600                 {
601                     // Have history.
602
out.println( ">" );
603                     
604                     // Save the history samples so that the newest is first.
605
out.print( "<history>" );
606                     out.print( history );
607                     out.println( "</history>" );
608                     
609                     // Close the node.
610
out.println( "</sample>" );
611                 }
612             }
613         }
614
615         if ( update )
616         {
617             updateListeners( value, time );
618         }
619     }
620
621     /**
622      * Loads the state into the InstrumentSample.
623      *
624      * @param state Configuration object to load state from.
625      *
626      * @throws ConfigurationException If there were any problems loading the
627      * state.
628      */

629     public final void loadState( Configuration state ) throws ConfigurationException
630     {
631         boolean update;
632         int sampleValue;
633         long sampleTime;
634         
635         synchronized( this )
636         {
637             // Set the time
638
long savedTime = m_time = state.getAttributeAsLong( "time" );
639
640             // Load the lease expiration time
641

642             long leaseExpirationTime = state.getAttributeAsLong( "lease-expiration", 0 );
643             if ( ( m_leaseExpirationTime != 0 ) && ( leaseExpirationTime > m_leaseExpirationTime ) )
644             {
645                 m_leaseExpirationTime = leaseExpirationTime;
646             }
647
648             // Set the history index.
649
m_historyIndex = 0;
650
651             int[] sampleValues;
652             
653             // Read in the samples, don't trust that the number will be correct.
654
// First sample is the current value, following sames go back in
655
// time from newest to oldest.
656
Configuration history = state.getChild( "history", false );
657             if ( history == null )
658             {
659                 // No history was saved.
660
sampleValues = new int[0];
661             }
662             else
663             {
664                 String JavaDoc compactSamples = history.getValue();
665     
666                 // Sample values are stored in newest to oldest order.
667
StringTokenizer JavaDoc st = new StringTokenizer JavaDoc( compactSamples, "," );
668                 sampleValues = new int[ st.countTokens() ];
669     
670                 for( int i = 0; i < sampleValues.length; i++ )
671                 {
672                     String JavaDoc token = st.nextToken();
673                     try
674                     {
675                         sampleValues[ i ] = Integer.parseInt( token );
676                     }
677                     catch( NumberFormatException JavaDoc e )
678                     {
679                         throw new ConfigurationException( "The compact sample data could not be " +
680                                                           "loaded, because of a number format " +
681                                                           "problem '" + token + "', " +
682                                                           "for InstrumentSample: " +
683                                                           m_name, history );
684                     }
685                 }
686             }
687             
688             // Get the current value
689
int value;
690             if( sampleValues.length > 0 )
691             {
692                 value = sampleValues[ 0 ];
693             }
694             else
695             {
696                 value = 0;
697             }
698             
699             for( int i = 0; i < m_size - 1; i++ )
700             {
701                 if( i < sampleValues.length - 1 )
702                 {
703                     m_historyOld[ m_size - 2 - i ] = sampleValues[ i + 1 ];
704                 }
705                 else
706                 {
707                     m_historyOld[ m_size - 2 - i ] = 0;
708                 }
709             }
710
711             loadState( value, state );
712             
713             // Always cause update to synch the sample with the current system time.
714
long now = System.currentTimeMillis();
715             update = update( now, true );
716             sampleValue = getValueInner();
717             sampleTime = m_time;
718
719             if( m_leaseExpirationTime > 0 )
720             {
721                 // This is a sample that was leased in a previous JVM invocation
722
// and needs to be registered with the InstrumentManager
723
getInstrumentProxy().getInstrumentableProxy().getInstrumentManager().
724                     registerLeasedInstrumentSample( this );
725             }
726         }
727
728         stateChanged();
729         
730         if ( update )
731         {
732             updateListeners( sampleValue, sampleTime );
733         }
734     }
735
736     /*---------------------------------------------------------------
737      * Methods
738      *-------------------------------------------------------------*/

739     /**
740      * Sets the configured flag.
741      */

742     void setConfigured()
743     {
744         m_configured = true;
745     }
746
747     /**
748      * Initializes the sample.
749      *
750      * @param fillValue The value to fill the buffer with.
751      */

752     private void init( int fillValue )
753     {
754         // Calculate an interval time based on the current time by removing the modulous
755
// value of the current time. This will allign the intervals to the start of computer
756
// time.
757
m_time = calculateSampleTime( System.currentTimeMillis() );
758
759         // Create the arrays which will hold the history points.
760
// History is build with m_value holding the current value and all previous values
761
// spanning accross 2 arrays that switch places as time progresses. This completely
762
// removes the need to manage large lists or do array copies.
763
m_historyIndex = 0;
764         if ( m_historyOld == null )
765         {
766             m_historyOld = new int[ m_size - 1 ];
767             m_historyNew = new int[ m_size - 1 ];
768         }
769         for ( int i = 0; i < m_historyOld.length; i++ )
770         {
771             m_historyOld[i] = fillValue;
772             m_historyNew[i] = fillValue;
773         }
774     }
775     
776     /**
777      * Allow subclasses to add information into the saved state.
778      *
779      * @param out PrintWriter to write to.
780      */

781     protected void writeStateAttributes( PrintWriter JavaDoc out )
782     {
783     }
784
785     /**
786      * Used to load the state, called from AbstractInstrumentSample.loadState();
787      * <p>
788      * Should only be called when synchronized.
789      *
790      * @param value Current value loaded from the state.
791      * @param state Configuration object to load state from.
792      *
793      * @throws ConfigurationException If there were any problems loading the
794      * state.
795      */

796     protected abstract void loadState( int value, Configuration state )
797         throws ConfigurationException;
798
799     /**
800      * Calculates the time of the sample which contains the specified time.
801      *
802      * @param time Time whose sample time is requested.
803      */

804     private long calculateSampleTime( long time )
805     {
806         // We want this to round to the nearest interval. For intervals
807
// over an hour, the current time zone needs to be taken into
808
// account so the interval will be alligned correctly.
809
// The timezone offset is calculated once when the class is loaded.
810
long offset = ( time + m_zoneOffset ) % m_interval;
811         return time - offset;
812     }
813
814     /**
815      * Gets the current value. Does not update.
816      * <p>
817      * Should only be called when synchronized.
818      *
819      * @return The current value.
820      */

821     protected abstract int getValueInner();
822
823     /**
824      * The current sample has already been stored. Reset the current sample
825      * and move on to the next.
826      * <p>
827      * Should only be called when synchronized.
828      *
829      * @param reset True if the next sample should be reset.
830      */

831     protected abstract void advanceToNextSample( boolean reset );
832
833     /**
834      * Returns the value to use for filling in the buffer when time is skipped.
835      * <p>
836      * Should only be called when synchronized.
837      */

838     protected abstract int getFillValue();
839
840     /**
841      * Brings the InstrumentSample's time up to date so that a new value can be added.
842      * <p>
843      * Should only be called when synchronized.
844      *
845      * @param time The time to which the InstrumentSample should be brought up to date.
846      * @param reset True if the next sample should be reset if an advance is necessary.
847      *
848      * @return True if listeners should be notified.
849      */

850     protected boolean update( long time, boolean reset )
851     {
852         //System.out.println("update(" + time + ")");
853
// If the lease has already expired, then do nothing
854
if( m_expired )
855         {
856             return false;
857         }
858
859         // See if we are already up to date.
860
if( time - m_time >= m_interval )
861         {
862             // Needs to move to a new sample.
863
if( time - m_time >= m_maxAge )
864             {
865                 // The history is too old, reset the sample.
866
advanceToNextSample( reset );
867                 init( getFillValue() );
868             }
869             else
870             {
871                 // Advance the history index.
872
while( time - m_time >= m_interval )
873                 {
874                     // Store the current value into the end of the history.
875
m_historyNew[ m_historyIndex ] = getValueInner();
876
877                     // Advance to the next sample.
878
m_time += m_interval;
879                     advanceToNextSample( reset );
880                     m_historyIndex++;
881
882                     if( m_historyIndex >= m_size - 1 )
883                     {
884                         // Need to swap the history arrays
885
int[] tmp = m_historyOld;
886                         m_historyOld = m_historyNew;
887                         m_historyNew = tmp;
888
889                         // Reset the history index
890
m_historyIndex = 0;
891                     }
892                 }
893             }
894             return true;
895         }
896         else
897         {
898             return false;
899         }
900     }
901
902     /**
903      * Gets a snapshot of the samples.
904      * <p>
905      * Should only be called after an update when synchronized.
906      *
907      * @return A snapshot of the samples in the InstrumentSample.
908      */

909     private int[] getHistorySnapshot()
910     {
911         // Create a new array to hold the snapshot of the history data.
912
// This method is a little slow but normal collection of sample points is
913
// extremely fast.
914
int[] history = new int[ m_size ];
915
916         int sizem1 = m_size - 1;
917
918         if( m_size > 1 )
919         {
920             // Copy samples from the old history first.
921
if( m_historyIndex < sizem1 )
922             {
923                 // Copy the last (size - 1 - historyIndex) samples from the old history.
924
System.arraycopy( m_historyOld, m_historyIndex, history, 0, sizem1 - m_historyIndex );
925             }
926
927             if( m_historyIndex > 0 )
928             {
929                 // Copy the first (historyIndex) samples from the new history.
930
System.arraycopy( m_historyNew, 0, history, sizem1 - m_historyIndex, m_historyIndex );
931             }
932         }
933         // Get the final sample from the current sample value.
934
history[ m_size - 1 ] = getValueInner();
935
936         return history;
937     }
938     
939     /**
940      * Generates a comma separated list of history values.
941      * <p>
942      * Should only be called after an update when synchronized.
943      *
944      * @return The history values or null if they are all 0.
945      */

946     private String JavaDoc getHistoryList()
947     {
948         int[] history = getHistorySnapshot();
949         
950         // Before we bother building up a string, see if it would be empty, start with the end
951
// as that is the most likely place to find a value.
952
boolean found = false;
953         for ( int i = history.length - 1; i >= 0; i-- )
954         {
955             if ( history[i] != 0 )
956             {
957                 found = true;
958                 break;
959             }
960         }
961         
962         if ( !found )
963         {
964             return null;
965         }
966
967         // Build up a string of the sample points.
968
StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
969         
970         // Store the first value outside the loop to simplify the loop.
971
sb.append( history[ history.length - 1 ] );
972         for( int i = history.length - 2; i >= 0; i-- )
973         {
974             sb.append( ',' );
975             sb.append( history[ i ] );
976         }
977         
978         return sb.toString();
979     }
980
981     /**
982      * Called whenever the state of the sample is changed.
983      */

984     protected void stateChanged()
985     {
986         m_stateVersion++;
987
988         // Propagate to the parent
989
m_instrumentProxy.stateChanged();
990     }
991     
992     /**
993      * Called by the InstrumentProxy class during configuration to make the sample permanent.
994      */

995     void makePermanent()
996     {
997         m_leaseExpirationTime = 0;
998     }
999
1000    /**
1001     * Returns a string representation of the sample.
1002     *
1003     * @return A string representation of the sample.
1004     */

1005    public String JavaDoc toString()
1006    {
1007        return "InstrumentSample[name=" + m_name + ", type=" +
1008            InstrumentSampleUtils.getInstrumentSampleTypeName( getType() ) + ", interval=" +
1009            m_interval + ", size=" + m_size + ", lease=" + m_leaseExpirationTime + "]";
1010    }
1011}
1012
Popular Tags