KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > info > monitorenter > reflection > ObjectRecorder


1 /*
2  * ObjectRecorder, a class that takes records of an objects state using reflection.
3  * Copyright (C) 2002 Achim Westermann, Achim.Westermann@gmx.de.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  * If you modify or optimize the code in a useful way please let me know.
20  * Achim.Westermann@gmx.de
21  */

22 package info.monitorenter.reflection;
23
24 import info.monitorenter.util.TimeStampedValue;
25 import info.monitorenter.util.collections.IRingBuffer;
26 import info.monitorenter.util.collections.RingBufferArrayFast;
27
28 import java.lang.reflect.Field JavaDoc;
29 import java.lang.reflect.InvocationTargetException JavaDoc;
30 import java.lang.reflect.Method JavaDoc;
31 import java.util.LinkedList JavaDoc;
32
33 import javax.naming.directory.NoSuchAttributeException JavaDoc;
34 import javax.swing.event.ChangeEvent JavaDoc;
35 import javax.swing.event.ChangeListener JavaDoc;
36 import javax.swing.event.EventListenerList JavaDoc;
37
38
39 /**
40  * The <code>ObjectRecorder</code> takes records(inspections) of an objects
41  * state using reflection and accessibility- framework.
42  * <p>
43  *
44  * It's strategy is to: <br>
45  *
46  * <pre>
47  * - try to set any field accessible.
48  * - try to get the value of the field.
49  * - if not suceed: try to invoke a bean- conform getter.
50  * - if NoSuchMethod, it's useless (no implementation of MagicClazz here).
51  * </pre>
52  *
53  * <p>
54  *
55  * Furthermore the <code>ObjectRecorder</code> has a history - size (buffer)
56  * and an adjustable distance between each inspection.
57  * <p>
58  *
59  * @author <a HREF='mailto:Achim.Westermann@gmx.de'>Achim Westermann </a>
60  *
61  * @version $Revision: 1.1 $
62  */

