File transfer is common on the Internet, and Java applets offer this functionality. However, the applets entail some security restrictions that you need to watch out for. I’m going to show you how to safely transfer files between two applets.
The security limitation of applets
If every applet could read or write files on your computer, you would be at a great risk for viruses or other malicious attacks when you browse the Web. So Sun Microsystems implemented restrictions to address this security concern. Generally speaking, applets have two main restrictions:
- · Reading or writing files on a client file system over the network is not allowed.
- · Establishing a network connection between two computers, except the server containing the applet source, is not allowed.
Algorithm of the solution
Do these limitations prohibit file transfer between two applets? Yes—if they transfer files directly, but not if they get help from a middleman.
The idea is that the middleman receives a file from an applet and sends it to the other applet. It functions like a router for file communications, so I call it a file router.
When you implement the program for file transfer, you need to accommodate the two applet security restrictions. First, no direct network connection between two applets can be made. The file router should be able to exchange instructions with the sending and receiving applets. Second, there should be no direct network connection between an applet and another computer except its source’s server. The file router should be located at the server as well. You also must consider some Java policy settings for network connection and file I/O of applets.
Download this sample code, and I will go through each snippet in detail.
Overview of classes and methods
Tables A, B, and C describe the classes and methods in the code to give you the big picture of the middleman.
Since the point is to demonstrate the algorithm of file transfer between two applets without violating their security limitations, I’ll examine only the FileTransferCtlClass::run() method, which is the core that performs file transfer. First, however, we need to look at how to use the class in an applet object and as the object of the middleman.
Code on the applet side
An applet object employs file transfer. This means that the applet object should provide methods to request the sending and receiving of a file, as well as to know whether the process of file transfer is completed. It is therefore recommended that the applet side should contain the following methods:
- · SendFile()
- · GetFile()
- · NotifyComplete()
The suggested format of sendFile is synchronized public void sendFile(). This method, as well as getFile and notifyComplete, should be invoked with the sign synchronized to ensure that they’ll work properly when background I/O (file transfer) and foreground I/O (normal operations) are performed simultaneously.
The sendFile() method should contain a constructor of FileTransferCtlClass and start its operation. The code should look like Listing A.
In this method, the input stream comes from a file and the output stream comes from an object called dout. This is a DataOutputStream object from a Socket object and aims to send data to the middleman. You can obtain the DataOutputStream object from the following statement:
DataOutputStream dout = new DataOutputStream(myFileSocket.getOutputStream());
The statement obtains an output stream object from a socket object, myFileSocket, which is supposed to connect to the middleman.
The sendFile() method sends the file to the middleman. The FileTransferCtlClass is set to Send Mode by the constant FileTransferCtlClass._SENDMODE.
The last parameter indicates the owner of the FileTransferCtlClass object so that any message coming from the FileTransferCtlClass object can display in the owner.
After the FileTransferCtlClass is constructed, the start method is invoked to start the thread of FileTransferCtlClass. The FileTransferCtlClass object will coordinate the other tasks until the file transfer process is completed.
Then there should be a getFile() method that handles receiving a file from the middleman. It should look something like Listing B.
As you see, the contents of getFile() are similar to those of sendFile(). The differences are that the input stream object is set to the socket, the output stream object is set to the file, and the mode is set to Get Mode. After the getFile() method constructs the FileTransferCtlClass object and starts the thread, the FileTransferCtlClass object will coordinate the whole process.
Finally, there should be a method called notifyComplete() on the applet side. The FileTransferCtlClass object invokes the notifyComplete() method when the process is completed. The code should look like this:
synchronized public void notifyComplete(int actionMode)
In this statement, the FileTransferCtlClass object reminds its owner what mode has been set before a file transfer uses actionMode.
The above three methods are required as an interface to cooperate with the middleman before and after file transfer.
Code for the middleman
The main task of the middleman is to route file data from the sender to the receiver. Similar to the case of the applet side, the middleman just has to construct a FileTransferCtlClass object and start it. The FileTransferCtlClass object will coordinate the process of file transfer.
Listing C shows the code on the middleman side. This method uses a FileTransferCtlClass object to route data from an InputStream object to an OutputStream object. The code is similar to that on the applet side, with the following differences:
- · The InputStream object and the OutputStream object come from a Socket object instead of a file. The InputStream object is constructed by a Socket object that connects to the sender, while the OutputStream object is constructed by a Socket object that connects to the receiver.
- · The mode of the FileTransferCtlClass object is set to Route Mode by the parameter FileTransferCtlClass._ROUTEMODE. It indicates that the owner of the FileTransferCtlClass object requires a file routing function.
- · The constructor is FileTransferCtlClass(InputStream myInputStream, OutputStream myOutputStream, int myMode) instead of FileTransferCtlClass(InputStream myInputStream, OutputStream myOutputStream, int myMode, chatapplet myOwner). This is because the FileTransferCtlClass object does not need to show a message to its owner, the middleman.
When three sides—the sender, the receiver, and the middleman—start the FileTransferCtlClass object, the file transfer will perform. The details of this operation can be discovered in the method FileTransferCtlClass::run().
Details in the run
The FileTransferCtlClass::run() method is the most important in the file transfer process, although other methods of FileTransferCtlClass are also essential. The method is invoked after the sender, the receiver, or the middleman invokes the start() method. Since FileTransferCtlClass extends from Thread, it must contain init(), start(), run(), and stop() methods. This method sequence suits the cycle of a thread. After a Thread object finishes a start() method, it will automatically invoke the run() method. This is why we invoke start() instead of run() directly.
Now let’s turn our attention to the main part of routing data. Listing D prepares the file transfer. In Send Mode, the InputStream object comes from a file and the available() method tells the FileTransferCtlClass object how many bytes it will send to the receiver. In Route Mode, the FileTransferCtlClass will pass the number of bytes available to be read to from the sender to the receiver. And in Get Mode, the OutputStream object will receive the number of bytes to be read from the middleman. This operation ensures that file transfer can perform for several files without opening a socket more than once.
Listing E shows the operation of file transfer. There are two loops in the operation. The inner loop reads from the InputStream object one byte at a time into a byte array, while the outer loop passes the byte array to the OutputStream object until the total number of bytes read is equal to the expected number of bytes to be read. After the operation completes, it cleans the OutputStream object so that it will be ready to read the next time and performs a closing operation using the turnOff() method (Listing F).
The turnOff() method closes the open file and informs the owner of the FileTransferCtlClass object that the process is complete. In other words, the InputStream object closes in Send Mode, and the OutputStream object closes in Get Mode. After finishing this method, the whole process of file transfer should be finished.
The sample code we looked at here is just a skeleton to demonstrate how you can perform a file transfer under a given limitation. You can extend or modify it to fulfill your own requirements. For more information on Java applet security limitations, check out Sun’s site.