Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 6
E:\classes\com\javaworld\jpitfalls\article3>java -Djava.compiler=NONE
com.javaworld.jpitfalls.article3.BadURLPost
http://localhost/cgi-bin/echocgi.exe
Received a : sun.net.www.protocol.http.HttpURLConnection
Getting an input stream...
Getting an output stream...
java.net.ProtocolException: Can't reset method: already connected
at
java.net.HttpURLConnection.setRequestMethod(HttpURLConnection.java:10
2)
at
sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLCo
nnection.java:349)
at
com.javaworld.jpitfalls.article2.BadURLPost.main(BadURLPost.java:38)
When we try to obtain the HttpURLConnection class's output stream, the program informs us that we cannot reset the method because we are already connected. The Javadoc
for the HttpURLConnection class contains no reference to setting a method. The program is referring to the HTTP method, which should be POST when we send data to the URL and GET when we retrieve data from the URL.
The getOutputStream() method causes the program to throw a ProtocolException with the error message "Can't reset the method." The JDK source code reveals that the error message results because the getInputStream() method has the side effect of sending the request (whose default request method is GET) to the Web server. This is similar to a side effect in the ObjectInputStream and ObjectOutputStream constructors, detailed in my book, Java Pitfalls: Time Saving Solutions and Workarounds to Improve Programs (John Wiley & Sons, 2000).
The pitfall is the assumption that the getInputStream() and getOutputStream() methods behave just as they do for a Socket connection. Since the underlying mechanism for communicating to the Web server actually is a Socket, it is not an unreasonable assumption. A better implementation of HttpURLConnection would postpone the side effects until the initial read or write to the respective input or output stream. You can do that
by creating an HttpInputStream and an HttpOutputStream, which would keep the Socket model intact. You could argue that HTTP is a request/response stateless protocol, and the Socket model does not fit. Nevertheless, the API should fit the conceptual model; if the current model is identical to a Socket connection, it should behave as such. If it does not, you have stretched the bounds of abstraction too far.
In addition to the error message, there are two problems with the above code:
setRequestProperty() method parameters are not checked, which we demonstrate by setting a property called stupid with a value of nonsense. Since those properties actually go into the HTTP request and are not validated by the method (as they should be), you must
take extra care to ensure that the parameter names and values are correct.
URLConnection indicates the sequence to set up a connection, although it does not state that it is a mandatory sequence.
If we did not have the luxury of examining the source code -- which should definitely not be a requirement to use an API --
we would be reduced to trial and error, the absolute worst way to program. Neither the documentation nor the API of the HttpURLConnection class afford us any understanding of how the protocol is implemented, so we feebly attempt to reverse the order of calls
to getInputStream() and getOutputStream(). Listing 5.2, BadURLPost1.java, is an abbreviated version of that program.