Search This Blog

Thursday, January 28, 2010

Defensive Programming in Java

Something crossed my mind today and I thought of Blogging about it. Defensive programming seems to be running in my decaying grey cells :-).
Consider the following best practice,

public class Foo {
public void doSomething() {
Date date;
Bar b = new Bar(date);
....
}
}

public class Bar {
private Date date;
public Bar(Date date) {
// Create a based of the provided date
this.date = immutable(date);
}
...
}


In the above example, Bar is being safe, it is ensuring that the Date object even if passed and changed subsequently externally, will be safely used within Bar.

What I ask, is that, when working with external API, shouldn't the caller be safe as well regarding the objects it passes around ?

As an example, when one works with Streams, Sockets, Connections etc, I am of the opinion that the creator or opener of the Resource should be the one to close it. For example,

public class Foo {
public void someOperation() {
OutputStream os = ...;
try {
doSomething(os);
....
// Do some other stuff with the stream
doSomethingElse(os);
} finally {
os.close();
}
}

public void doSomething(OutputStream os) {
Bar b = new Bar(os);
try {
bar.process();
} finally {
// Closes Bar and as a side effect closes os
bar.close();
}
}
}

Now for the sake of discussion let us assume that Bar uses the Stream, but it also has a close() method associated with it. The behavior of the close() method could be to close the output stream that we created in Foo. As Bar is a third party software, one would not know the behavior of the close() method without looking at Bar's javadoc or actual code (might require decompilation). For the sake of discussion, let us say that Bar is the following code:

public class Bar {
private OutputStream os;

public Bar(OutputStream os) {
this.os = os;
}

public void process() {
....
}
...
public void close() {
close(os);
}
}

Clearly our Foo code would fail as Bar's close() method is closing out the provided stream and rendering it unusable for the code in Foo. At this time, the close() of Bar method does not do anything else apart from closing the provided stream. Can we rely on the same being the only operation performed by the close() code in the future? Can we safely agree that as long as we do not call the close() method on Bar, our code in Foo is good? Can we safely walk away knowing that we don't call the close() method on Bar?

One concern that I see is that if we do not call close() on Bar and should tomorrow a new version of Bar get included in the build that is binary compatible but has other additional resources it releases as part of the close() method, what happens then ? Maybe leakage of some sort?

For example let us say that the next version of Bar does some socket management internally as well as shown below,

public class Bar {
private Socket s;
private OutputStream os;
public Bar(OutputStream os) {
this.os = os;
this.s = new Socket(...);
}
...
public void close() {
close(os);
close(s);
}
}


Third party implementation cannot be guaranteed unless the documentation certifies the same. We cannot imagine that the behavior of the close() method will not require the closing of addition resources opened by Bar.

So how does one be defensive in development in this case ? As defensive programmers, what if we provided a stream to Bar that even if Bar attempted to close() the stream, the operation would have no effect?

The same could be achieved using decorator or maybe a Dynamic Proxies where we allow every other method to go through but prevent the closing of the provided stream by Bar.

public class Foo {
public void someOperation() {
OutputStream os = ...;
Bar b = new Bar();
// Created proxy will do nothing on close() method call
OutputStream proxy = createProxy(os);
try {
doSomething(proxy);
....
doSomethingElse(proxy);
} finally {
os.close();
}
}

public void doSomething(OutputStream os) {
Bar b = new Bar(os);
try {
bar.process();
} finally {
// This operation now will honor the close but
// prevent the closing of our stream as the proxy will
// intercept and do nothing on the call to close()
bar.close();
}
}
}

In the above example, the caller Foo is being careful and guaranteeing the integrity of the Stream, a way of immutability. I am probably being too dramatic. At the same time, I think there is value in being defensive. I also think that things work well when the creator is the destroyer :-). BTW this is coded entirely developed using BLOG SPOT's editor...wonder how many mistakes I have made...

No comments: