15.01.2015 Views

4th International Conference on Principles and Practices ... - MADOC

4th International Conference on Principles and Practices ... - MADOC

4th International Conference on Principles and Practices ... - MADOC

SHOW MORE
SHOW LESS

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

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!