BUILDING THE TEXTTOOL BACK-END


As stated previously, the TextTool project is divided into two parts: the back-end and the UI. Because the back-end is actually somewhat functional without the UI (the opposite of which is not true the UI is useless without the back-end), let's start by building the back-end.

There are a few emulators you can use during the development process to simulate the Pocket PC environment on your PC. I haven't used any of them. In fact, you could say I'm fundamentally opposed to the whole idea of emulators in the context of Java development because one of the main points of Java is that I can write code on my PC and run it on any platform with a similar profile. True, there are always one or two surprises in store for me the first few times I try to run the software on my iPAQ, but for the most part, it is not difficult to stay within the bounds of the hardware and the JVM. Go the emulator route if that provides a more comfortable environment for you; however, I believe that you are just as well off looking through the PersonalJava specification to get an idea of what is supported and what isn't, then simply building as lean an application as you can.

The first problem I had to consider when designing this project was how I has going to get the Flash UI to communicate with the Java back-end (see Figure 9.2). Users interact with the UI enter text, tap buttons, and so on and eventually that data has to be handed off the back-end so that files can be saved, read, or deleted. The answer I came up with was to use Flash's ability to, in turn, use the browser's ability to make socket connections through the XML ActionScript object. The point where the UI ends, therefore, is the moment it opens a socket connection, and the point at which the back-end starts is where that connection is handled.

Figure 9.2. The Flash UI talking to the Java back-end.

graphics/09fig02.gif

Fortunately, the client-server paradigm is an old one (in computer terms), and has proven itself over time to be useful and reliable. Time has also provided us with countless examples and models, which go a long way toward simplifying the design phase of any client-server application. Clearly, the TextTool back-end needed to consist of a Java process listening for socket connections on a specified port, then parsing and interpreting the content sent over those connections to determine what services to perform and how to perform them. Enter PocketServer.

The back-end of TextTool is divided into two parts: the server and the services that run inside the server (see Figure 9.3). If you're familiar with the JSDK, the PocketServer is analogous to a servlet engine (Tomcat, Jserv, Resin, Jrun, and so on), while the Pocket services are not unlike the servlets that run inside the engine.

Figure 9.3. The server is responsible for dictating the life cycle of the services running in it.

graphics/09fig03.gif

graphics/01icon12.gif

Most people don't really care about how the server works (unless it does not work as designed) as long as they know how to write code that will run inside of it, and some people want the broader picture. If you are interested in how the server works, read on. If you don't care how the server works, but just want to be able to write custom services to run inside the server, feel free to skip to the section entitled " Understanding PocketServices."


Understanding the PocketServer

I chose the name PocketServer rather than something more specific such as TextToolServer to demonstrate an extremely important concept in object-oriented design. The wrong way to design this project would have been to write a TextToolServer that only knew how to service requests from the TextTool UI. Although that would have seen us safely through to the end of this chapter, it is a shortsighted and ultimately limiting approach to application development. What would you do when you then wanted to build a messaging application for your Pocket PC? Would you have to start from scratch and write a ChatServer, half of which would be duplicating code already written and well tested from the TextToolServer? The key here is to abstract out (a favorite term among programmers of object-oriented languages meaning, essentially, to separate) the concept of a server from the specific intent of the application, so that the server can be reused in the context of any application that requires the same architecture as TextTool. Hence, the more generic name PocketServer.

To make the PocketServer capable of performing any type of service for any type of application, the concept of a service must be abstracted out into either an interface or an abstract class. I chose to go with the abstract class PocketService rather than an interface because I was able to identify some common code between all PocketServices.

graphics/01icon12.gif

If it hadn't been for the fact that I was trying to keep this example relatively simple, I actually would have probably implemented a class between PocketService and SaveFile, ReadFile, and RemoveFile called something like FileService. Specifically, FileService would have extended PocketService and SaveFile, ReadFile, and RemoveFile would have all extended FileService where all my file-specific code would have gone. For example, I might have put code in FileService to check to make sure the paths passed in through the config file pointed to valid directories, and that the Java process had permission to write to and read from those directories. I would have abstracted SaveFile's, ReadFile's, and RemoveFile's initialization codes out into FileService because all three are initialized in the same way. Remember that the more code you can move into base classes, the less there is to maintain and fix.


TextTool required the implementation of three specific types of PocketServices: SaveFile, ReadFile, and RemoveFile.

