XML-RPC και Java

Η Κλήση Απομακρυσμένης Διαδικασίας (Remote Procedure Call, RPC) είναι ένας σχετικά παλιός μηχανισμός για την απομακρυσμένη εκτέλεση μιάς απομακρυσμένης εφαρμογής. Το RPC βασίζεται στην ιδέα της δημοσιοποίησης της διεπαφής της απομακρυσμένης εφαρμογής, ώστε αυτή να μπορεί να κληθεί μέσω δικτύου.

Το XML-RPC είναι μια επέκταση του RPC που βρίσκει μεγάλη εφαρμογή στις υπηρεσίες, παρέχοντας ένα εύχρηστο και ισχυρό μηχανισμό εκτέλεσηε απομακρυσμένων εφαρμογών. Το XML-RPC χρησιμοποεί ένα περιορισμένο λεξιλόγιο XML (Extensible Markup Language) για το καθορισμό της διεπαφής του RPC.

Επισκόπηση

Το XML-RPC αποτελείται από τρία τμήματα:

Το Μοντέλο δεδομένων XML-RPC ορίζει 6 βασικούς και 2 σύνθετους τύποιυς δεδομένων.

Βασικοί τύποι δεδομένων XML-RPC
 Τύπος  Εύρος Τιμών  Παραδείγματα
int or i4 32-bit ακέραιοι μεταξύ - 2,147,483,648 και 2,147,483,647. <int>27<int>
<i4>27<i4>
double 64-bit floating-point αριθμοί <double>27.31415</double>
<double>-1.1465</double>
Boolean true (1) ήr false (0) <boolean>1</boolean>
<boolean>0</boolean>
string ASCII, με πιθανή υποστήριξη Unicode <string>Hello</string>
<string>bonkers! @</string>
dateTime.iso8601 Ημερομηνια σε ISO8601 format: CCYYMMDDTHH:MM:SS <dateTime.iso8601>
20021125T02:20:04
</dateTime.iso8601>
<dateTime.iso8601>
20020104T17:27:30
</dateTime.iso8601>
base64 Δυαδική πληροφορία σε κωδικοποίηση Base 64, όπως ορίζεται στο RFC 2045 <base64>
SGVsbG8sIFdvcmxkIQ==
</base64>

Οι βασικοί τύποι πάντα περικλείονται από στοιχεία value. Μόνο τα strings μπορεί να  περικλείονται σε στοιχεία value χωρίς τα στοιχεία string. Οι βασικοί τύποι μπορούν να συνδυαστούν σε σύνθετους τύπους, array και struct. Το array παριστά ακολουθιακά αποθηκευμένη πληροφορία, ενώ το struct παριστά ζεύγη ονόματος-τιμής.

Οι τιμές στο τύπο array περικλείονται από στοιχεία value, array, data.
Για παράδειγμα, ένα array με 4 strings:

<value>
<array>
<data>
<value><string>This </string></value>
<value><string>is </string></value>
<value><string>an </string></value>
<value><string>array.</string></value>
</data>
</array>
</value>

Και ένα array με 4 ints:

<value>
<array>
<data>
<value><int>7</int></value>
<value><int>1247</int></value>
<value><int>-91</int></value>
<value><int>42</int></value>
</data>
</array>
</value>

Τα arrays μπορεί να περιέχουν διαφορετικούς βασικούς τύπους:

<value>
<array>
<data>
<value><boolean>1</boolean></value>
<value><string>Chaotic collection, eh?</string></value>
<value><int>-91</int></value>
<value><double>42.14159265</double></value>
</data>
</array>
</value>

Τα πολυδιάστατα arrays είναι arrays μέσα σε  arrays. Εδώ ένα 2d array με 2 στήλες και 3 γραμμές ints:

<value>
<array>
<data>
<value>
<array>
<data>
<value><int>10</int></value>
<value><int>20</int></value>
<value><int>30</int></value>
</data>
</array>
</value>
<value>
<array>
<data>
<value><int>15</int></value>
<value><int>25</int></value>
<value><int>35</int></value>
</data>
</array>
</value>
</data>
</array>
</value>

Ένα απλό παράδειγμα struct:

<value>
<struct>
<member>
<name>givenName</name>
<value><string>Joseph</string></value>
</member>
<member>
<name>familyName</name>
<value><string>DiNardo</string></value>
</member>
<member>
<name>age</name>
<value><int>27</int></value>
</member>
</struct>
</value>

