Java tutorial
/******************************************************************************* * Copyright (c) 2011 MadRobot. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v2.1 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * Contributors: * Elton Kent - initial API and implementation ******************************************************************************/ package com.madrobot.net.client; import android.util.Base64; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.net.URI; import java.net.URL; import java.util.Map; import java.util.Vector; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserFactory; /** * XMLRPCClient allows to call remote XMLRPC method. * * <p> * The following table shows how XML-RPC types are mapped to java call parameters/response values. * </p> * * <p> * <table border="2" align="center" cellpadding="5"> * <thead> * <tr> * <th>XML-RPC Type</th> * <th>Call Parameters</th> * <th>Call Response</th> * </tr> * </thead> * * <tbody> * <td>int, i4</td> * <td>byte<br /> * Byte<br /> * short<br /> * Short<br /> * int<br /> * Integer</td> * <td>int<br /> * Integer</td> * </tr> * <tr> * <td>i8</td> * <td>long<br /> * Long</td> * <td>long<br /> * Long</td> * </tr> * <tr> * <td>double</td> * <td>float<br /> * Float<br /> * double<br /> * Double</td> * <td>double<br /> * Double</td> * </tr> * <tr> * <td>string</td> * <td>String</td> * <td>String</td> * </tr> * <tr> * <td>boolean</td> * <td>boolean<br /> * Boolean</td> * <td>boolean<br /> * Boolean</td> * </tr> * <tr> * <td>dateTime.iso8601</td> * <td>java.util.Date<br /> * java.util.Calendar</td> * <td>java.util.Date</td> * </tr> * <tr> * <td>base64</td> * <td>byte[]</td> * <td>byte[]</td> * </tr> * <tr> * <td>array</td> * <td>java.util.List<Object><br /> * Object[]</td> * <td>Object[]</td> * </tr> * <tr> * <td>struct</td> * <td>java.util.Map<String, Object></td> * <td>java.util.Map<String, Object></td> * </tr> * </tbody> * </table> * </p> * <p> * You can also pass as a parameter any object implementing XMLRPCSerializable interface. In this case your object * overrides getSerializable() telling how to serialize to XMLRPC protocol * </p> */ public class XMLRPCClient extends XMLRPCCommon { private HttpClient client; private HttpParams httpParams; // These variables used in the code inspired by erickok in issue #6 private boolean httpPreAuth = false; private String password = ""; private HttpPost postMethod; private String username = ""; /** * Convenience constructor. Creates new instance based on server String address * * @param XMLRPC * server address */ public XMLRPCClient(String url) { this(URI.create(url)); } /** * Convenience constructor. Creates new instance based on server String address * * @param XMLRPC * server address * @param HttpClient * to use */ public XMLRPCClient(String url, HttpClient client) { this(URI.create(url), client); } /** * Convenience constructor. Creates new instance based on server String address * * @param XMLRPC * server address * @param HTTP * Server - Basic Authentication - Username * @param HTTP * Server - Basic Authentication - Password */ public XMLRPCClient(String url, String username, String password) { this(URI.create(url), username, password); } /** * Convenience constructor. Creates new instance based on server String address * * @param XMLRPC * server address * @param HTTP * Server - Basic Authentication - Username * @param HTTP * Server - Basic Authentication - Password * @param HttpClient * to use */ public XMLRPCClient(String url, String username, String password, HttpClient client) { this(URI.create(url), username, password, client); } /** * XMLRPCClient constructor. Creates new instance based on server URI (Code contributed by sgayda2 from issue #17, * and by erickok from ticket #10) * * @param XMLRPC * server URI */ public XMLRPCClient(URI uri) { SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", new PlainSocketFactory(), 80)); registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443)); postMethod = new HttpPost(uri); postMethod.addHeader("Content-Type", "text/xml"); // WARNING // I had to disable "Expect: 100-Continue" header since I had // two second delay between sending http POST request and POST body httpParams = postMethod.getParams(); HttpProtocolParams.setUseExpectContinue(httpParams, false); this.client = new DefaultHttpClient(new ThreadSafeClientConnManager(httpParams, registry), httpParams); } /** * XMLRPCClient constructor. Creates new instance based on server URI (Code contributed by sgayda2 from issue #17) * * @param XMLRPC * server URI * @param HttpClient * to use */ public XMLRPCClient(URI uri, HttpClient client) { postMethod = new HttpPost(uri); postMethod.addHeader("Content-Type", "text/xml"); // WARNING // I had to disable "Expect: 100-Continue" header since I had // two second delay between sending http POST request and POST body httpParams = postMethod.getParams(); HttpProtocolParams.setUseExpectContinue(httpParams, false); this.client = client; } /** * Convenience constructor. Creates new instance based on server String address * * @param XMLRPC * server address * @param HTTP * Server - Basic Authentication - Username * @param HTTP * Server - Basic Authentication - Password */ public XMLRPCClient(URI uri, String username, String password) { this(uri); ((DefaultHttpClient) client).getCredentialsProvider().setCredentials( new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_REALM), new UsernamePasswordCredentials(username, password)); } /** * Convenience constructor. Creates new instance based on server String address * * @param XMLRPC * server address * @param HTTP * Server - Basic Authentication - Username * @param HTTP * Server - Basic Authentication - Password * @param HttpClient * to use */ public XMLRPCClient(URI uri, String username, String password, HttpClient client) { this(uri, client); ((DefaultHttpClient) this.client).getCredentialsProvider().setCredentials( new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_REALM), new UsernamePasswordCredentials(username, password)); } /** * Convenience XMLRPCClient constructor. Creates new instance based on server URL * * @param XMLRPC * server URL */ public XMLRPCClient(URL url) { this(URI.create(url.toExternalForm())); } /** * Convenience XMLRPCClient constructor. Creates new instance based on server URL * * @param XMLRPC * server URL * @param HttpClient * to use */ public XMLRPCClient(URL url, HttpClient client) { this(URI.create(url.toExternalForm()), client); } /** * Convenience constructor. Creates new instance based on server String address * * @param XMLRPC * server url * @param HTTP * Server - Basic Authentication - Username * @param HTTP * Server - Basic Authentication - Password */ public XMLRPCClient(URL url, String username, String password) { this(URI.create(url.toExternalForm()), username, password); } /** * Convenience constructor. Creates new instance based on server String address * * @param XMLRPC * server url * @param HTTP * Server - Basic Authentication - Username * @param HTTP * Server - Basic Authentication - Password * @param HttpClient * to use */ public XMLRPCClient(URL url, String username, String password, HttpClient client) { this(URI.create(url.toExternalForm()), username, password, client); } /** * Convenience method call with no parameters * * @param method * name of method to call * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method) throws XMLRPCException { return callEx(method, null); } /** * Convenience method call with one parameter * * @param method * name of method to call * @param p0 * method's parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Object p0) throws XMLRPCException { Object[] params = { p0, }; return callEx(method, params); } /** * Convenience method call with two parameters * * @param method * name of method to call * @param p0 * method's 1st parameter * @param p1 * method's 2nd parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Object p0, Object p1) throws XMLRPCException { Object[] params = { p0, p1, }; return callEx(method, params); } /** * Convenience method call with three parameters * * @param method * name of method to call * @param p0 * method's 1st parameter * @param p1 * method's 2nd parameter * @param p2 * method's 3rd parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Object p0, Object p1, Object p2) throws XMLRPCException { Object[] params = { p0, p1, p2, }; return callEx(method, params); } /** * Convenience method call with four parameters * * @param method * name of method to call * @param p0 * method's 1st parameter * @param p1 * method's 2nd parameter * @param p2 * method's 3rd parameter * @param p3 * method's 4th parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Object p0, Object p1, Object p2, Object p3) throws XMLRPCException { Object[] params = { p0, p1, p2, p3, }; return callEx(method, params); } /** * Convenience method call with five parameters * * @param method * name of method to call * @param p0 * method's 1st parameter * @param p1 * method's 2nd parameter * @param p2 * method's 3rd parameter * @param p3 * method's 4th parameter * @param p4 * method's 5th parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Object p0, Object p1, Object p2, Object p3, Object p4) throws XMLRPCException { Object[] params = { p0, p1, p2, p3, p4, }; return callEx(method, params); } /** * Convenience method call with six parameters * * @param method * name of method to call * @param p0 * method's 1st parameter * @param p1 * method's 2nd parameter * @param p2 * method's 3rd parameter * @param p3 * method's 4th parameter * @param p4 * method's 5th parameter * @param p5 * method's 6th parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) throws XMLRPCException { Object[] params = { p0, p1, p2, p3, p4, p5, }; return callEx(method, params); } /** * Convenience method call with seven parameters * * @param method * name of method to call * @param p0 * method's 1st parameter * @param p1 * method's 2nd parameter * @param p2 * method's 3rd parameter * @param p3 * method's 4th parameter * @param p4 * method's 5th parameter * @param p5 * method's 6th parameter * @param p6 * method's 7th parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6) throws XMLRPCException { Object[] params = { p0, p1, p2, p3, p4, p5, p6, }; return callEx(method, params); } /** * Convenience method call with eight parameters * * @param method * name of method to call * @param p0 * method's 1st parameter * @param p1 * method's 2nd parameter * @param p2 * method's 3rd parameter * @param p3 * method's 4th parameter * @param p4 * method's 5th parameter * @param p5 * method's 6th parameter * @param p6 * method's 7th parameter * @param p7 * method's 8th parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7) throws XMLRPCException { Object[] params = { p0, p1, p2, p3, p4, p5, p6, p7, }; return callEx(method, params); } /** * Convenience method call with a vectorized parameter (Code contributed by jahbromo from issue #14) * * @param method * name of method to call * @param paramsv * vector of method's parameter * @return deserialized method return value * @throws XMLRPCException */ public Object call(String method, Vector paramsv) throws XMLRPCException { Object[] params = new Object[paramsv.size()]; for (int i = 0; i < paramsv.size(); i++) { params[i] = paramsv.elementAt(i); } return callEx(method, params); } /** * Call method with optional parameters. This is general method. If you want to call your method with 0-8 * parameters, you can use more convenience call() methods * * @param method * name of method to call * @param params * parameters to pass to method (may be null if method has no parameters) * @return deserialized method return value * @throws XMLRPCException */ @SuppressWarnings("unchecked") public Object callEx(String method, Object[] params) throws XMLRPCException { try { // prepare POST body String body = methodCall(method, params); // set POST body HttpEntity entity = new StringEntity(body); postMethod.setEntity(entity); // This code slightly tweaked from the code by erickok in issue #6 // Force preemptive authentication // This makes sure there is an 'Authentication: ' header being send before trying and failing and retrying // by the basic authentication mechanism of DefaultHttpClient if (this.httpPreAuth == true) { String auth = this.username + ":" + this.password; postMethod.addHeader("Authorization", "Basic " + Base64.encode(auth.getBytes(), Base64.DEFAULT).toString()); } // Log.d(Tag.LOG, "ros HTTP POST"); // execute HTTP POST request HttpResponse response = client.execute(postMethod); // Log.d(Tag.LOG, "ros HTTP POSTed"); // check status code int statusCode = response.getStatusLine().getStatusCode(); // Log.d(Tag.LOG, "ros status code:" + statusCode); if (statusCode != HttpStatus.SC_OK) { throw new XMLRPCException("HTTP status code: " + statusCode + " != " + HttpStatus.SC_OK); } // parse response stuff // // setup pull parser XmlPullParser pullParser = XmlPullParserFactory.newInstance().newPullParser(); entity = response.getEntity(); Reader reader = new InputStreamReader(new BufferedInputStream(entity.getContent())); // for testing purposes only // reader = new // StringReader("<?xml version='1.0'?><methodResponse><params><param><value>\n\n\n</value></param></params></methodResponse>"); pullParser.setInput(reader); // lets start pulling... pullParser.nextTag(); pullParser.require(XmlPullParser.START_TAG, null, Tag.METHOD_RESPONSE); pullParser.nextTag(); // either Tag.PARAMS (<params>) or Tag.FAULT (<fault>) String tag = pullParser.getName(); if (tag.equals(Tag.PARAMS)) { // normal response pullParser.nextTag(); // Tag.PARAM (<param>) pullParser.require(XmlPullParser.START_TAG, null, Tag.PARAM); pullParser.nextTag(); // Tag.VALUE (<value>) // no parser.require() here since its called in XMLRPCSerializer.deserialize() below // deserialize result Object obj = iXMLRPCSerializer.deserialize(pullParser); entity.consumeContent(); return obj; } else if (tag.equals(Tag.FAULT)) { // fault response pullParser.nextTag(); // Tag.VALUE (<value>) // no parser.require() here since its called in XMLRPCSerializer.deserialize() below // deserialize fault result Map<String, Object> map = (Map<String, Object>) iXMLRPCSerializer.deserialize(pullParser); String faultString = (String) map.get(Tag.FAULT_STRING); int faultCode = (Integer) map.get(Tag.FAULT_CODE); entity.consumeContent(); throw new XMLRPCFault(faultString, faultCode); } else { entity.consumeContent(); throw new XMLRPCException( "Bad tag <" + tag + "> in XMLRPC response - neither <params> nor <fault>"); } } catch (XMLRPCException e) { // catch & propagate XMLRPCException/XMLRPCFault throw e; } catch (Exception e) { e.printStackTrace(); // wrap any other Exception(s) around XMLRPCException throw new XMLRPCException(e); } } private String methodCall(String method, Object[] params) throws IllegalArgumentException, IllegalStateException, IOException { StringWriter bodyWriter = new StringWriter(); serializer.setOutput(bodyWriter); serializer.startDocument(null, null); serializer.startTag(null, Tag.METHOD_CALL); // set method name serializer.startTag(null, Tag.METHOD_NAME).text(method).endTag(null, Tag.METHOD_NAME); serializeParams(params); serializer.endTag(null, Tag.METHOD_CALL); serializer.endDocument(); return bodyWriter.toString(); } /** * Convenience Constructor: Sets basic authentication on web request using plain credentials * * @param username * The plain text username * @param password * The plain text password */ public void setBasicAuthentication(String username, String password) { setBasicAuthentication(username, password, false); } /** * Sets basic authentication on web request using plain credentials * * @param username * The plain text username * @param password * The plain text password * @param doPreemptiveAuth * Select here whether to authenticate without it being requested first by the server. */ public void setBasicAuthentication(String username, String password, boolean doPreemptiveAuth) { // This code required to trigger the patch created by erickok in issue #6 if (doPreemptiveAuth = true) { this.httpPreAuth = doPreemptiveAuth; this.username = username; this.password = password; } else { ((DefaultHttpClient) client).getCredentialsProvider() .setCredentials(new AuthScope(postMethod.getURI().getHost(), postMethod.getURI().getPort(), AuthScope.ANY_REALM), new UsernamePasswordCredentials(username, password)); } } /** * Amends user agent (Code contributed by mortenholdflod from issue #28) * * @param userAgent * defining the new User Agent string */ public void setUserAgent(String userAgent) { postMethod.removeHeaders("User-Agent"); postMethod.addHeader("User-Agent", userAgent); } }