Putting the Load on the Server

As instructional as our Hello example has been in demonstrating how to use XML-RPC with Java, it isn’t very realistic. In addition to being a trivial example, the server is not very flexible and the handler itself doesn’t give any indication of how a practical XML-RPC handler might operate. Here we try to give examples of using XML-RPC in a production environment by increasing the usefulness of the handler and the usability of the server. These, while still not code you might want to immediately add to your current project, should at least begin to demonstrate to you how XML-RPC might be of use in your future projects, and how to build applications that can use XML-RPC but are not limited by it.

A Shared Handler

Our HelloHandler class was simple but useless in a practical application. Remember that we said most XML-RPC uses relate to letting events occur on a server that is more suited for complex tasks, while allowing a thin client to request procedures be executed and use the returned results. In addition, it is possible that part or even all of the computations needed to respond to a request can be done in advance; in other words, the handler class may be running tasks and ensuring that results are already available when a method call comes in. As a Java coder, threads and shared instance data should leap to your mind. Here we take a look at a very simple Scheduler class to illustrate these principles.

Our scheduler should allow clients to add and remove events. We also want to be able to query our scheduler for a list of all events in the queue. To make this a little more practical (and to have a task for our server to perform later), we want the result of an events query to return the events sorted by the time they occur. These events for our example will simply be a String event name and a time for the event (in a java.util.Date format). Though this is not a complete scheduler implementation, it can demonstrate how to let the server do some behind-the-scenes work for us.

First we will code our addEvent( ) and removeEvent( ) methods. Because these are both client-triggered events, there is nothing particularly remarkable about them; what is worth thinking about is how to store these events in our Scheduler class. Although our XML-RPC server will instantiate this class, and that instance will be used for all XML-RPC calls that come into that server, it is possible and even probable that other classes or even XML-RPC servers may interact with our scheduler as well. If we store a list of events as a member variable of our class, multiple instances will not be able to share data. To solve this problem in our example, we will make our storage static, causing it to be shared across all Scheduler class instances. To store both an event name and an event time, a Hashtable would seem appropriate, allowing the use of key-value pairs. In addition to this Hashtable, we store the names of the events in a Vector . Although this uses some extra storage space (and memory in the Java Virtual Machine), we can sort our Vector and not have to deal with sorting our Hashtable; the advantage here is that we can swap the event names in our Vector (a single swap) and not have to swap the event times in our Hashtable (two swaps for each exchange). Let’s code the skeleton of this class, and add these first two methods to allow addition and removal of events. For now, we add our storage as well, but we leave the implementation of the retrieval and sorting of events for later. Example 10.6 is a code listing for this new handler.

Example 10-6. The Scheduler Class

import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;

/**
 * <b><code>Scheduler</code></b> is a class that allows
 *   addition, removal, and retrieval of a list of events, sorted
 *   by their occurrence time.
 * 
 * @version 1.0
 */
public class Scheduler {
    
    /** List of event names (for sorting) */
    private static Vector events = null;
    
    /** Event details (name, time) */
    private static Hashtable eventDetails = null;    

    /**
	 * <p>
	 * This will initialize the storage.
	 * </p>
	 */
    public Scheduler(  ) {
        events = new Vector(  );
        eventDetails = new Hashtable(  );     
    }
  
    /**
	 * <p>
	 * This will add the requested event.
	 * </p>
	 *
	 * @param eventName <code>String</code> name of event to add.
	 * @param eventTime <code>Date</code> of event.
	 * @return <code>boolean</code> - indication of if event was added.
	 */
    public boolean addEvent(String eventName, Date eventTime) {      
        // Add this event to the list of events
        if (!events.contains(eventName)) {
            events.addElement(eventName);
            eventDetails.put(eventName, eventTime);
        }
      
        return true;
    }
    
