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.
Table A

Class Description
FileTransferCtlClass This public class handles exchanging and will be owned and used by an Applet class. It extends from Thread so that it can perform file transfer in the background.
TransferFrame FileTransferCtlClass objects, which will display any message in an Applet object, use this class. Since it aims to provide the function of displaying messages for FileTransferCtlClass, it is private and accessed only by a FileTransferCtlClass object.

 
Table B


FileTransferCtlClass
Methods Description
FileTransferCtlClass(InputStream myInputStream, OutputStream myOutputStream, int myMode)

This is a constructor of FileTransferCtlClass. This constructor should be invoked by an object that does not need any display during transfer.

myInputStream is an InputStream object that receives data. It can be used to receive data from a file sender or to read a local file.

myOutStream is an OutputStream object that sends data. It can be used to send data to a file receiver or to write data into a local file.

myMode indicates what kind of job the FileTransferCtlClass object performs. If it is set to _SENDMODE, the object will read data from a selected local file and send the data to a receiver. If it is set to _GETMODE, the object will receive data from a sender and write the data into a local file. If it is set to _ROUTEMODE, the object will receive data from a sender and send the data to a receiver.

FileTransferCtlClass(InputStream myInputStream, OutputStream myOutputStream, int myMode, chatApplet myOwner)

This is another constructor of FileTransferCtlClass. An object that needs displayed during file transfer should call this constructor.

The first three parameters are the same as those in the above constructor. myOwner refers to an owner, which is a chatApplet object, that will use the FileTransferCtlClass object.

Since that sample code was designed for an object belonging to chatApplet, which is derived from an Applet, myOwner’s type is chatApplet.

turnOff() This method is used to notify the owner of the FileTransferCtlClass object that a file transfer is completed. The FileTransferCtlClass object does not help its owner close its I/O Stream object and leaves this job to its owner for flexibility.
run() This method will be invoked to perform a file transfer after the sender, the receiver, and the File Router are all ready.
long newTransferID() You use this method to get a new TransferID, a unique identifier for file transfer, which is just a reference for developers.
long getTransferID() This method is used to get the current TransferID.
boolean isCompleted() This method is used to check whether the process of a file transfer is completed after invoking the
run() method.

 
Table C


TransferFrame

Methods

Description
TransferFrame(FileTransferCtlClass myOwner) This constructor aims to create a Frame object to display a message from a FileTransferCtlClass object, myOwner.
setEnabled(boolean myStatus)

This method is used to set whether a TransferFrame object can display.

A TransferFrame object is set to be enabled for a sender or a receiver, which is usually an Applet object. But it is set to be disabled for a File Router because it may not be an Applet object.

showMe() This method asks the TransferFrame object to show itself. It works only after the object is set to enable its display by the setEnabled() method.
setRate(float myRate) This method is used by a FileTransferCtlClass object to display its percentage of file completely transferred.
setMessage(String myMessage) This method is used to display a message of myMessage. It works only after successfully invoking the showMe() method.
close() This method is used to shut down the TransferFrame object.

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.

Routing data
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.

Conclusion
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.