4th International Conference on Principles and Practices ... - MADOC
4th International Conference on Principles and Practices ... - MADOC
4th International Conference on Principles and Practices ... - MADOC
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
By representing resources as trees, <strong>and</strong> releasing entire trees at<br />
<strong>on</strong>ce, this method can be simplified, as shown in Figure 2-5. The<br />
try-catch-finally complexity has been reduced to a single trycatch-finally<br />
block. The previous close() calls have been replaced<br />
by a single release() call, <strong>on</strong> line 27. Excepti<strong>on</strong>s during the<br />
resource release phase are now passed to the Excepti<strong>on</strong>Logger,<br />
described in Secti<strong>on</strong> 2.3, to avoid cluttering up the code.<br />
Additi<strong>on</strong>ally, it is no l<strong>on</strong>ger necessary to declare most of the<br />
resources outside the try block (e.g., the Socket, InputStream, <strong>and</strong><br />
OutputStream). Because the resources are accessible through the<br />
tree, they do not need to be directly accessible by the finally<br />
block. This allows more variables to be kept in smaller scopes,<br />
where they are relevant. By using Furm, the number of lines has<br />
been significantly cut. More importantly, communicating with<br />
the server is now the focus of the code. The resource<br />
management code has been pushed into the background, where it<br />
bel<strong>on</strong>gs.<br />
In this example, a ResourceGroup is the root of the tree, <strong>and</strong> the<br />
ManagedSocket is its <strong>on</strong>ly child. When getInputStream() <strong>and</strong><br />
getOutputStream() are called, the ManagedSocket automatically<br />
creates a ManagedInputStream <strong>and</strong> a ManagedOutputStream as<br />
its children. Figure 2-6 shows the tree immediately before<br />
01 static byte [] fetch(InetAddress addr,<br />
02 int port,<br />
03 byte [] msg) {<br />
04 Socket s = null;<br />
05 InputStream in = null;<br />
06 OutputStream out = null;<br />
07 ByteArrayOutputStream resp<strong>on</strong>se =<br />
08 new ByteArrayOutputStream();<br />
09 boolean success = false;<br />
10 try {<br />
11 s = new Socket(addr,port);<br />
12 out = s.getOutputStream();<br />
13 out.write(msg);<br />
14 out.flush();<br />
15 in = s.getInputStream();<br />
16 int i;<br />
17 while ((i = in.read()) != -1) {<br />
18 resp<strong>on</strong>se.write(i);<br />
19 }<br />
20 success = true;<br />
21 } catch (IOExcepti<strong>on</strong> exc) {<br />
22 // log excepti<strong>on</strong><br />
23 } finally {<br />
24 try {<br />
25 if (out != null) out.close();<br />
26 } catch (IOExcepti<strong>on</strong> exc) {<br />
27 // log excepti<strong>on</strong><br />
28 } finally {<br />
29 try {<br />
30 if (in != null) in.close();<br />
31 } catch (IOExcepti<strong>on</strong> exc) {<br />
32 // log excepti<strong>on</strong><br />
33 } finally {<br />
34 try {<br />
35 if (s != null) s.close();<br />
36 } catch (IOExcepti<strong>on</strong> exc) {<br />
37 // log excepti<strong>on</strong><br />
38 }<br />
39 }<br />
40 }<br />
41 }<br />
42 if (!success) return null;<br />
43 return resp<strong>on</strong>se.toByteArray();<br />
44 }<br />
Figure 2-4. C<strong>on</strong>necting to a server to send a message<br />
release() is called.<br />
For another example, let us borrow a method from Apache<br />
Tomcat, an open source Java Servlet c<strong>on</strong>tainer, produced by the<br />
Apache Software Foundati<strong>on</strong>. Tomcat is the official Reference<br />
Implementati<strong>on</strong> for the Java Servlet <strong>and</strong> JavaServer Pages<br />
technologies [2].<br />
The copy() method in Figure 2-7 is from the St<strong>and</strong>ardC<strong>on</strong>text<br />
class, part of the Java engine (Catalina) built into Tomcat. Its task<br />
is to copy from <strong>on</strong>e file to another. A significant amount of effort<br />
has g<strong>on</strong>e into h<strong>and</strong>ling resources in excepti<strong>on</strong>al situati<strong>on</strong>s in this<br />
code. All resource releases are nicely wrapped in try-catch<br />
blocks, which are in turn in a finally block. The method is well<br />
thought out, carefully coded, <strong>and</strong> flawed. If the close() operati<strong>on</strong><br />
<strong>on</strong> line 19 throws an Error, then the close() operati<strong>on</strong> <strong>on</strong> line 22<br />
will never occur, despite all the careful excepti<strong>on</strong> h<strong>and</strong>ling. This<br />
is unlikely, but not impossible. This may have slipped by the<br />
developers because the cleanup code is difficult to follow.<br />
Note that the close() calls are duplicated. Lines 13 <strong>and</strong> 14 close<br />
the resources in the normal c<strong>on</strong>diti<strong>on</strong>s, while lines 19 <strong>and</strong> 22<br />
close the resources in excepti<strong>on</strong>al c<strong>on</strong>diti<strong>on</strong>s. Such duplicati<strong>on</strong><br />
leads to less maintainable code. Note also that excepti<strong>on</strong>s are<br />
completely discarded, losing potentially useful informati<strong>on</strong>.<br />
Figure 2-8 displays the code rewritten to use Furm. This code<br />
correctly closes resources in both normal <strong>and</strong> excepti<strong>on</strong>al<br />
situati<strong>on</strong>s. The basic functi<strong>on</strong>ality is retained. However, the<br />
cleanup code is completely different. Instead of nested try-catch<br />
blocks, a single try-catch-finally block suffices. Rather than two<br />
different close() calls (or four, counting the duplicates), a single<br />
release() call is resp<strong>on</strong>sible for releasing the input <strong>and</strong> output<br />
streams correctly in both normal <strong>and</strong> excepti<strong>on</strong>al cases. The<br />
return value of the release() call is checked <strong>on</strong> line 19 to<br />
determine whether the cleanup was successful. A return value of<br />
01 static byte [] fetchAgain(InetAddress addr,<br />
02 int port,<br />
04 byte [] msg) {<br />
05 ResourceGroup tree = new ResourceGroup(<br />
06 new Excepti<strong>on</strong>Logger());<br />
07 ByteArrayOutputStream resp<strong>on</strong>se =<br />
08 new ByteArrayOutputStream();<br />
09 boolean success = false;<br />
11 try {<br />
12 Socket s = new ManagedSocket(<br />
13 addr,port,tree);<br />
14 OutputStream out = s.getOutputStream();<br />
15 out.write(msg)<br />
16 out.flush();<br />
17 InputStream in = s.getInputStream();<br />
18 int i;<br />
19 while ((i = in.read()) != -1) {<br />
20 resp<strong>on</strong>se.write(i);<br />
21 }<br />
22 success = true;<br />
23 } catch (IOExcepti<strong>on</strong> exc) {<br />
24 tree.getListener().<br />
25 excepti<strong>on</strong>Thrown(null,exc);<br />
26 } finally {<br />
27 tree.release();<br />
28 }<br />
29 if (!success) return null;<br />
30 return resp<strong>on</strong>se.toByteArray();<br />
31 }<br />
Figure 2-5. C<strong>on</strong>necting to a server using Furm<br />
117