    /**
	 * <p>
	 * This will remove the requested event.
	 * </p>
	 *
	 * @param eventName <code>String</code> name of event to remove.
	 * @return <code>boolean</code> - indication of if event was removed.
	 */
    public synchronized boolean removeEvent(String eventName) {
        events.remove(eventName);
        eventDetails.remove(eventName);
      
        return true;
    }

}

Our addEvent( ) method adds the name of the event to both storage objects, and the time to the Hashtable. Our removeEvent( ) method does the converse. Both methods return a boolean value. Although in the example this value is always true, in a more complex implementation, this value could be used to indicate problems in the addition or removal of events.

With the ability to add and remove events, we now need to add a method that returns a list of events. This method returns all events added to the event store, regardless of what client or application added those events; in other words, these could be events added by a different XML-RPC client, a different XML-RPC server, another application, or a standalone implementation of this same scheduler. Since we have to return a single Object result, we can return a Vector of formatted String values that contain the name of each event and its time. Certainly, in a more useful implementation this might return the Vector of events, or some other form of the events in a typed format (with the date as a Date object, etc.). This method acts more as a view of the data, though, and does not allow the client to further manipulate it. To return this list of events, we use the event store and the java.text.SimpleDateFormat class, which allows textual formatting of Date objects. Iterating through all events, a String is created with the event name and the time it is set for; each String is inserted into the Vector result list, and this list is then returned to the client. Let’s add the required import statement and the code to return the events in the store to the scheduler code:

               import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import java.util.Vector;
...
    /**
               
     * <p>
               
     * This returns the current listing of events.
               
     * </p>
               
     *
               
     * @return <code>Vector</code> - list of events.
               
     */
               
    public Vector getListOfEvents(  ) {     
               
        Vector list = new Vector(  );
               
        
               
        // Create a Date Formatter
               
        SimpleDateFormat fmt = 
               
            new SimpleDateFormat("hh:mm a MM/dd/yyyy");        
               
        
               
        // Add each event to the list
               
        for (int i=0; i<events.size(  ); i++) {
               
            String eventName = (String)events.elementAt(i);
               
            list.addElement("Event "" + eventName + 
               
                            "" scheduled for " +                           
               
                            fmt.format(
               
                                (Date)eventDetails.get(eventName)));
               
        }
               
        
               
        return list;
               
    }
...

At this point, we could use this class as an XML-RPC handler without any problems. However, the point of this exercise is to look at how work can be done by the server while the client is performing other tasks. The getListOfEvents( ) method assumes that the event list (the Vector variable events) is ordered in the correct way when this method is called; this means that sorting has already occurred. We haven’t written code to sort our events yet, but more importantly, we haven’t written code to trigger this sorting. Furthermore, as the event store gets large, sorting it can be very time-consuming, and this task should not cause the client to wait for it to complete. First we must add a method that our class can use to sort the events. For simplicity, a bubble sort is used; discussion of sorting algorithms is beyond the scope of this book, so this code is presented without any explanation of its workings. At the end of the method, though, the Vector variable events is sorted in order of the time the events within it occur. For information on this and other sorting algorithms, you should refer to Algorithms in Java, by Robert Sedgewick and Tim Lindholm (Addison-Wesley). The algorithm and method to handle sorting of the events is presented here, and should be added to your code:

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
 * <b><code>Scheduler</code></b> is a class that allows
 *   addition, removal, and retrieval of a list of events, sorted
 *   by their occurrence time.
 * 
 * @author <a href="mailto:[email protected]">Brett McLaughlin</a>
 * @version 1.0
 */
public class Scheduler {
    
    /** List of event names (for sorting) */
    private static Vector events = null;
    
    /** Event details (name, time) */
    private static Hashtable eventDetails = null; 
    
    /** Flag to indicate if events are sorted */
               
    private static boolean eventsSorted;
                
    // Other existing method implementations

    /**
               
     * <p>
               
     * Sort the events in the current list.
               
     * <p>
               
     */
               
