Storing Information with PersonalJava

Files

Unlike MIDP platforms such as the Palm implementation, PersonalJava platforms such as the PocketPC have a filesystem based on directories and files. With PersonalJava, the developer can therefore make use of the file I/O APIs available in JDK1.1.8.

Reading and Writing a File

Files can be read and written in the same way as in J2SE. For example, a reference to a BufferedReader can be created as follows:

file = new File(directory, filename);
in = new BufferedReader(new FileReader(file));

As a demonstration of file I/O on the PocketPC, the following simple application displays the FileDialog when the user presses the Open button (Figure 6.5).

package com.javaonpdas.persistence;

import java.awt.*;
import java.awt.event.*;
import java.io.*;

public class FileIO extends Frame implements ActionListener {

  TextArea textArea = null;
  Button saveButton = null;
  File file = null;

  public FileIO(String title) {
    super(title);
    // handle frame closing events
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    } );
    setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10));
    Button button = new Button("Open");
    add(button);
    button.addActionListener(this);
    textArea = new TextArea(12,28);
    add(textArea);
    saveButton = new Button("Save");
    add(saveButton);
    saveButton.addActionListener(this);
  }

  public void actionPerformed(ActionEvent evt) {
    String cmd = evt.getActionCommand();
    if (cmd.startsWith("Open")) {
      FileDialog dialog = new FileDialog(this, "Open a file",
        FileDialog.LOAD);
      dialog.show();
      // open and read the file
      String filename = dialog.getFile();
      String directory = dialog.getDirectory();
      BufferedReader in = null;
      try {
        file = new File(directory, filename);
        in = new BufferedReader(new FileReader(file));
        String fileLine = null;
        textArea.setText("");
        for (;;) {
          fileLine = in.readLine();
          if (fileLine == null)
            break;
          else
            textArea.append(fileLine + "\n");
        }
      }
      catch (IOException e) {
        textArea.setText("Error: " + e.getMessage());
      }
      finally {
        try {
          if (in != null) in.close();
        } catch (IOException e) {}
      }
    }
    else {
      // save the text area into the same file
      BufferedWriter out = null;
      try {
        out = new BufferedWriter(new FileWriter(file));
        out.write(textArea.getText());
      }
      catch (IOException e) {
        textArea.setText("Error: " + e.getMessage());
      }
      finally {
        try {
          if (out != null) out.close();
        } catch (IOException e) {}
      }
    }
  }

  public static void main(String[] args) {
    Frame f = new FileIO("FileIO");
    f.setSize(f.getToolkit().getScreenSize());
    f.show();
  }
}
Figure 6.5. FileIO

graphics/06fig05.gif

When the user selects a text file, the file is read into the text area on the application's window (Figure 6.6). The user is then able to modify the text in the text area and save the modified text into the same file, by pressing the Save button.

Figure 6.6. The BasicWindow.lnk file

graphics/06fig06.gif

Serializing Objects to a File

PersonalJava supports ObjectOutputStream and ObjectInputStream, so it is possible to persist objects in the file system.

Suppose we have a Hashtable called animals. To write the Hashtable to the file system:

private static String dataFileName = "animals.dat";
private ObjectOutputStream out = null;
...
out = new ObjectOutputStream(new FileOutputStream(dataFileName));
out.writeObject(animals);
out.flush();
out.close();

To read the Hashtable from the file system into memory:

in = new ObjectInputStream(new FileInputStream(dataFileName));
animals = (Hashtable)in.readObject();
in.close();

As an example of this, the following application displays the name of an animal that starts with a selected letter. The animal name can be updated, and it is then saved to a file. If the application is restarted, the previous state of the Hashtable is restored from the file.

package com.javaonpdas.persistence;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;

