Programming with URLs

22 Apr

Programming with URLs
All of the previous examples of networking involved the use of the Socket and ServerSocket classes. Before leaving our discussion of network programming, there’s one more topic to be touched upon, the topic of URL objects. A URL, or Uniform Resource Locator, typically refers to an Internet address such as http://java.sun.com. But in the java.net class library, there is a URL class. The URL class provides a variety of useful methods for getting network information and for parsing information out of a provided URL. The URL class also provides ways for us to read directly from a URL or form connections. Below is a simple example. This program is a simple HTTP web client, very much like our first socket example. It takes a string URL as a command line argument, then creates a URL object, webURL, from that argument. An IO stream is created using the openStream() method of the webURL object, which will allow us to read directly from the URL we gave as an argument. We create a BufferedReader, webReader, to read from the URL object, webURL. The readLine() method of the webReader object returns lines of text of the web page we entered as an argument. We stop reading when there are no more lines, and then close the IO stream. Finally, we use various methods of the webURL object to parse out information about the URL we just read from. One interesting thing to note about this process is that we didn’t have to write any HTTP client request information to the server, as we did when we made the simple HTTP client using sockets. This is because the URL class acts as a protocol handler for us. It takes care of issuing those commands in its methods, transparent to us.

Listing 3-18: HttpURLClient.java
import java.net.*;
import java.io.*;
public class HttpURLClient {
public static void main(String[] args) {
try {
URL webURL = new URL(args[0]);
BufferedReader webReader = new BufferedReader(
new InputStreamReader(
webURL.openStream()));
String singleLine;
while ((singleLine = webReader.readLine()) != null) {
System.out.println(singleLine);
}
webReader.close();
System.out.println(“Protocol = “ + webURL.getProtocol());
System.out.println(“Path = “ + webURL.getPath());
System.out.println(“Port = “ + webURL.getPort());
System.out.println(“File = “ + webURL.getFile());
System.out.println(“Host = “ + webURL.getHost());
} catch(Exception e) {e.printStackTrace();}
}
}


To test this program, we’ll run it in conjunction with our HttpImprovedServer example.

c:\> java HttpURLClient http://127.0.0.1:80/
<HEAD>
<TITLE>This is a counting test web page</TITLE>
<H3><CENTER>An HTTP counter</CENTER></H3>
</HEAD>
<BODY><CENTER>
Push the Button to Update the Count
<FORM>
<INPUT TYPE=SUBMIT NAME=’Increment’ VALUE=’Increment’>
<INPUT TYPE=SUBMIT NAME=’QUIT’ VALUE=’QUIT’>
</FORM>
The current count = 1<CENTER><BODY>
Protocol = http
Path = /
Port = 80
File = /
Host = 127.0.0.1

Meanwhile, the window we run the server from looks like this:

c:\> javac HttpImprovedServer.java
c:\> java HttpImprovedServer
GET / HTTP/1.1

Nowhere in our example HttpURLClient are we explicitly writing the GET command to the stream. It’s taken care of for us by the URL class. The URL class has many applications; we will be using it primarily for the parsing methods. This section has been a quick look at network programming. Many of the concepts, especially that of reading and writing to sockets, will be used and expanded on in later sections of this book.

Threads
The simple HTTP servers in the previous sections raise an interesting issue: while functional, they were not very useful because they only talked to one client at a time. That’s not really the true spirit of networking. Networking is all about connecting many clients to a single server. That requires the server to be multitasking, or doing more than one thing at a time. Java has a multitasking capability called threads.

A thread, in Java, refers to a single sequential flow of control within a program. Java programs can be multi-threaded. Many different sequential flows can be operating in parallel, independently. We could be doing completely different things at the same time in different threads, or we could be doing multiple copies of the same activity in different threads. As with Exceptions, and Sockets, and URLs, Threads are also a Java class.

There are two basic ways  to implement  threads in a Java program.

1. Make your own Java class that extends the Thread class.
2. Make your own Java class that implements the runnable interface.

Implementing threads by extending the thread class
This is the method of choice when your threads might be used by other classes. We will start by looking at a simple example.

Listing 3-19: CountingThread.java

class CountingThread extends Thread {
String threadName;
int waitTime;
public CountingThread(String tName, int wTime) {
this.threadName = tName;
this.waitTime = wTime;
}
public void run() {
int count = 0;
while(count < 25) {
count++;
System.out.println(this.threadName + “ “ + count);
try {
sleep((long)this.waitTime);
} catch(Exception e) {System.out.println(“Couldn’t Sleep”);}
}
}
}
public class ExtFourThreads {
public static void main(String[] args) {
int one = Integer.parseInt(args[0]);
int two = Integer.parseInt(args[1]);
int three = Integer.parseInt(args[2]);
int four = Integer.parseInt(args[3]);
new CountingThread(“One”, one).start();
new CountingThread(“Two”, two).start();
new CountingThread(“Three”, three).start();
new CountingThread(“Four”, four).start();
}
}

