Sun Java Solaris Communities My SDN Account Join SDN
 
Article

Getting Started with the FileConnection APIs

By Qusay Mahmoud, December 2004  

The Connected Limited Device Configuration (CLDC) and the most popular profile based on it, the Mobile Information Device Profile (MIDP), focus on providing a sound runtime environment and basic application services. Neither the configuration nor the profile includes APIs for access to file systems on mobile devices or external memory cards. This omission is intentional: Not all MIDP devices have file systems, and the creators of those that do may not want to expose them to applications.

The Java 2 Platform, Standard Edition (J2SE) includes java.io.File and its related classes to support such access, but these APIs are too heavyweight to be useful on mobile devices. The solution lies in the FileConnection Optional Package, a simple but useful set of file-system APIs for the Java 2 Platform, Micro Edition (J2ME). This optional package is part of JSR 75, PDA Optional Packages for the J2ME Platform. On devices that implement JSR 75, this package enables J2ME-based applications to create, read, and write files and directories located on mobile devices and external memory cards.

This tutorial provides a code-intensive introduction to the FileConnection APIs; it:

  • Introduces JSR 75
  • Describes the javax.microedition.io.file package
  • Provides details of the FileConnection APIs
  • Gives you a taste of the effort involved in using these APIs
  • Provides code you can adapt to your own wireless applications
  • Optional Packages for the J2ME Platform

    JSR 75 provides some very useful APIs that J2ME developers really needed to take advantage of features commonly found on PDAs in the J2ME space, in the form of two optional packages that extend and enhance software stacks based on CLDC:

    1. The FileConnection Optional Package (FC) APIs give J2ME devices access to file systems residing on mobile devices, primarily access to removable storage media such as external memory cards.
    2. The PIM Optional Package (PIM) APIs give J2ME devices access to personal information management data native to mobile devices, such as address books, calendars, and to-do lists.

    It's important to note that the FC and PIM packages are independent of each other. This article will focus on the FileConnection APIs.

    Because any device that meets the minimum requirements of CLDC 1.0 can also support JSR 75, and because the Connected Device Configuration (CDC) is a superset of CLDC, the FileConnection APIs can be deployed on top of any CLDC- or CDC-based profile.


    Note: In the phrase "optional package," the word "optional" indicates that whoever is responsible for maintaining the platform software on the device, usually the manufacturer, has the option to include implementation of the API. In general, neither end users nor application developers can download an optional package and install it in the device. An application that relies on an optional package will run properly only if that package is already installed.

    The FileConnection Optional Package

    To gain access to file systems located in a device's internal memory, or on removable memory media such as SmartMedia cards and CompactFlash cards, the FC APIs use the Generic Connection Framework (GCF) for file-system connectivity.

    The FileConnection APIs

    The FC APIs are defined in the package javax.microedition.io.file, which includes two interfaces and three classes:

    Interface
    Description
    FileConnection
    Interface for access to files or directories.
    FileSystemListener
    Listener interface for receiving status notification when adding or removing a file-system root.
    Class
    Description
    FileSystemRegistry
    Central registry for listeners that need to add or remove a file system.
    ConnectionClosedException
    Exception thrown when a method of a file connection is invoked but cannot be completed because the connection is closed.
    IllegalModeException
    Exception thrown when a method is invoked that requires a particular security mode, such as READ or WRITE, but the connection opened is not in that mode.

    The FC optional package may not be available on all J2ME platforms. To find out whether it is, invoke System.getproperty() with a key of microedition.io.file.FileConnection.version. This method will return the version number of the API if the package is present, null if it's not. Another useful property is the file.separator, a string representing the file separator character, "/" for example.


    Note: CLDC permits implementations to refuse to load an application that refers to classes that aren't present, so a device that doesn't have the FileConnection APIs installed might not let your application check for them at runtime. In this case, you must be prepared to package two different versions of the application, one that uses the FileConnection APIs and one that doesn't.

    Security Issues

    In addition to including all packages, classes, and interfaces defined in the FileConnection optional package, a JSR 75-compliant implementation must also provide a security model for accessing the FileConnection APIs. In particular:

  • To protect users' files and data from inadvertent or malicious access, an implementation may allow access to files that are public and bar access to files that are private or sensitive. The implementation may not allow access to MIDP RMS databases, and should not allow access to system configuration files, or to files and directories that are device- or OS-specific, private to another application, or private to a different user. In such cases the Connector.open() method throws a java.lang.SecurityException.
  • The security model must be applied when opening a connection to a file using Connector.open() and when opening a stream for the connection using openInputStream(), openOutputStream(), openDataInputStream(), or openDataOutputStream().

  • Again, it's up to the including platform or profile to define a security model. There is one special case: The JSR 75 expert group has provided a recommended practice for using the FileConnection APIs when the including profile is MIDP 2.0, which states:

  • Untrusted MIDlet suites that access the protected APIs and functions of the FileConnection APIs must be subject to confirmation by the user.
  • Trusted MIDlet suites must specify the permissions that are applicable to the FileConnection APIs. For more information on the permissions and protected methods, please refer to the FC specification.


  • Establishing Connections

    An application opens a connection using Connector.open(). The input string must comprise a fully qualified, absolute pathname of the form file://<host>/<root>/<directory>/<directory>/.../<name>. The host element may be empty - and often will be, when the string refers to a file on the local host. The root directory corresponds to a logical mount point for a particular storage unit. Root names are device-specific. The following table provides some examples of root values and how to open them:

    Root Value
    How to Open a FileConnection
    CFCard/
    FileConnection fc = (FileConnection) Connector.open("file:///CFCard/");
    SDCard/
    FileConnection fc = (FileConnection) Connector.open("file:///SDCard/");
    MemoryStick/
    FileConnection fc = (FileConnection) Connector.open("file:///MemoryStick/");
    C:/
    FileConnection fc = (FileConnection) Connector.open("file:///C:/");
    /
    FileConnection fc = (FileConnection) Connector.open("file:////");

    Note well that a connection object like fc in these examples refers to a single file or directory at any given time. The best way to refer to multiple directories or files is to establish a separate connection to each, using Connector.open().

    Once you've established a connection to a file system, you can perform several kinds of queries, using the FileConnection object's methods, including among others:

  • Get a filtered list of files and directories using the method list(String filter, boolean includeHidden). In the filter parameter you can use * as a wildcard to specify zero or more occurences of any character. The includeHidden parameter specifies whether you want to list only visible files, or hidden files as well.
  • Discover whether a file or directory exists using exists().
  • Discover whether a file or directory is hidden using isHidden().
  • Create or delete a file or directory using create(), mkdir(), or delete().

  • For a list of all the valid root values in a device, call the listRoots() method of FileSystemRegistry.

    Note that a FileConnection behaves differently from other Generic Connection Framework connections in one way: The Connector.open() method can return successfully without referring to an existing entity such as a file or a directory. This capability enables you to create new files and directories. Here is a segment of code that creates a new file; assume SDCard is a valid file-system root:

    public void createFile() {
       try {
          FileConnection filecon = (FileConnection)
             Connector.open("file:///SDCard/mynewfile.txt");
          // Always check whether the file or directory exists.
          // Create the file if it doesn't exist.
          if(!filecon.exists()) {
             filecon.create();
          }
          filecon.close();
       } catch(IOException ioe) {
       }
    }
    


    Reference Implementations

    The official reference implementation of JSR 75 can be downloaded from IBM. This RI is aimed at the PocketPC operating system, so it requires the J9 Java Virtual Machine for the PocketPC. An implementation of JSR 75 is included in the beta release in the J2ME Wireless Toolkit 2.2 from Sun Microsystems. We'll use the toolkit to test the examples in the rest of this article.

    FileConnection Demo in J2ME Wireless Toolkit 2.2

    The J2ME Wireless Toolkit 2.2 comes with a FileConnection demo: a file browser that lets the user list files and directories and view the contents of text files. To experiment with this demo, download the toolkit and install it if you haven't already done so; then:

    1. Start KToolbar.
    2. Open the project PDAPDemo.
    3. Run the application. Note that because the application is going to browse local files, you're prompted to give permission, as shown in Figure 1.
    4. Figure 1: User Confirmation for Access


    5. Once permission is granted, you can begin to browse the file system, as in Figure 2:
    6. Figure 2: Browsing the File System


    7. Select the root1 directory, then open the file Readme to see the contents of that file, shown in Figure 3:

    Figure 3: Viewing a File's Contents



    Note: The J2ME Wireless Toolkit emulator sets aside a directory on your desktop computer's hard disk, /<toolkit>/appdb/DefaultColorPhone/filesystem, to hold representations of a mobile device's file-system roots. It then uses the FileConnection APIs to give MIDlets access to files stored in subdirectories of filesystem. The emulator comes with one root directory already installed, called root1, which contains the file Readme. In the toolkit emulator, each immediate subdirectory of filesystem is treated as a root.
    Using the FileConnection APIs

    To give you an idea how little effort is involved in using the FileConnection APIs, we'll examine a simple application that allows you to list the valid roots as well as the files and directories in a particular root. To start, you need to set up the valid roots as follows:

    1. Go to /toolkit/appdb/DefaultColorPhone/filesystem; you'll notice that a subdirectory, root1, already exists.
    2. In filesystem create two new subdirectories and call them CFCard and SDCard.
    3. In CFCard create a new subdirectory called pix.
    4. In CFCard create two new files and name them readme.txt and personal.txt. Write a few lines in each so that later you can check file sizes. If you think you'll want to experiment with the includeHidden parameter to FileConnection.list(), flag one of the files as hidden. Note that under Windows you can do so with the command attrib +h <filename>.
    /<toolkit>/appdb/DefaultColorPhone/filesystem/
                                              CFCard/
                                                 readme.txt
                                                 personal.txt
                                                 pix/
                                              SDCard/
    


    The FileConnectionDemo MIDlet in Code Sample 1 demonstrates how to use the FileConnection APIs to access files and directories. Two methods deserve special attention:

    • getRoots() uses FileSystemRegister.listRoots() to list the valid root values.
    • GetCFcardContent() iterates through the list of files and directories under the CFCard/ root value. For each, the method indicates whether it's a file or directory, displays its name, and if it's a file reports its size.

    Code Sample 1: FileConnectionDemo.java

    import java.io.*;
    import java.util.*;
    import javax.microedition.io.*;
    import javax.microedition.midlet.*;
    import javax.microedition.io.file.*;
    
    public class FileConnectionDemo extends MIDlet {
    
       public void startApp() {
          System.out.println("MIDlet Started....");
          getRoots();
          GetSDcardContent();
          //showFile("readme.txt");
       }
    
       public void pauseApp() {
       }
    
       public void destroyApp(boolean condition) {
          notifyDestroyed();
       }
    
       private void getRoots() {
          Enumeration drives = FileSystemRegistry.listRoots();
          System.out.println("The valid roots found are: ");
          while(drives.hasMoreElements()) {
             String root = (String) drives.nextElement();
             System.out.println("\t"+root);
          }
       }
    
       private void GetSDcardContent() {
          try {
             FileConnection fc = (FileConnection)
                Connector.open("file:///CFCard/");
             // Get a filtered list of all files and directories.
             // True means: include hidden files.
             // To list just visible files and directories, use
             // list() with no arguments.
             System.out.println
                ("List of files and directories under CFCard:");
             Enumeration filelist = fc.list("*", true);
             while(filelist.hasMoreElements()) {
                String fileName = (String) filelist.nextElement();
                fc = (FileConnection)
                   Connector.open("file:///CFCard/" + fileName);
                if(fc.isDirectory()) {
                   System.out.println("\tDirectory Name: " + fileName);
                } else {
                   System.out.println
                      ("\tFile Name: " + fileName + 
                       "\tSize: "+fc.fileSize());
                }
                
             }   
             fc.close();
          } catch (IOException ioe) {
             System.out.println(ioe.getMessage());
          }
       }
    
       public void showFile(String fileName) {
          try {
             FileConnection fc = (FileConnection)
                Connector.open("file:///CFCard/" + fileName);
             if(!fc.exists()) {
                throw new IOException("File does not exist");
             }
             InputStream is = fc.openInputStream();
             byte b[] = new byte[1024];
             int length = is.read(b, 0, 1024);
             System.out.println
                ("Content of "+fileName + ": "+ new String(b, 0, length));
          } catch (Exception e) {
          }
       }
    }
    

    This example hard-codes the root "CFCard/" – poor practice in anything but a demonstration, of course. You could improve this MIDlet by using the listRoots() method as shown earlier to obtain the valid root values. At this point, though, experiment with FileConnectionDemo as it stands:

    1. Start KToolbar.
    2. Create a new project and call it anything you like – FC in my example – but do name the MIDlet FileConnectionDemo.
    3. Configure the project to include JSR 75 as shown in Figure 4:
    4. Figure 4: Configuring the Project to Use JSR 75


    5. Copy the program in Code Sample 1, and in /<toolkit>/apps/FC/ save it as FileConnectionDemo.java.
    6. Build and run the application. You'll see it reports the directory structure you created earlier, as in Figure 5:

    Figure 5: Listing of Roots, Files, and Directories


    If you run the toolkit's FileBrowser demo again, you'll see output similar to Figure 6:

    Figure 6: The New Directory Structure, Reported by FileBrowser


    The program in Code Sample 1 lists roots, files, and directories, but doesn't display the contents of any file. The following method handles this chore. Note that the contents appear on the console but you can easily change the code to display them in a box on the device's display.

    public void showFile(String fileName) {
       try {
          FileConnection fc = (FileConnection)
             Connector.open("file:///CFCard/" + fileName);
          if(!fc.exists()) {
             throw new IOException("File does not exist");
          }
          InputStream is = fc.openInputStream();
          byte b[] = new byte[1024];
          int length = is.read(b, 0, 1024);
          System.out.println
             ("Content of "+fileName + ": "+ new String(b, 0, length));
       } catch (Exception e) {
       }
    }
    


    Conclusion

    JSR 75 defines two APIs: the PIM Optional Package and the FileConnection Optional Package. The first of these interfaces give J2ME-based applications easy access to personal information management data that resides on mobile devices, often in a native form, such as address books, calendars, and to-do lists. The second interface provides similar access to conventional hierarchical file systems residing on mobile devices and external memory cards.

    This article introduced the PDA Optional Packages for the J2ME Platform, and presented a tutorial on the FileConnection APIs. The sample code showed how easy it is to develop MIDlets that give their users access to device-local file systems. Learn only a handful of classes and interfaces and you're ready to begin. Remember that the FileConnection APIs are part of an optional package that may not be available on all J2ME devices.

    For More Information
    Acknowledgements

    Special thanks to Stuart Marks of Sun Microsystems, whose feedback helped me improve this article.

    About the Author

    Qusay H. Mahmoud provides Java technology consulting and training services. He has published dozens of Java articles, and is the author of Distributed Programming with Java (Manning Publications, 1999) and Learning Wireless Java (O'Reilly, 2002).

    Rate and Review
    Tell us what you think of the content of this page.
    Excellent   Good   Fair   Poor  
    Comments:
    Your email address (no reply is possible without an address):
    Sun Privacy Policy

    Note: We are not able to respond to all submitted comments.