    private synchronized void sortEvents(  ) {        
               
        if (eventsSorted) {
               
            return;
               
        }
               
      
               
        // Create array of events as they are (unsorted)
               
        String[] eventNames = new String[events.size(  )];
               
        events.copyInto(eventNames);
               
                
               
        // Bubble sort these
               
        String tmpName;
               
        Date date1, date2;
               
        for (int i=0; i<eventNames.length - 1; i++) {
               
            for (int j=0; j<eventNames.length - i - 1; j++) {
               
                // Compare the dates for these events                
               
                date1 = (Date)eventDetails.get(eventNames[j]);
               
                date2 = (Date)eventDetails.get(eventNames[j+1]);
               
                if (date1.compareTo(date2) > 0) {
               
                    
               
                    // Swap if needed
               
                    tmpName = eventNames[j];
               
                    eventNames[j] = eventNames[j+1];
               
                    eventNames[j+1] = tmpName;
               
                      
               
                }
               
            }
               
        }
               
        
               
        // Put into new Vector (ordered)
               
        Vector sortedEvents = new Vector(  );
               
        for (int i=0; i<eventNames.length; i++) {
               
            sortedEvents.addElement(eventNames[i]);
               
        }
               
        
               
        // Update the global events
               
        events = sortedEvents;
               
        eventsSorted = true;
               
     
               
    }
...
}

In addition to the core algorithm, we import the java.util.Enumeration class and add a boolean member variable, eventsSorted. This flag allows short-circuiting of the execution of the sorting when the events are already ordered. Although we have not yet added code to update this flag, we can easily do so. Our sorting method already indicates that events are sorted at its completion. Our constructor should initially set this value to true, indicating that all events are in order. It is only when events are added that the list may become unordered, so in our addEvents( ) method we need to set this flag to false if an event is added. This will let our Scheduler class know that something should occur that will trigger the sort. Then when the getListOfEvents( ) method is invoked, the events will be ordered and ready for retrieval. Let’s add code to our constructor and the method for adding events that will update this flag:

/**
	 * <p>
	 * This will initialize the storage.
	 * </p>
	 */
    public Scheduler(  ) {
        events = new Vector(  );
        eventDetails = new Hashtable(  );
        eventsSorted = true;     
    }

    /**
	 * <p>
	 * This will add the requested event.
	 * </p>
	 *
	 * @param eventName <code>String</code> name of event to add.
	 * @param eventTime <code>Date</code> of event.
	 * @return <code>boolean</code> - indication of if event was added.
	 */
    public boolean addEvent(String eventName, Date eventTime) {      
        // Add this event to the list of events
        if (!events.contains(eventName)) {
            events.addElement(eventName);
            eventDetails.put(eventName, eventTime);
            eventsSorted = false;
        }
      
        return true;
    }

We do not need to make any changes to the removeEvent( ) method, as removing an entry does not affect the order of the events. The ideal mechanism to handle server-side processing while freeing the client for further action is a thread that sorts events. With this thread started in the JVM, client processing can continue without waiting for the thread to complete. This is particularly important in a multi-threaded environment where synchronization and threads waiting for object locks would be in use. In this example, we avoid those issues (this is a chapter about XML-RPC, not threading), but you can add the relevant code to handle these issues fairly easily. In our example, we want to create an inner class that extends Thread, and does nothing but invoke the sortEvents( ) method. We then add to our addEvents( ) method code that creates and starts this thread when events are added. This results in the addition of events triggering a re-sorting of the events, but allows the client to continue with its actions (which might include adding additional events, which in turn starts more threads to sort the data). When the client does request the list of events, the events should be sorted when returned, all without the client ever waiting on this action to occur, or having to spend processing power to make it happen. The addition of the inner class to sort, and code to run that class as a thread in our addEvents( ) method rounds out the Scheduler class:

public class Scheduler {
...
    public boolean addEvent(String eventName, Date eventTime) {
        // Add this event to the list of events
        if (!events.contains(eventName)) {
            events.addElement(eventName);
            eventDetails.put(eventName, eventTime);
            eventsSorted = false;

            // Start thread on server sorting
               
            SortEventsThread sorter = new SortEventsThread(  );
            sorter.start(  );
        }   
      
        return true;
    }
...
    /**
               
     * <p>
               
     * This inner class handles starting the sorting as
               
     *   a <code>Thread</code>.
               
     */
               
