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.
| Prev | Home | Next |