graphics/01icon12.gif

If you are at all familiar with Sun's Java Servlet API, you will find this passage quite familiar (and it will only get more familiar from here), because I have borrowed several key concepts from the extremely well-designed servlet specification. PocketServer and PocketService are far simpler and "thinner" than the servlet specification, both for the sake of demonstration and because it is designed to run on a handheld device rather than server hardware.


Most servers have three milestones in their lives: one initialization, several (hopefully) instances of servicing requests, and one period of shutting down. For the sake of simplicity, I chose to include PocketServer's initialization routine in its constructor; however, an init() method would have worked just as well.

For those of you approaching this chapter from more of a Flash background, do not be deterred or discouraged by the Java code that follows. It is relatively simple, and I think you will find it similar enough to ActionScript that you will not have difficulty following along.

 public PocketServer() throws PocketException {    props = new Properties();    // Use the ClassLoader to load the Config file.    try {       InputStream configStream = ClassLoader.getSystemResourceAsStream(CONFIG_FILE);       props.load(configStream);    }    catch (Exception exception) {      throw new PocketException("Problem loading the configuration properties.", exception);  graphics/ccc.gif }  // Get the "verbose" property. If verbose is true, I periodically  // output information about the server. If false, I'll output nothing.  String verStr = props.getProperty("verbose");  verbose = (verStr == null) ? false : Boolean.valueOf(verStr).booleanValue();  logger = PocketLogger.getInstance();  if (verbose) logger.log("PocketServer created.");  // Get a ";"-delimited list of the PocketServices this server will support.  String allServices = props.getProperty("PocketServices");  if (allServices == null) {      throw new PocketException("No PocketServices were specified in  your config file.");  }  StringTokenizer servicesTokens = new StringTokenizer(allServices,  ";");  // Initialize the Hashtable we keep the services in.  services = new Hashtable();  while (servicesTokens.hasMoreTokens()) {     String className = null;     try {        className = servicesTokens.nextToken();        Class serviceClass = Class.forName(className);        PocketService pServ = (PocketService) serviceClass.newInstance();        pServ.init(props);        services.put(className, pServ);        if (verbose)  logger.log(className + "successfully initialized.");     }     // So many different exceptions that can happen here that I'm just     // going to catch a single Exception.     catch (Exception exception) {        throw new PocketException("Problem loading and initializing" + className + ".",  graphics/ccc.gifexception);     }  }  // Set the isolated property. If the server is configured to be  // isolated, it will not service requests from any machine other  // the one it is running on.  String isolatedStr = props.getProperty("isolated");  if (isolatedStr == null) {      throw new PocketException("Cannot find \"isolated\" property in config file.");  }  isolatedStr = isolatedStr.toLowerCase();  if (!isolatedStr.equals("true") && !isolatedStr.equals("false")) {      throw new PocketException("The \"isolated\" property must be either \"true\" or  graphics/ccc.gif\"false\".");  }  this.isolated = new Boolean(isolatedStr).booleanValue();  if (verbose) {      String v = (isolated) ? "": "not ";     logger.log("PocketServer is" + v + "isolated.");  }  // Start to set up the actual server.  String portStr = props.getProperty("port");  if (portStr == null) {      throw new PocketException("Your config file does not specify a port on which to  graphics/ccc.giflisten.");  }  int port;  try {      port = Integer.parseInt(portStr);  } catch (NumberFormatException exception) {      throw new PocketException("The config property \"port\" must be numeric.");  }  if (verbose) logger.log("PocketServer listening on port "+ port + ".");  ServerSocket server;  try {      server = new ServerSocket(port);  }  catch (IOException exception) {      throw new PocketException("Problem opening a socket on port  port + ".");  } catch (SecurityException exception) {      throw new PocketException("Problem opening a socket on port "+                       port + ". Try using an unprivileged "+                       "port above 1024.");  }  // Start the actual server.  while (true) {      try {         new SocketHandler(server.accept());          } catch (IOException exception) {             throw new PocketException("An IOException was thrown while waiting for a  graphics/ccc.gifconnection.");          }      }  } 

Notice that the first thing that happens during initialization is the loading and parsing of the configuration file a simple text file containing parameters to configure the server. Don't worry about how each property affects the application just yet they will all be explained when their values become relevant.

 verbose=true  port=2000  isolated=false  PocketServices=SaveFile;RemoveFile;ReadFile  SaveFile.saveBaseDir=\\TextTool\\Files\\  RemoveFile.removeBaseDir=\\TextTool\\Files\\  ReadFile.readBaseDir=\\TextTool\\Files\\ 

This configuration file makes PocketServer far more flexible and easier to work with. The idea of a configuration file in general (often called a properties file) is to externalize those aspects of an application that are likely to change most often or that need to be customizable from one piece of hardware to another. The port on which we run the server, for example, is much more likely to have to change (due to conflicts with other servers, perhaps) than the implementation of the code which writes a file to disk (assuming the code was written in the proper platform-independent manner). Because it would be inconvenient to have to recompile the entire server just to change the port number, that value gets stashed in an external text file.

graphics/01icon12.gif

Currently, most configuration or properties files I deal with are written in XML rather than simple name/value format. For the sake of simplicity, however, I decided to use the Properties object's ability to parse straightforward Windows style ini files.


You should also notice the way in which the config file is loaded as a system resource stream rather than just a file because this method goes a long way toward making the code more portable. We already know that the end result of TextTool will be at least three files the jar file containing PocketServer, the Flash file representing the UI, and the HTML page in which the UI is embedded therefore the last thing we want is to add a fourth with a configuration file. Using the ClassLoader to find the config file allows us to jar it up with the rest of the class files, which saves us from somehow having to tell the PocketServer where the file is and from having to worry about platform-specific file structures (because development can actually happen on any type of machine that supports Java and Flash rather than on your Pocket PC device itself).

Now that we have our configuration object, we can initialize our logging facility. It's imperative that we have some type of output mechanism in place so that we know what's happening inside the server. As anyone who has written any kind of software can attest to, it is virtually impossible to develop and debug without frequent visibility into the execution of your work.

The tools the PocketServer uses to log are as lightweight as possible, both for the sake of simplicity for this example, and to keep resource consumption to a minimum. In fact, the PocketLogger is really nothing more than a singleton an object that allows only a single instance that wraps up a static call to write to stdout. The verbose flag is simply a Boolean as opposed to the more complex and flexible approach of specifying one of a set of integers referring to various levels of logging. You will notice that at least the PocketLogger is encapsulated, however, so you can change the implementation throughout the entire application to be more sophisticated if you need it to be just by re-factoring a single file.

Stateless Versus Stateful PocketServices

There were two approaches I considered when I designed the PocketService model: stateful and stateless. A stateful PocketService is one that is instantiated at request time, has various properties set on it, and eventually is called on to perform some service. Conversely, the stateless PocketService doesn't have a concept of properties, but rather has a single method you pass all your data into, and then that single method also performs the specified service.

The advantage of the stateless model is that as many threads can access the same instance of the service simultaneously as is needed without fear of clobbering other thread's data. For example, if one thread set a property called fileName on the SaveFile PocketService to "my_file" , then another thread set the same property to "your_file" before the first thread could call the method on the PocketService that actually wrote the file, then the first value could be overwritten with the second and the file would be saved as "your_file" . To make your code thread-safe, you would either have to synchronize access to your PocketService (meaning only one thread could access it at a time), or have a different instance of SaveFile for every thread, which is not an efficient use of memory (especially on a handheld device), or time, for that matter, because object instantiation is one of the most expensive processes in Java.

The stateless approach, however, allows you to instantiate all the PocketServices at initialization time (which happens only once) and reuse them as much as you want. The configuration parameter "PocketServices" , therefore, refers to a semicolon-delimited list of services that the PocketServer supports. They are all, in turn, loaded by name, initialized, and put into a Hashtable for use at request-time.

graphics/01icon12.gif

If my intended platform were a PC rather than a handheld device, I would use a HashMap because a HashMap is unsynchronized and a Hashtable synchronizes access. However, PersonalJava is based on the Java 1.1.8, which does not support HashMaps.


The "isolated" property addresses a potential security risk inherent in running any type of server on any type of device that could be connected to a network. Setting the isolated property to true ensures that if your device is connected to a network, the server will ignore all requests from clients other than the one the server itself is running on. You will learn more about the significance of isolating the PocketServer in the next section.

Using ServerSocket to Create the Actual Server

The final initialization task is to create the actual server. Without getting into too much server theory, the concept of "starting" a server in Java is really nothing more than calling accept() on an instance of ServerSocket configured to listen on a specified port. ServerSocket will automatically block while waiting for requests and queue them up if more than one arrives simultaneously. The idea is for the server to hand off connections to other processes (preferably in other threads) as quickly as possible so it can spend as much time listening for connections as possible, because that is its primary job. Realistically, the PocketServer is far more robust in terms of handling connections than it really needs to be because it will probably only service a single client the UI which is unlikely to be making more than one request at a time. However, you certainly could use the PocketServer to provide services to multiple UIs, and any given UI could conceivably request more than one service at a time. I found that creating new threads at request time did not adversely affect the performance of the application, so I decided to go with the multi-threaded approach.

When the ServerSocket receives a connection and the accept() method returns, the resulting Socket object is passed to the constructor of the following inner class called SocketHandler:

 /**   * An inner class for handling the socket connections. It extends Thread   * because we want our server to be multi-threaded, meaning it can service   * multiple requests simultaneously.   */  private class SocketHandler extends Thread {      // The socket we are going to read from and write to. It's ok to      // declare member variables at this point because a new instance of      // a SocketHandler is made for every request.      private Socket socket;      private SocketHandler(Socket socket) {         InetAddress address = socket.getInetAddress();         // If the isolated property is set to true, make sure the request         // has come from 127.0.0.1.         if (isolated) {             if (!(address.toString().endsWith("127.0.0.1"))) {                 System.err.println("Rejecting request from ["+ address +"].");                 return;             }         }         this.socket = socket;         if (verbose) logger.log("Connection from ["+ address +"] accepted.");         start();      }      public void run() {         String fullRequest = null;         PocketResponse response = null;         try {            // Get the InputStream from the Socket and read it in.            InputStream in = socket.getInputStream();            response = new PocketResponse(socket.getOutputStream());            byte[] buffer = new byte[1024];            ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();            for (int cnt = in.read(buffer);cnt != -1;cnt = in.read(buffer)) {               byteArrayOut.write(buffer, 0, cnt);               if (in.available() == 0) {                   break;               }            }            fullRequest = byteArrayOut.toString();            if (verbose) {               logger.log("Full request:");               logger.log(fullRequest);            }         } catch (Exception exception) {            logger.log("Problem reading in a request.", exception);            response.badRequest("An unknown error occurred while reading in the request.",  graphics/ccc.gifexception);            return;         }         // Extract the portion of the request which pertains to service         // instructions. Handle the StringIndexOutOfBoundsException as an         // error.         String service = null;         PocketRequest request = null;         try {            String serviceRequest = fullRequest.substring(fullRequest.indexOf("/"),                                        fullRequest.indexOf("",                                        fullRequest.indexOf("/")));        // Check to see  graphics/ccc.gifif the server should exit.         if (serviceRequest.indexOf("?") == -1 &&            serviceRequest.endsWith("KillServer"))         {            System.exit(0);         }            // Now parse the service instructions.            service = serviceRequest.substring(serviceRequest.indexOf("/") + 1,  graphics/ccc.gifserviceRequest.indexOf("?"));            if (verbose) logger.log("A request was made for service ["+service + "] with  graphics/ccc.gifthe following "+                               "arguments: ");            String allArgs = serviceRequest.substring(serviceRequest.indexOf("?") + 1,                                  serviceRequest.length());            StringTokenizer argTokens = new StringTokenizer(allArgs, "&");            request = new PocketRequest();            while (argTokens.hasMoreTokens()) {               String arg = argTokens.nextToken();               String name = arg.substring(0,arg.indexOf("="));               String val = arg.substring(arg.indexOf("=") + 1,  arg.length());               val = URLDecoder.decode(val);               if (verbose)  {                   logger.log(name + "=" + val);               }               request.put(name, val);            }         } catch (StringIndexOutOfBoundsException exception) {            logger.log("A malformed request was made. Requests should "+                     "look like: http://<ip>:<port>/<PocketService>?" +                     "<argName>=<argValue>&<argName>=<argValue>");            response.badRequest("Malformed request.", exception);            return;         }         // Now get the requested PocketService from the Hashtable of         // PocketServices and call service() on it.         PocketService pServ = (PocketService) services.get(service);         if (pServ == null) {            logger.log("Error: A request was made for the unknown PocketService ["+ service +  graphics/ccc.gif"]");            response.notFound("Resource not found. ["+ service + "] does not exist on this " +  graphics/ccc.gif"server.",null);            return;         } try {            pServ.service(request,response);         } catch (PocketServiceException exception) {            logger.log("The PocketService" + service + "threw a PocketServiceException.",  graphics/ccc.gifexception);            response.internalServerError("Internal Server Error. ", exception);            return;         }      }  } 

Notice that the first thing the SocketHandler does when it is instantiated is to check the IP address of the machine the request came from and, if the configuration parameter isolated is true, to make sure it is 127.0.0.1. The IP address 127.0.0.1 always represents the machine on which the server is running, or the localhost . If isolated is set to true, therefore, only requests coming from the machine the server is running will be serviced. As mentioned previously, this is a security measure put in place just in case you ever have your device connected to a network. An iPAQ or other Pocket PC device can be seen during a port scan just like any other computer, so if you are not careful to isolate the PocketServer, other machines can run PocketServices remotely on your device (which also happens to be a pretty cool feature especially during testing and debugging but one you would certainly want to control).

The SocketHandler's job is essentially to start a new thread in which to do its work, determine which PocketService is being requested, package up the data sent in the request, and pass it along to the requested PocketService (see Figure 9.4). In order for it to understand what the client is telling it to do, however, there must be a protocol established between the SocketHandler and the client (the Flash UI).

Figure 9.4. The SocketHandler and PocketServices.

graphics/09fig04.gif

Because I was already borrowing concepts from the JSDK, and because my architecture is a relatively simple client-server one, HTTP seemed like the natural choice for a protocol for the client and server to use to communicate. Not only is it fairly simple, but also by making the PocketServer understand HTTP, it would be fully testable from a browser or any other client capable of making HTTP requests. Likewise, the UI could be tested against a regular web server (albeit, to a much lesser extent than the PocketServer) or other process capable of servicing HTTP requests.

graphics/01icon07.gif

Notice how the PocketServer will call System.exit(0) if it finds that the request was for a service called KillServer. This is to compensate for an issue I ran into while testing the PocketServer under Sun's PersonalJava on an HP Jornada 568 running PocketPC 2002. Consistently when I tried to close the server, I got a message stating that the program was not responding and giving me an option to close it immediately. Even if I chose "yes", however, I found that although the OS thought the process had been killed, it was actually still running in the background, allowing me to continue using TextTool. Adding the PocketServer's ability to exit, all you have to do to be sure the PocketServer has been stopped is to make a request through Pocket Internet Explorer to http://127.0.0.1:2000/KillServe.


The details of parsing the request are probably best left to the code itself. At this point, an HTTP request, which comes in to the PocketServer either from a browser or from a Flash ActionScript LoadVariables action, looks something like this:

 GET /SaveFile?fileName=myFile&fileContent=Hello%2EWorld&append=false HTTP/1.1  Accept: /  Accept-Encoding: gzip, deflate  User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)  Host: 192.168.1.5:2000  Connection: Keep-Alive 

This code results in a PocketRequest object containing the name/value pairs from the query string (fileName=myFile , fileContent=Hello World ) and a PocketResponse object containing the output stream from the socket connection getting passed into the SaveFile PocketService object through the abstract service method. The rest of the headers in the request are ignored. Once service() is called, assuming no exceptions get thrown, the PocketServer and the SocketHandler have done their jobs and it is up to the PocketService to do its job.

graphics/01icon12.gif

Once again, my methodology is borrowed directly from Java Server Development Kit; however, this is a much simpler, lighter weight implementation suitable for devices with limited resources.


Understanding PocketServices

A PocketService is any class that extends the abstract class PocketService and implements the abstract service() method. As long as the service() method is properly implemented (and the optional initSupport() ), the PocketServer doesn't care what the particular service does. It could send email, make database queries, write to a file, or even act as a proxy and simply make another socket connection to someplace else.

It just so happens that the TextTool application requires three simple PocketServices: SaveFile, ReadFile, and RemoveFile. I don't want to waste space describing the particulars of these three implementations as they are simple examples and are easy enough to understand by looking through the code. I will, however, discuss the configuration of the PocketServices before moving on and describing how to implement your own.

Each of TextTool's three PocketServices requires one configuration parameter each: saveBaseDir , removeBaseDir , and readBaseDir . Notice in the configuration file how the name of the relevant PocketService is prepended to (comes before) the parameter name, separated by a dot.

graphics/01icon13.gif

Starting your parameter name with the name of the PocketService it applies to is not required, but it does help preserve a namespace and keep parameters from overwriting each other's values.


Because you can assume that all PocketServices whether you write them or you get them from some other source will have a unique name, including that unique name in the parameter, ensures that all parameters will be unique. If two people write PocketServices that both require a property called randomSeed , for example, they can both simultaneously hold different values because the name of the two PocketServices would be different. In the case of TextTool, each PocketService has a different configuration parameter, but they all have the same value, which obviously specifies a directory. The values of all three parameters indicate the directories in which files are saved, read, and deleted during use of TextTool. Be sure the parameters are appropriate for the platform on which you are running, and because backslashes are escape characters in Java, don't forget to double escape in the context of the Windows directory structure.

It's important to keep in mind that these properties are not meant to control file access on your device because it is actually possible to save, load, and delete files outside of these specified directories. For example, if you created the directory \Windows\Java\TextTool\Files for storing files, you could actually save a file by the name ../../../MyFile.txt and have that file be written to the Windows directory. A malicious application of this functionality might be to save a text file in the Windows directory as the same name as a dll file already there, thereby replacing the dll and probably doing serious harm to your device's operating system. More robust versions of SaveFile and RemoveFile might use the java.io.FilePermission object to specify which directories users can write to and delete from.

graphics/01icon07.gif

It is decidedly inconvenient that Windows uses backslashes as file separators. Whenever you are referring to the file system in a language that uses backslashes as escape characters, you have to remember to use two backslashes to indicate that you mean a literal backslash as opposed to trying to escape the character after the backslash. On a Pocket PC device, you end up with directories that look like this: \\TextTool\\Files.


Writing Your Own PocketService

For the sake of simplicity, neither the PocketServer nor any of the PocketServices are in any type of a package structure, so don't worry about a package declaration in your PocketService. All you have to do is extend the abstract class PocketService and implement a service() method. Keep in mind that by extending PocketService, you automatically have access to the Properties object, which contains all your configuration information (so remember to externalize parameters that are likely to change).

graphics/01icon07.gif

Creating a package structure would mean having to maintain a classpath on your device, and because Pocket PC does not have a concept of environment variables, you have to edit the Registry. There are some nice tools out there for both remote and local editing of the Pocket PC Registry, but to avoid that digression here, I've simply opted not to use any packages.


If your new PocketService needs custom initialization, you can override the protected support method initSupport() , which will get called once at the time the PocketService() is loaded (which occurs during PocketServer initialization) and never again. Now compile the source, be sure your new PocketService is included in the semicolon-delimited list of PocketServices in the config file, and you're ready to go.

Running the PocketServer on Your PC

I definitely recommend running the PocketServer on your PC first because running it on your Pocket PC device may require some additional steps and tinkering, especially if you are writing your own PocketServices. Running the PocketServer on a PC is as simple as compiling the source in whatever manner you are accustomed (either through an IDE or from the command line), making sure the binaries are in your classpath, and running the PocketServer with a command such as java PocketServer .

If anything goes wrong, there should be enough information in the exception for you to debug it without too much difficulty. The error you are probably most likely to get is a NoClassDefFoundError , indicating that the PocketServer simply was not correctly added to your classpath. Assuming the code compiled and your classpath was successfully configured, there is not much else that can go wrong, so you should see output similar to this:

 PocketServer created.  SaveFile successfully initialized.  RemoveFile successfully initialized.  ReadFile successfully initialized.  PocketServer is isolated.  PocketServer listening on port 2000. 
Figure 9.5. PocketServer running on my Mac. This is the exact same code that will be copied over and executed on my iPAQ.

graphics/09fig05.gif

You can then exercise the code by making requests from your browser. For example, typing the following in the location bar

http://127.0.0.1:2000/SaveFile?fileName=myFile&fileContent=this+is+a+test results in the string "this is a test" getting saved in a file called "myFile" in the directory specified as the value of the configuration parameter SaveFile.saveBaseDir . The request

http://127.0.0.1:2000/ReadFile?fileName=myFile

returns the string "this is a test" (which may or may not actually be displayed in your browser depending on the browser you are using because it is not HTML). Finally, you can remove the file with the request

http://127.0.0.1:2000/RemoveFile?fileName=myFile

If all three operations work on your PC, it's time to try running PocketServer on a Pocket PC device.

Running the PocketServer on Your Pocket PC Device

Before you do anything else, be sure to adapt the directories specified in your config file to the directory structure of your Pocket PC device. Again, if your PC is a Unix derivative, don't forget to turn the forward slashes into backslashes and escape them.

The next thing you need to do is get all the class files over to your device. By far the easiest way to move and execute class files on Pocket PC is to jar (Java archive) them up into a single file. To be able to simply tap on the jar file and have the right class automatically execute, however, you have to include your own custom MANIFEST file in the archive (jar file) that tells the JVM which class it should load first. The following MANIFEST file tells the JVM to load the class PocketServer and start it by calling its main() method.

 Manifest-Version: 1.0  Main-Class: PocketServer 

graphics/01icon12.gif

A Java archive is similar to a zip or a tar file. It is primarily a convenient method for packaging up class files, moving them, and executing them. To learn more about jar files, check any Java reference, or simply type jar from the command line of any computer that has Java installed.


Navigate to the directory where all the PocketServer class files are and type the following command from the command line:

 jar  cfm PocketServer.jar MANIFEST.MF *.class config 

This builds a jar file with all the classes in the current directory and the config file, and replaces the default MANIFEST.MF file with your custom version. You may want to type the command the first time just to test it; however, I strongly recommend you eventually automate the process with an alias, a batch file, or a shell script. Here is the bash script (for Unix platforms) I used while developing the PocketServer:

 #!/bin/bash  jar  cfm PocketServer.jar MANIFEST.MF *.class config 

Create a folder on your Pocket PC device where you want to store your TextTool files (for example, \TextTool\Java\) and copy PocketServer.jar into it. Assuming you have PersonalJava (or the JVM of your choice) installed properly, tapping on the jar file should start the JVM and load the PocketServer. As a testament to Sun's "write once, run anywhere" motto, you should see the exact same output on your Pocket PC device that you saw on your PC.

Figure 9.6. The same code I ran on my Mac now running on my iPAQ. Notice how the output is nearly identical.

graphics/09fig06.gif

If you are using Pocket PC 2002, you should now be able to make the same requests to test the PocketServer through Pocket Internet Explorer that you made through the full version of Internet Explorer to test the PocketServer on your desktop. If you are using the original version of Pocket PC, however, such requests will yield the message The page you are looking for cannot be found . This is a result of an undesirable feature (some might say bug) of PIE on the original version of Pocket PC that prevents the browser from even attempting to make a socket connection to any server (even itself) if there is not some sort of network connection to the device already established. If you were to put your device in its cradle, therefore, your requests would be successful, as even a serial connection is sufficient. Likewise, any type of Ethernet connection will do the trick, as well.

Fortunately, I am not the only one to have ever run into this problem on Pocket PC, and a company called Cambridge Computer Corporation has actually solved it. Cambridge sells a full web server for the Pocket PC platform called vxWeb, which is both multi-threaded and supports CGI. To use it locally (from your Pocket PC device), they provide a tool called ieFix, which, when you start it, makes the device believe it has established a connection. ieFix can be downloaded from the Cambridge Computer Corporation web site (www.cam.com) and although there is a licensing fee for vxWeb, ieFix can be used free of charge (you will not need the actual vxWeb web server). Once the ieFix process is started, the PocketServer works perfectly.

graphics/01icon12.gif

I found that it was necessary to use ieFix.exe on an iPAQ running Pocket PC; however, it may not be necessary on other platforms or with other operating systems or clients. For example, using my Jornada 568 running Pocket PC, the PocketServer works perfectly by itself. You might want to take the time to determine that you have a problem on whichever platform you are using before you attempt to solve it.


graphics/01icon07.gif

I found that my iPAQ was easily confused when I swapped between the three forms of network connections I had available to me Ethernet adapter, cradle, and ieFix.exe and I sometimes found that a soft reset was the only way I could get the device to behave properly. I have had much better luck with Pocket PC 2002 and would highly recommend upgrading if you have not already.




Macromedia Flash Enabled. Flash Design and Development for Devices
Macromedia Flash Enabled. Flash Design and Development for Devices
ISBN: 735711771
EAN: N/A
Year: 2002
Pages: 178

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net