public class SerializedObjects extends Frame
  implements TextListener, ItemListener {

  private File file = null;
  private Choice startsWith = null;
  private TextField animalName = null;
  private Hashtable animals = null;
  private static String[] letters = {"a", "b", "c", "d", "e",
                                     "f", "g", "h", "i", "j",
                                     "k", "l", "m", "n", "o",
                                     "p", "q", "r", "s", "t",
                                     "u", "v", "w", "x", "y",
                                     "z"};
  private static String dataFileName =
    "\\My Documents\\JavaOnPDAs\\animals.object";
  private ObjectInputStream in = null;
  private ObjectOutputStream out = null;

  public SerializedObjects(String title) {
    super(title);
    // handle frame closing events
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    } );
    setLayout(new FlowLayout(FlowLayout.LEFT, 5, 5));
    // add the UI components
    add(new Label("Animal starting with"));
    startsWith = new Choice();
    for (int i=0; i<letters.length; i++) {
      startsWith.add(letters[i]);
    }
    startsWith.addItemListener(this);
    add(startsWith);
    animalName = new TextField(25);
    animalName.addTextListener(this);
    add(animalName);
    // fill the hashtable
    try {
      in = new ObjectInputStream(new GZIPInputStream(
        new FileInputStream(dataFileName)));
      animals = (Hashtable)in.readObject();
      in.close();
    }
    catch (Exception e) {
      animals = new Hashtable();
      animals.put("a", "ape");
      animals.put("b", "bear");
      animals.put("c", "cheetah");
      animals.put("d", "deer");
      animals.put("e", "elephant");
      animals.put("f", "fox");
      animals.put("g", "gorilla");
      animals.put("h", "hippopotamus");
      animals.put("i", "iguana");
      animals.put("j", "jazelle");
      animals.put("k", "koala");
      animals.put("l", "lion");
      animals.put("m", "monkey");
      animals.put("n", "<<no animal found>>");
      animals.put("o", "<<no animal found>>");
      animals.put("p", "parrot");
      animals.put("q", "<<no animal found>>");
      animals.put("r", "rat");
      animals.put("s", "snake");
      animals.put("t", "tortoise");
      animals.put("u", "<<no animal found>>");
      animals.put("v", "<<no animal found>>");
      animals.put("w", "<<no animal found>>");
      animals.put("x", "<<no animal found>>");
      animals.put("y", "<<no animal found>>");
      animals.put("z", "zebra");
    }
    finally {
      try {
        if (in != null) in.close();
      } catch (IOException e) {}
    }
  }

  public void textValueChanged(TextEvent evt) {
    String selectedItem = startsWith.getSelectedItem();
    if (animals.containsKey(selectedItem)) {
      String currentValue = (String)animals.get(selectedItem);
      String newValue = animalName.getText();
      if (!currentValue.equals(newValue)) {
        // update the current key with the selected value
        animals.put(selectedItem, newValue);
        // save the hashtable
        try {
          out = new ObjectOutputStream(new GZIPOutputStream(
            new FileOutputStream(dataFileName)));
          out.writeObject(animals);
          out.flush();
          out.close();
        }
        catch (Exception e) {
        }
        finally {
          try {
            if (out != null) out.close();
          } catch (IOException e) {}
          }
        }
      }
    }

    public void itemStateChanged(ItemEvent evt) {
    String value = (String)animals.get((String)evt.getItem());
    animalName.setText(value);
  }

  public static void main(String[] args) {
    Frame f = new SerializedObjects("SerializedObjects");
    f.setSize(f.getToolkit().getScreenSize());
    f.show();
  }
}

The main window of this application when the letter 'e' is selected is shown in Figure 6.7.

Figure 6.7. SerializedObjects

graphics/06fig07.gif

Compressed Files

A useful feature available in J2SE is the ability to wrap the GZIPOutputStream and GZIPInputStream classes around an output and input stream, respectively, in order to automatically compress the data being read and written by the stream. This feature is also available in PersonalJava. GZIPOutputStream is optional in PersonalJava, but the Jeode implementation implements it on the PocketPC.

The following code fragment shows how to compress an object output stream and read from a compressed file as an object input stream.

out = new ObjectOutputStream(new GZIPOutputStream (new FileOutputStream(dataFileName)));
...
in = new ObjectInputStream(new GZIPInputStream (new FileInputStream(dataFileName)));

As an example of the effect of compressing an object stream, the animals.object is 395 bytes without compression and 293 bytes with compression.

JDBC Databases

PointBase is a relational database system for small devices running, among others, MIDP on Palm devices and PersonalJava on the PocketPC. It has a footprint of less than 45 KB for MIDP, and less than 90 KB for PersonalJava. It supports username and password security, database encryption, a SQL92 subset including transactions, Unicode, and it uses the underlying persistence of the platform.

The PointBase sample application HelloDatabase connects to a database, tests whether the TEST database exists, either inserts an initial value or updates an incremented value of a counter, and queries the result.

import com.pointbase.me.*;

public class HelloDatabase {
    public static void main(String argv[]) {
        try {
            Class.forName("com.pointbase.me.DriverManager");
            Connection conn=DriverManager.getConnection(
                "jdbc:pointbase:micro:test","PBPUBLIC","PBPUBLIC");
            Statement stat=conn.createStatement();
            DatabaseMetaData meta=conn.getMetaData();
            ResultSet rs=meta.getTables(null,null,"TEST",null);
            if(rs.next()) {
                // the table already exists
                stat.execute("UPDATE TEST SET ID=ID+1");
            } else {
                // the table doesn't exist yet
                stat.execute("CREATE TABLE TEST(ID INT)");
                stat.execute("INSERT INTO TEST VALUES(1)");
            }
            rs=stat.executeQuery("SELECT ID FROM TEST");
            rs.next();
            int count=rs.getInt(1);
            System.out.println("Hello Database!");
            System.out.println("This application was run "+count +" time(s)");
            conn.close();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

To run HelloDatabase, copy pbmicro43.jar from C:\pointbase\lib and HelloDatabase.class from C:\pointbase\samples\micro\HelloWorld\classes to the PocketPC's \My Documents\JavaOnPDAs. Create the HelloDatabase shortcut using the Ant build.xml and copy that to the PocketPC's \My Documents\ JavaOnPDAs as well.