Τα αιτήματα XML-RPC είναι συνδυασμός περιεχομένου XML και κεφαλίδων HTTP. Το περιεχόμενο XML παρέχει πληροφορία για τη διαδικασία που καλείται και τις παραμέτρους που απαιτούνται, ενώ οι κεφαλίδες HTTP παρέχουν ένα κέλυφος για την μεταβίβαση του περιεχομένοτ μέσω του Ιστού.

Κάθε αίτημα περιέχει μόνο ένα κείμενο XML, με αρχικό στοιχείο το methodCall. Το methodCall περιέχει ένα στοιχείο methodName και ένα στοιχείο params. Το methodName περιέχει το όνομα της καλούμενης διαδικάσίας, ενώ το params περιέχει τη λίστα των παραμέτρων με τις τιμές τους, δηλαδή στοιχεία value.

Στο παράδειγμα φαίνεται ένα αίτημα XML-RPC για την κλήσης της απομακρυσμένης μεθόδου με όνομα circleArea, που δέχεται ως παράμετρο ένα Double (την ακτίνα του κύκλου):

<?xml version="1.0"?>
<methodCall>
<methodName>circleArea</methodName>
<params>
<param>
<value><double>2.41</double></value>
</param>
</params>
</methodCall>

Οι κεφαλίδες HTTP του αιτήματος περιγράφει τον πελάτη (αποστολέα του αιτήματος) και το περιεχόμενο:

POST /target HTTP 1.0
User-Agent: Identifier
Host: host.making.request
Content-Type: text/xml
Content-Length: length of request in bytes

Για το συγκεκριμένο παράδειγμα, αν η μέοθοδος circleArea διατίθεται από ένα διακομιστή XML-RPC που ακούει στο /xmlrpc, οι κεφαλίδες θα είναι:

POST /xmlrpc HTTP 1.0
User-Agent: myXMLRPCClient/1.0
Host: 192.168.124.2
Content-Type: text/xml
Content-Length: 169

Συνολικά το ΗΤΤP αίτημα θα έχει τη μορφή:

POST /xmlrpc HTTP 1.0
User-Agent: myXMLRPCClient/1.0
Host: 192.168.124.2
Content-Type: text/xml
Content-Length: 169
<?xml version="1.0"?>
<methodCall>
<methodName>circleArea</methodName>
<params>
<param>
<value><double>2.41</double></value>
</param>
</params>
</methodCall>

Οι απαντήσεις XML-RPC, σε περίπτωση επιτυχούς εκτέλεσης, μοιάζουν πολύ με τα αιτήματα. Οι διαφορές είναι εξής: το στοιχείο methodCall αντικαθίσται από ένα στοιχείο methodResponse και δεν υπάρχει στoιχείο methodName:

<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><double>18.24668429131</double></value>
</param>
</params>
</methodResponse

Επιπλέον, η απάντηση XML-RPC περιέχει μόνο μια παράμετρο. Όμως με το δεδομένο οτι η παράμετρος επιστροφής μπορεί να είναι array ή struct, μπορούμε να επιστρέψουμε πολλαπλές τιμές.  Η απάντηση πρέπει να περιέχει οπωδήποτε μια παράμετρο. Στην απλούστερη περίπτωση επιστρέφουμε μια τιμή "επιτυχούς εκτέλεσης", πχ μια τιμή boolean true (1)

Όπως και τα αιτήματα, οι απαντήσεις XML-RPC έχουν κεφαλίδες HTTP. Οι απαντήσεις χρησιμοποιούν τον κώδικό 200 OK, ακόμη και αν η απάντηση περιέχει μήνυμα σφάλματος. Μια τυπική απάντηση περιέχει τις κεφαλίδες:

HTTP/1.1 200 OK
Date: Sat, 06 Oct 2001 23:20:04 GMT
Server: Apache.1.3.12 (Unix)
Connection: close
Content-Type: text/xml
Content-Length: 124

Μια πλήρης απάντηση για το αίτημα στο παράδειγμά μας είναι η εξής:

HTTP/1.1 200 OK
Date: Sat, 06 Oct 2001 23:20:04 GMT
Server: Apache.1.3.12 (Unix)
Connection: close
Content-Type: text/xml
Content-Length: 124

<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><double>18.24668429131</double></value>
</param>
</params>
</methodResponse>

Μόλις η απάντηση παραληφθεί από τον πελάτη η σύνδεση XML-RPC κλείνει. Κάθε νέο αίτημα απαιτεί ξεχωριστή σύνδεση XML-RPC.