This example implements four threads, each counting to 25 and printing the count out as it goes. Each thread has its own delay between increments that is set when the program is invoked. The first few lines,

class CountingThread extends Thread {
String threadName;
int waitTime;
public CountingThread(String tName, int wTime) {
this.threadName = tName;
this.waitTime = wTime;
}

represent the constructor for our threads. Note that we are extending the thread through the use of the extends keyword. Each of our threads has two variables, a name and a delay time. Next comes the run() method.

public void run() {
int count = 0;
while(count < 25) {
count++;
System.out.println(this.threadName + “ “ + count);
try {
sleep((long)this.waitTime);
} catch(Exception e) {System.out.println(“Couldn’t Sleep”);}
}
}

The run() method is a pre-existing method of the Thread superclass. It is executed when we invoke the Thread.start() command, since CountingThread is a subclass of Thread, and they both have a method named run(). We are overriding the run() method in the Thread class. Lastly, we have a second class in the same file as our CountingThread class, called FourThreads. This class is actually the program we will run from the command line. The ExtFourThreads class makes use of the CountingThread class. This is often the case when you implement threads by extending the Thread class: they get used by other classes.

public class ExtFourThreads {
public static void main(String[] args) {
int one = Integer.parseInt(args[0]);
int two = Integer.parseInt(args[1]);
int three = Integer.parseInt(args[2]);
int four = Integer.parseInt(args[3]);
new CountingThread(“One”, one).start();
new CountingThread(“Two”, two).start();
new CountingThread(“Three”, three).start();

new CountingThread(“Four”, four).start();
}
}

ExtFourThreads reads in four strings from the command line (spaces between them) and converts them to long values for use as the sleep values for the different threads. The constructor is called four times, generating four new threads and at the same time the start() method is invoked. This causes our overridden version of the run() method to execute. Four separate counting processes proceed, each with user-defined delays between increments. The program output looks like this (much abbreviated):

c:\> javac ExtFourThreads.java
c:\> java ExtFourThreads 1000 200 500 5000
….
three 25
one 14
one 15
four 4
….

That’s a very simple example illustrating one form of the thread concept. The same concept can be applied to the HTTP servers we looked at in the previous section on networking. The example below is a threaded HTTP server that serves a simple web page that shows the current time. The time can be updated by pushing an HTML form button on the page. There is also a QUIT button on the form. It doesn’t really cause the program to “quit,” but it takes you to a different page. It’s included in the program because it illustrates reading from the socket as well as writing. This program is closely modeled after the nonthreaded incrementing page we saw earlier in the section on networking.

Listing 3-20: HttpThread.java

import java.util.*;
import java.io.*;
import java.net.*;
class HttpThread extends Thread {
Socket threadSocket;
HttpThread(Socket thrdSock) {
this.threadSocket = thrdSock;
}
public void run() {
try {
BufferedWriter serverResponse = new BufferedWriter(
new OutputStreamWriter(this.threadSocket.getOutputStream()));
BufferedReader clientRequest = new BufferedReader(
new InputStreamReader(this.threadSocket.getInputStream()));
String str;
str=clientRequest.readLine();
System.out.println(str);
Date currentDate = new Date();
String currentTime = currentDate.toString();
if (str.startsWith(“GET /?QUIT”)) {
sayGoodbye(serverResponse);
clientRequest.close();
this.threadSocket.close();
System.exit(0);
} else {
writePage(serverResponse,  currentTime);
}
clientRequest.close();
this.threadSocket.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println(“Problem with run”);
}
}
public static void writePage(BufferedWriter wr, String str) {
try {
wr.write(“HTTP/1.0 200 OK\r\n”);
wr.write(“Content-type:  text/html\n\n”);
wr.write(“<HEAD>\r\n”);
wr.write(“<TITLE>Java Http Time Server</TITLE>\r\n”);
wr.write(“<H3><CENTER>Java HTTP Time Server</CENTER>< H3>\r\n”);
wr.write(“</HEAD>\r\n”);
wr.write(“<BODY><CENTER>\r\n”);
wr.write(“Push the Button to Update the Time\r\n”);
wr.write(“<FORM>”);
wr.write(“<INPUT TYPE=SUBMIT NAME=’UPDATE’ VALUE=’UPDATE’>”);
wr.write(“<INPUT TYPE=SUBMIT NAME=’QUIT’ VALUE=’QUIT’>”);
wr.write(“</FORM>”);
wr.write(“The current time = “ + str);
wr.write(“</CENTER></BODY>\r\n”);
wr.flush();
} catch (IOException e) {
e.printStackTrace();
System.out.println(“Problem with writePage”);
}
}
public static void sayGoodbye(BufferedWriter wr) {
try {
wr.write(“HTTP/1.0 200 OK\r\n”);
wr.write(“Content-type:  text/html\n\n”);
wr.write(“<HEAD>\r\n”);
wr.write(“<TITLE>Goodbye!</TITLE>\r\n”);
wr.write(“<H3><CENTER>Goodbye</CENTER></H3>\r\n”);
wr.write(“</HEAD>\r\n”);
wr.write(“<BODY><CENTER>\r\n”);
wr.write(“</CENTER></BODY>\r\n”);
wr.flush();
} catch (IOException e) {
e.printStackTrace();
System.out.println(“Problem with sayGoodbye”);
}
}
}
public class HttpThreadServer {
public static void main(String[] args) {
int port = 80;
try {
ServerSocket srv = new ServerSocket(port);
while(true) {
Socket mySocket = srv.accept();
new  HttpThread(mySocket).start();
}
} catch(Exception e) {System.out.println(“Problem with main()”);}
}



The program begins with a simple constructor. The argument for the constructor is a socket. As in our simple example,  we are extending the thread class and overriding the run() method.

class HttpThread extends Thread {
Socket threadSocket;
HttpThread(Socket thrdSock) {
this.threadSocket = thrdSock;
}

The run() method contains the bulk of the code. A BufferedReader and BufferedWriter is formed using the socket as an instance variable of the thread object. The input stream is scanned to see if the “GET /?QUIT” is in the input stream. If it is, that means the HTTP request was the result of the “QUIT” form button being pressed. If not, it calls a method that prints a web page to the socket client showing the current time.

public void run() {
try {
BufferedWrigter serverResponse = new BufferedWriter(
new OutputStreamWriter(this.threadSocket.getOutputStream()));
BufferedReader clientRequest = new BufferedReader(
new  InputStreamReader(this.threadSocket.getInputStream()));
String str;
str=clientRequest.readLine();
System.out.println(str);
Date currentDate = new Date();
String currentTime = currentDate.toString();
if (str.startsWith(“GET /?QUIT”)) {
sayGoodbye(serverResponse);
clientRequest.close();
this.threadSocket.close();
System.exit(0);
} else {
writePage(serverResponse,  currentTime);
}
clientRequest.close();
this.threadSocket.close();
} catch (IOException e) {
e.printStackTrace();
System.out.println(“Problem with run”);
}

}
}

The methods writePage() and sayGoodbye() print web information to the socket connection. They are only slight modifications of the methods of the same name we used in Listing 3-17, so we won’t go into them further. Finally, we have a short class that contains the main() method. This is the class that we will execute from the command line. It creates a server socket and then listens on port 80 for connections. When connections are being requested from clients, a socket is created and given as an argument to the thread constructor. The Thread.start() method is called, which executes the run() method. The thread then services the http request and main() is free to continue listening for more clients.

public class HttpThreadServer {
public static void main(String[] args) {
int port = 80;
try {
ServerSocket srv = new ServerSocket(port);
while(true) {
Socket mySocket = srv.accept();
new  HttpThread(mySocket).start();
}
} catch(Exception e) {System.out.println(“Problem with main()”);}
}
}

To try this program out, invoke the HttpThreadServer program from the command line and then open up a web browser and type in the URL http://127.0.0.1/ Both the command line window and the browser should respond as shown below. The IP address 127.0.0.1 is what as known as the loop-back address. It refers to the machine you are running on (the localhost).

c:\> javac HttpThreadServer.java
c:\> java HttpThreadServer
GET /?UPDATE=UPDATE HTTP/1.1
GET /?UPDATE=UPDATE HTTP/1.1
GET /?UPDATE=UPDATE HTTP/1.1
GET /?UPDATE=UPDATE HTTP/1.1
GET /?UPDATE=UPDATE HTTP/1.1
GET /?UPDATE=UPDATE HTTP/1.1
GET /?QUIT=QUIT HTTP/1.1

The previous two examples have shown the first way of implementing threads. That method is to extend the thread class and override the run() method. In both examples, we had a separate class that made use of our thread subclass. The second way of implementing threads is to implement the Runnable interface.

Random Posts

Comments are closed.