The
URLConnection
class has seven protected instance
fields that define exactly how the client will make the request to
the server. These are:
protected URL url; protected boolean doInput = true; protected boolean doOutput = false; protected boolean allowUserInteraction = defaultAllowUserInteraction; protected boolean useCaches = defaultUseCaches; protected long ifModifiedSince = 0; protected boolean connected = false;
For instance, if doOutput
is
true
, then you’ll be able to write data to
the server over this URLConnection
as well as read
data from it. If useCaches
is
false
, the connection will bypass any local
caching and download the file from the server afresh.
Since these fields are all protected, their values are accessed and modified via obviously named setter and getter methods:
public URL getURL( ) public void setDoInput(boolean doInput) public boolean getDoInput( ) public void setDoOutput(boolean doOutput) public boolean getDoOutput( ) public void setAllowUserInteraction(boolean allowUserInteraction) public boolean getAllowUserInteraction( ) public void setUseCaches(boolean useCaches) public boolean getUseCaches( ) public void setIfModifiedSince(long ifModifiedSince) public long getIfModifiedSince( )
You can modify these fields only before the
URLConnection
is connected (that is, before you
try to read content or headers from the connection). Most of the
methods that set fields throw an
IllegalAccessError
if they are called while the
connection is open. In general, you can set the properties of a
URLConnection
object only before the connection is
opened.
Throwing an error instead of an
exception here is very unusual. An error
generally indicates an unpredictable, generally unhandleable fault in
the VM, whereas an exception indicates a predictable, manageable
problem. More specifically, a
java.lang.IllegalAccessError
is supposed to
indicate that an application is trying to access a nonpublic field it
doesn’t have access to. According to the class library documentation,
“Normally, this error is caught by the compiler; this error can only
occur at run time if the definition of a class has incompatibly
changed.” Clearly, that’s not what’s going on here. This is simply a
mistake on the part of the programmer who wrote this class.
A better solution here would be to throw an
IllegalStateException
or some other runtime
exception. Sun has acknowledged the problem—it’s bug
#4082758 in the Bug Parade on the Java Developer Connection at
http://developer.java.sun.com/developer/bugParade/index.html—however,
they have not yet fixed it, nor apparently do they have any plans to
fix it in future releases.
There are also some private static fields that define the default
behavior for all instances of URLConnection
. These
are:
private static boolean defaultAllowUserInteraction = false; private static boolean defaultUseCaches = true; private static FileNameMap fileNameMap;
These fields are also accessed and modified via obviously named setter and getter methods:
public boolean getDefaultUseCaches( ) public void setDefaultUseCaches(boolean defaultUseCaches) public static void setDefaultAllowUserInteraction( boolean defaultAllowUserInteraction) public static boolean getDefaultAllowUserInteraction( ) public static FileNameMap getFileNameMap( ) public static void setFileNameMap(FileNameMap map)
Unlike the instance fields, these fields can be changed at any time.
The new defaults will apply only to URLConnectio
n
objects constructed after the new default values are set.
The url
field specifies the URL
that this URLConnection
connects to. It is set by
the constructor when the URLConnection
is created
and should not change. You can retrieve the value by calling the
getURL( )
method. Example 15.5
opens a URLConnection
to http://www.oreilly.com/, gets the
URL
of that connection, and prints
it.
Example 15-5. Print the URL of a URLConnection to http://www.oreilly.com/
import java.net.*; import java.io.*; public class URLPrinter { public static void main(String args[]) { try { URL u = new URL("http://www.oreilly.com/"); URLConnection uc = u.openConnection( ); System.out.println(uc.getURL( )); } catch (IOException e) { System.err.println(e); } } }
Here’s the result, which should be no great surprise. The URL
that is printed is the one used to create the
URLConnection
.
% java URLPrinter
http://www.oreilly.com/
The boolean field
connected
is true
if
the connection is open and false
if it’s
closed. Since the connection has not yet been opened when a new
URLConnection
object is created, its initial value
is false
. This variable can be accessed only by
instances of java.net.URLConnection
and its
subclasses.
There are no methods that directly read or change the value of
connected
. However, any method that causes the
URLConnection
to connect should set this variable
to true. This includes connect( )
,
getInputStream( )
, and getOutputStream( )
. Any method that causes the
URLConnection
to disconnect should set this field
to false. There are no such methods in
java.net.URLConnection
, but some of its
subclasses, such as java.net.HttpURLConnection
,
have disconnect( )
methods.
If you subclass URLConnection
to write a protocol
handler, you are responsible for setting connected
to true
when you are connected and resetting it to
false
when the connection closes. Many methods in
java.net.URLConnection
read this variable to
determine what they can do. If it’s set incorrectly, your
program will have severe bugs that are not easy to diagnose.
Some URLConnection
s need to interact with a user.
For example, a web browser may need to ask for a username and
password. However, many applications cannot assume that a user is
present to interact with it. For instance, a search engine robot is
probably running in the background without any user to provide a
username and password. As its name suggests, the
allowUserInteraction
field specifies whether
user interaction is allowed. It is false
by
default.
Since this variable is protected, you use the public
getAllowUserInteraction( )
method to read its value, and
the public setAllowUserInteraction( )
method to
set it:
public void setAllowUserInteraction(boolean allowUserInteraction) throws IllegalAccessError public boolean getAllowUserInteraction( )
The value true
indicates that user interaction is
allowed; false
indicates that there is no user
interaction. The value may be read at any time but may be set only
when the connection is closed. Calling
setAllowUserInteraction( )
when the connection is
open throws an IllegalAccessError
. Programs
usually don’t catch errors (unlike exceptions); an uncaught
error usually forces the program to terminate.
Example 15.6 creates a new
HttpURLConnection
, uses
getAllowUserInteraction( )
to see whether user
interaction is allowed, and, if it isn’t, uses
setAllowUserInteraction( )
to allow user
interaction. Of the major standalone VMs, only Apple’s
Macintosh Runtime for Java will pop up an authentication dialog box
in a standalone application like Example 15.6, even
when allowUserInteraction
is
true
. Specifically, Sun’s JDK on Windows and
Unix will not ask the user for authentication unless you’ve
installed an Authenticator
as was discussed in
Chapter 7. Unfortunately, this class is available
only in Java 1.2 and later. However, most web browsers will ask if
allowUserInteraction
is true
and the request is made from inside an applet.
You actually can get Sun’s JDK to pop up an authentication dialog in Java 1.1, but you have to use undocumented methods in sun.net.www.protocol.http.HttpURLConnection
and sun.net.www.protocol.http.HttpAuthenticator
classes to do so.
Example 15-6. A URLConnection That’s Allowed to Interact with the User If Necessary
import java.net.*; import java.io.*; import java.awt.*; public class PasswordedPageViewer { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { try { URL u = new URL(args[i]); URLConnection uc = u.openConnection( ); uc.setAllowUserInteraction(true); InputStream in = uc.getInputStream( ); Reader r = new InputStreamReader(in); int c; while ((c = r.read( )) != -1) { System.out.print((char) c); } System.out.println( ); } catch (IOException e) { System.err.println(e); } } } }
Figure 15.1 shows the dialog box that pops up when
you try to access a passwordsprotected page. If you cancel this
dialog, you’ll get 401 Authorization Required error and
whatever text the server sends to unauthorized users. However, if you
refuse to send authorization at all, which you can do by pressing OK,
then answering No when asked if you want to retry authorization,
getInputStream( )
will throw a
ProtocolException
.
The static
defaultAllowUserInteraction
field determines whether
URLConnection
objects whose
setAllowUserInteraction( )
method is not
explicitly invoked are allowed to pop up dialogs or otherwise
interact with the user. It may be read by calling the public method
getDefaultAllowUserInteraction( )
and set by
calling the public method setDefaultAllowUserInteraction( )
. Since this field is static
(i.e., a
class variable instead of an instance variable), setting it changes
the default behavior for all instances of the
URLConnection
class that are created after
setDefaultAllowUserInteraction( )
is called.
For instance, the following code fragment checks to see whether user
interaction is allowed by default with
getDefaultAllowUserInteraction( )
. If user
interaction is not allowed by default, the code uses
setDefaultAllowUser-Interaction( )
to make
allowing user interaction the default behavior.
if (!URLConnection.getDefaultAllowUserInteraction( )) { URLConnection.setDefaultAllowUserInteraction(true); }
Most URLConnection
objects provide input to a
client program. For example, a connection to a web server with the
GET method would produce input for the client. However, a connection
to a web server with the POST method might not. A
URLConnection
can be used for input to the
program, output from the program, or both. The protected boolean
field doInput
is true
if the
URLConnection
can be used for input,
false
if it cannot be. The default is
true
. To access this protected variable, use the
public getDoInput( )
and setDoInput( )
methods:
public void setDoInput(boolean doInput) public boolean getDoInput( )
For example:
try { URL u = new URL("http://www.oreilly.com"); URLConnection uc = u.openConnection( ); if (!uc.getDoInput( )) { uc.setDoInput(true); } // read from the connection... catch (IOException e) { System.err.println(e); }
Programs can use a URLConnection
to send output
back to the server. For example, a program that needs to send data to
the server using the POST method could do so by getting an output
stream from a URLConnection
. The protected boolean
field doOutput
is true
if the
URLConnection
can be used for output,
false
if it cannot be; it is
false
by default. To access this protected
variable, use the getDoOutput( )
and
setDoOutput( )
methods:
public void setDoOutput(boolean dooutput) public boolean getDoOutput( )
For example:
try { URL u = new URL("http://www.oreilly.com"); URLConnection uc = u.openConnection( ); if (!uc.getDoOutput( )) { uc.setDoOutput(true); } // write to the connection... catch (IOException e) { System.err.println(e); }
When you set doOutput
to true for an http URL, the request method is changed from
GET to POST. In Chapter 7, you saw how to send
data to CGI programs with GET. GET is straightforward to work with, but it
does limit the amount of data you can send. Some web servers have
maximum numbers of characters they’ll accept as part of a GET
request, typically 255 or 1,024. This is generally enough for a
simple search request or page navigation, but not enough for a form
that allows journalists to submit articles, for example. Forms that
allow larger blocks of text should use POST instead. We’ll
explore this more below when we talk about writing data to a server.
Many clients, especially web clients, keep caches of previously retrieved documents. If the user asks for the same document again, it can be retrieved from the cache. However, it may have changed on the server since it was last retrieved. The only way to tell is to ask the server. Clients can include an If-modified-since in the client request MIME header. This header includes a date and time. If the document has changed since that time, the server should send it. Otherwise, it should not. Typically, this time is the last time the client fetched the document. For example, this client request says the document should be returned only if it has changed since 7:22:07 A.M., October 31, 1999, Greenwich Mean Time:
GET / HTTP 1.1 User-Agent: Java1.3beta Host: login.metalab.unc.edu:56452 Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: close If-Modified-Since: Sun, 31 Oct 1999 19:22:07 GMT
If the document has changed since that time, the server will send it as usual. Otherwise, it will reply with a 304 Not Modified message like this:
HTTP 1.0 304 Not Modified Server: WN/1.15.1 Date: Mon, 01 Nov 1999 16:26:16 GMT Last-modified: Fri, 29 Oct 1999 23:40:06 GMT
The client will then load the document from its cache. Not all web servers respect the If-modified-since field. Some will send the document whether it’s changed or not.
The ifModifiedSince
field in the
URLConnection
class specifies the date (in
milliseconds since midnight, Greenwich Mean Time, January 1, 1970),
which will be placed in the If-modified-since MIME header field.
Because ifModifiedSince
is
protected
, programs should call the
getIfModifiedSince( )
and
setIfModifiedSince( )
methods to read or modify
it:
public long getIfModifiedSince( ) public void setIfModifiedSince(long ifModifiedSince)
Example 15.7 prints the default value of
ifModifiedSince
, sets its value to 24 hours ago,
and prints the new value. It then downloads and displays the document
but only if it’s been modified in the last 24 hours.
Example 15-7. Set ifModifiedSince to 24 Hours Prior to Now
import java.net.*; import java.io.*; import java.util.*; public class Last24 { public static void main (String[] args) { // Initialize a Date object with the current date and time Date today = new Date( ); long millisecondsPerDay = 24 * 60 * 60 * 1000; for (int i = 0; i < args.length; i++) { try { URL u = new URL(args[i]); URLConnection uc = u.openConnection( ); System.out.println("Will retrieve file if it's modified since " + new Date(uc.getIfModifiedSince( ))); uc.setIfModifiedSince((new Date(today.getTime( ) - millisecondsPerDay)).getTime( )); System.out.println("Will retrieve file if it's modified since " + new Date(uc.getIfModifiedSince( ))); InputStream in = new BufferedInputStream(uc.getInputStream( )); Reader r = new InputStreamReader(in); int c; while ((c = r.read( )) != -1) { System.out.print((char) c); } System.out.println( ); } catch (Exception e) { System.err.println(e); } } } }
Here’s the result. First, we see the default value: midnight, January 1, 1970, GMT, converted to Pacific Standard Time. Next, we see the new time, which we set to 24 hours prior to the current time:
% java Last24 http://www.oreilly.com
Will retrieve file if it's been modified since Wed Dec 31 16:00:00 PST 1969
Will retrieve file if it's been modified since Sun Oct 31 11:17:04 PST 1999
Since this document hasn’t changed in the last 24 hours, it is not reprinted.
Some clients, notably web browsers, can retrieve a document from a
local cache, rather than retrieving it from a server. The
useCaches
variable determines whether a cache will
be used if it’s available. The default value is
true
, meaning that the cache will be used;
false
means the cache won’t be used. Because
useCaches
is protected
,
programs access it using the getUseCaches( )
and
setUseCaches( )
methods:
public void setUseCaches(boolean useCaches) public boolean getUseCaches( )
This code fragment disables caching to ensure that the most recent version of the document is retrieved:
try { URL u = new URL("http://www.sourcebot.com/sourcebot/"); URLConnection uc = u.openConnection( ); if (uc.getUseCaches( )) { uc.setUseCaches(false); } } catch (IOException e) { System.err.println(e); }
defaultUseCaches
defines the initial value of the
useCaches
field.
defaultUseCaches
can be read and modified by the
public getDefaultUseCaches( )
and
setDefaultUseCaches( )
methods:
public void setDefaultUseCaches(boolean useCaches) public boolean getDefaultUseCaches( )
Since this variable is static
(i.e., a class
variable instead of an instance variable), setting it changes the
default behavior for all instances of the
URLConnection
class created after the change. The
next code fragment disables caching by default; after this code runs,
URLConnection
s that want caching must enable it
explicitly using setUseCaches(true)
.
if (uc.getDefaultUseCaches( )) { uc.setDefaultUseCaches(false); }
3.139.82.23