1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.millscript.commons.alert;
22
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.LinkedList;
26 import java.util.Set;
27
28 /**
29 * This class represents an alert and is used in combination with a normal
30 * exception to indicate a abnormal condition in the system.
31 */
32 public class Alert extends RuntimeException {
33
34 /**
35 * This is the ID from the first release for future compatibility.
36 */
37 private static final long serialVersionUID = 3258416140119128372L;
38
39 /**
40 * This alerts complaint.
41 */
42 private final String complaint;
43
44 /**
45 * List of this alerts culprits.
46 */
47 private final LinkedList<Culprit> culprits = new LinkedList<Culprit>();
48
49 /**
50 * The decorators which have decorated this alert are logged here.
51 */
52 private Set<Object> decorators = new HashSet<Object>();
53
54 /**
55 * Indicates if this alert has had origin information set.
56 */
57 private boolean hasOrigin = false;
58
59 /**
60 * Indicates if this alert is a mishap. Once this flag has been set to
61 * <code>true</code> it should not be set to <code>false</code>, i.e. once
62 * a mishap - always a mishap.
63 * <p>
64 * TODO - In the future this should be replaced by the traffic light system
65 * Steve was thinking about...
66 * </p>
67 */
68 private boolean isMishap = false;
69
70 /**
71 * The underlying alert that caused this alert.
72 */
73 private Alert parentAlert = null;
74
75 /**
76 * The underlying exception that caused this alert.
77 */
78 private Throwable parentThrowable = null;
79
80 /**
81 * The phase the alert occured during.
82 */
83 private Phase phase = null;
84
85 /**
86 * The reason for the complaint.
87 */
88 private String reason = null;
89
90 /**
91 * Constructs a new alert with the specified complaint.
92 *
93 * @param comp the complaint
94 */
95 public Alert( final String comp ) {
96 super( comp );
97 this.complaint = comp;
98 }
99
100 /**
101 * Constructs a new alert with the specified complaint and explanation.
102 *
103 * @param comp the complaint
104 * @param exp the complaints explanation
105 */
106 public Alert( final String comp, final String exp ) {
107 this( comp );
108 this.reason = exp;
109 }
110
111 /**
112 * Constructs a new alert with the specified complaint and alert cause.
113 *
114 * @param comp the complaint
115 * @param pa the alert that caused this alert to be constructed
116 */
117 public Alert( final String comp, final Alert pa ) {
118 this( comp );
119 this.parentAlert = pa;
120 }
121
122 /**
123 * Constructs a new alert with the specified complaint and throwable cause.
124 *
125 * @param comp the complaint
126 * @param pt the throwable that caused this alert to be constructed
127 */
128 public Alert( final String comp, final Throwable pt ) {
129 this( comp );
130 this.parentThrowable = pt;
131 }
132
133 /**
134 * Attach a new culprit to this alert, with a boolean as the culprit.
135 *
136 * @param type the type of culprit
137 * @param value the boolean culprit
138 * @return a reference to this alert.
139 */
140 public Alert culprit( final String type, final boolean value ) {
141 culprits.addLast( new Culprit( type, Boolean.valueOf( value ) ) );
142 return this;
143 }
144
145 /**
146 * Attach a new culprit to this alert, with a byte as the culprit.
147 *
148 * @param type the type of culprit
149 * @param value the byte culprit
150 * @return a reference to this alert.
151 */
152 public Alert culprit( final String type, final byte value ) {
153 culprits.addLast( new Culprit( type, new Byte( value ) ) );
154 return this;
155 }
156
157 /**
158 * Attach a new culprit to this alert, with a char as the culprit.
159 *
160 * @param type the type of culprit
161 * @param value the char culprit
162 * @return a reference to this alert.
163 */
164 public Alert culprit( final String type, final char value ) {
165 culprits.addLast( new Culprit( type, new Character( value ) ) );
166 return this;
167 }
168
169 /**
170 * Attach a new culprit to this alert, with a double as the culprit.
171 *
172 * @param type the type of culprit
173 * @param value the double culprit
174 * @return a reference to this alert.
175 */
176 public Alert culprit( final String type, final double value ) {
177 culprits.addLast( new Culprit( type, new Double( value ) ) );
178 return this;
179 }
180
181 /**
182 * Attach a new culprit to this alert, with a float as the culprit.
183 *
184 * @param type the type of culprit
185 * @param value the float culprit
186 * @return a reference to this alert.
187 */
188 public Alert culprit( final String type, final float value ) {
189 culprits.addLast( new Culprit( type, new Float( value ) ) );
190 return this;
191 }
192
193 /**
194 * Attach a new culprit to this alert, with an int as the culprit.
195 *
196 * @param type the type of culprit
197 * @param value the int culprit
198 * @return a reference to this alert.
199 */
200 public Alert culprit( final String type, final int value ) {
201 culprits.addLast( new Culprit( type, new Integer( value ) ) );
202 return this;
203 }
204
205 /**
206 * Attach a new culprit to this alert, with a long as the culprit.
207 *
208 * @param type the type of culprit
209 * @param value the long culprit
210 * @return a reference to this alert.
211 */
212 public Alert culprit( final String type, final long value ) {
213 culprits.addLast( new Culprit( type, new Long( value ) ) );
214 return this;
215 }
216
217 /**
218 * Attach a new culprit to this alert, with a general object as the
219 * culprit.
220 *
221 * @param type the type of culprit
222 * @param value the object culprit
223 * @return a reference to this alert.
224 */
225 public Alert culprit( final String type, final Object value ) {
226 culprits.addLast( new Culprit( type, value ) );
227 return this;
228 }
229
230 /**
231 * Decorates this alert with the information from the supplied decorator. A
232 * given decorator will only decorate an alert once, on the first call to this
233 * method, regardless of how many times you might subsequently try. This
234 * behaviour is to protect you from repeating yourself in the alert detail
235 * message.
236 *
237 * @param decorator the decorator to get information from
238 * @return this alert after decorating it
239 */
240 public Alert decorate( final AlertDecorator decorator ) {
241 if ( ! decorators.contains( decorator ) ) {
242 decorator.decorate( this );
243 decorators.add( decorator );
244 }
245 return this;
246 }
247
248 /**
249 * Decorates this alert with information from the supplied object. If the
250 * object is an instance of an {@link AlertDecorator}, it's
251 * {@link AlertDecorator#decorate(Alert)} method will be called otherwise a
252 * generic culprit message will be added to the alert.
253 * <p>
254 * The method allows you to hide implementations of AlertDecorator behind
255 * interfaces and such, so you can use alerts without have to include the
256 * AlertDecorator interface in general API's.
257 * </p>
258 *
259 * @param o the object to decorate this alert with
260 * @return this alert after decorating it
261 */
262 public Alert decorate( final Object o ) {
263 if ( ! decorators.contains( o ) ) {
264 if ( o instanceof AlertDecorator ) {
265 ( (AlertDecorator) o ).decorate( this );
266 } else {
267 this.culprit( "object", o.toString() );
268 }
269 decorators.add( o );
270 }
271 return this;
272 }
273
274 /**
275 * Reports this alert as an escape, by throwing it wrapped in an escape
276 * exception. This is intended to be used as a by-pass for your normal
277 * alert handling code.
278 *
279 * @return a new escape exception wrapping this alert
280 */
281 public EscapeException escape() {
282 throw new EscapeException( this );
283 }
284
285 /**
286 * Returns this alerts complaint.
287 *
288 * @return a String with this alerts complaint
289 */
290 public String getComplaint() {
291 return complaint;
292 }
293
294 /**
295 * Returns this alerts culprits.
296 *
297 * @return a LinkedList of this alerts culprits.
298 */
299 public LinkedList< Culprit > getCulprits() {
300 return culprits;
301 }
302
303 /**
304 * @see java.lang.Throwable#getMessage()
305 */
306 @Override
307 public String getMessage() {
308 final StringBuffer buffer = new StringBuffer();
309 Iterator it = this.culprits.iterator();
310 while ( it.hasNext() ) {
311 buffer.append( it.next() );
312 buffer.append( '\n' );
313 }
314 return buffer.toString();
315 }
316
317 /**
318 * Returns the Alert that caused this Alert to be generated.
319 *
320 * @return the Alert that caused this Alert to be generated
321 */
322 public Alert getParentAlert() {
323 return parentAlert;
324 }
325
326 /**
327 * Returns the Throwable that caused this Alert to be generated.
328 *
329 * @return the Throwable that caused this Alert to be generated
330 */
331 public Throwable getParentThrowable() {
332 return parentThrowable;
333 }
334
335 /**
336 * Returns the phase this Alert occured in.
337 *
338 * @return the Phase this Alert occured in
339 */
340 public Phase getPhase() {
341 return phase;
342 }
343
344 /**
345 * Returns the reason for this Alerts complaint
346 *
347 * @return the reason for this Alerts complaint
348 */
349 public String getReason() {
350 return reason;
351 }
352
353 /**
354 * Returns the mishap indicator flag, <code>true</code> if this
355 * {@link Alert} is a mishap and <code>false</code> otherwise.
356 *
357 * @return <code>true</code> if this {@link Alert} is a mishap and
358 * <code>false</code> otherwise
359 */
360 public boolean isMishap() {
361 return isMishap;
362 }
363
364 /**
365 * Marks this alert as a mishap and throws itself.
366 *
367 * @return this {@link Alert}
368 */
369 public Alert mishap() {
370
371 this.isMishap = true;
372
373
374 throw this;
375 }
376
377 /**
378 * Decorates this alert with origin information from the supplied alert
379 * origin. An alert can only have one set of origin information, so once
380 * set it cannot be set again.
381 *
382 * @param origin the decorator to get information from
383 * @return this alert after decorating it
384 */
385 public Alert origin( final AlertOrigin origin ) {
386 if ( ! this.hasOrigin ) {
387 if ( origin.getOrigin() != null ) {
388 this.culprit( "origin", origin.getOrigin() );
389 }
390 if ( origin.getLineNumber() > 0 ) {
391 this.culprit( "line no.", new Integer( origin.getLineNumber() ) );
392 }
393 this.hasOrigin = true;
394 }
395 return this;
396 }
397
398 /**
399 * Reports this alert as a mishap, by throwing it as an exception. This is
400 * intended to be used when this alert needs to be re-thrown after being
401 * caught but not then chained.
402 *
403 * @return an AlertException for this alert
404 */
405 public Alert remishap() {
406 throw this;
407 }
408
409 /**
410 * Reports this alert to the specified reporter as a mishap.
411 *
412 * @param reporter the AlertReporter to report this mishap Alert to
413 */
414 public void report( final AlertReporter reporter ) {
415 reporter.report( this );
416 }
417
418 /**
419 * Sets the parent alert for this alert.
420 *
421 * @param alert the parent alert
422 * @return this alert
423 */
424 public Alert setParentAlert( final Alert alert ) {
425 this.parentAlert = alert;
426 return this;
427 }
428
429 /**
430 * Sets the parent throwable for this alert.
431 *
432 * @param throwable the throwable that caused this alert
433 * @return this alert
434 */
435 public Alert setParentThrowable( final Throwable throwable ) {
436 this.parentThrowable = throwable;
437 return this;
438 }
439
440 /**
441 * Sets the phase this alert occured in.
442 *
443 * @param p the phase for this alert
444 */
445 public Alert setPhase( final Phase p ) {
446 if ( this.phase == null ) {
447 this.phase = p;
448 } else {
449
450
451
452
453
454
455
456
457
458 }
459 return this;
460 }
461
462 /**
463 * @see java.lang.Object#toString()
464 */
465 @Override
466 public String toString() {
467 final StringBuffer buffer = new StringBuffer();
468 buffer.append( this.getMessage() );
469 buffer.append( this.getClass().getName() );
470 return buffer.toString();
471 }
472
473 }