Getting Started with the FileConnection APIs
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;
- 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:
- 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.
- 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://////…/. 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
FileConnection fc = (FileConnection) Connector.open("file:///CFCard/"); FileConnection fc = (FileConnection) Connector.open("file:///SDCard/"); FileConnection fc = (FileConnection) Connector.open("file:///MemoryStick/"); 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) { } }
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.
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.