    class SortEventsThread extends Thread {
               
        
               
        /**
               
         * <p>
               
         * Start the sorting.
               
         * </p>
               
         */
               
        public void run(  ) {
               
            sortEvents(  );
               
        }    
               
    }

}

You can now compile the modified source code, and we have a threaded scheduler that performs the process-intensive task of sorting on the server, allowing any clients to work uninterrupted while that sorting occurs. This is still a simple example of using a handler class properly, but it does introduce the concepts of resource distribution and letting a server handle the workload when possible. To complement this more advanced handler class, we next look at building a more robust XML-RPC server implementation.

A Configurable Server

Our XML-RPC server class still needs some work. The current version requires us to specifically add our handler classes to the server in the code. This means that the addition of a new handler class requires coding and recompilation. Not only is this undesirable from a change control perspective, but it is annoying and time-consuming. Obtaining the newest code from a source control system, adding the change, and testing to add one or two handlers is not practical, and won’t win you friends among your management. What is preferred is to have a robust server that can read this sort of information from a configuration file and load the needed classes at runtime. We can build a lightweight server to do this now.

To begin, we create a new server class. You can either start from scratch, or copy and paste from the HelloServer class given earlier in this chapter. We start by setting up our framework, adding the required import statements, and instantiating our server, similar to the earlier example; however, we do not add any code that registers handlers, as we will write a helper method to load the needed information from a file. The one change from our earlier version is that we require an additional command-line parameter; this parameter should be the name of a file. We will read this file in our methods later to add handlers to the server. You can create the LightweightXmlRPcServer class (part of the com.oreilly.xml utility package), which continues to use the thin WebServer helper class, with the code shown in Example 10.7. The complete com.oreilly.xml package is also available for download at http://www.oreilly.com/catalog/javaxml or http://www.newInstance.com.

Example 10-7. The LightweightXmlRpcServer Class

package com.oreilly.xml;

import java.io.IOException;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.WebServer;

/**
 * <b><code>LightweightXmlRpcServer</code></b> is a utility class
 *   that will start an XML-RPC server listening for HTTP requests
 *   and register a set of handlers, defined in a configuration file.
 * 
 * @author 
 *   <a href="mailto:[email protected]">Brett McLaughlin</a>
 * @version 1.0
 */
public class LightweightXmlRpcServer {
  
    /** The XML-RPC server utility class */
    private WebServer server;
    
    /** Port number to listen on */
    private int port;
    
    /** Configuration file to use */
    private String configFile;
    
    /**
     * <p>
     * This will store the requested port and configuration file
     *   for the server to use.
     * </p>
     *
     * @param port <code>int</code> number of port to listen to
     * @param configFile <code>String</code> filename to read for
     *                   configuration information.
     */
    public LightweightXmlRpcServer(int port, String configFile) {
        this.port = port;
        this.configFile = configFile;
    }
    
    /**
     * <p>
     * This will start up the server.
     * </p>
     *
     * @throws <code>IOException</code> when problems occur.
     */
    public void start(  ) throws IOException {
        try {
            // Use Apache Xerces SAX Parser
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

            System.out.println("Starting up XML-RPC Server...");
            server = new WebServer(port);                      
            
            // Register handlers
            
        } catch (ClassNotFoundException e) {
            throw new IOException("Error loading SAX parser: " + 
                e.getMessage(  ));
        }         
    }   
  
    /**
     * <p>
     * Provide a static entry point.
     * </p>
     */
    public static void main(String[] args) {
      
        if (args.length < 2) {
            System.out.println(
                "Usage: " +
                "java com.oreilly.xml.LightweightXmlRpcServer " +
                "[port] [configFile]");
            System.exit(-1);
        }
        
        LightweightXmlRpcServer server =
            new LightweightXmlRpcServer(Integer.parseInt(args[0]),
                                        args[1]);   

        try {
            // Start the server
            server.start(  );
        } catch (IOException e) {
            System.out.println(e.getMessage(  ));
        }                               
    }

}