Ένας τύπος απάντησης XML-RPC είναι τα σφάλματα. Αν παρουσιαστεί κάποιο πρόβλημα στην επεξεργασία του αιτήματος από τον διακομιστή, το στοιχείο methodResponse θα περιέχει ένα στοιχείο σφάλματος αντί του στοιχείου της παραμέτρου. Το στοιχείο σφάλματος έχει και αυτό μια μοναδική τιμή, η οποία δείχνει τι πήγε στραβά.

Ένα παράδειγμα απάντησης σφάλματος:

<?xml version="1.0"?>
<methodResponse>
<fault>
<value><string>No such method!</string></value>
</fault>
</methodResponse>

Το σφάλμα θα μπορούσε να περιέχει όχι μόνο μήνυμα αλλά και κωδικό σφάλματος. Το XML-RPC δεν έχει τυποποιημένους αριθμούς σφάλματος, θα πρέπει να ελέγξετε τις συγκεκριμένες υλοποιήσεις.

Παράδειγμα απάντησης με κωδικό σφάλματος:

<?xml version="1.0"?>
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>code</name>
<value><int>26</int></value>
</member>
<member>
<name>message</name>
<value><string>No such method!</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>

Απλή εφαρμογή XML-RPC σε Java

Για την υλοποίηση του πελάτη και του διακομιστή σε Java θα χρησιμοποιηθεί το πακέτο Apache XML-RPC, διαθέσιμο στο http://xml.apache.org/xmlrpc/

Μεταφορτώνουμε τα αρχεία και αποσυμπιέζουμε το αρχείο και δημιουργείται ένας κατάλογος. Τα αρχεία .jar που υπάρχουν στο κατάλογο πρέπει να αποσυμπισεστούν ώστε να δημιουργηθεί το πακέτο Java org.apache.xmlrpc.

Πελάτης XML-RPC

Ο πελάτης XML-RPC καλεί μια απομακρυσμένη διαδικασία (ή μέθοδο) με όνομα sum. Η διαδικασία δέχεται ως παραμέτρους δύο ακεραίους και επιστρέφει το άθροισμά τους.

import java.util.*;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;

public class JavaClient {
public static void main (String [] args) {
try {

XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL("http://127.0.0.1:8080/xmlrpc"));
XmlRpcClient client = new XmlRpcClient();
client.setConfig(config);
 
Vector params = new Vector();
params.addElement(new Integer(17));
params.addElement(new Integer(13));

Integer result = (Integer) client.execute("Calculator.add", params);
Object result = server.execute("sample.sum", params);

int sum = ((Integer) result).intValue();
System.out.println("The sum is: "+ sum);

} catch (Exception exception) {
System.err.println("JavaClient: " + exception);
}
}
}

Ας δούμε το κώδικα του παραδείγματος.

Because of the above call, a client sends following message to the server. Note that this is handled by server.execute(...) internally and you have nothing to do with it.

<?xml version="1.0" encoding="ISO-8859-1"?>
<methodCall>
<methodName>sample.sum</methodName>
<params>
<param>
<value><int>17</int></value>
</param>
<param>
<value><int>13</int></value>
</param>
</params>
</methodCall>

Διακομιστής XML-RPC

Following is the source code of XML-RPC Server written in JAVA. This makes use of built-in class available in org.apache.xmlrpc.*
import org.apache.xmlrpc.*;

public class JavaServer {

public Integer sum(int x, int y) {
return new Integer(x+y);
}

public static void main (String [] args) {
try {

System.out.println("Attempting to start XML-RPC Server...");
WebServer server = new WebServer(80);
server.addHandler("sample", new JavaServer());
server.start();
System.out.println("Started successfully.");
System.out.println("Accepting requests. (Halt program to stop.)");
} catch (Exception exception) {
System.err.println("JavaServer: " + exception);
}
}
}

Let us see what we have done in the above example server.

For the call mentioned in the given exmaple client, server sends following response back to the client:

<?xml version="1.0" encoding="ISO-8859-1"?>
<methodResponse>
<params>
<param>
<value><int>30</int></value>
</param>
</params>
</methodResponse>

Εκτέλεση της εφαρμογής

Now your server is ready so complile it and run it at your prompt as follows:

C:\ora\xmlrpc\java>java JavaServer
Attempting to start XML-RPC Server...
Started successfully.
Accepting requests. (Halt program to stop.)

Now to test the functionality, give a call to this server as follows:

C:\ora\xmlrpc\java>java JavaClient
30