Most Read This Week
Java Industry News
Sending Data to a Java Server
Sending Data to a Java Server
By: Andrew Idsinga
Feb. 1, 1996 12:00 AM
When I read about the opportunity to submit articles to Java Developer's Journal one of the first topics that came to mind concerns the desire to be able to record or log data from a web page on the server. Every day I hear questions about outputting data to the server, be it to simply log user information in a guest book, or to have some other, more critical business data sent to the server for more processing; such as an order form.
Of course, you and I both know this is possible using some sort of CGI (considerably goofy intermediary?) program that will write the file. You could also hook your web server up to a several thousand dollar database back end and use another method to get the data there.
No, what many of us are looking for is a simple way, in Java, to send data to the server. Why in Java and not a CGI program? The simplest answer is so that we can have a cross platform solution written in a simple, stable, OO language. While we're at it we'll plop half of it into a web page so that customers or friends will have easy access from a browser!
Enter DataReceiver, DataSender and DataApplet. All three combine into a simple solution to get some formatted text from an applet in a web page to a file on your web server.
What It Looks Like at a Glance
The "Under the Hood" Basics
Meanwhile, on the server, DataReceiver is waiting for incoming connections. After accepting a connection, it reads the incoming data and writes it to a file. In the example code the file is formatted as a comma delimited text file so that it can easily be imported into your favorite spread sheet or database program (See Figure 2).
Java Sockets and How They Relate to Our Problem
To break things down further (you may want to peek at the code in Listings 2 and 3) let's talk about sockets. Sockets, simply put, are end points of communication. When using them directly, as I do with DataSender and DataReceiver, we are sending data from one "end" to the other, or from the client socket to the server socket. When programming with sockets I usually think in terms of what I have to do to get the data to the socket on the local side of the link and then how I will handle it on the other side. Whatever happens between here and there is left up to the socket implementation and network conditions. Basically, with sockets, most of the hairy details of network programming are hidden from the programmer.
In Java, sockets are quite elegant, thanks to Sun's abstraction of sockets into the Socket class and the ServerSocket class. The Socket class represents a connection to another Socket that data can be both read from and written to. By using the getInputStream() and getOutputStream() methods with an instance of Socket you can read and write data the way you would anywhere else in Java. ServerSocket, as its name implies, is meant for accepting connections, which coincidentally is accomplished by calling its accept()' method. On the server side, when accept() gets an incoming connection it returns a Socket object that can then be used to read from and write to.
In my example, DataSender uses an instance of the Socket class to connect to DataReceiver, which uses an instance of the ServerSocket class. Let's move on into the code.
On the Receiving End
It doesn't seem too difficult, and it isn't! The only tricky part is handling the "possibly many" connections. This is where that almighty concept of threaded programming comes into play. The makers of Java of course have built threads into the language and these threads are exactly what I use in DataReceiver. A thread basically does its work and then dies. A Thread class has a run()' method which is analogous to the main() method of a Java application. The run() method is where a thread's execution starts (and usually finishes).
DataReceiver's main' method creates a ServerSocket object and then starts a loop which calls the ServerSocket's accept() method. Since accept() won't return until an inbound connection is made a loop seemed like the simplest way to always be waiting for a connection. When a connection is made we create a new DataReceiver object and pass it the Socket returned by accept(). Since DataReceiver extends the Java Thread class all we have to do to get it going is call its start() method. When DataReceiver's start() method is called, the loop execution resumes at the top and the DataReceiver object starts running (concurrently) with its run() method (See Listing 2).
Now that we have an inbound connection established and a DataReceiver thread running we have to get the appropriate input stream, read the incoming data and then write it to the file.
DataReceiver's constructor handles creating a DataInputStream object by using the passed Socket's getInputStream() method. This means that any data read from this DataInputStream object will be data that has come in from the network, and hence data that needs to be written to the file. DataInputStream seemed like a good choice for this project because it provides some handy methods for reading the simple data being sent from DataSender (i.e. readLine()).
In the run() method, where thread execution actually begins, we call the DataInputStream's readLine() method. This method will stop reading when an end of line character is reached and then return a String object containing the data read. This String is then passed to the local writeData() method where it is then written to the file. You may notice that the writeData() method is declared with the synchronized' modifier. This is very important to avoid file corruption and general havoc on the server end. The reason being is that there is a good chance that many DataReceiver threads could running concurrently (possibly because of several hits to your web page), our job is to make sure that only one will be writing its data to the file at any given time. When the synchronized keyword is used, Java guarantees that only one thread will be executing that method and others will be queued and have access when it becomes available. You may also want to take note of the FILENAME' constant in DataReceiver.java. This represents a system dependent filename. If your server machine is not running on Windows NT, Windows 95, or does not otherwise understand DOS style directory names you should change this to represent a valid path + filename for your system (See Listing 3).
Sending Data with DataSender
The Socket object constructor I use in DataSender takes the above host name and also a port number. The host name argument is the name of the machine that DataReceiver is running on. This could be a DNS name such as inetserver.company.com' or an IP address string such as 22.214.171.124'. The port used with the Socket in DataSender must match the port being used by the ServerSocket in DataReceiver. If they don't match, a connection will not be made, so, be sure that the USE_PORT constants in both DataSender.java and DataReceiver.java match (see the "Additional information" section for more about ports). The Socket, on construction will attempt a connection to the server.
DataSender's constructor also creates a DataOutputStream using the newly created Socket's getOutputStream() method. The DataSender run() method simply uses DataOutputStream's writeBytes() method to write the passed String to the socket. After data has been written to the Socket I close the output stream and the Socket. The run() method then completes and exits the thread.
DataApplet is still around , so if the customer wants to enter some different information, or clicks the "Submit Request" button again another DataSender thread will start and send that data to DataReceiver.
In the example code provided in this article DataApplet manages collecting and formatting the data and then creates the DataSender object to send it. I decided it would be best to have DataSender extend from Java's Thread class so that the class could be used in more advanced situations than DataApplet (I'll leave those to you). Making use of threads in communications also makes good sense because of the wait that could occur when establishing connections or when data is being sent or received. When a thread is handling communications the main execution of the application can continue. This is especially useful in a windowed environment where we might need to accept user input while communications occur in the background.
That About Says It!
Threads are useful tools to handle communications in the "background" without interrupting the current flow of execution. The key factor to using threads is properly using the synchronized' modifier on thread methods that should only be in use by a single thread at any given time.
A Java Applet can be a quick and easy front end for user input that needs to be sent to the server. Currently, for security reasons applets can only make socket connections to the machine they were loaded from, so, in our example the DataReceiver must be running on the web server machine.
Additional Information (and the Gotchas)
The Socket.close() method is currently not working as designed on Win NT and 95 platforms. This didn't have any ill effects in my testing as the sockets eventually timed out and were cleaned up by the system (netstat -a 5' revealed this). This is listed on Sun's known bugs list and should be corrected in a future JDK release.
If you are planning on using the example code with an Internet service provider they must be using an operating system that supports a Java interpreter. This is because DataReceiver is a Java application and must be run directly on the server via the Java interpreter. These days this doesn't seem like a real problem as many OS vendors are rushing to support the wave of Java applications and applets.
Steps to run the examples as provided: First, be sure that the USE_PORT constants in DataReceiver.java and DataSender.java match. Then be sure the FILENAME constant in DataReceiver.java represents a system dependent path + filename that will work on your system. Compile the source with javac (or your favorite).
To get things running you need put the compiled .class files from DataReceiver, DataSender, and DataApplet in a directory accessible to your web server. Add the <applet> tag provided in DataApplet.html to the .html document your users/customers can access, as shown in Listing 4. Run the DataReceiver application on your server and then load the page that you added the <applet> tag to into your favorite Java aware browser.
Reader Feedback: Page 1 of 1
Subscribe to the World's Most Powerful Newsletters
Today's Top Reads