There is really nothing remarkable here. We ensure that the required parameters are passed in and start the server on the requested port. We now need to add in methods to load our handlers from a file, and then add those handlers one by one to our server.

Because each handler needs a name and an associated class, we can create a configuration file that has these two pieces of information. With Java, it is easy to load and instantiate a class with its complete package and name. This means we can completely represent a new handler with a pair of textual values. Within this file, we can add both our original HelloHandler class as well as our new Scheduler class. Since we are writing the file parser as well, we can arbitrarily decide to use commas as delimiters and the pound sign (#) as a comment marker. In fact, you can use whatever format you wish as long as you write code that uses your conventions in parsing the file. Create the configuration file shown in Example 10.8 that will add the HelloHandler class under the class identifier “hello” and the Scheduler class under the class identifier “scheduler,” and save it as xmlrpc.conf.

Example 10-8. XML-RPC Handler Configuration File

# Hello Handler: sayHello(  )
hello,HelloHandler

# Scheduler: addEvent(), removeEvent(), getEvents(  )
scheduler,Scheduler

For documentation purposes, we specify the methods available to each handler in our comments. This allows future maintainers of our code to know what methods are available for each handler.

Java’s I/O classes make it easy to load this file and read its contents. We can create a helper method that reads the specified file and stores the pairs of values in a Java Hashtable. This object can then be passed on to another helper that loads and registers each handler. This example method does not do extensive error checking, which a production ready server might, and it simply ignores any line without a pair of comma-separated values; certainly it is easy enough to add in error handling if you want to use this code in your applications. Once we find a line with a pair of values, the line is broken up and the class identifier and class name are stored as an entry within the Hashtable. Add the import statements for the required classes and then the new getHandlers( ) method to the LightweightServer class now:

               import java.io.BufferedReader;
               import java.io.FileReader;
import java.io.IOException;
import java.util.Hashtable;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.WebServer;
...
    /**
               
     * <p>
               
     * This is a method that parses the configuration file
               
     *   (in a very simplistic manner) and reads the handler
               
     *   definitions supplied.
               
     * </p>
               
     * 
               
     * @return <code>Hashtable</code> - class id/class pairs.
               
     * @throws <code>IOException</code> - when errors occur in 
               
     *                                    reading/parsing the file.
               
     */
               
    private Hashtable getHandlers(  ) throws IOException {
                
               
        Hashtable handlers = new Hashtable(  );
               
        
               
        BufferedReader reader = 
               
            new BufferedReader(new FileReader(configFile));
               
        String line = null;
               
        
               
        while ((line = reader.readLine(  )) != null) {
               
            // Syntax is "handlerName, handlerClass"
               
            int comma;
               
            
               
            // Skip comments
               
            if (line.startsWith("#")) {
               
                continue;
               
            }
               
            
               
            // Skip empty or useless lines            
               
            if ((comma = line.indexOf(",")) < 2) {
               
                continue;
               
            }
               
            
               
            // Add the handler name and the handler class
               
            handlers.put(line.substring(0, comma), 
               
                         line.substring(comma+1));
               
        }
               
        
               
        return handlers;        
               
    }
...

Instead of adding code to save the result of this method, we can use that result as input to a method that iterates through the Hashtable and adds each handler to the server. The code needed to accomplish this task is not complicated; the only notable items are that the addHandler( ) method of WebServer requires an instantiated class as a parameter. This requires us to take the name of the class to register from the Hashtable, load that class into the JVM with Class.forName( ), and then instantiate that class with newInstance( ). This is the methodology used in class loaders and other dynamic applications in Java, but may be unfamiliar to you if you are new to Java or have not had to dynamically instantiate classes from a textual name before. Once the class is loaded in this way, it and the class identifier are passed to the addHandler( ) method, and the iteration continues. Once the contents of the Hashtable are loaded, the server is set up and ready to go. We use the Enumeration class to cycle through the keys in the Hashtable, so we must add this import statement to our file:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;

import helma.xmlrpc.XmlRpc;
import helma.xmlrpc.WebServer;
...
    /**
               
     * <p>
               
     * This will register the handlers supplied in the XML-RPC
               
     *   server (typically from <code>{@link #getHandlers(  )}</code>.
               
     * </p>
               
     *
               
     * @param handlers <code>Hashtable</code> of handlers to register.
               
     */ 
               
    private void registerHandlers(Hashtable handlers) {        
               
        Enumeration handlerNames = handlers.keys(  );
               
        
               
        // Loop through the requested handlers
               
        while (handlerNames.hasMoreElements(  )) {
               
            String handlerName = (String)handlerNames.nextElement(  );
               
            String handlerClass = (String)handlers.get(handlerName);
               
            
               
            // Add this handler to the server
               
            try {
               
                server.addHandler(handlerName, 
               
                    Class.forName(handlerClass).newInstance(  ));
               
                
               
                System.out.println("Registered handler " + handlerName +
               
                                   " to class " + handlerClass);
               
            } catch (Exception e) {
               
                System.out.println("Could not register handler " + 
               
                                   handlerName + " with class " + 
               
                                   handlerClass);
               
            }
               
        }
               
    }
...

This is simply a complement to our getHandlers( ) method; in fact, it takes the result of that method as input. It uses the String values within the Hashtable and registers each, and just that simply, our server is running and will have any handlers in the configuration file loaded and available for remote calls. Be aware that we could have just as easily consolidated these methods into one larger method. However, the purpose of the two methods is significantly different; while one, getHandlers( ), deals with parsing a file, the other, registerHandlers( ), deals with registering handlers once information about the handlers is available. With this methodology, we can change the way we parse the configuration file (or even have it read from a database or other medium) without having to worry about the way the handlers are registered. In fact, in the next chapter we remove the getHandlers( ) method in lieu of a helper class that reads this information from an XML configuration file! In this case, a good design decision early in the process (here) avoids a lot of change to our working code later in the process (in the next chapter).

Once you have added these two helper methods, add their invocation to the start( ) method of our server class:

/**
 * <p>
 * This will start up the server.
 * </p>
 *
 * @throws <code>IOException</code> when problems occur.
 */
 public void start(  ) throws IOException {
    try {
        // Use Apache Xerces SAX Parser
        XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");

        System.out.println("Starting up XML-RPC Server...");
        server = new WebServer(port);                      
            
        // Register handlers
               
        registerHandlers(getHandlers(  ));
            
    } catch (ClassNotFoundException e) {
        throw new IOException("Error loading SAX parser: " + 
            e.getMessage(  ));
    }         
}

We add a try -catch block around the method invocations we have added so that we can distinguish between exceptions that occur in the server itself (the outer block) as opposed to exceptions that occur specifically related to the loading of handlers. In this latter case, we report the error as being generated by our handler methods. Compile this code, ensure you have created the configuration file, and our server is ready for use.

A Useful Client

Our client has no new concepts or techniques in it; just as our HelloClient class was simple, so is the SchedulerClient class. It needs to start up an XML-RPC client, invoke handler methods, and print out the result of those handlers. The complete code for the client is here. Comments indicate what is occurring, and since this is all ground we have covered you can simply enter the code in Example 10.9 into your editor and compile it.

Example 10-9. The SchedulerClient Class

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import helma.xmlrpc.XmlRpc;

import helma.xmlrpc.XmlRpcClient;
import helma.xmlrpc.XmlRpcException;

/**
 * <b><code>SchedulerClient</code></b> is an XML-RPC client
 *   that makes XML-RPC requests to <code>Scheduler</code>.
 * 
 * @version 1.0
 */
public class SchedulerClient {

    /**
     * <p>
     * Add events to the Scheduler.
     * </p>
     *
     * @param client <code>XmlRpcClient</code> to connect to
     */
    public static void addEvents(XmlRpcClient client) 
        throws XmlRpcException, IOException {
          
        System.out.println("
Adding events...
");          
          
        // Parameters for events
        Vector params = new Vector(  );
        
        // Add an event for next month
        params.addElement("Proofread final draft");
            
        Calendar cal = Calendar.getInstance(  );
        cal.add(Calendar.MONTH, 1);
        params.addElement(cal.getTime(  ));
            
        // Add the event
        if (((Boolean)client.execute("scheduler.addEvent", params))
                            .booleanValue(  )) {
            System.out.println("Event added.");
        } else {
            System.out.println("Could not add event.");
        }        
            
        // Add an event for tomorrow
        params.clear(  );
        params.addElement("Submit final draft");
            
        cal = Calendar.getInstance(  );
        cal.add(Calendar.DAY_OF_MONTH, 1);
        params.addElement(cal.getTime(  ));
        
        // Add the event
        if (((Boolean)client.execute("scheduler.addEvent", params))
                            .booleanValue(  )) {
            System.out.println("Event added.");
        } else {
            System.out.println("Could not add event.");
        }        
            
    }
    
    /**
     * <p>
     * List the events currently in the Scheduler.
     * </p>
     *
     * @param client <code>XmlRpcClient</code> to connect to
     */
    public static void listEvents(XmlRpcClient client) 
        throws XmlRpcException, IOException {  
          
        System.out.println("
Listing events...
");                

        // Get the events in the scheduler
        Vector params = new Vector(  );
        Vector events = 
            (Vector)client.execute("scheduler.getListOfEvents", params);
        for (int i=0; i<events.size(  ); i++) {
            System.out.println((String)events.elementAt(i));
        }
    }
  
    /**
     * <p>
     * Static entry point for the demo.
     * </p>
     */
    public static void main(String args[]) {
        
        try {
            // Use the Apache Xerces SAX Parser Implementation
            XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");
          
            // Connect to server
            XmlRpcClient client = 
                new XmlRpcClient("http://localhost:8585/");                                         
              
            // Add some events
            addEvents(client);                        
            
            // List events
            listEvents(client);            
        
        } catch (Exception e) {
            System.out.println(e.getMessage(  ));
        }
        
    }
    
}

As you are entering this code, notice that the events are added in reverse order of the event time. Our server should rearrange these events with the sortEvents( ) method to facilitate correctly ordered results when the getListOfEvents( ) method is called. We can see that our server takes care of this sorting next.

Talk to Me (Again)

Once you have entered in the code for the handler, server, and client, compile all of the source files. You also will need to create the configuration file that lists handlers to register with the XML-RPC server that we discussed in that section. First, start up the XML-RPC server as a separate process:

D:prod>start java com.oreilly.xml.LightweightXmlRpcServer 8585
                   D:prodconfxmlrpc.conf

In Unix, use:

$ java com.oreilly.xmlrpc.LightweightServer 8585 conf/xmlrpc.conf &

You should see the server indicate that the handlers in the supplied configuration file are registered to the names you provided:

Starting up XML-RPC Server...
Registered handler scheduler to class Scheduler
Registered handler hello to class HelloHandler

Warning

If you never stopped the previous XML-RPC server, HelloServer, you will get an error trying to start another server on the same port. Be sure to stop the HelloServer before trying to start the LightweightXmlRpcServer.

Finally, execute your client and see the results:

$ java SchedulerClient

Adding events...

Event added.
Event added.

Listing events...

Event "Submit final draft" scheduled for 10:13 AM 02/14/2000
Event "Proofread final draft" scheduled for 10:13 AM 03/13/2000

You should not notice a significant pause as your client adds and lists events, yet the server still sorts the events in a separate thread within the server JVM (and bubble sorting is not a quick algorithm!). You have written your first useful XML-RPC application!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.30.236