So far, you have learned how to retrieve and handle a short text string entered from the keyboard into a simple graphical user interface (GUI). But programs also retrieve, handle, and store data in files and databases.
This lesson expands the examples from previous lessons to perform basic file access using the application programming interfaces (APIs) in the java.io
package. It also shows you how to grant applets permission to access specific files, and how to restrict an application so it has access to specific files only.
The Java® 2 Platform software provides a rich range of classes for reading character or byte data into a program, and writing character or byte data out to an external file, storage device, or program. The source or destination might be on the local computer system where the program is running or anywhere on the network.
This section shows you how to read data from and write data to a file on the local computer system. See The Java Tutorial trail on Reading and Writing for information on transferring data between programs, between a program and memory, and performing operations such as buffering or character encoding on data as it is read or written.
When Application Starts
When Button Clicked
The conversion from the SwingUI.java program for Lesson 4 to the FileIO.java program for this lesson primarily involves the constructor
and the actionPerformed
method as described here.
Constructor and Instance Variable Changes
A JTextfield
instance variable is added to the class so the constructor
can instantiate the object and the actionPerformed
method can access the text the end user types into it.
The constructor
instantiates the JTextField
with a value of 20. This value tells the Java platform the number of columns to use to calculate the preferred width of the field. Lower values result in a narrower display, and likewise, higher values result in a wider display.
The text
label is added to the North
section of the BorderLayout
so the JTextField
can be added to the Center
section.
Note: You can learn more about component sizing in The Java Tutorial sections on Solving Common Layout Problems and Layout Management.
//Instance variable for text field
JTextField textField;
FileIO(){
text = new JLabel("Text to save to file:");
clicked = new
JLabel("Text retrieved from file:");
button = new JButton("Click Me");
button.addActionListener(this);
clickButton = new JButton("Click Again");
clickButton.addActionListener(this);
//Text field instantiation
textField = new JTextField(20);
panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.setBackground(Color.white);
getContentPane().add(panel);
//Adjustments to layout to add text field
panel.add("North", text);
panel.add("Center", textField);
panel.add("South", button);
}
Method Changes
The actionPerformed
method uses the FileInputStream
and FileOutputStream
classes to read data from and write data to a file. These classes handle data in byte streams, as opposed to character streams, which are shown in the applet example. A more detailed explanation of the changes to the method implementation follows the code.
public void actionPerformed(
ActionEvent event){
Object source = event.getSource();
if(source == button){
//Variable to display text read from file
String s = null;
if(_clickMeMode){
try{
//Code to write to file
String text = textField.getText();
byte b[] = text.getBytes();
String outputFileName =
System.getProperty("user.home",
File.separatorChar + "home" +
File.separatorChar + "zelda") +
File.separatorChar + "text.txt";
File outputFile = new File(outputFileName);
FileOutputStream out = new
FileOutputStream(outputFile);
out.write(b);
out.close();
//Code to read from file
String inputFileName =
System.getProperty("user.home",
File.separatorChar + "home" +
File.separatorChar + "zelda") +
File.separatorChar + "text.txt";
File inputFile = new File(inputFileName);
FileInputStream in = new
FileInputStream(inputFile);
byte bt[] = new
byte[(int)inputFile.length()];
in.read(bt);
s = new String(bt);
in.close();
}catch(java.io.IOException e){
System.out.println("Cannot access text.txt");
}
//Clear text field
textField.setText("");
//Display text read from file
text.setText("Text retrieved from file:");
textField.setText(s);
button.setText("Click Again");
_clickMeMode = false;
} else {
//Save text to file
text.setText("Text to save to file:");
textField.setText("");
button.setText("Click Me");
_clickMeMode = true;
}
}
}
To write the end user text to a file, the text is retrieved from the textField
and converted to a byte array.
String text = textField.getText();
byte b[] = text.getBytes();
Next, a File
object is created for the file to be written to and used to create a FileOutputStream
object.
String outputFileName =
System.getProperty("user.home",
File.separatorChar + "home" +
File.separatorChar + "zelda") +
File.separatorChar + "text.txt";
File outputFile = new File(outputFileName);
FileOutputStream out = new
FileOutputStream(outputFile);
FileOutputStreamFile
out.write(b);
out.close();
The code to open a file for reading is similar. To read text from a file, a File
object is created and used to create a FileInputStream
object.
String inputFileName =
System.getProperty("user.home",
File.separatorChar + "home" +
File.separatorChar + "zelda") +
File.separatorChar + "text.txt";
File inputFile = new File(inputFileName);
FileInputStream out = new
FileInputStream(inputFile);
Next, a byte
array is created the same size as the file into which the file contents are read.
byte bt[] = new byte[(int)inputFile.length()];
in.read(bt);
Finally, the byte array is used to construct a String
object, which is used to create the text for the label
component. The FileInputStream
is closed when the operation completes.
String s = new String(bt);
label.setText(s);
in.close();
The above code used a call to System.getProperty
to create the pathname to the file in the user's home directory. The System
class maintains a set of properties that define attributes of the current working environment. When the Java platform starts, system properties are initialized with information about the runtime environment including the current user, Java platform version, and the character used to separate components of a file name ( File.separatorChar
).
The call to System.getProperty
uses the keyword user.home
to get the user's home directory and supplies the default value File.separatorChar + "home" + File.separatorChar + "zelda")
in case no value is found for this key.
The above code used the java.io.File.separatorChar
variable to construct the directory pathname. This variable is initialized to contain the file separator value stored in the file.separator
system property and gives you a way to construct platform-independent pathnames.
For example, the pathname /home/zelda/text.txt
for Unix and \home\zelda\text.txt
for Windows are both represented as File.separatorChar + "home" + File.separatorChar + "zelda" + File.separatorChar + "text.txt"
in a platform-independent construction.
An exception is a class that descends from either java.lang.Exception
or java.lang.RuntimeException
that defines mild error conditions your program might encounter. Rather than letting the program terminate, you can write code to handle exceptions and continue program execution.
The file input and output code in the actionPerformed
method is enclosed in a try
and catch
block to handle the java.lang.IOException
that might be thrown by code within the block.
java.lang.IOException
is what is called a checked exception. The Java platform requires that a method catch or specify all checked exceptions that can be thrown within the scope of a method.
Checked exceptions descend from java.lang.Throwable
. If a checked exception is not either caught or specified, the compiler throws an error.
In the example, the try
and catch
block catches and handles the java.io.IOException
checked exception. If a method does not catch a checked exception, the method must specify that it can throw the exception because an exception that can be thrown by a method is really part of the method's public interface. Callers of the method must know about the exceptions that a method can throw so they can take appropriate actions.
However, the actionPerformed
method already has a public interface definition that cannot be changed to specify the java.io.IOException
, so in this case, the only thing to do is catch and handle the checked exception. Methods you define yourself can either specify exceptions or catch and handle them, while methods you override must catch and handle checked exceptions. Here is an example of a user-defined method that specifies an exception so callers of this method can catch and handle it:
public int aComputationMethod(int number1,
int number2)
throws IllegalValueException{
//Body of method
}
Note: You can find more information on this topic in The Java Tutorial trail on Handling Errors with Exceptions.
When you catch exceptions in your code, you should handle them in a way that is friendly to your end users. The exception and error classes have a toString
method to print system error text and a printStackTrace
method to print a stack trace, which can be very useful for debugging your application during development. But, it is probably better to deploy the program with a more user-friendly approach to handling errors.
You can provide your own application-specific error text to print to the command line, or display a dialog box with application-specific error text. Using application-specific error text that you provide will also make it much easier to internationalize the application later on because you will have access to the text.
For the example programs in this lesson, the error message for the file input and output is handled with application-specific error text that prints at the command line as follows:
//Do this during development
}catch(java.io.IOException e){
System.out.println(e.toString());
System.out.println(e.printStackTrace());
}
//But deploy it like this
}catch(java.io.IOException e){
System.out.println("Cannot access text.txt");
}
If you want to make your code even more user friendly, you could separate the write and read operations and provide two try
and catch
blocks. The error text for the read operation could be Cannot read text.txt, and the error text for the write operation could be Cannot write text.txt.
As an exercise, change the code to handle the read and write operations separately. Give it a try before peeking at the solution.
The file access code for the FileIOAppl.java code is equivalent to the FileIO.java application, but shows how to use the APIs for handling data in character streams instead of byte streams. You can use either approach in applets or applications. In this lesson, the choice to handle data in bytes streams in the application and in character streams in the applet is purely random. In real-life programs, you would base the decision on your specific application requirements.
The changes to instance variables and the constructor
are identical to the application code, and the changes to the actionPerformed
method are nearly identical with these two exceptions:
textField
text is retrieved, it is passed directly to the out.write
call.
public void actionPerformed(ActionEvent event){
Object source = event.getSource();
if(source == button){
//Variable to display text read from file
String s = null;
if(_clickMeMode){
try{
//Code to write to file
String text = textField.getText();
String outputFileName =
System.getProperty("user.home",
File.separatorChar + "home" +
File.separatorChar + "zelda") +
File.separatorChar + "text.txt";
File outputFile = new File(outputFileName);
FileWriter out = new
FileWriter(outputFile);
out.write(text);
out.close();
//Code to read from file
String inputFileName =
System.getProperty("user.home",
File.separatorChar + "home" +
File.separatorChar + "zelda") +
File.separatorChar + "text.txt";
File inputFile = new File(inputFileName);
FileReader in = new FileReader(inputFile);
char c[] = new
char[(char)inputFile.length()];
in.read(c);
s = new String(c);
in.close();
}catch(java.io.IOException e){
System.out.println("Cannot access text.txt");
}
//Clear text field
textField.setText("");
//Display text read from file
text.setText("Text retrieved from file:");
textField.setText(s);
button.setText("Click Again");
_clickMeMode = false;
} else {
//Save text to file
text.setText("Text to save to file:");
textField.setText("");
button.setText("Click Me");
_clickMeMode = true;
}
}
}
If you tried to run the applet example, you undoubtedly saw errors when you clicked the Click Me
button. This is because the Java 2 Platform security does not permit an applet to write to and read from files without explicit permission.
An applet has no access to local system resources unless it is specifically granted the access. So for the FileUIAppl
program to read from text.txt
and write to text.txt
, the applet has to be given the appropriate read or write access permission for each file.
Access permission is granted with a policy file, and appletviewer is launched with the policy file to be used for the applet being viewed.
Creating a Policy File
Policy tool is a Java 2 Platform security tool for creating policy files. The Java Tutorial trail on Controlling Applets explains how to use Policy Tool in good detail. Here is the policy file you need to run the applet. You can use Policy tool to create it or copy the text below into an ASCII file.
grant {
permission java.util.PropertyPermission
"user.home", "read";
permission java.io.FilePermission
"${user.home}/text.txt", "read,write";
};
Running an Applet with a Policy File
Assuming the policy file is named polfile
and is in the same directory with an HTML file named fileIO.html
that contains the HTML to run the FileIOAppl
applet, you would run the application in appletviewer like this:
appletviewer -J-Djava.security.policy=polfile fileIO.html
Note: If your browser is enabled for the Java 2 Platform or if you have Java Plug-in installed, you can run the applet from the browser if you put the policy file in your local home directory.
fileIO.html FileIOAppl
<HTML>
<BODY>
<APPLET CODE=FileIOAppl.class WIDTH=200 HEIGHT=100>
</APPLET>
</BODY>
</HTML>
You can use the default security manager and a policy file to restrict the application's access as follows.
java -Djava.security.manager
-Djava.security.policy=apppolfile FileIO
Because the application runs within the security manager, which disallows all access, the policy file needs two additional permissions. One so the security manager can access the event queue and load the user interface components, and another so the application does not display the banner warning that its window was created by another program (the security manager).
grant {
permission java.awt.AWTPermission
"accessEventQueue";
permission java.awt.AWTPermission
"showWindowWithoutWarningBanner";
permission java.util.PropertyPermission
"user.home", "read";
permission java.io.FilePermission
"${user.home}/text.txt", "read,write";
};
Although servlets are invoked from a browser, they are under the security policy in force for the web server under which they run. When file input and output code is added to ExampServlet.java
from Lesson 5, FileIOServlet for this lesson executes without restriction under Java WebServer 1.1.1.
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class FileIOServlet extends HttpServlet {
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<title>Example<title>" +
"<body bgcolor=FFFFFF>");
out.println("<h2>Button Clicked</h2>");
String DATA = request.getParameter("DATA");
if(DATA != null){
out.println("<STRONG>Text from
form:</STRONG>");
out.println(DATA);
} else {
out.println("No text entered.");
}
try{
//Code to write to file
String outputFileName=
System.getProperty("user.home",
File.separatorChar + "home" +
File.separatorChar + "zelda") +
File.separatorChar + "text.txt";
File outputFile = new File(outputFileName);
FileWriter fout = new FileWriter(outputFile);
fout.write(DATA);
fout.close();
//Code to read from file
String inputFileName =
System.getProperty("user.home",
File.separatorChar + "home" +
File.separatorChar + "zelda") +
File.separatorChar + "text.txt";
File inputFile = new File(inputFileName);
FileReader fin = new
FileReader(inputFile);
char c[] = new
char[(char)inputFile.length()];
int i;
i = fin.read(c);
String s = new String(c);
out.println("<P>
<STRONG>Text from file:</STRONG>");
out.println(s);
fin.close();
}catch(java.io.IOException e){
System.out.println("Cannot access text.txt");
}
out.println("<P>Return to
<A HREF="../simpleHTML.html">Form</A>");
out.close();
}
}
So far the examples have shown you how to read in and write out streams of data in their entirety. But often, you want to append data to an existing file or read in only certain amounts. Using the RandomAccessFile class, alter the FileIO.java class to append to the file.
Give it a try before taking a peek at the Solution.
For more infomation on file input and output, see the Reading and Writing trail in The Java Tutorial.
You can learn more about component sizing in The Java Tutorial sections on Solving Common Layout Problems and Layout Management.