Original Components

Before explaining each component in detail, we will present some conventions that will be used throughout this appendix, as well as a brief introduction to XML-RPC. All text that contains source code will be written in a Courier type face. We assume that the Apache XML-RPC library will be used for Java applications or the xmlrpc-c library will be used for C and C++ applications. The information in this documentation applies to all XML-RPC implementations, but the source code is specific to those two libraries. The Apache and xmlrpc-c libraries require that a parameter always be passed to the XML-RPC call. However, some of the XML-RPC handlers that are being called do not take any parameters. The solution is to pass an empty parameter to the XML-RPC library when executing RPCs that do not take any parameters. In Java, parameters are passed as java.util.Vector objects; passing an empty parameter means passing a Vector that contains zero elements, as in new Vector(). In C++, parameters are passed as xmlrpc_c::paramList objects. To pass an empty parameter, create a paramList object without using the new operator, and then pass that newly created object to the appropriate XML-RPC method.

Although the xmlrpc_c::paramList object is used to send parameters to an XML-RPC with the xmlrpc-c library, the return value of an XML-RPC is often an std::vector object. The return value will be an std::vector if it is a compound value, for example a string and two integers. To avoid using implementation-specific terminology, and to keep a consistent style, we will simply refer to compound return values as vectors. The meaning of the term vector should be interpreted appropriately based on the programming language and XML-RPC library you have chosen to use.

The way in which most XML-RPC libraries handle parameters is not necessarily intuitive, so it deserves further explanation. No matter how many parameters an RPC accepts, the client executing that RPC must wrap them in a vector. Recall that in the context of building parameters for an XML-RPC, the term vector may mean java.util.Vector, xmlrpc_c::paramList, or some other type, depending on your programming language and XML-RPC library. Also, even though the client makes the XML-RPC call by passing a vector, the RPC handler itself should not accept a vector as its parameter, but rather the object types contained within the vector. For example, consider an RPC that accepts two strings followed by an integer and returns a boolean. The client executing that RPC would create a vector and add the strings and integer in the specified order. The signature of the RPC handler, however, should look like the following:

boolean HandlerName(String arg0, String arg1, int arg2)

Notice that the signature of the handler does not take a vector, but the types contained within the vector. The XML-RPC library removes the elements from the vector, searches for a handler with a matching signature, and then calls that handler, passing in the elements that were extracted from the vector.

It is important to be familiar with XML-RPC before attempting to develop any project components. The following block of code demonstrates how to execute an XML-RPC using the xmlrpc-c library in C++. For complete examples, please see the example directory in the latest release of the framework.

// The HTTP connections will be made using libwww.
xmlrpc_c::clientXmlTransport_libwww xmlrpcTransport;

// Create the XML-RPC client.
xmlrpc_c::client_xml xmlrpcClient(&xmlrpcTransport);

// Create an "empty parameter."
xmlrpc_c::paramList params;

// Create an RPC with the name of the RPC handler and the
// parameter list.
xmlrpc_c::rpcPtr xmlrpc("server.getLastWorkUnit", params);

// Set the URL of the XML-RPC server.
// Note the format, http://host:port/RPC2
xmlrpc_c::carriageParm_curl0 cParm("http://localhost:2080/RPC2");

// Execute the RPC.
try {
	xmlrpc->call(xmlrpcClient, &cParm);
} catch (char const* e) {
	cerr << "Exception thrown while making XML-RPC: ";
	cerr << e << endl;
	exit(1);
} catch (girerr::error e) {
	// The host could not be found, or the connection 
	// was refused.
	cerr << "ERROR: Could not connect to the XML-RPC ";
	cerr << "server.  ";
	exit(1);
}

if (xmlrpc->isSuccessful() == false) {
	// Some XML-RPC problem occurred.
	// Ex. RPC not found on server.
	// Ex. Incorrect number of parameters passed to RPC.
	// Ex. Server threw an exception (not necessarily a 
	// problem).
	if (xmlrpc->isFinished()) {
		// NOTE: xmlrpc->isFinished() must be true 
		// before calling xmlrpc->getFault().
		xmlrpc_c::fault err = xmlrpc->getFault();

		// err.getCode() will be 0 if an exception was thrown 
		// by the server.
		if (err.getCode() == 0) {
			// Take some action if exceptions are expected.
		}
		else {
			// An exception was not thrown by the server.
			// Some other problem has happened.
			// Read the error message.
			xmlrpc_c::fault err = xmlrpc->getFault();
			cerr << "recoverStartRange: XML-RPC error ";
			cerr << err.getCode() << ": ";
			cerr << err.getDescription() << endl;
		}
	}
}

