On this page
Example: Accessing a service using Java
Last updated on
30 April 2025
For this to work, you'll need to download and include a few *.jar files for ApacheXmlRpc.
- From http://www.apache.org/dyn/closer.cgi/ws/xmlrpc/ download the appropriate "apache-xmlrpc-current-bin.*" file.
- Extract the files to a location that can be used to import them for NetBeans in you project.
- Add them to your projects Libraries.
To use it, you will need to know the sites Service Key, Service Domain, and the Service URL. Below is a sample of how you use the class
Here's how you would use the class
public static void main(String[] args) throws Exception {
DrupalXmlRpcService service = new DrupalXmlRpcService(
"aperture",
"442c5629267cc4568ad43ceaa7f3dbe4",
"http://localhost/organikdrupal/?q=services/xmlrpc");
service.connect();
service.login("root", "root");
DrupalNode node = new DrupalNode();
node.setType(DrupalNode.TYPE_STORY);
node.setTitle("HEllo WORLD");
node.setBody("at "+new Date().toGMTString());
service.nodeSave(node);
service.logout();
System.out.println("done");
}
Sample Class
package org.semanticdesktop.aperture.drupalhandler;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
/**
* XML-RPC client for the server offered by Drupal.
* Drupal has a weird authentication scheme on the server side involving to sign every method
* call using a HMAC signature generated with SHA256 hashing and the API key of Drupal as
* key. This class implements this authentication scheme and some method calls based on the
* apache XML-RPC implementation.
*
* Usage:
<pre>
DrupalXmlRpcService service = new DrupalXmlRpcService("domain", "442c5629267cc4568ad43ceaa7f3dbe4", "http://www.example.com/drupal/?q=services/xmlrpc");
service.connect();
service.login(user, password);
service.nodeSave(mynode);
service.logout();
</pre>
*
* see http://drupal.org/node/632844, with adaptations by Leo Sauermann
*
* Changelog 16.2.2010 - made exceptions where exceptions are due, changed logging to JUL
*
* This class is written by Leo Sauermann on the basis of work published by Aaron Moline.
*
* It is currently part of the Aperture sourceforge project which is BSD licensed,
* if you want to put it elsewhere, do so under this license.
*
* @author Aaron Moline <Aaron.Moline@molinesoftware.com>
* @author Leo Sauermann <leo.sauermann@dfki.de>
*/
public class DrupalXmlRpcService {
/**
* Method names
* @author sauermann
*/
public static final String MethodNodeSave = "node.save";
public static final String MethodSystemConnect = "system.connect";
public static final String MethodUserLogout = "user.logout";
public static final String MethodUserLogin = "user.login";
public static final String MethodFileSave = "file.save";
public static final String MethodTestCount = "test.count";
Logger log = Logger.getLogger(DrupalXmlRpcService.class.getName());
final String serviceURL;
final String serviceDomain;
final String apiKey;
/**
* SessionID is set by "connect"
*/
String sessionID;
XmlRpcClient xmlRpcClient;
/**
* needed for signing
*/
final Charset asciiCs = Charset.forName("US-ASCII");
/**
* Message authentification code algorithm already initialized with the ApiKey.
*/
Mac apikeyMac;
/**
* Initialize the Drupal XML-RPC Service.
* The serviceURL must be a valid URL.
* @param serviceDomain domain
* @param apiKey the API key with the domain
* @param serviceURL the URL of the Drupal service. example: http://www.example.com/drupal/?q=services/xmlrpc
* @throws MalformedURLException if the serviceURL is not a URL
*/
public DrupalXmlRpcService(String serviceDomain, String apiKey,
String serviceURL) throws MalformedURLException {
this.serviceDomain = serviceDomain;
this.apiKey = apiKey;
this.serviceURL = serviceURL;
// initialize the xmlRpcClient, it won't change as the parameters are final
XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
config.setServerURL(new URL(this.serviceURL));
xmlRpcClient = new XmlRpcClient();
xmlRpcClient.setConfig(config);
}
/**
* generate a random string for signing methods
* @return
*/
private String generateNonce(){
/*
* //TODO:Get None Generator Working String allowedCharacters =
* "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";
* StringBuilder password = new StringBuilder();
* Random rand = new Random(); for (int i = 0; i < length; i++) {
* password.append() //password.append(append); }
* return password.toString();
*/
return "" + System.currentTimeMillis();
}
/**
* Generate the default parameters that need to precede every call
* @param methodname pass in the method name which should be signed.
* @return the default parameters. The Vector can be reused to pass other objects via XML-RPC,
* hence the resulting vector is typed to "Object" rather than String.
*/
private Vector<Object> generateDefaultParams(String methodname) throws Exception {
String nonce = generateNonce();
long timestamp = System.currentTimeMillis();
// Build String for hashing. As this is one line, no StringBuilder is used.
String hashString = Long.toString(timestamp) + ";" + serviceDomain + ";" + nonce + ";" + methodname;
String hash = generateHmacHash(hashString);
Vector<Object> params = new Vector<Object>();
params.add(hash);
params.add(this.serviceDomain);
params.add(Long.toString(timestamp));
params.add(nonce);
params.add(this.sessionID);
return params;
}
/**
* Compute the HMAC-SHA256 hash of the passed message.
* As key, the API key is used.
* @param message the message to hash
* @return the hash as hex-encoded string.
* @throws Exception if the encoding algorithm HmacSHA256 can't be found or other problems arise.
*/
public String generateHmacHash(String message) throws Exception {
byte[] hash = getApikeyMac().doFinal(asciiCs.encode(message).array());
String result = "";
for (int i = 0; i < hash.length; i++) {
// Leo: I don't understand why Aaron put this here. If its overcomplex, please simplify. I don't care for now.
result += Integer.toString((hash[i] & 0xff) + 0x100, 16)
.substring(1);
}
log.finest("Created HMAC: " + result+ " of "+message);
return result;
}
/**
* Getter for HMAC, as this is used on every call, its good to buffer it.
* As the ApiKey is final, this can be buffered.
* @return
*/
private Mac getApikeyMac() throws Exception {
if (apikeyMac==null)
{
SecretKeySpec keySpec = new javax.crypto.spec.SecretKeySpec(asciiCs
.encode(this.apiKey).array(), "HmacSHA256");
apikeyMac = javax.crypto.Mac.getInstance("HmacSHA256");
apikeyMac.init(keySpec);
}
return apikeyMac;
}
/**
* Connect to the remote service
*
* @return
* @throws Exception
*/
public void connect() throws Exception {
try {
Map map = (Map) xmlRpcClient
.execute(MethodSystemConnect,
new Object[] {});
this.sessionID = (String) map.get("sessid");
log.fine("Connected to server using SessionID: " + this.sessionID);
} catch (Exception x) {
throw new Exception("cannot connect to "+serviceURL+": "+x.getMessage(),x);
}
}
/**
* Call user.login
* @return
*/
public void login(String username, String password) throws Exception {
Vector<Object> params = generateDefaultParams(MethodUserLogin);
// Add Login Paramaters
params.add(username);
params.add(password);
Map o = (Map) xmlRpcClient.execute(
MethodUserLogin, params);
// IMPORTANT: the login changes the session id! The new session ID is authorized, the old one not.
this.sessionID = (String) o.get("sessid");
log.fine("Successfull Login");
}
/**
* Call user.logout
*/
public void logout() throws Exception {
Vector<Object> params = generateDefaultParams(MethodUserLogout);
params.add(this.sessionID);
xmlRpcClient.execute(MethodUserLogout, params);
log.finer("Logout Sucessfull");
}
/**
* Call file.save.
* Pass in the file as byte-array.
* TODO: Leo says: This does not conform to the Drupal interface of file.save - its a leftover from Aaron's code.
*/
public void fileSave(byte[] file) throws Exception {
Vector<Object> params = generateDefaultParams(MethodFileSave);
params.add(file);
Object o = xmlRpcClient.execute(MethodFileSave, params);
if (log.isLoggable(Level.FINEST))
log.finest(MethodFileSave+" returned "+o.toString());
}
/**
* Call node.save
* @param node the node to save
*/
public void nodeSave(DrupalNode node) throws Exception {
Vector<Object> params = generateDefaultParams(MethodNodeSave);
params.add(node);
Object o = xmlRpcClient.execute(MethodNodeSave, params);
if (log.isLoggable(Level.FINEST))
log.finest(MethodNodeSave+" returned "+o.toString());
}
}
/**
*
*/
package org.semanticdesktop.aperture.drupalhandler;
import java.util.HashMap;
import java.util.Map;
/**
* A drupal Node.
* It sets and gets all its variables as MAP entries.
* @author sauermann
*
*/
public class DrupalNode extends HashMap<String, Object> {
/**
* types
*/
public static final String TYPE_STORY = "story";
public static String NID = "nid";
public static String TYPE = "type";
public static String LANGUAGE = "language";
public static String UID = "uid";
public static String STATUS = "status";
public static String CREATED = "created";
public static String CHANGED = "changed";
public static String TITLE = "title";
public static String BODY = "body";
/* more for future
* public static String NID = "comment";
public static String NID = "promote";
public static String NID = "moderate";
public static String NID = "sticky";
public static String NID = "tnid";
public static String NID = "translate";
public static String NID = "vid";
public static String NID = "revision_uid";
public static String NID = "teaser";
public static String NID = "log";
public static String NID = "revision_timestamp";
public static String NID = "format";
public static String NID = "name";
public static String NID = "picture";
public static String NID = "data";
public static String NID = "rdf";
public static String NID = "last_comment_timestamp";
public static String NID = "last_comment_name";
public static String NID = "comment_count";
public static String NID = "taxonomy";
public static String NID = "build_mode";
public static String NID = "readmore";
public static String NID = "content";
*/
public DrupalNode() {
super();
}
public DrupalNode(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public DrupalNode(int initialCapacity) {
super(initialCapacity);
}
public DrupalNode(Map m) {
super(m);
}
public long getNid() {
Object nid = get(NID);
if (nid == null)
throw new RuntimeException("nid not set (null)");
if (nid instanceof String) {
long result = Long.parseLong((String)nid);
put(NID, result); // replace, for future
return result;
} else
return (Long)nid;
}
public void setNid(long nid) {
put(NID, nid);
}
public String getTitle() {
Object o = get(TITLE);
if (o==null)
return null;
else
return o.toString();
}
public void setTitle(String o) {
put(TITLE, o);
}
public String getBody() {
Object o = get(BODY);
if (o==null)
return null;
else
return o.toString();
}
public void setBody(String o) {
put(BODY, o);
}
public void setType(String o) {
put(TYPE, o);
}
}
For more information: https://aperture.svn.sourceforge.net/svnroot/aperture/aperture-webserver...
Help improve this page
Page status: Not set
You can:
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion