Skip to main content

Access Desktop Data from Mobile Devices

November 30, 2006

alt="{cs.r.title}" border="0" align="left" hspace="10" vspace="0">






Java ME has
made it possible to write platform-independent applications for
mobile devices and deploy them fairly easily. Now that we have a
number of computing platforms strewn all over--office, home, and
pocket--we need to think of interconnecting them. As far as
desktop devices are concerned, wired internet has provided the
necessary linkage for quite some time now. With the advent of GPRS,
it has now become fairly easy to access data residing on desktops
through mobile devices.

In this article we look at an application called Access that
enables a MIDP-2.0-compatible mobile phone to locate an entry in a
phone directory on a desktop PC and to call that number. The
application also implements a rudimentary password-based
authentication scheme that will not be dealt with in detail
here.

In order to run this application you will need a PC that can be
accessed from the internet. You may need to talk to your ISP to make
sure you can do this.

You also need to have a servlet container running on the PC. I
have used Apache Tomcat as
the servlet container for implementing this project. Downloading
Tomcat and installing in your computer is fairly simple; a good
tutorial is Marty Hall's "
Configuring and Using Apache Tomcat
." The root directory for
this project is AccessServlet in the C:\j2sdk1.4.2_05
directory. The web.xml file and the entry for root>/conf/server.xml are available as a .zip in the Resources section. For my computer, root> is C:\jakarta-tomcat-4.1.31. Note that I have
not changed the operating port to 80.

The application was developed on WTK
2.2
. As noted in the concluding paragraph, the application can
also be tested on the toolkit's emulator (except for call
initiation). If you are testing the application on the same PC that
hosts the server, you may do without an internet connection
provided you use 127.0.0.1 as the server URL. This is the URL in
the source code provided here. If, on the other hand, you want to
try it out from a mobile device, the server address will have to be
changed accordingly.

I assume that the reader is familiar with basic Java programming,
including Swing and servlets. Familiarity with J2ME is also
assumed.

The Mobile Client

The mobile client comprises the following elements:

  • The Access MIDlet, which serves primarily as the entry
    point.
  • A set of user interfaces (UIs or screens).
  • CommandProcessor: This class has a method
    corresponding to every user interaction and completion of each
    system activity.
  • DisplayController: This class sets up screens
    and alerts to be displayed.
  • RecordStoreHandler: This class takes care of
    reading from and writing to persistent storage.
  • Communication controllers that extend
    HttpGetCommController: These classes communicate
    with the servlet in the desktop.

Access kicks off the application by calling the
selectLoginMode method in
CommandProcessor. This method then calls
accessRecordExists in RecordStoreHandler
to check if the username and password have been already saved in
the device. If login information exists, the user is prompted to
select a login mode, auto or manual (see Figure 1). Otherwise, only
the manual login screen is displayed.

Selecting login mode
Figure 1. Selecting login mode

In the case of manual login, the user is asked whether she would
like to save the information (as seen in Figure 2).

Prompt for saving login information
Figure 2. Prompt for saving login information

The screen shown in Figure 2 is not a standard J2ME
TextBox, nor is it a Form with a
StringItem. This is a customized "window"-like display
created by the MessageCanvas class. The message canvas
is fairly flexible in the sense that it adjusts its dimensions
according to the size of available display area. It is capable of
displaying single- or multi-line strings. It uses its companion
class TextFormatter to break long strings up into an
array of smaller ones. MessageCanvas does have a
couple of weaknesses, though: it cannot understand formatting
instructions like newline and does not support scrolling.

It is important to note that the actual logging in does not take
place at the end of the auto or manual login action. At this point,
the username and password are combined into a single
semicolon-separated string, Base64 encoded, and saved as the
upw field in CommandProcessor.

Once the login information has been made available, the user is
shown the screen for selecting a function; he can choose to access
the phonebook or change the password. We will walk through the
sequence of events that take place if the user selects the
"Access phonebook" function. The sequence of screens for this
operation (assuming no errors are encountered) can be found in the
Resources section.

The mobile client communicates with the server (the desktop,
that is) over an HTTP connection. The communicating classes extend
the abstract HttpGetCommController class, which
provides the basic framework for opening a connection and receiving
a response from a GET method. For getting information
from the phonebook, the client uses the ReadClientH
class. Forming the URL and processing the response as
well as any other customization has to be taken care of by the
subclasses. Since the GET method is being accessed,
the URL incorporates not only the information required
for getting to the servlet but also the required parameters. The
syntax for the parameter is:

   command+*+upw+*+commandinfo

where:

  • command can be either read or
    changeupw
  • commandinfo can be either target or
    newupw

When accessing the phone book, a name has to be specified, and
this is the target. Similarly, when changing the password,
the existing username is combined with the new password to form
newupw. The asterisk is chosen as the separator because it
is one of the few special characters that are allowed to be a part
of a URL.

The actions that take place in the desktop when it receives a
request are described in the next section. However, let us first look
at what happens when the desktop returns information to the client.
Once the response is received, it is checked by
ReadClientH to see if starts with "tel:". If it does
not, then the response contains an error message and is displayed
in a message canvas with an option to try again. If the response
does begin with "tel:", then it is the desired response and is
passed on to InputPage (via
CommandProcessor, of course) for presentation to the
user:

protected void processReply(String reply)
{
   if(reply.startsWith("tel:"))
   {
      //show textfield with phone number and call command
      cp.showInfo(reply);
   }
   else
   {
      //error info
      cp.showNote(reply);
   }
}
InputPage parses the response string to separate it
out into its two constituents: the name and the
number. These are then displayed within separate text
fields. A third string--callmessage--is also created
here. One of the commands on InputPage is Call,
which allows the user to initiate a call to the number retrieved
from the phonebook. If Call is selected, then the method
makeCall in CommandProcessor is invoked
with callmessage as the parameter. MIDP 2.0 allows call
initiation through the platformRequest method in the
MIDlet class. Accordingly, the call request is routed
to the setUpCall method in Access. In
case there is a communication error and the response received at
InputPage is not properly formatted, an error message
is displayed. Otherwise the number is called:

protected void setUpCall(String callnumber)
{
   //call the number found from the directory
   try
   {
      platformRequest(callnumber);
   }
   catch(ConnectionNotFoundException cnfe)
   {
      cp.showConnectAlert(cnfe.getMessage());
   }
}

The other function that the user may select is "Edit
password." The screens that are displayed during "Edit
password" function are available in the Resources section. These screens also show the result of
an error, namely entering the wrong password entry for editing.

Once the network activities for the functions are over the user
gets a chance to re-run the application, regardless of whether the
outcome was successful or not.

The Servlet and Associated Classes

The servlet has only a doGet method, in addition to
the usual init.

The first time the mobile client opens a connection to Tomcat
after it is started, AccessServlet is instantiated and
the init method is executed. Thereafter, for all calls
to AccessServlet, the same instance is reused. When
AccessServlet is instantiated, it checks to see if the
file containing user information (password.pwd) exists. If
not, then it creates one (with a username of "guest" and a password of
"allow") by calling the static method createAccount in
the SetUpAccount class. This is just to provide ease
of use. SetUpAccount can also be called from
the command line with the desired username and password as
arguments. Note that SetUpAccount does not create
multiple accounts, but simply overwrites any existing account
information. This form of user information is used only as an
example. More secure forms will be required for a general-purpose
application. If an application like this is created for a
multi-user environment, then at least three modifications are
required:

  • Creating a database for user information. This will also mean a
    different approach to password editing.
  • Making the servlet thread-safe. Of course, this may not be
    considered critical, as access to the directory is essentially
    read-only.
  • Changing the interfaces for directory access.

The structure of the directory also merits a few comments. The
example shown here is designed for simplicity. It stores
name-number entries as strings separated by a ; character. The name and the
number in each entry are joined by a - character and the name
itself is entered without any spaces so that it can be integrated
into the URL for HTTP connection. The first letter of
each part of the name (first name, middle name or initial if any,
and last name) is capitalized. The phone number also needs to be
entered without intervening spaces. A directory with two entries
could look like this:

JaneDoe-1112222;JohnBSmith-3334444

One consequence of this format and the way the a specific entry
is searched for is that partial matches are accepted. So a
target entered as "J" would be located in the above
directory as the entry for JaneDoe.

The doGet method in AccessServlet
parses the incoming parameter to extract and check the command. If
the command is read, the user information (upw) and the
target entry are then extracted and the open method in
the GetInfo class is called with upw and
target as parameters. However, upw has to be decoded
before open is called.