else {
	// XML-RPC was successful.  Now extract the data.
	// xmlrpc->isFinished() must be true in order to continue.
	assert(xmlrpc->isFinished());

	// Get the xmlrpc_c::value object that represents 
	// the result returned by the server.
	xmlrpc_c::value result = xmlrpc->getResult();

	// Cast the result to an std::vector 
	// if it is not a simple data type (like an integer).
	std::vector resultVector = 
		xmlrpc_c::value_array(result).vectorValueValue();

	// Now it is possible to extract each element of the 
	// vector, casting each element to its known type.
}

The next example demonstrates the execution of the same RPC, but in Java using the Apache XML-RPC library.

// Create the XML-RPC client.
private XmlRpcClient xmlrpcClient = null;

// Initialize the client.
try {
	// Set up the XML-RPC client to connect to the project 
	// server.
	// Note the format, http://host:port/RPC2
	xmlrpcClient = new 
		XmlRpcClient("http://localhost:2080/RPC2");
} catch (MalformedURLException e) {
	// The URL given to the client was invalid.
	e.printStackTrace();
	System.exit(1);
}

// Execute the RPC.
try {
	Object returnValue = 
		xmlrpcClient.execute("server.getLastWorkUnit", 
			new Vector());
	Vector resultVector = null;
	
	// Get the return value.
	try {
		resultVector = (Vector)returnValue;

		// Now it is possible to extract each element of the 
		// vector, casting each element to its known type.
	} catch (Exception e) {
		// The return value was not a Vector, it was an 
		// Exception.
		// We can interpret the Exception and take appropriate 
		// action.
	}
} catch (XmlRpcException e) {
	// Some XML-RPC problem occurred.
	// Ex. RPC not found on server.
	// Ex. Incorrect number of parameters passed to RPC.
	// Ex. Server threw an exception (not necessarily a 
	// problem).

	System.exit(1);
} catch (IOException e) {
	// The host could not be found, or the connection 
	// was refused.

	System.exit(1);
}

Creating an XML-RPC server is slightly more complicated, but it is necessary to do so in order to develop a science application. The following block of code is an example of the steps needed to create a C++ XML-RPC server with one RPC handler, called sciapp.shutdown. A more complete example can be found in the example directory of the framework.

// Thread in which the XML-RPC server will run.
pthread_t serverThread;

// Flag that controls whether the application should be running.
bool isRunning = true;

// BEGINNING OF XML-RPC HANDLERS
// ...

/**
 * Sets the isRunning flag to false so the loop in main will
 * terminate, causing the application to shutdown.
 * @return Returns true.
 */
class ShutdownMethod : public xmlrpc_c::method {
public:
	/**
	 * This method is called when the sciapp.shutdown RPC is 
	 * handled.
	 * @param paramList List of RPC parameters.
	 * @param retvalP Pointer to the return value of the 
	 * method.
	 */
	void execute(xmlrpc_c::paramList const& paramList, 
		xmlrpc_c::value* const retvalP) {
		isRunning = false;
		*retvalP = xmlrpc_c::value_boolean(true);
	}
};

// ...
// END OF XML-RPC HANDLERS

/**
 * Starts the XML-RPC server in a separate thread.
 * @param arg Pointer to the xmlrpc_c::serverAbyss object to run.
 */
void* startServerThread(void* arg) {
	// Cast the void* argument to a pointer to an
	// xmlrpc::serverAbyss
	xmlrpc_c::serverAbyss* server = 
		static_cast(arg);
	server->run();
	pthread_exit(EXIT_SUCCESS);
}

/**
 * Cleanly shuts down the application.
 */
void shutdown() {
	isRunning = false;
	pthread_cancel(serverThread);
	pthread_join(serverThread, NULL);
	exit(EXIT_SUCCESS);
}

