1 /**
2 * .
3 * Copyright � 1999 Erich P G.
4 *
5 */
6
7 package autohit.utils;
8
9 import java.util.Vector;
10 import java.util.Date;
11 import java.io.InputStream;
12 import java.io.FileInputStream;
13 import java.io.FileOutputStream;
14 import java.io.EOFException;
15 import java.io.File;
16 import java.io.FileWriter;
17 import java.io.BufferedWriter;
18
19 import autohit.vm.VM;
20
21 /**
22 * A logging mechanism. It will add timing information and is syncronized
23 * so more than one thread can post. The data is put in a temp file, so
24 * memory usage should not get out of hand ( as does a StringBuffer).
25 * <p>
26 * All entries will automatically have a LF appended to the end. Timing
27 * information shows up to 7 digits--[0000000]. Granularity is passed to the
28 * constructor; note that the timing will automatically be scaled
29 * by the VM class. DO NOT USE the default constructor.
30 * <p>
31 * Note that while the log timing does support pause() and resume(), it
32 * does not account for the timing of client threads, so timing information
33 * for items posted by threads may or may not be completely accurate. (For
34 * instance, pausing all the running VMs AND the Log will not happen
35 * instantaniously, so there may be some variances in timing when they
36 * are all resumed.)
37 * <p>
38 * To ensure that the temp file is removed, call the close() method when
39 * done.
40 * <p>
41 * Any exceptions that are caught during a put() or putSub() are caught
42 * and ignored. I really don't expect any to come out of those
43 * methods, and if they do, then other REAL bad stuff is probibly
44 * already underway...
45 * <p>
46 * This class actually extends the VM base class, as it provides the
47 * timing functionality that we want.
48 * <p>
49 * NOTE! The timing is started on construction. If you want to wait,
50 * immeadiatly call the pause() method.
51 *
52 * @see autohit.vm.VM
53 *
54 * @author Erich P. Gatejen
55 * @version 1.0
56 * <i>Version History</i>
57 * <code>EPG - Initial - 15Jan99</code>
58 *
59 */
60 public class Log extends VM {
61
62 // --- FINAL FIELDS ------------------------------------------------------
63
64 /*
65 * Speed things up a bit...
66 */
67 String zeros[] = { "0000000", "000000", "00000", "0000",
68 "000", "00", "0", "" };
69
70
71 // --- FIELDS ------------------------------------------------------------
72
73 /**
74 * The temporary file.
75 */
76 private File tempFile;
77
78 /**
79 * An output writer for the tempFile;
80 */
81 private BufferedWriter out;
82
83 /**
84 * An input stream for reading the log temp file.
85 */
86 private FileInputStream is = null;
87
88 /**
89 * The object monitor for this log.
90 */
91 private ObjectMonitor lock;
92
93 /**
94 * Granulatiry for each tick of the VM's clock. This will
95 * scale the tick value.
96 *
97 * A typical value would be 1000, as this would show timing
98 * information in seconds (given that the VM class granularity
99 * is set to milliseconds).
100 */
101 public int gran;
102
103 /**
104 * Constructor. It throws any exceptions related to creating
105 * the temporary file.
106 *
107 * It will start the timing. See the VM base class to see how
108 * timing is implimented.
109 *
110 * @param granulariy granularity for timing information. This
111 * should be a Positive number. If it is
112 * 0, the constructor will throw an Exception
113 * (since it would cause divide by 0 exceptions
114 * in other methods of this class).
115 *
116 * @throws Exception
117 * @see autohit.vm.VM
118 */
119 public Log(int granularity) throws Exception {
120 super();
121
122 lock = new ObjectMonitor();
123
124 gran = granularity;
125 if (gran == 0) { throw new Exception("Zero granularity."); }
126
127 try {
128
129 // Base the tempfile name on the system time;
130 Date d = new Date();
131 tempFile = File.createTempFile
132 (Integer.toString((int)d.getTime()),".tmp");
133
134 out = new BufferedWriter(new FileWriter(tempFile));
135
136 } catch (Exception e) { throw e; }
137
138 this.start();
139 }
140
141 /**
142 * Put an entry in the log. It will append timing information.
143 *
144 * @param text the text to log.
145 */
146 public void put(String text) {
147
148 lock.lock(true);
149
150 try {
151 if (state == STATE_PAUSED) return;
152
153 // Build the timing field
154 String num = Integer.toString(this.ticks()/gran);
155 int l = num.length();
156 if (l < 8) {
157 out.write("[" + zeros[l] + num + "] ", 0, 10);
158 } else {
159 out.write("[" + num + "] ", 0, l + 3);
160 }
161
162 out.write(text, 0, text.length());
163 out.write("\n", 0, 1);
164
165 } catch (Exception e) {}
166
167 lock.lock(false);
168 }
169
170 /**
171 * Put a sub-entry in the log. It will not append timing
172 * information. It will be tabbed to match the time text.
173 *
174 * @param text the text to log.
175 */
176 public void putSub(String text) {
177
178 lock.lock(true);
179
180 if (state == VM.STATE_PAUSED) return;
181
182 try {
183 out.write(" ", 0, 10);
184 out.write(text, 0, text.length());
185 out.write("\n", 0, 1);
186
187 } catch (Exception e) {}
188
189 lock.lock(false);
190 }
191
192 /**
193 * Done with the log. It returns an InputStream from which the
194 * log can be read. Once this method is called, no further
195 * entries will be logged when calling put() or putSub() (though
196 * no error will occur).
197 *
198 * This method (or its overloaded peer) can only be called once. After that, a null
199 * will be returned, instead of an InputStream.
200 *
201 * @return an InputStream presenting the log data.
202 */
203 public InputStream done() {
204
205 lock.lock(true);
206
207 if (state != VM.STATE_DONE) {
208 state = VM.STATE_DONE;
209
210 try {
211 out.close();
212
213 is = new FileInputStream(tempFile);
214
215 } catch (Exception e) {}
216
217 }
218
219 lock.lock(false);
220 return is;
221 }
222
223 /**
224 * Done with the log. Returns a File pointing to a file where
225 * the log resides.
226 *
227 * This method (or its overloaded peer) can only be called once. After that, a null
228 * will be returned, instead of a File.
229 *
230 * @param pathName pathname to the file where the log should be written.
231 *
232 * @return a File describing the new log file.
233 */
234 public File done(String pathName) {
235
236 File lf = null;
237
238 lock.lock(true);
239
240 if (state != VM.STATE_DONE) {
241 state = VM.STATE_DONE;
242
243 // If for any reason something goes wrong, abort the whole affair.
244 try {
245 out.close();
246
247 File fout = new File(pathName);
248
249
250 FileInputStream fis = new FileInputStream(tempFile);
251 FileOutputStream fos = new FileOutputStream(fout);
252
253 try {
254
255 byte[] buf = new byte[512];
256 int a = fis.read(buf);
257 while (a > 0) {
258
259 fos.write(buf, 0, a);
260 a = fis.read(buf);
261 }
262
263 } catch (EOFException e) {
264 // arggrgr. The API doc LIES! sometimes this gets thrown...
265 } catch (Exception e) {
266 throw e;
267 }
268
269 fis.close();
270 fos.close();
271 lf = fout;
272
273 } catch (Exception e) {
274 }
275
276 }
277
278 lock.lock(false);
279 return lf;
280 }
281
282 /**
283 * Close the log and free/close any resources.
284 */
285 public void close() {
286
287 try {
288 if (is != null) is.close();
289 } catch (Exception e) {}
290
291 try {
292
293 tempFile.delete();
294 } catch (Exception e) {}
295
296 }
297
298 /**
299 * Doesn't do a thing. This isn't an actual VM. However,
300 * we have to impliment it since Log extends the abstract VM.
301 */
302 public void execute() { }
303
304 }
|