63 public class ObjectRecorder extends Thread JavaDoc {
64
65   /**
66    * Data container for the inspection of the internal intance.
67    * <p>
68    *
69    * @author <a HREF="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
70    *
71    *
72    * @version $Revision: 1.1 $
73    */

74   public final class ObjectInspection {
75     /** Timestamp of the inspection. */
76     protected long m_time;
77
78     /** The values taken on the inspection. */
79     private LinkedList JavaDoc m_values;
80
81     /**
82      * Creates an instance linked to the outer recorder.
83      * <p>
84      *
85      */

86     private ObjectInspection() {
87       this.m_time = new java.util.Date JavaDoc().getTime();
88       this.m_values = new LinkedList JavaDoc();
89     }
90
91     /**
92      * Adds an inspected value to this inpsection.
93      * <p>
94      *
95      * @param value
96      * an inspected value of this inpsection.
97      */

98     private void add(final Object JavaDoc value) {
99       this.m_values.add(value);
100     }
101
102     /**
103      * Get the value for the attribute at the given index.
104      * <p>
105      *
106      * @param index
107      * the index of the inspected value according to the order it was
108      * found on the instance by {@link Class#getDeclaredFields()}.
109      * <p>
110      *
111      * @return the value for the attribute at the given index.
112      */

113     public Object JavaDoc get(final int index) {
114       return this.m_values.get(index);
115     }
116
117     /**
118      * Returns the timestamp in ms of this inspection.
119      * <p>
120      *
121      * @return the timestamp in ms of this inspection.
122      */

123     public long getTime() {
124       return this.m_time;
125     }
126
127     /**
128      * Removes the inspected value from this inspection.
129      * <p>
130      *
131      * The value is identified by means of
132      * {@link Object#equals(java.lang.Object)}.
133      * <p>
134      *
135      * @param value
136      * the inspected value from this inspection.
137      */

138     protected void remove(final Object JavaDoc value) {
139       this.m_values.remove(value);
140     }
141
142     /**
143      * Returns a pretty print of this inspection.
144      * <p>
145      *
146      * @return a pretty print of this inspection.
147      *
148      * @see java.lang.Object#toString()
149      */

150     public String JavaDoc toString() {
151       StringBuffer JavaDoc ret = new StringBuffer JavaDoc("\nObjectInspection:\n");
152       ret.append("-----------------\n");
153       ret.append("Inspected: ").append(ObjectRecorder.this.getInspected().toString()).append("\n");
154       ret.append("time: ").append(this.m_time).append("\n");
155       for (int i = ObjectRecorder.this.m_fields.length - 1; i >= 0; i--) {
156         ret.append(ObjectRecorder.this.m_fields[i].getName()).append(": ").append(
157             this.m_values.get(i).toString()).append("\n");
158       }
159       return ret.toString();
160     }
161   }
162
163   /** Verbosity constant. */
164   protected static final boolean VERBOSE = false;
165
166   /** Fast buffer to store recorded fiels. */
167   protected IRingBuffer m_buffer = new RingBufferArrayFast(100);
168
169   /** The listeners on this recorder. */
170   protected EventListenerList JavaDoc m_changeListeners = new EventListenerList JavaDoc();
171
172   /** The fields to inspect on the instance. */
173   protected Field JavaDoc[] m_fields;
174
175   /**
176    * The time - interval between to inspections of the Object.
177    */

178   protected long m_interval;
179
180   /** The instance to instpec. */
181   protected Object JavaDoc m_toinspect;
182
183   /**
184    * Creates an instance that will inspect the given Object in the given time
185    * interval.
186    * <p>
187    *
188    * @param toinspect
189    * the instance to inspect.
190    *
191    * @param interval
192    * the interval of inspection in ms.
193    */

194   public ObjectRecorder(final Object JavaDoc toinspect, final long interval) {
195     this.m_interval = interval;
196     this.m_toinspect = toinspect;
197     // getting the fieldnames.
198
this.m_fields = toinspect.getClass().getDeclaredFields();
199     this.start();
200   }
201
202   /**
203    * Adds a change listener that will be informed about new recordings of the
204    * inpected instances.
205    * <p>
206    *
207    * @param x
208    * the change listener that will be informed about new recordings of
209    * the inpected instances.
210    */

211   public void addChangeListener(final ChangeListener JavaDoc x) {
212     this.m_changeListeners.add(ChangeListener JavaDoc.class, x);
213     // x.stateChanged(new ChangeEvent(this)); // Aufruf des neuen
214
// ChangeListeners um zu aktualisieren.
215
}
216
217   /**
218    * Informs the listeners about a change of this instance.
219    * <p>
220    *
221    */

222   protected void fireChange() {
223     ChangeEvent JavaDoc ce = new ChangeEvent JavaDoc(this);
224     Object JavaDoc[] listeners = this.m_changeListeners.getListenerList();
225     for (int i = listeners.length - 1; i >= 0; i -= 2) {
226       ChangeListener JavaDoc cl = (ChangeListener JavaDoc) listeners[i];
227       cl.stateChanged(ce);
228     }
229   }
230
231   /**
232    * The History returned by this Method represents the past of the field
233    * specified by attributeName. It starts from low index with the newest values
234    * taken from the inspected Object and ends with the oldest.
235    *
236    * @param attributeName
237    * field name of the internal instance to inspect.
238    *
239    * @return An array filled with TimeStampedValues that represent the past of
240    * the last inspections of the field with attributeName.
241    *
242    * @throws NoSuchAttributeException
243    * if the attribute / field described by the given argument does not
244    * exist on the internal Object to instpect.
245    *
246    * @see ObjectRecorder#getInspected()
247    */

248   public TimeStampedValue[] getAttributeHistory(final String JavaDoc attributeName)
249       throws NoSuchAttributeException JavaDoc {
250     // search for the field
251
int attribindex = -1;
252     for (int i = this.m_fields.length - 1; i >= 0; i--) {
253       if (this.m_fields[i].getName().equals(attributeName)) {
254         attribindex = i;
255         break;
256       }
257     }
258     if (attribindex == -1) {
259       throw new NoSuchAttributeException JavaDoc("The Attribute with the name: " + attributeName
260           + " does not exist in " + this.m_toinspect.getClass().getName());
261     }
262     int stop = this.m_buffer.size();
263     TimeStampedValue[] ret = new TimeStampedValue[stop];
264     ObjectInspection tmp;
265     synchronized (this.m_buffer) {
266       java.util.Iterator JavaDoc it = this.m_buffer.iteratorF2L();
267       int i = 0;
268       while (it.hasNext()) {
269         tmp = (ObjectInspection) it.next();
270         ret[i++] = new TimeStampedValue(tmp.getTime(), tmp.get(attribindex));
271       }
272     }
273     return ret;
274   }
275
276   /**
277    * Returns the names of the fields to inspect.
278    * <p>
279    *
280    * @return the names of the fields to inspect.
281    */

282   public String JavaDoc[] getAttributeNames() {
283     String JavaDoc[] ret = new String JavaDoc[this.m_fields.length];
284     for (int i = 0; i < this.m_fields.length; i++) {
285       ret[i] = this.m_fields[i].getName();
286     }
287     return ret;
288
289   }
290
291   /**
292    * Returns the inspected instance.
293    * <p>
294    *
295    * @return the inspected instance.
296    */

297   public Object JavaDoc getInspected() {
298     return this.m_toinspect;
299   }
300
301   /**
302    * Returns the last recoreded value taken from the given field along with the
303    * timestamp identifying the time this value was recored.
304    * <p>
305    *
306    * @param fieldname
307    * the field whose value was recorded.
308    *
309    * @return the last recoreded value taken from the given field along with the
310    * timestamp identifying the time this value was recored.
311    *
312    * @throws NoSuchAttributeException
313    * if no such field exists on the Object to inspect.
314    *
315    */

316   public TimeStampedValue getLastValue(final String JavaDoc fieldname) throws NoSuchAttributeException JavaDoc {
317     // search for the field
318
int attribindex = -1;
319     for (int i = this.m_fields.length - 1; i >= 0; i--) {
320       if (this.m_fields[i].getName().equals(fieldname)) {
321         attribindex = i;
322         break;
323       }
324     }
325     if (attribindex == -1) {
326       throw new NoSuchAttributeException JavaDoc("The Attribute with the name: " + fieldname
327           + " does not exist in " + this.m_toinspect.getClass().getName());
328     }
329     ObjectInspection tmp = (ObjectInspection) this.m_buffer.getYoungest();
330     return new TimeStampedValue(tmp.getTime(), tmp.get(attribindex));
331   }
332
333   /**
334    * Returns the internal fifo buffer that stores the
335    * {@link ObjectRecorder.ObjectInspection} instances that have been done.
336    * <p>
337    *
338    * @return the internal fifo buffer that stores the
339    * {@link ObjectRecorder.ObjectInspection} instances that have been
340    * done.
341    */

342   public IRingBuffer getRingBuffer() {
343     return this.m_buffer;
344   }
345
346   /**
347    * Makes a record of the state of the object specified in the constructor. The
348    * new record is stored in a RingBuffer and contains all retrieveable values
349    * of the Object specified in the constructor. Reflection is used to get the
350    * values. If a field is private it's value is tried to be taken from the
351    * Object by invoking a getter - method conform with the bean - specification:
352    * The name of the method has to be "get" followed by the name of the field
353    * with first letter uppercase.
354    */

355   public void inspect() {
356     ObjectInspection newentry = new ObjectInspection();
357     for (int i = 0; i < this.m_fields.length; i++) {
358       if (ObjectRecorder.VERBOSE) {
359         System.out.println(this.getClass().getName() + " inpspecting " + this.m_fields[i].getName()
360             + " of " + this.m_toinspect.getClass().getName() + ".");
361       }
362       try {
363         this.m_fields[i].setAccessible(true);
364         newentry.add(this.m_fields[i].get(this.m_toinspect));
365       } catch (IllegalAccessException JavaDoc e) {
366         if (ObjectRecorder.VERBOSE) {
367           System.err.println(this.getClass().getName() + ".inspect(): No public access to "
368               + this.m_fields[i].getName() + " of " + this.m_toinspect.getClass().getName());
369         }
370         // Try to invoke bean- conform getter method.
371
String JavaDoc fieldname = this.m_fields[i].getName();
372         char[] fieldnm = fieldname.toCharArray();
373         fieldnm[0] = Character.toUpperCase(fieldnm[0]);
374         fieldname = new String JavaDoc(fieldnm);
375         String JavaDoc methodname = new StringBuffer JavaDoc("get").append(fieldname).toString();
376         // name of method constructed. Now invoke it.
377
try {
378           Method JavaDoc toinvoke = this.m_toinspect.getClass().getDeclaredMethod(methodname,
379               new Class JavaDoc[] {});
380           newentry.add(toinvoke.invoke(this.m_toinspect, new Object JavaDoc[] {}));
381
382         } catch (NoSuchMethodException JavaDoc f) {
383           if (ObjectRecorder.VERBOSE) {
384             System.err.println(this.getClass().getName() + ".inspect(): Failure at getting field "
385                 + this.m_fields[i].getName() + " by trying to invoke a method: " + methodname);
386           }
387         } catch (SecurityException JavaDoc g) {
388           g.printStackTrace();
389         } catch (IllegalAccessException JavaDoc h) {
390           h.printStackTrace();
391         } catch (InvocationTargetException JavaDoc l) {
392           l.printStackTrace();
393         }
394       }
395     }
396     this.m_buffer.add(newentry);
397     this.fireChange();
398   }
399
400   /**
401    * Removes the given listener for changes of the inpsected instance.
402    * <p>
403    *
404    * @param x
405    * the listener to remove.
406    */

407   public void removeChangeListener(final ChangeListener JavaDoc x) {
408     this.m_changeListeners.remove(ChangeListener JavaDoc.class, x);
409   }
410
411   /**
412    *
413    * @see java.lang.Runnable#run()
414    */

415   public void run() {
416     while (true) {
417       try {
418         sleep(this.m_interval);
419       } catch (InterruptedException JavaDoc e) {
420         // nop
421
}
422       this.inspect();
423     }
424   }
425
426   /**
427    * Define the amount of recorded states of the Object to inspect that remain
428    * in memory.
429    * <p>
430    *
431    * Default value is 100.
432    * <p>
433    *
434    * @param length
435    * the amount of recorded states of the Object to inspect that remain
436    * in memory.
437    */

438   public void setHistoryLength(final int length) {
439     this.m_buffer.setBufferSize(length);
440   }
441
442   /**
443    * Sets the interval for inpection of the instance to inspect in ms.
444    * <p>
445    *
446    * @param sleeptime
447    * the interval for inpection of the instance to inspect in ms.
448    *
449    * @see ObjectRecorder#ObjectRecorder(Object, long)
450    */

451   public void setInterval(final long sleeptime) {
452     this.m_interval = sleeptime;
453   }
454
455   /**
456    * @see java.lang.Object#toString()
457    */

458   public String JavaDoc toString() {
459     return this.m_buffer.toString();
460   }
461 }
462
Popular Tags