int main(int argc, char** argv) {
	xmlrpc_c::registry xmlrpcRegistry;

	// Register the RPC handlers.
	xmlrpc_c::methodPtr const shutdownMethod(
		new ShutdownMethod);
	// "sciapp.shutdown" is the name of the RPC
	// shutdownMethod is the handler.
	xmlrpcRegistry.addMethod("sciapp.shutdown", 
		shutdownMethod);
	// ...
	
	// Initialize the XML-RPC server.
	// The second parameter is the port number on which to
	// listen.
	// The third parameter is the path to the log file.
	xmlrpc_c::serverAbyss abyssServer(xmlrpcRegistry, 2082, 
		"log/sciapp.xmlrpc.log");
	
	// Start the XML-RPC server thread.
	// Pass a pointer to the server as an argument to 
	// startServerThread().
	pthread_create(&serverThread, NULL, startServerThread, 
		&abyssServer);
	
	// XML-RPC server is in its own thread, so make the main
	// thread sleep until the application should terminate.
	while (isRunning == true) {
		sleep(5);
	}
	
	// Shut down the application.
	shutdown();
}

The next block is the Java implementation of the code necessary to start an XML-RPC server containing the sciapp.shutdown RPC handler.

/**
 * This is the class that will start the XML-RPC server.
 */
public class ExampleXMLRPCServer {
	// The XML-RPC server object.
	private WebServer xmlrpcServer = null;

	// Flag that controls whether the application should be 
	// running.
	private boolean isRunning = false;

	/**
	 * Initializes and starts the XML-RPC server.
	 */
	private void initXmlRpcServer() {
		try {
			// Instantiate the web server.
			// The parameter is the port number on which to 
			// listen.
			xmlrpcServer = new WebServer(2082);
			
			// Register the RPC handler class.
			// Any public methods in the handler will be 
			// treated as RPC handlers.  It is possible to 
			// use "this" as the RPC handler, but it is 
			// cleaner to use a separate class.
			// We pass a reference to "this" to the handler 
			// class so that the handler can access our 
			// methods.  An alternative would be
			// to make ExampleXMLRPCServer a singleton.
			// We will call the handler "sciapp"
			// All clients connecting to this server will use 
			// RPCs in the form: sciapp.rpcName
			xmlrpcServer.addHandler("sciapp", new 
				ExampleXMLRPCHandler(this));
		} catch (Exception e) {
			System.err.print("Error initializing the ¿);
			System.err.println(¿XML-RPC server: " + e);
			System.exit(1);
		}
	}
	
	/**
	 * Starts the XML-RPC server and begins handling requests.
	 */
	public void run() {
		if (isRunning == false) {
			// Note: the server starts in a new thread.
			xmlrpcServer.start();
			isRunning = true;
		}
	}
	
	/**
	 * Performs any tasks necessary to cleanly shut down the 
	 * science application.
	 * @return Returns true.
	 */
	public boolean shutdown() {
		xmlrpcServer.shutdown();
		isRunning = false;
		return true;
	}
	
	public static void main(String[] args) {
		ExampleXMLRPCServer server = 
			new ExampleXMLRPCServer();
		server.run();
		
		// The XML-RPC server is in another thread, so just 
		// sleep until the application is shut down.
		while (sciApp.isRunning()) {
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		// The application is shut down.
		System.exit(0);
	}
}

/**
 * This is the class that handles the RPCs.
 * Each method in this class is interpreted 
 * as an RPC.
 */
public class ExampleSciAppRPCHandler {
	// Reference to the ExampleXMLRPCServer
	// that created this handler.
	private ExampleXMLRPCServer server = null;
	
	/**
	 * Constructor that takes a reference to the 
	 * ExampleXMLRPCServer that instantiated this object.
	 * @param server The ExampleXMLRPCHandler that instantiated 
	 * this RPC handler.
	 */
	public ExampleXMLRPCHandler(ExampleXMLRPCServer server) {
		this.server = server;
	}
	
	
	// BEGINNING OF XML-RPC HANDLERS
	// ...
	
	/**
	 * Performs any tasks necessary to clearnly shut down this 
	 * application.
	 * @return Returns true.  Note: All XML-RPC handlers MUST 
	 * return a value.
	 */
	public boolean shutdown() {
		return server.shutdown();
	}
	
	// ...
	// END OF XML-RPC HANDLERS
}

The two server-side components that can be developed by each project are the work unit generator and the result validator. Neither of these components needs to implement an XML-RPC server because they both periodically poll the project server, and only take action based on the result of each poll. The server will never need to contact them directly.