Deprecated Methods: stop(), suspend(), and resume()

Chapter 12 - Exception Callback

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Additional Methods to Support ExceptionListener
In the monitored class, three new methods are needed to support the existence of optional external ExceptionListener observers:
public void addExceptionListener(ExceptionListener l)
public void removeExceptionListener(ExceptionListener l)
private void sendException(Exception x)
The method addExceptionListener() adds a new listener for exceptions within the active object. The method removeExceptionListener() is used to clear out the current listener, if it exists, without setting a new one so that there are no longer any listeners.
Inside the active object, the private method sendException() should be invoked whenever an exception occurs that should be noted. Not all exceptions should be reported . For example, when InterruptedException is caught, it is generally not reported but used as a signal that someone has requested that the internal thread clean up and die soon.
For those exceptions that are reported, sendException() passes the exception on to all of the ExceptionListener s that have been added. If no listeners have been added, it is generally a good idea to instead print a stack trace to the console for the exception.
After the exception is reported, the internal thread can take different actions. If the exception was not very serious and can be worked around, the internal thread should proceed. If the exception is a critical error, the thread should clean up and return from run() soon.
The class ExceptionCallback , shown in Listing 12.2, supports monitoring by an ExceptionListener .
Listing 12.2  ExceptionCallback.javaAn Active Object That Supports Monitoring by an ExceptionListener
  1: import java.io.*;
  2: import java.util.*;
  3:
  4: public class ExceptionCallback extends Object {
  5:     private Set exceptionListeners;
  6:     private Thread internalThread;
  7:     private volatile boolean noStopRequested;
  8:
  9:     public ExceptionCallback(ExceptionListener[] initialGroup) {
10:         init(initialGroup);
11:     }
12:
13:     public ExceptionCallback(ExceptionListener initialListener) {
14:         ExceptionListener[] group = new ExceptionListener[1];
15:         group[0] = initialListener;
16:         init(group);
17:     }
18:
19:     public ExceptionCallback() {
20:         init(null);
21:     }
22:
23:     private void init(ExceptionListener[] initialGroup) {
24:         System.out.println(in constructor - initializing...);
25:
26:         exceptionListeners =
27:                 Collections.synchronizedSet(new HashSet());
28:
29:         // If any listeners should be added before the internal
30:         // thread starts, add them now.
31:         if (initialGroup != null) {
32:             for (int i = 0; i < initialGroup.length; i++) {
33:                 addExceptionListener(initialGroup[i]);
34:             }
35:         }
36:
37:         // Just before returning from the constructor,
38:         // the thread should be created and started.
39:         noStopRequested = true;
40:
41:         Runnable r = new Runnable() {
42:                 public void run() {
43:                     try {
44:                         runWork();
45:                     } catch (Exception x) {
46:                         // in case ANY exception slips through
47:                         sendException(x);
48:                     }
49:                 }
50:             };
51:
52:         internalThread = new Thread(r);
53:         internalThread.start();
54:     }
55:
56:     private void runWork() {
57:         try {
58:             makeConnection(); // will throw an IOException
59:         } catch (IOException x) {
60:             sendException(x);
61:             // Probably in a real scenario, a return
62:             // statement should be here.
63:         }
64:
65:         String str = null;
66:         int len = determineLength(str); // NullPointerException
67:     }
68:
69:     private void makeConnection() throws IOException {
70:         // A NumberFormatException will be thrown when
71:         // this String is parsed.
72:         String portStr = j20;
73:         int port = 0;
74:
75:         try {
76:             port = Integer.parseInt(portStr);
77:         } catch (NumberFormatException x) {
78:             sendException(x);
79:             port = 80; // use default;
80:         }
81:
82:         connectToPort(port); // will throw an IOException
83:     }
84:
85:     private void connectToPort(int portNum) throws IOException {
86:         throw new IOException(connection refused );
87:     }
88:
89:     private int determineLength(String s) {
90:         return s.length();
91:     }
92:
93:     public void stopRequest() {
94:         noStopRequested = false;
95:         internalThread.interrupt();
96:     }
97:
98:     public boolean isAlive() {
99:         return internalThread.isAlive();
100:     }
101:
102:     private void sendException(Exception x) {
103:         if (exceptionListeners. size () == 0) {
104:             // If there arent any listeners, dump the stack
105:             // trace to the console.
106:             x.printStackTrace();
107:             return;
108:         }
109:
110:         // Used synchronized to make sure that other threads
111:         // do not make changes to the Set while iterating.
112:         synchronized (exceptionListeners) {
113:             Iterator iter = exceptionListeners.iterator();
114:             while (iter.hasNext()) {
115:                 ExceptionListener l =
116:                         (ExceptionListener) iter. next ();
117:
118:                 l.exceptionOccurred(x, this);
119:             }
120:         }
121:     }
122:
123:     public void addExceptionListener(ExceptionListener l) {
124:         // Silently ignore a request to add a null listener.
125:         if (l != null) {
126:             // If a listener was already in the Set, it will
127:             // silently replace itself so that no duplicates
128:             // accumulate.
129:             exceptionListeners.add(l);
130:         }
131:     }
132:
133:     public void removeExceptionListener(ExceptionListener l) {
134:         // Silently ignore a request to remove a listener
135:         // that is not in the Set.
136:         exceptionListeners.remove(l);
137:     }
138:
139:     public String toString() {
140:         return getClass().getName() +
141:             [isAlive()= + isAlive() + ];
142:     }
143: }
This class uses the self-running, anonymous inner class pattern explained in Chapter 11, Self-Running Objects. It expands on it to include support for monitoring by an ExceptionListener .
There are three constructors and they all invoke init() . The first constructor (lines 911) takes an ExceptionListener[] as a parameter. All of the listeners in the array will be added before the internal thread is started to ensure that no exceptions are missed. The second constructor (lines 1317) takes a single ExceptionListener as a parameter. This listener will be added before the internal thread is started. The third constructor (lines 1921) is used when no initial listeners are needed.
All three constructors end up calling init() (lines 2354) to finish up the object initialization. The member variable exceptionListeners refers to a Set that is holding the group of current ExceptionListener s (line 5). I used a Set to automatically keep out duplicate listeners. Specifically, a HashSet is used and wrapped in synchronization for multithread safety (lines 2627). For more information on using the Collections API in a multithreaded environment, see Chapter 7 . If any ExceptionListener s should be added before the internal thread is started, they are passed one by one to addExceptionListener() (lines 3135). Inside the anonymous inner class, if any exception slips through from runWork() (line 44), it is caught (line 45) and sent to sendException() (line 47).
Inside runWork() (lines 5667), a few methods are called to produce some mock exceptions. First, makeConnection() is invoked. It declares that it might throw an IOException , so a try / catch block is used and if an IOException is thrown, it will be passed to sendException() (line 60). In the real world, this might be a serious enough error that a return should be used to let the thread die, but in this scenario, Ill let it proceed to create a null String reference (line 65). This reference is passed to determineLength() (line 66). Although determineLength() might throw a NullPointerException (and in this case it does!), no try / catch clause is required because NullPointerException is a subclass of RuntimeException . If the exception occurs, it will propagate up the call stack to run() in the inner class, and there it will be caught (line 45) and passed to sendException() (line 47).
The makeConnection() method (lines 6983) first tries to parse the string j20 (line 72) into an integer (line 76). It will fail because of the j in the string, and a NumberFormatException will be thrown. This exception is caught (line 77) and passed off to sendException() for logging purposes, and a default port number of 80 is used instead, and then processing proceeds. This might be more representative of a real-world situation: one where you want to report the error, but also want to continue with a reasonable default value. This default port number is then passed to the connectToPort() method (line 82). The connectToPort() method (lines 8587) simply throws a new IOException with the message connection refused . This exception will propagate up to runWork() , will be caught there (line 59), and reported (line 60).
All that determineLength() (lines 8991) does is to return the length of the string passed into it. In this case, it will be passed null , which will cause a NullPointerException to be thrown. This exception will propagate all the way back up to run() and be caught there (line 45).
The private method sendException() (lines 102121) first checks to see if there are any listeners (line 103). If there currently are no listeners, a stack trace of the exception is printed to the console (line 106) and the method returns right away (line 107). If there are listeners in the set, each one has its exceptionOccurred() method invoked passing in the exception and a reference to this in case the listener needs to know in which object the exception occurred (lines 112120).
The addExceptionListener() method (lines 123131) is used to add an ExceptionListener to the set of listeners that will be called if an exception occurs. If null is passed in, it is silently ignored (line 125). Otherwise, the new listener is added to the set of current listeners. Because Set does not allow duplicates, if the new listener is already in the Set , it is silently replaced with itself (line 129). In addition, because the Set was wrapped in synchronization (lines 2627), adding elements is thread-safe and will temporarily block if sendException() is currently notifying the listeners.
The removeExceptionListener() method (lines 133137) is used to stop a specific ExceptionListener from receiving any more exceptions. If the specified listener is not in the Set , the request to remove it is silently ignored. In addition, because the Set was wrapped in synchronization (lines 2627), removing elements is thread-safe and will temporarily block if sendException() is currently notifying the listeners.
ExceptionCallbackMain in Listing 12.3 demonstrates how ExceptionCallback can be monitored.
Listing 12.3  ExceptionCallbackMain.javaUsed to Demonstrate ExceptionCallback
1: public class ExceptionCallbackMain
2:         extends Object
3:         implements ExceptionListener {
4:
5:     private int exceptionCount;
6:
7:     public ExceptionCallbackMain() {
8:         exceptionCount = 0;
9:     }
10:
11:     public void exceptionOccurred(Exception x, Object source) {
12:         exceptionCount++;
13:         System.err.println(EXCEPTION # + exceptionCount +
14:                 , source= + source);
15:         x.printStackTrace();
16:     }
17:
18:     public static void main(String[] args) {
19:         ExceptionListener xListener = new ExceptionCallbackMain();
20:         ExceptionCallback ec = new ExceptionCallback(xListener);
21:     }
22: }
ExceptionCallbackMain implements the ExceptionListener interface (line 3) so that it will have its exceptionOccurred() method called when an object that it is monitoring throws an exception. The variable exceptionCount (line 5) is simply used to keep track of how many exceptions are reported.
When the exceptionOccurred() method is called, exceptionCount is incremented and a header message is printed along with the results of invoking toString() on the source object (lines 1314). After that, the stack trace of the exception is printed (line 15).
In main() , an ExceptionListener reference is created by constructing an ExceptionCallbackMain (line 19). This reference is passed into the constructor of ExceptionCallback so that exceptions are reported right away (line 20). ExceptionCallback is a self-running object, so no further action is necessary to get things going.
When ExceptionCallbackMain is run, the following output occurs (your output should match):
1: in constructor - initializing...
2: EXCEPTION #1, source=ExceptionCallback[isAlive()=true]
3: java.lang.NumberFormatException: j20
4:     at java.lang.Integer.parseInt(Compiled Code)
5:     at java.lang.Integer.parseInt(Integer.java:458)
6:     at ExceptionCallback.makeConnection(ExceptionCallback.java:76)
7:     at ExceptionCallback.runWork(ExceptionCallback.java:58)
8:     at ExceptionCallback.access$0(ExceptionCallback.java:56)
9:     at ExceptionCallback$1.run(ExceptionCallback.java:44)
10:     at java.lang.Thread.run(Thread.java:479)
11: EXCEPTION #2, source=ExceptionCallback[isAlive()=true]
12: java.io.IOException: connection refused
13:     at ExceptionCallback.connectToPort(ExceptionCallback.java:86)
14:     at ExceptionCallback.makeConnection(ExceptionCallback.java:82)
15:     at ExceptionCallback.runWork(ExceptionCallback.java:58)
16:     at ExceptionCallback.access$0(ExceptionCallback.java:56)
17:     at ExceptionCallback$1.run(ExceptionCallback.java:44)
18:     at java.lang.Thread.run(Thread.java:479)
19: EXCEPTION #3, source=ExceptionCallback[isAlive()=true]
20: java.lang.NullPointerException
21:     at ExceptionCallback.determineLength(ExceptionCallback.java:90)
22:     at ExceptionCallback.runWork(ExceptionCallback.java:66)
23:     at ExceptionCallback.access$0(ExceptionCallback.java:56)
24:     at ExceptionCallback$1.run(ExceptionCallback.java:44)
25:     at java.lang.Thread.run(Thread.java:479)
All of the rigged exceptions are passed back to ExceptionCallbackMain and printed.

Toc


Java Thread Programming
Java Thread Programming
ISBN: 0672315858
EAN: 2147483647
Year: 2005
Pages: 149
Authors: Paul Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net