1 /**
2 * AUTOHIT 2003
3 * Copyright Erich P Gatejen (c) 1989,1997,2003,2004
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Additional license information can be found in the documentation.
19 * @author Erich P Gatejen
20 */
21 package autohit.common;
22
23 import java.io.OutputStream;
24 import java.io.OutputStreamWriter;
25 import java.io.Writer;
26 import java.util.Calendar;
27 import java.util.GregorianCalendar;
28
29 import autohit.common.channels.Atom;
30 import autohit.common.channels.ChannelException;
31 import autohit.common.channels.Drain;
32 import autohit.common.channels.Receipt;
33
34 /**
35 * An abstract logging formatter for Autohit Tools. It will try to log anything that
36 * has a String in the atom. You can route Atom.senderIDs to different
37 * output streams.
38 * <p>
39 * The subclasses need to implement a setWriter() and initchain().
40 * <p>
41 * A pretty flag tells the formatter to keep the lines under
42 * 80 characters, applying wrapping where possible. TRUE means
43 * pretty. The default is FALSE. It will use the line.separator
44 * property to put a return on the wrapped lines.
45 * <p>
46 * NOT STAMPED
47 * <code>
48 * 0123456789...............{79}
49 * IINNNN:tttttttttt......t[EOL]
50 * :tttttttttt......t[EOL]
51 * [ 7 ][ 69 ]
52 * </code>
53 * <p>
54 * STAMPED (with default formatter)
55 * We will reserve 4 extra spacers that an overloading formatter can use.
56 * <code>
57 * 012345678901234567890123456789...............{79}
58 * IINNNN[DD:HHMMSS]tttttttttt.................t[EOL]
59 * ]tttttttttt.................t[EOL]
60 * [ 6 ][9+3extra ][ 54 ]
61 * [18] + [61] = 79
62 * </code>
63 *
64 * Cs0000:223600: XMLCompiler: Software Detected Fault: SAXException mad
65 e it out of the builder.
66 * @author Erich P. Gatejen
67 * @version 1.0
68 * <i>Version History</i>
69 * <code>EPG - Rewrite - 27Apr03</code>
70 *
71 */
72 public abstract class AutohitLogDrain implements Drain {
73
74 private boolean prettyFlag;
75 private boolean stampFlag;
76 private String lineSep;
77 private Calendar cachedCalendar;
78 private OutputStream output;
79 protected Writer myWriter;
80 private Formatter formatter;
81
82 static final String EMPTY_MESSAGE_STRING = "No text in entry.";
83
84 /**
85 * Default constructor
86 * You must init() the drain before it is valid.
87 */
88 public AutohitLogDrain() {
89
90 }
91
92 /**
93 * Post an item
94 * Don't care about receipts
95 * @return null -- always
96 */
97 public Receipt post(Atom a) throws ChannelException {
98
99 // TODO do I really care if AutohitLogDrain requires a senderID
100 if (a.senderID == null) {
101 throw new ChannelException(
102 "AutohitLogDrain required senderID",
103 ChannelException.CODE_CHANNEL_DRAIN_REQUIRES_ID_ERROR);
104 }
105
106 try {
107
108 // prequalify the atom. Print anything that has a string.
109 if (!(a.thing instanceof String))
110 return null;
111
112 setWriter(a.senderID);
113
114 if (stampFlag == true) {
115 if (prettyFlag == true) {
116 logStampPretty((Atom) a);
117 } else {
118 logStamp((Atom) a);
119 }
120 } else {
121 if (prettyFlag == true) {
122 logNoStampPretty((Atom) a);
123 } else {
124 logNoStamp((Atom) a);
125 }
126 }
127
128 } catch (ClassCastException ce) {
129 throw new ChannelException(
130 "AutohitLogDrain failed. Something sent a TYPE.Log that isn't a String.",
131 AutohitErrorCodes.CODE_VM_SOFTWARE_DETECTED_FAULT);
132
133 } catch (Exception e) {
134 throw new ChannelException(
135 "AutohitLogDrain failed drain post. " + e.getMessage(),
136 ChannelException.CODE_CHANNEL_FAULT,
137 e);
138 }
139 // don't care about receipts
140 return null;
141 }
142
143 /**
144 * Log with no timestamp
145 * @param a the atom
146 */
147 private void logNoStamp(Atom a) {
148 String s = (String) a.thing;
149 if (s.length() == 0)
150 s = EMPTY_MESSAGE_STRING;
151
152 try {
153
154 if (s.charAt(0) == ':') {
155 myWriter.write(" :");
156 } else {
157 myWriter.write(a.senderID);
158 myWriter.write(numericFormatter(a.numeric));
159 myWriter.write(":");
160 }
161 myWriter.write(s);
162 myWriter.write(lineSep);
163 myWriter.flush();
164
165 } catch (Exception e) {
166 this.dump(e);
167 }
168 }
169
170 /**
171 * Log with no timestamp
172 * @param a the atom
173 */
174 private void logNoStampPretty(Atom a) {
175
176 String s = (String) a.thing;
177 if (s.length() == 0)
178 s = EMPTY_MESSAGE_STRING;
179
180 try {
181 formatter.prefixedWriter(
182 a.senderID + numericFormatter(a.numeric) + "]",
183 " ]",
184 s,
185 myWriter);
186 } catch (Exception e) {
187 this.dump(e);
188 }
189
190 }
191
192 /**
193 * Log with a timestamp with no pretty
194 * @param a the atom
195 * @throws ChannelException
196 */
197 private void logStamp(Atom a) throws ChannelException {
198
199 String s = (String) a.thing;
200 if (s.length() == 0)
201 s = EMPTY_MESSAGE_STRING;
202
203 try {
204 if (s.charAt(0) == ':') {
205 myWriter.write(" ]");
206 } else {
207 myWriter.write(a.senderID);
208 myWriter.write(numericFormatter(a.numeric));
209 myWriter.write(this.timestampFormatter(a.stamp));
210 }
211 myWriter.write(s);
212 myWriter.write(lineSep);
213 myWriter.flush();
214
215 } catch (Exception e) {
216 this.dump(e);
217 }
218 }
219
220 /**
221 * Log with a timestamp and pretty format
222 * @param a the atom
223 */
224 private void logStampPretty(Atom a) throws ChannelException {
225
226 String s = (String) a.thing;
227 if (s.length() == 0)
228 s = EMPTY_MESSAGE_STRING;
229
230 try {
231 formatter.prefixedWriter(
232 a.senderID
233 + numericFormatter(a.numeric)
234 + this.timestampFormatter(a.stamp),
235 " ]",
236 s,
237 myWriter);
238 } catch (Exception e) {
239 this.dump(e);
240 }
241 }
242
243 /**
244 * This formats the the numeric into a four digit string.
245 * @param n numeric value
246 * @return a string representation of the numeric
247 */
248 public String numericFormatter(int n) {
249
250 StringBuffer buf = new StringBuffer();
251
252 if (n < 0) {
253 return "UNDR";
254 } else if (n < 10) {
255 return "000" + n;
256 } else if (n < 100) {
257 return "00" + n;
258 } else if (n < 1000) {
259 return "0" + n;
260 } else if (n < 10000) {
261 return Integer.toString(n);
262 }
263 return "OVER";
264 }
265
266 /**
267 * This formats the timestamp for stamped entries. You can overload
268 * this if you want a different format.
269 * This one will do [DD:HHMMSS]
270 * @param stamp timestamp in milliseconds. This formatter assumes it is the unmodified system time.
271 * @return a string representation of the timestamp
272 */
273 public String timestampFormatter(long stamp) {
274
275 int t;
276 cachedCalendar.setTimeInMillis(stamp);
277 StringBuffer buf = new StringBuffer(cachedCalendar.get(Calendar.DATE));
278 buf.append('[');
279
280 t = cachedCalendar.get(Calendar.DAY_OF_MONTH);
281 if (t < 10) {
282 buf.append("0" + t);
283 } else {
284 buf.append(t);
285 }
286 buf.append(':');
287
288 t = cachedCalendar.get(Calendar.HOUR_OF_DAY);
289 if (t < 10) {
290 buf.append("0" + t);
291 } else {
292 buf.append(t);
293 }
294 t = cachedCalendar.get(Calendar.MINUTE);
295 if (t < 10) {
296 buf.append("0" + t);
297 } else {
298 buf.append(t);
299 }
300 t = cachedCalendar.get(Calendar.SECOND);
301 if (t < 10) {
302 buf.append("0" + t);
303 } else {
304 buf.append(t);
305 }
306 buf.append(']');
307 return buf.toString();
308 }
309
310 /**
311 * Form this string into an entry.
312 * @param s string
313 * @return formed string
314 */
315 protected String form(String s) {
316
317 StringBuffer t = new StringBuffer();
318 int runlen = formatter.lineLength - 10;
319
320 if (s.charAt(0) == ':') {
321 t.append(" ");
322 t.append(s);
323 } else if (prettyFlag) {
324
325 int rover = 0;
326 int endspot = s.length();
327
328 try {
329
330 while (rover < formatter.lineLimit) {
331 if ((rover + runlen) >= endspot) {
332 t.append(s.substring(rover, endspot));
333 break;
334 } else {
335 t.append(s.substring(rover, rover + runlen));
336 t.append(lineSep);
337 rover = rover + runlen;
338 }
339 }
340
341 } catch (IndexOutOfBoundsException e) {
342 // ignore this one. :^)
343 }
344
345 } else {
346 t.append("--");
347 t.append(" : ");
348 t.append(s);
349 }
350 return t.toString();
351 }
352
353 /**
354 * This sets the pretty flag. This will try and keep log lines
355 * at under 80 characters, applying wrapping where possible.
356 * TRUE means pretty. The default is FALSE. Not syncronized. It
357 * will take effect on the next log write.
358 * @param b
359 */
360 public void setPrettyFlag(boolean b) {
361 prettyFlag = b;
362 }
363
364 /**
365 * This sets the timestamp flag. This will tell it to timestamp the
366 * entries. TRUE means do it. The default is FALSE. Not syncronized. It
367 * will take effect on the next log write.
368 * @param b
369 */
370 public void setTimestampFlag(boolean b) {
371 stampFlag = b;
372 }
373
374 /**
375 * Set the max number characters printed per line
376 * @param limit the number of characters printed in a line
377 */
378 public void setLineLimit(int limit) {
379 formatter.lineLimit = limit;
380 }
381
382
383
384 /**
385 * Dump
386 * @param e Exception object, not an thrown exception
387 */
388 private void dump(Exception e) {
389 System.out.println("!!!!!!!!!LOGGING SYSTEM FAILED!!!!!!!!!!!");
390 e.printStackTrace();
391 }
392
393 /**
394 * The subclass uses this to set the Writer. the Writer is the
395 * field myWriter.
396 * @param id
397 */
398 public abstract void setWriter(String id) throws Exception;
399
400 /**
401 * The subclass uses this to discard the Writer. It says this id isn't
402 * being used anymore.
403 * @param id
404 */
405 public abstract void discardWriter(String id) throws Exception;
406
407 /**
408 * The subclass should implement this to do any initialization.
409 */
410 public abstract void initchain();
411
412 /**
413 * Call this to initialize. Need to provide at least a default Stream.
414 */
415 public void init(OutputStream os) throws Exception {
416
417 output = os;
418 myWriter = new OutputStreamWriter(output);
419
420 prettyFlag = false;
421 stampFlag = false;
422 lineSep = System.getProperty("line.separator");
423
424 cachedCalendar = new GregorianCalendar();
425
426 formatter = new Formatter();
427 formatter.lineLimit = AutohitProperties.LOGS_ARBITRARY_ENTRY_LIMIT_DEFAULT;
428 formatter.lineLength = AutohitProperties.LOGS_LINE_SIZE_DEFAULT;
429
430 // chain the initialization - don't change this!
431 this.initchain();
432 }
433
434 /**
435 * Call this to initialize. Need to provide at least a default Stream.
436 */
437 public void init(OutputStream os, int linesize) throws Exception {
438 this.init(os);
439 formatter.lineLength = linesize;
440 }
441
442 }
|