XMLRPCClient.java (21483B)
1 package org.xmlrpc.android; 2 3 import java.io.BufferedInputStream; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 import java.io.Reader; 7 import java.io.StringWriter; 8 import java.net.URI; 9 import java.net.URL; 10 import java.util.Map; 11 import java.util.Vector; 12 13 import org.apache.http.HttpEntity; 14 import org.apache.http.HttpResponse; 15 import org.apache.http.HttpStatus; 16 import org.apache.http.auth.AuthScope; 17 import org.apache.http.auth.UsernamePasswordCredentials; 18 import org.apache.http.client.CookieStore; 19 import org.apache.http.client.HttpClient; 20 import org.apache.http.client.methods.HttpPost; 21 import org.apache.http.client.protocol.ClientContext; 22 import org.apache.http.conn.scheme.PlainSocketFactory; 23 import org.apache.http.conn.scheme.Scheme; 24 import org.apache.http.conn.scheme.SchemeRegistry; 25 import org.apache.http.conn.ssl.SSLSocketFactory; 26 import org.apache.http.entity.StringEntity; 27 import org.apache.http.impl.client.BasicCookieStore; 28 import org.apache.http.impl.client.DefaultHttpClient; 29 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 30 import org.apache.http.params.HttpParams; 31 import org.apache.http.params.HttpProtocolParams; 32 import org.apache.http.protocol.BasicHttpContext; 33 import org.apache.http.protocol.HttpContext; 34 import org.xmlpull.v1.XmlPullParser; 35 import org.xmlpull.v1.XmlPullParserFactory; 36 37 import de.mtbnews.android.util.IBC; 38 39 import android.util.Log; 40 41 /** 42 * XMLRPCClient allows to call remote XMLRPC method. 43 * 44 * <p> 45 * The following table shows how XML-RPC types are mapped to java call 46 * parameters/response values. 47 * </p> 48 * 49 * <p> 50 * <table border="2" align="center" cellpadding="5"> 51 * <thead> 52 * <tr> 53 * <th>XML-RPC Type</th> 54 * <th>Call Parameters</th> 55 * <th>Call Response</th> 56 * </tr> 57 * </thead> 58 * 59 * <tbody> 60 * <td>int, i4</td> 61 * <td>byte<br /> 62 * Byte<br /> 63 * short<br /> 64 * Short<br /> 65 * int<br /> 66 * Integer</td> 67 * <td>int<br /> 68 * Integer</td> 69 * </tr> 70 * <tr> 71 * <td>i8</td> 72 * <td>long<br /> 73 * Long</td> 74 * <td>long<br /> 75 * Long</td> 76 * </tr> 77 * <tr> 78 * <td>double</td> 79 * <td>float<br /> 80 * Float<br /> 81 * double<br /> 82 * Double</td> 83 * <td>double<br /> 84 * Double</td> 85 * </tr> 86 * <tr> 87 * <td>string</td> 88 * <td>String</td> 89 * <td>String</td> 90 * </tr> 91 * <tr> 92 * <td>boolean</td> 93 * <td>boolean<br /> 94 * Boolean</td> 95 * <td>boolean<br /> 96 * Boolean</td> 97 * </tr> 98 * <tr> 99 * <td>dateTime.iso8601</td> 100 * <td>java.util.Date<br /> 101 * java.util.Calendar</td> 102 * <td>java.util.Date</td> 103 * </tr> 104 * <tr> 105 * <td>base64</td> 106 * <td>byte[]</td> 107 * <td>byte[]</td> 108 * </tr> 109 * <tr> 110 * <td>array</td> 111 * <td>java.util.List<Object><br /> 112 * Object[]</td> 113 * <td>Object[]</td> 114 * </tr> 115 * <tr> 116 * <td>struct</td> 117 * <td>java.util.Map<String, Object></td> 118 * <td>java.util.Map<String, Object></td> 119 * </tr> 120 * </tbody> 121 * </table> 122 * </p> 123 * <p> 124 * You can also pass as a parameter any object implementing XMLRPCSerializable 125 * interface. In this case your object overrides getSerializable() telling how 126 * to serialize to XMLRPC protocol 127 * </p> 128 */ 129 130 public class XMLRPCClient extends XMLRPCCommon 131 { 132 private HttpClient client; 133 private HttpPost postMethod; 134 private HttpParams httpParams; 135 // These variables used in the code inspired by erickok in issue #6 136 private boolean httpPreAuth = false; 137 private String username = ""; 138 private String password = ""; 139 140 public CookieStore cookieStore = new BasicCookieStore(); 141 HttpContext localContext = new BasicHttpContext(); 142 143 /** 144 * XMLRPCClient constructor. Creates new instance based on server URI (Code 145 * contributed by sgayda2 from issue #17, and by erickok from ticket #10) 146 * 147 * @param XMLRPC 148 * server URI 149 */ 150 public XMLRPCClient(URI uri) 151 { 152 153 localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore); 154 155 SchemeRegistry registry = new SchemeRegistry(); 156 registry.register(new Scheme("http", new PlainSocketFactory(), 80)); 157 registry.register(new Scheme("https", SSLSocketFactory 158 .getSocketFactory(), 443)); 159 160 postMethod = new HttpPost(uri); 161 postMethod.addHeader("Content-Type", "text/xml"); 162 163 // WARNING 164 // I had to disable "Expect: 100-Continue" header since I had 165 // two second delay between sending http POST request and POST body 166 httpParams = postMethod.getParams(); 167 HttpProtocolParams.setUseExpectContinue(httpParams, false); 168 this.client = new DefaultHttpClient(new ThreadSafeClientConnManager( 169 httpParams, registry), httpParams); 170 } 171 172 /** 173 * XMLRPCClient constructor. Creates new instance based on server URI (Code 174 * contributed by sgayda2 from issue #17) 175 * 176 * @param XMLRPC 177 * server URI 178 * @param HttpClient 179 * to use 180 */ 181 182 public XMLRPCClient(URI uri, HttpClient client) 183 { 184 postMethod = new HttpPost(uri); 185 postMethod.addHeader("Content-Type", "text/xml"); 186 187 // WARNING 188 // I had to disable "Expect: 100-Continue" header since I had 189 // two second delay between sending http POST request and POST body 190 httpParams = postMethod.getParams(); 191 HttpProtocolParams.setUseExpectContinue(httpParams, false); 192 this.client = client; 193 } 194 195 /** 196 * Amends user agent (Code contributed by mortenholdflod from issue #28) 197 * 198 * @param userAgent 199 * defining the new User Agent string 200 */ 201 public void setUserAgent(String userAgent) 202 { 203 postMethod.removeHeaders("User-Agent"); 204 postMethod.addHeader("User-Agent", userAgent); 205 } 206 207 /** 208 * Convenience constructor. Creates new instance based on server String 209 * address 210 * 211 * @param XMLRPC 212 * server address 213 */ 214 public XMLRPCClient(String url) 215 { 216 this(URI.create(url)); 217 } 218 219 /** 220 * Convenience constructor. Creates new instance based on server String 221 * address 222 * 223 * @param XMLRPC 224 * server address 225 * @param HttpClient 226 * to use 227 */ 228 public XMLRPCClient(String url, HttpClient client) 229 { 230 this(URI.create(url), client); 231 } 232 233 /** 234 * Convenience XMLRPCClient constructor. Creates new instance based on 235 * server URL 236 * 237 * @param XMLRPC 238 * server URL 239 */ 240 public XMLRPCClient(URL url) 241 { 242 this(URI.create(url.toExternalForm())); 243 } 244 245 /** 246 * Convenience XMLRPCClient constructor. Creates new instance based on 247 * server URL 248 * 249 * @param XMLRPC 250 * server URL 251 * @param HttpClient 252 * to use 253 */ 254 public XMLRPCClient(URL url, HttpClient client) 255 { 256 this(URI.create(url.toExternalForm()), client); 257 } 258 259 /** 260 * Convenience constructor. Creates new instance based on server String 261 * address 262 * 263 * @param XMLRPC 264 * server address 265 * @param HTTP 266 * Server - Basic Authentication - Username 267 * @param HTTP 268 * Server - Basic Authentication - Password 269 */ 270 public XMLRPCClient(URI uri, String username, String password) 271 { 272 this(uri); 273 274 ((DefaultHttpClient) client).getCredentialsProvider() 275 .setCredentials( 276 new AuthScope(uri.getHost(), uri.getPort(), 277 AuthScope.ANY_REALM), 278 new UsernamePasswordCredentials(username, password)); 279 } 280 281 /** 282 * Convenience constructor. Creates new instance based on server String 283 * address 284 * 285 * @param XMLRPC 286 * server address 287 * @param HTTP 288 * Server - Basic Authentication - Username 289 * @param HTTP 290 * Server - Basic Authentication - Password 291 * @param HttpClient 292 * to use 293 */ 294 public XMLRPCClient(URI uri, String username, String password, 295 HttpClient client) 296 { 297 this(uri, client); 298 299 ((DefaultHttpClient) this.client).getCredentialsProvider() 300 .setCredentials( 301 new AuthScope(uri.getHost(), uri.getPort(), 302 AuthScope.ANY_REALM), 303 new UsernamePasswordCredentials(username, password)); 304 } 305 306 /** 307 * Convenience constructor. Creates new instance based on server String 308 * address 309 * 310 * @param XMLRPC 311 * server address 312 * @param HTTP 313 * Server - Basic Authentication - Username 314 * @param HTTP 315 * Server - Basic Authentication - Password 316 */ 317 public XMLRPCClient(String url, String username, String password) 318 { 319 this(URI.create(url), username, password); 320 } 321 322 /** 323 * Convenience constructor. Creates new instance based on server String 324 * address 325 * 326 * @param XMLRPC 327 * server address 328 * @param HTTP 329 * Server - Basic Authentication - Username 330 * @param HTTP 331 * Server - Basic Authentication - Password 332 * @param HttpClient 333 * to use 334 */ 335 public XMLRPCClient(String url, String username, String password, 336 HttpClient client) 337 { 338 this(URI.create(url), username, password, client); 339 } 340 341 /** 342 * Convenience constructor. Creates new instance based on server String 343 * address 344 * 345 * @param XMLRPC 346 * server url 347 * @param HTTP 348 * Server - Basic Authentication - Username 349 * @param HTTP 350 * Server - Basic Authentication - Password 351 */ 352 public XMLRPCClient(URL url, String username, String password) 353 { 354 this(URI.create(url.toExternalForm()), username, password); 355 } 356 357 /** 358 * Convenience constructor. Creates new instance based on server String 359 * address 360 * 361 * @param XMLRPC 362 * server url 363 * @param HTTP 364 * Server - Basic Authentication - Username 365 * @param HTTP 366 * Server - Basic Authentication - Password 367 * @param HttpClient 368 * to use 369 */ 370 public XMLRPCClient(URL url, String username, String password, 371 HttpClient client) 372 { 373 this(URI.create(url.toExternalForm()), username, password, client); 374 } 375 376 /** 377 * Sets basic authentication on web request using plain credentials 378 * 379 * @param username 380 * The plain text username 381 * @param password 382 * The plain text password 383 * @param doPreemptiveAuth 384 * Select here whether to authenticate without it being requested 385 * first by the server. 386 */ 387 public void setBasicAuthentication(String username, String password, 388 boolean doPreemptiveAuth) 389 { 390 // This code required to trigger the patch created by erickok in issue 391 // #6 392 if (doPreemptiveAuth = true) 393 { 394 this.httpPreAuth = doPreemptiveAuth; 395 this.username = username; 396 this.password = password; 397 } 398 else 399 { 400 ((DefaultHttpClient) client) 401 .getCredentialsProvider() 402 .setCredentials( 403 new AuthScope(postMethod.getURI().getHost(), 404 postMethod.getURI().getPort(), 405 AuthScope.ANY_REALM), 406 new UsernamePasswordCredentials(username, password)); 407 } 408 } 409 410 /** 411 * Convenience Constructor: Sets basic authentication on web request using 412 * plain credentials 413 * 414 * @param username 415 * The plain text username 416 * @param password 417 * The plain text password 418 */ 419 public void setBasicAuthentication(String username, String password) 420 { 421 setBasicAuthentication(username, password, false); 422 } 423 424 /** 425 * Call method with optional parameters. This is general method. If you want 426 * to call your method with 0-8 parameters, you can use more convenience 427 * call() methods 428 * 429 * @param method 430 * name of method to call 431 * @param params 432 * parameters to pass to method (may be null if method has no 433 * parameters) 434 * @return deserialized method return value 435 * @throws XMLRPCException 436 */ 437 @SuppressWarnings("unchecked") 438 public Object callEx(String method, Object[] params) throws XMLRPCException 439 { 440 try 441 { 442 // prepare POST body 443 String body = methodCall(method, params); 444 445 // set POST body 446 HttpEntity entity = new StringEntity(body); 447 postMethod.setEntity(entity); 448 449 // This code slightly tweaked from the code by erickok in issue #6 450 // Force preemptive authentication 451 // This makes sure there is an 'Authentication: ' header being send 452 // before trying and failing and retrying 453 // by the basic authentication mechanism of DefaultHttpClient 454 if (this.httpPreAuth == true) 455 { 456 String auth = this.username + ":" + this.password; 457 postMethod.addHeader("Authorization", "Basic " 458 + Base64Coder.encode(auth.getBytes()).toString()); 459 } 460 461 // Log.d(Tag.LOG, "ros HTTP POST"); 462 // execute HTTP POST request 463 464 Log.d("IBC", "XMLRPC method: " + method); 465 // for (Cookie cookie : cookieStore.getCookies()) 466 // { 467 // Log.d("IBC", cookie.toString()); 468 // } 469 470 HttpResponse response = client.execute(postMethod, localContext); 471 // Log.d(Tag.LOG, "ros HTTP POSTed"); 472 473 // check status code 474 int statusCode = response.getStatusLine().getStatusCode(); 475 476 // Log.d("IBC", "Status: " + statusCode); 477 // for (Cookie cookie : cookieStore.getCookies()) 478 // { 479 // Log.d("IBC", cookie.toString()); 480 // } 481 // Log.d("IBC", ""); 482 483 // Log.d(Tag.LOG, "ros status code:" + statusCode); 484 if (statusCode != HttpStatus.SC_OK) 485 { 486 throw new XMLRPCException("HTTP status code: " + statusCode 487 + " != " + HttpStatus.SC_OK, statusCode); 488 } 489 490 // parse response stuff 491 // 492 // setup pull parser 493 XmlPullParser pullParser = XmlPullParserFactory.newInstance() 494 .newPullParser(); 495 entity = response.getEntity(); 496 Reader reader = new InputStreamReader(new BufferedInputStream( 497 entity.getContent(), 250)); 498 // for testing purposes only 499 // reader = new 500 // StringReader("<?xml version='1.0'?><methodResponse><params><param><value>\n\n\n</value></param></params></methodResponse>"); 501 pullParser.setInput(reader); 502 503 // lets start pulling... 504 pullParser.nextTag(); 505 506 // Log.d("XMLRPC response",this.inputStreamToString(entity.getContent())); 507 // entity.getContent().reset(); 508 509 pullParser.require(XmlPullParser.START_TAG, null, 510 Tag.METHOD_RESPONSE); 511 512 pullParser.nextTag(); // either Tag.PARAMS (<params>) or Tag.FAULT 513 // (<fault>) 514 String tag = pullParser.getName(); 515 516 if (tag.equals(Tag.PARAMS)) 517 { 518 // normal response 519 pullParser.nextTag(); // Tag.PARAM (<param>) 520 pullParser.require(XmlPullParser.START_TAG, null, Tag.PARAM); 521 pullParser.nextTag(); // Tag.VALUE (<value>) 522 // no parser.require() here since its called in 523 // XMLRPCSerializer.deserialize() below 524 525 // deserialize result 526 Object obj = iXMLRPCSerializer.deserialize(pullParser); 527 entity.consumeContent(); 528 return obj; 529 } 530 else if (tag.equals(Tag.FAULT)) 531 { 532 // fault response 533 pullParser.nextTag(); // Tag.VALUE (<value>) 534 // no parser.require() here since its called in 535 // XMLRPCSerializer.deserialize() below 536 537 // deserialize fault result 538 Map<String, Object> map = (Map<String, Object>) iXMLRPCSerializer 539 .deserialize(pullParser); 540 String faultString = (String) map.get(Tag.FAULT_STRING); 541 int faultCode = (Integer) map.get(Tag.FAULT_CODE); 542 entity.consumeContent(); 543 throw new XMLRPCFault(faultString, faultCode); 544 } 545 else 546 { 547 entity.consumeContent(); 548 throw new XMLRPCException("Bad tag <" + tag 549 + "> in XMLRPC response - neither <params> nor <fault>"); 550 } 551 } 552 catch (XMLRPCException e) 553 { 554 // catch & propagate XMLRPCException/XMLRPCFault 555 throw e; 556 } 557 catch (Exception e) 558 { 559 e.printStackTrace(); 560 // wrap any other Exception(s) around XMLRPCException 561 throw new XMLRPCException(e); 562 } 563 } 564 565 private String methodCall(String method, Object[] params) 566 throws IllegalArgumentException, IllegalStateException, IOException 567 { 568 StringWriter bodyWriter = new StringWriter(); 569 serializer.setOutput(bodyWriter); 570 serializer.startDocument(null, null); 571 serializer.startTag(null, Tag.METHOD_CALL); 572 // set method name 573 serializer.startTag(null, Tag.METHOD_NAME).text(method).endTag(null, 574 Tag.METHOD_NAME); 575 576 serializeParams(params); 577 578 serializer.endTag(null, Tag.METHOD_CALL); 579 serializer.endDocument(); 580 581 return bodyWriter.toString(); 582 } 583 584 /** 585 * Convenience method call with no parameters 586 * 587 * @param method 588 * name of method to call 589 * @return deserialized method return value 590 * @throws XMLRPCException 591 */ 592 public Object call(String method) throws XMLRPCException 593 { 594 return callEx(method, null); 595 } 596 597 /** 598 * Convenience method call with a vectorized parameter (Code contributed by 599 * jahbromo from issue #14) 600 * 601 * @param method 602 * name of method to call 603 * @param paramsv 604 * vector of method's parameter 605 * @return deserialized method return value 606 * @throws XMLRPCException 607 */ 608 609 public Object call(String method, Vector paramsv) throws XMLRPCException 610 { 611 Object[] params = new Object[paramsv.size()]; 612 for (int i = 0; i < paramsv.size(); i++) 613 { 614 params[i] = paramsv.elementAt(i); 615 } 616 return callEx(method, params); 617 } 618 619 /** 620 * Convenience method call with one parameter 621 * 622 * @param method 623 * name of method to call 624 * @param p0 625 * method's parameter 626 * @return deserialized method return value 627 * @throws XMLRPCException 628 */ 629 public Object call(String method, Object p0) throws XMLRPCException 630 { 631 Object[] params = { p0, }; 632 return callEx(method, params); 633 } 634 635 /** 636 * Convenience method call with two parameters 637 * 638 * @param method 639 * name of method to call 640 * @param p0 641 * method's 1st parameter 642 * @param p1 643 * method's 2nd parameter 644 * @return deserialized method return value 645 * @throws XMLRPCException 646 */ 647 public Object call(String method, Object p0, Object p1) 648 throws XMLRPCException 649 { 650 Object[] params = { p0, p1, }; 651 return callEx(method, params); 652 } 653 654 /** 655 * Convenience method call with three parameters 656 * 657 * @param method 658 * name of method to call 659 * @param p0 660 * method's 1st parameter 661 * @param p1 662 * method's 2nd parameter 663 * @param p2 664 * method's 3rd parameter 665 * @return deserialized method return value 666 * @throws XMLRPCException 667 */ 668 public Object call(String method, Object p0, Object p1, Object p2) 669 throws XMLRPCException 670 { 671 Object[] params = { p0, p1, p2, }; 672 return callEx(method, params); 673 } 674 675 /** 676 * Convenience method call with four parameters 677 * 678 * @param method 679 * name of method to call 680 * @param p0 681 * method's 1st parameter 682 * @param p1 683 * method's 2nd parameter 684 * @param p2 685 * method's 3rd parameter 686 * @param p3 687 * method's 4th parameter 688 * @return deserialized method return value 689 * @throws XMLRPCException 690 */ 691 public Object call(String method, Object p0, Object p1, Object p2, Object p3) 692 throws XMLRPCException 693 { 694 Object[] params = { p0, p1, p2, p3, }; 695 return callEx(method, params); 696 } 697 698 /** 699 * Convenience method call with five parameters 700 * 701 * @param method 702 * name of method to call 703 * @param p0 704 * method's 1st parameter 705 * @param p1 706 * method's 2nd parameter 707 * @param p2 708 * method's 3rd parameter 709 * @param p3 710 * method's 4th parameter 711 * @param p4 712 * method's 5th parameter 713 * @return deserialized method return value 714 * @throws XMLRPCException 715 */ 716 public Object call(String method, Object p0, Object p1, Object p2, 717 Object p3, Object p4) throws XMLRPCException 718 { 719 Object[] params = { p0, p1, p2, p3, p4, }; 720 return callEx(method, params); 721 } 722 723 /** 724 * Convenience method call with six parameters 725 * 726 * @param method 727 * name of method to call 728 * @param p0 729 * method's 1st parameter 730 * @param p1 731 * method's 2nd parameter 732 * @param p2 733 * method's 3rd parameter 734 * @param p3 735 * method's 4th parameter 736 * @param p4 737 * method's 5th parameter 738 * @param p5 739 * method's 6th parameter 740 * @return deserialized method return value 741 * @throws XMLRPCException 742 */ 743 public Object call(String method, Object p0, Object p1, Object p2, 744 Object p3, Object p4, Object p5) throws XMLRPCException 745 { 746 Object[] params = { p0, p1, p2, p3, p4, p5, }; 747 return callEx(method, params); 748 } 749 750 /** 751 * Convenience method call with seven parameters 752 * 753 * @param method 754 * name of method to call 755 * @param p0 756 * method's 1st parameter 757 * @param p1 758 * method's 2nd parameter 759 * @param p2 760 * method's 3rd parameter 761 * @param p3 762 * method's 4th parameter 763 * @param p4 764 * method's 5th parameter 765 * @param p5 766 * method's 6th parameter 767 * @param p6 768 * method's 7th parameter 769 * @return deserialized method return value 770 * @throws XMLRPCException 771 */ 772 public Object call(String method, Object p0, Object p1, Object p2, 773 Object p3, Object p4, Object p5, Object p6) throws XMLRPCException 774 { 775 Object[] params = { p0, p1, p2, p3, p4, p5, p6, }; 776 return callEx(method, params); 777 } 778 779 /** 780 * Convenience method call with eight parameters 781 * 782 * @param method 783 * name of method to call 784 * @param p0 785 * method's 1st parameter 786 * @param p1 787 * method's 2nd parameter 788 * @param p2 789 * method's 3rd parameter 790 * @param p3 791 * method's 4th parameter 792 * @param p4 793 * method's 5th parameter 794 * @param p5 795 * method's 6th parameter 796 * @param p6 797 * method's 7th parameter 798 * @param p7 799 * method's 8th parameter 800 * @return deserialized method return value 801 * @throws XMLRPCException 802 */ 803 public Object call(String method, Object p0, Object p1, Object p2, 804 Object p3, Object p4, Object p5, Object p6, Object p7) 805 throws XMLRPCException 806 { 807 Object[] params = { p0, p1, p2, p3, p4, p5, p6, p7, }; 808 return callEx(method, params); 809 } 810 }