The actual work of exploring the file structure is performed by
open. Here we create the two objects that are required
for this: a FileChooser object to select both files
and directories, and a FileSystemView object:

public String open(String upw, String target) throws IOException
{
   File f, f1;
   JFileChooser fc;
   FileSystemView fsv;
               
   f = new File("C:\\Access");
   fc = new JFileChooser(f);
   fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
   fsv = fc.getFileSystemView();
   .
   .
   .
}

The FileSystemView object (fc) can now be used to
see inside File f. The getFiles method of
FileSystemView returns an array of (non-hidden)
File objects for us to examine and act upon. For example,
we may choose to list out the contents of the array. The user may
then select a folder from this list for further exploration. The
application can then call the getFiles method on the
selected folder to get its contents. Performing this function
repeatedly will allow us to explore the entire file structure.

However, for the present application we are not interested in
folders, which is why we do nothing when we encounter a folder. If,
on the other hand, we find a file, we check if it is the one we
want:

//find number of objects in "Access"
int j = fsv.getFiles(f, true).length;

for(int i=0;i&ltj;i++)
{
   f1 = fsv.getFiles(f, true)[i];

   //if the item is a folder
   if(f1.isDirectory())
   {
      // we can unravel the directory structure here
      //but right now we do nothing
   }
   //otherwise it is a file
   else
   {
      //if f1.toString().equals("C:\\Access\\Directory.txt")
      //then get the wanted information and return
      if(f1.toString().equals("C:\\Access\\Directory.txt"))
      {
         DataHandler datahandler = new DataHandler();

         //check upw
         if(datahandler.authenticate(upw))
         {
            //locate entry by target
            try
            {
               return datahandler.getNumber(f1, target);
            }
            catch(Exception e)
            {
               return "Error in accessing information";
            }
         }
         else
         {
            return "Authentication failure";
         }
      }
   }
}

Once the Directory file is found, the
getNumber method in DataHandler class is
called to locate the desired entry and return the phone number.
Well known methods of the String class are used to find
the target and the entire entry is extracted. The resulting string
is then passed to the parseEntry method, which gets the
number and carries out some simple checks to see whether we have
something that seems to be a phone number. It then creates and
returns the string that has to be sent to the client. This string
is returned to the open method, which, in turn, returns
it to doGet in AccessServlet, where it is
transmitted to the client.

So the calling sequence for methods mentioned above is:

doGet (in AccessServlet) -> open (in GetInfo) -> getNumber (in DataHandler)

Similarly, the retrieved information flows back to the client
like this:

getNumber -> open -> doGet -> client

Many of you would be wondering at this point why
FileChooser has been used here. Since we know the path
to the file we are looking for, normal file I/O routines would have
been quite adequate. The reason for bringing in
FileChooser and FileSystemView is to show
that it is possible to create a Windows-Explorer-type front end on
a mobile handset using these classes on the desktop.

Conclusion

This article is meant to show the mechanics of hooking
up a desktop with a small device like a cell phone. It also lays
the foundation for creating an application for remote exploration
of the file structure of a desktop PC. While reading this article,
you should keep the following issues in mind:

  • The application as presented here cannot be fully tested on an
    emulator because a real phone is required for placing a call.
    However, I have tested it extensively on a Nokia 6101 and it works
    just fine.

  • The platformRequest method is not supported by MIDP
    1.0 and that is why the profile setting for the client-side project
    has to be MIDP 2.0. If this feature is omitted, the project can be
    compiled under the MIDP 1.0 profile.

Note that there are several enhancements that can be
implemented. The issue of security has been paid only token
attention here. Stronger security measures can be implemented at
various levels. You can find more information in the article
"
MIDP Application Security 1: Design Concerns and
Cryptography
."

One weakness of this application, as already mentioned, is that
the first partial match is accepted and returned by the search
method. A possible approach to improving this would be to return an
array of all entries to the client that satisfy partial match. The
client could then show the entries one by one to the user for the
final selection.

In addition to the two enhancements mentioned above you can, I
am sure, think of many more. Trying to implement them will give you
many hours of fun.

Resources


Biswajit Sarkar is an electrical engineer with specialization in Programmable Industrial Automation. Biswajit is the author of "LWUIT 1.1 for Java ME Developers" published by PACKT Publishing.
Related Topics >> Mobility   |