KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > xmlrpc > applet > SimpleXmlRpcClient


1 /*
2  * Copyright 1999,2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17
18 package org.apache.xmlrpc.applet;
19
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.io.OutputStream JavaDoc;
23 import java.io.UnsupportedEncodingException JavaDoc;
24 import java.net.MalformedURLException JavaDoc;
25 import java.net.URL JavaDoc;
26 import java.net.URLConnection JavaDoc;
27 import java.text.DateFormat JavaDoc;
28 import java.text.ParseException JavaDoc;
29 import java.text.SimpleDateFormat JavaDoc;
30 import java.util.Date JavaDoc;
31 import java.util.Enumeration JavaDoc;
32 import java.util.Hashtable JavaDoc;
33 import java.util.Stack JavaDoc;
34 import java.util.Vector JavaDoc;
35 import org.apache.commons.codec.binary.Base64;
36 import org.apache.commons.codec.DecoderException;
37 import org.apache.commons.codec.EncoderException;
38 import org.xml.sax.AttributeList JavaDoc;
39 import org.xml.sax.HandlerBase JavaDoc;
40 import org.xml.sax.InputSource JavaDoc;
41 import org.xml.sax.SAXException JavaDoc;
42 import org.xml.sax.SAXParseException JavaDoc;
43 import uk.co.wilson.xml.MinML;
44
45 /**
46  * A simple XML-RPC client.
47  *
48  * FIXME: This code is VERY out of date with the rest of the package.
49  *
50  * @version $Id: SimpleXmlRpcClient.java,v 1.10 2005/04/22 10:25:58 hgomez Exp $
51  */

52 public class SimpleXmlRpcClient
53 {
54     URL JavaDoc url;
55
56     /**
57      * Construct a XML-RPC client with this URL.
58      */

59     public SimpleXmlRpcClient(URL JavaDoc url)
60     {
61         this.url = url;
62     }
63
64     /**
65      * Construct a XML-RPC client for the URL represented by this String.
66      */

67     public SimpleXmlRpcClient(String JavaDoc url) throws MalformedURLException JavaDoc
68     {
69         this.url = new URL JavaDoc(url);
70     }
71
72     /**
73      * Construct a XML-RPC client for the specified hostname and port.
74      */

75     public SimpleXmlRpcClient(String JavaDoc hostname, int port)
76             throws MalformedURLException JavaDoc
77     {
78         this.url = new URL JavaDoc("http://" + hostname + ":" + port + "/RPC2");
79     }
80     
81     /**
82      *
83      * @param method
84      * @param params
85      * @return
86      * @throws XmlRpcException
87      * @throws IOException
88      */

89     public Object JavaDoc execute(String JavaDoc method, Vector JavaDoc params)
90             throws XmlRpcException, IOException JavaDoc
91     {
92         return new XmlRpcSupport (url).execute (method, params);
93     }
94 }
95
96 /**
97  * FIXME: Leverage the XmlRpc class.
98  */

99 class XmlRpcSupport extends HandlerBase JavaDoc
100 {
101
102     URL JavaDoc url;
103     String JavaDoc methodName;
104     boolean fault = false;
105     Object JavaDoc result = null;
106     
107     Base64 base64 = new Base64();
108
109     // the stack we're parsing our values into.
110
Stack JavaDoc values;
111     Value currentValue;
112
113     boolean readCdata;
114
115     // formats for parsing and generating dateTime values
116
static final DateFormat JavaDoc format = new SimpleDateFormat JavaDoc("yyyyMMdd'T'HH:mm:ss");
117
118     // used to collect character data of parameter values
119
StringBuffer JavaDoc cdata = new StringBuffer JavaDoc ();
120
121     // XML RPC parameter types used for dataMode
122
static final int STRING = 0;
123     static final int INTEGER = 1;
124     static final int BOOLEAN = 2;
125     static final int DOUBLE = 3;
126     static final int DATE = 4;
127     static final int BASE64 = 5;
128     static final int STRUCT = 6;
129     static final int ARRAY = 7;
130
131     // for debugging output
132
public static boolean debug = false;
133     final static String JavaDoc types[] = {"String", "Integer", "Boolean", "Double",
134             "Date", "Base64", "Struct", "Array"};
135
136
137     /**
138      *
139      * @param url
140      */

141     public XmlRpcSupport(URL JavaDoc url)
142     {
143         this.url = url;
144     }
145
146     /**
147      * Switch debugging output on/off.
148      */

149     public static void setDebug(boolean val)
150     {
151         debug = val;
152     }
153
154     /**
155      * Parse the input stream. For each root level object, method
156      * <code>objectParsed</code> is called.
157      */

158     synchronized void parse(InputStream JavaDoc is) throws Exception JavaDoc
159     {
160         values = new Stack JavaDoc();
161         long now = System.currentTimeMillis();
162         MinML parser = new MinML();
163         parser.setDocumentHandler(this);
164         parser.setErrorHandler(this);
165
166         parser.parse(new InputSource JavaDoc(is));
167
168         if (debug)
169         {
170             System.out.println("Spent " + (System.currentTimeMillis() - now)
171                     + " parsing");
172         }
173     }
174
175     /**
176      * Writes the XML representation of a supported Java object to the XML writer.
177      */

178     void writeObject (Object JavaDoc what, XmlWriter writer) throws IOException JavaDoc
179     {
180         writer.startElement("value");
181         if (what instanceof String JavaDoc)
182         {
183             writer.write(what.toString());
184         }
185         else if (what instanceof Integer JavaDoc)
186         {
187             writer.startElement("int");
188             writer.write (what.toString());
189             writer.endElement("int");
190         }
191         else if (what instanceof Boolean JavaDoc)
192         {
193             writer.startElement("boolean");
194             writer.write(((Boolean JavaDoc) what).booleanValue() ? "1" : "0");
195             writer.endElement("boolean");
196         }
197         else if (what instanceof Double JavaDoc)
198         {
199             writer.startElement("double");
200             writer.write (what.toString());
201             writer.endElement("double");
202         }
203         else if (what instanceof Date JavaDoc)
204         {
205             writer.startElement("dateTime.iso8601");
206             Date JavaDoc d = (Date JavaDoc) what;
207             writer.write(format.format(d));
208             writer.endElement("dateTime.iso8601");
209         }
210         else if (what instanceof byte[])
211         {
212             writer.startElement("base64");
213             try
214             {
215                 writer.write((byte[]) base64.encode(what));
216             }
217             catch (EncoderException e)
218             {
219                 throw new RuntimeException JavaDoc("Possibly incompatible version " +
220                                            "of '" + Base64.class.getName() +
221                                            "' used: " + e);
222             }
223             writer.endElement("base64");
224         }
225         else if (what instanceof Vector JavaDoc)
226         {
227             writer.startElement("array");
228             writer.startElement("data");
229             Vector JavaDoc v = (Vector JavaDoc) what;
230             int l2 = v.size();
231             for (int i2 = 0; i2 < l2; i2++)
232             {
233                 writeObject(v.elementAt(i2), writer);
234             }
235             writer.endElement("data");
236             writer.endElement("array");
237         }
238         else if (what instanceof Hashtable JavaDoc)
239         {
240             writer.startElement("struct");
241             Hashtable JavaDoc h = (Hashtable JavaDoc) what;
242             for (Enumeration JavaDoc e = h.keys (); e.hasMoreElements (); )
243             {
244                 String JavaDoc nextkey = (String JavaDoc) e.nextElement ();
245                 Object JavaDoc nextval = h.get(nextkey);
246                 writer.startElement("member");
247                 writer.startElement("name");
248                 writer.write(nextkey);
249                 writer.endElement("name");
250                 writeObject(nextval, writer);
251                 writer.endElement("member");
252             }
253             writer.endElement("struct");
254         }
255         else
256         {
257             String JavaDoc unsupportedType = what == null ? "null"
258                     : what.getClass().toString();
259             throw new IOException JavaDoc("unsupported Java type: " + unsupportedType);
260         }
261         writer.endElement("value");
262     }
263
264     /**
265      * Generate an XML-RPC request and send it to the server. Parse the result
266      * and return the corresponding Java object.
267      *
268      * @exception XmlRpcException If the remote host returned a fault message.
269      * @exception IOException If the call could not be made for lower level
270      * problems.
271      */

272     public Object JavaDoc execute(String JavaDoc method, Vector JavaDoc arguments)
273             throws XmlRpcException, IOException JavaDoc
274     {
275         fault = false;
276         long now = System.currentTimeMillis();
277         try
278         {
279             StringBuffer JavaDoc strbuf = new StringBuffer JavaDoc();
280             XmlWriter writer = new XmlWriter(strbuf);
281             writeRequest(writer, method, arguments);
282             byte[] request = strbuf.toString().getBytes();
283             URLConnection JavaDoc con = url.openConnection();
284             con.setDoOutput(true);
285             con.setDoInput(true);
286             con.setUseCaches(false);
287             con.setAllowUserInteraction(false);
288             con.setRequestProperty("Content-Length",
289                     Integer.toString(request.length));
290             con.setRequestProperty("Content-Type", "text/xml");
291             // con.connect ();
292
OutputStream JavaDoc out = con.getOutputStream();
293             out.write(request);
294             out.flush();
295             InputStream JavaDoc in = con.getInputStream();
296             parse(in);
297             System.out.println("result = " + result);
298         }
299         catch (Exception JavaDoc x)
300         {
301             x.printStackTrace();
302             throw new IOException JavaDoc(x.getMessage());
303         }
304         if (fault)
305         {
306             // generate an XmlRpcException
307
XmlRpcException exception = null;
308             try
309             {
310                 Hashtable JavaDoc f = (Hashtable JavaDoc) result;
311                 String JavaDoc faultString = (String JavaDoc) f.get("faultString");
312                 int faultCode = Integer.parseInt(f.get("faultCode").toString());
313                 exception = new XmlRpcException(faultCode, faultString.trim());
314             }
315             catch (Exception JavaDoc x)
316             {
317                 throw new XmlRpcException(0, "Invalid fault response");
318             }
319             throw exception;
320         }
321         System.out.println("Spent " + (System.currentTimeMillis() - now)
322                 + " in request");
323         return result;
324     }
325
326     /**
327      * Called when the return value has been parsed.
328      */

329     void objectParsed(Object JavaDoc what)
330     {
331         result = what;
332     }
333
334     /**
335      * Generate an XML-RPC request from a method name and a parameter vector.
336      */

337     void writeRequest (XmlWriter writer, String JavaDoc method, Vector JavaDoc params)
338             throws IOException JavaDoc
339     {
340         writer.startElement("methodCall");
341         writer.startElement("methodName");
342         writer.write(method);
343         writer.endElement("methodName");
344         writer.startElement("params");
345         int l = params.size();
346         for (int i = 0; i < l; i++)
347         {
348             writer.startElement("param");
349             writeObject(params.elementAt (i), writer);
350             writer.endElement("param");
351         }
352         writer.endElement("params");
353         writer.endElement("methodCall");
354     }
355
356     ////////////////////////////////////////////////////////////////
357
// methods called by XML parser
358

359     /**
360      * Method called by SAX driver.
361      */

362     public void characters(char ch[], int start, int length)
363             throws SAXException JavaDoc
364     {
365         if (! readCdata)
366         {
367             return;
368         }
369         cdata.append (ch, start, length);
370     }
371
372     /**
373      * Method called by SAX driver.
374      */

375     public void endElement(String JavaDoc name) throws SAXException JavaDoc
376     {
377         if (debug)
378         {
379             System.err.println("endElement: " + name);
380         }
381
382         // finalize character data, if appropriate
383
if (currentValue != null && readCdata)
384         {
385             currentValue.characterData(cdata.toString());
386             cdata.setLength(0);
387             readCdata = false;
388         }
389
390         if ("value".equals(name))
391         {
392             int depth = values.size();
393             // Only handle top level objects or objects contained in arrays here.
394
// For objects contained in structs, wait for </member> (see code below).
395
if (depth < 2 || values.elementAt (depth - 2).hashCode () != STRUCT)
396             {
397                 Value v = currentValue;
398                 values.pop();
399                 if (depth < 2)
400                 {
401                     // This is a top-level object
402
objectParsed(v.value);
403                     currentValue = null;
404                 }
405                 else
406                 {
407                     // add object to sub-array; if current container is a struct, add later (at </member>)
408
currentValue = (Value) values.peek();
409                     currentValue.endElement(v);
410                 }
411             }
412         }
413
414         // Handle objects contained in structs.
415
if ("member".equals(name))
416         {
417             Value v = currentValue;
418             values.pop();
419             currentValue = (Value) values.peek();
420             currentValue.endElement(v);
421         }
422
423         else if ("methodName".equals(name))
424         {
425             methodName = cdata.toString();
426             cdata.setLength(0);
427             readCdata = false;
428         }
429     }
430
431     /**
432      * Method called by SAX driver.
433      */

434     public void startElement (String JavaDoc name, AttributeList JavaDoc atts)
435             throws SAXException JavaDoc
436     {
437         if (debug)
438         {
439             System.err.println("startElement: " + name);
440         }
441
442         if ("value".equals(name))
443         {
444             // System.err.println ("starting value");
445
Value v = new Value();
446             values.push(v);
447             currentValue = v;
448             // cdata object is reused
449
cdata.setLength(0);
450             readCdata = true;
451         }
452         else if ("methodName".equals(name))
453         {
454             cdata.setLength(0);
455             readCdata = true;
456         }
457         else if ("name".equals(name))
458         {
459             cdata.setLength(0);
460             readCdata = true;
461         }
462         else if ("string".equals(name))
463         {
464             // currentValue.setType (STRING);
465
cdata.setLength(0);
466             readCdata = true;
467         }
468         else if ("i4".equals(name) || "int".equals(name))
469         {
470             currentValue.setType(INTEGER);
471             cdata.setLength(0);
472             readCdata = true;
473         }
474         else if ("boolean".equals(name))
475         {
476             currentValue.setType(BOOLEAN);
477             cdata.setLength(0);
478             readCdata = true;
479         }
480         else if ("double".equals(name))
481         {
482             currentValue.setType(DOUBLE);
483             cdata.setLength(0);
484             readCdata = true;
485         }
486         else if ("dateTime.iso8601".equals(name))
487         {
488             currentValue.setType(DATE);
489             cdata.setLength(0);
490             readCdata = true;
491         }
492         else if ("base64".equals(name))
493         {
494             currentValue.setType(BASE64);
495             cdata.setLength(0);
496             readCdata = true;
497         }
498         else if ("struct".equals(name))
499         {
500             currentValue.setType(STRUCT);
501         }
502         else if ("array".equals(name))
503         {
504             currentValue.setType(ARRAY);
505         }
506     }
507
508     /**
509      *
510      * @param e
511      * @throws SAXException
512      */

513     public void error(SAXParseException JavaDoc e) throws SAXException JavaDoc
514     {
515         System.err.println("Error parsing XML: " + e);
516         // errorLevel = RECOVERABLE;
517
// errorMsg = e.toString ();
518
}
519
520     /**
521      *
522      * @param e
523      * @throws SAXException
524      */

525     public void fatalError(SAXParseException JavaDoc e) throws SAXException JavaDoc
526     {
527         System.err.println("Fatal error parsing XML: " + e);
528         // errorLevel = FATAL;
529
// errorMsg = e.toString ();
530
}
531
532     /**
533      * This represents an XML-RPC Value while the request is being parsed.
534      */

535     class Value
536     {
537         int type;
538         Object JavaDoc value;
539         // the name to use for the next member of struct values
540
String JavaDoc nextMemberName;
541
542         Hashtable JavaDoc struct;
543         Vector JavaDoc array;
544
545         /**
546          * Constructor.
547          */

548         public Value()
549         {
550             this.type = STRING;
551         }
552
553         /**
554          * Notification that a new child element has been parsed.
555          */

556         public void endElement(Value child)
557         {
558             if (type == ARRAY)
559             {
560                 array.addElement(child.value);
561             }
562             else if (type == STRUCT)
563             {
564                 struct.put(nextMemberName, child.value);
565             }
566         }
567
568         /**
569          * Set the type of this value. If it's a container, create the
570          * corresponding java container.
571          */

572         public void setType(int type)
573         {
574             // System.err.println ("setting type to "+types[type]);
575
this.type = type;
576             if (type == ARRAY)
577             {
578                 value = array = new Vector JavaDoc();
579             }
580             if (type == STRUCT)
581             {
582                 value = struct = new Hashtable JavaDoc();
583             }
584         }
585
586         /**
587          * Set the character data for the element and interpret it according to
588          * the element type
589          */

590         public void characterData (String JavaDoc cdata)
591         {
592             switch (type)
593             {
594                 case INTEGER:
595                     value = new Integer JavaDoc(cdata.trim());
596                     break;
597                 case BOOLEAN:
598                     value = new Boolean JavaDoc("1".equals(cdata.trim()));
599                     break;
600                 case DOUBLE:
601                     value = new Double JavaDoc(cdata.trim());
602                     break;
603                 case DATE:
604                     try
605                     {
606                         value = format.parse(cdata.trim());
607                     }
608                     catch (ParseException JavaDoc p)
609                     {
610                         // System.err.println ("Exception while parsing date: "+p);
611
throw new RuntimeException JavaDoc(p.getMessage());
612                     }
613                     break;
614                 case BASE64:
615                     try
616                     {
617                         value = base64.decode((Object JavaDoc) cdata.getBytes());
618                     }
619                     catch (DecoderException e) {
620                         /* FIXME: We should Probably throw an
621                          * Exception here. Punting because this class
622                          * is slated for complete overhaul using the
623                          * core library.
624                          */

625                         value = cdata;
626                     }
627                     break;
628                 case STRING:
629                     value = cdata;
630                     break;
631                 case STRUCT:
632                     // this is the name to use for the next member of this struct
633
nextMemberName = cdata;
634                     break;
635             }
636         }
637
638         /**
639          * This is a performance hack to get the type of a value without casting
640          * the Object. It breaks the contract of method hashCode, but it doesn't
641          * matter since Value objects are never used as keys in Hashtables.
642          */

643         public int hashCode ()
644         {
645             return type;
646         }
647
648         /**
649          *
650          * @return
651          */

652         public String JavaDoc toString ()
653         {
654             return (types[type] + " element " + value);
655         }
656     }
657
658     /**
659      * A quick and dirty XML writer.
660      * TODO: Replace with core package's XmlWriter class.
661      *
662      * @see <a
663      * HREF="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=28982">Bugzilla
664      * bug 28982</a>
665      */

666     class XmlWriter
667     {
668         StringBuffer JavaDoc buf;
669         String JavaDoc enc;
670
671         /**
672          *
673          * @param buf
674          */

675         public XmlWriter(StringBuffer JavaDoc buf)
676         {
677             this.buf = buf;
678             buf.append("<?xml version=\"1.0\"?>");
679         }
680
681         /**
682          *
683          * @param elem
684          */

685         public void startElement(String JavaDoc elem)
686         {
687             buf.append("<");
688             buf.append(elem);
689             buf.append(">");
690         }
691
692         /**
693          *
694          * @param elem
695          */

696         public void endElement(String JavaDoc elem)
697         {
698             buf.append("</");
699             buf.append(elem);
700             buf.append(">");
701         }
702
703         /**
704          *
705          * @param elem
706          */

707         public void emptyElement(String JavaDoc elem)
708         {
709             buf.append("<");
710             buf.append(elem);
711             buf.append("/>");
712         }
713
714         /**
715          *
716          * @param text
717          */

718         public void chardata(String JavaDoc text)
719         {
720             int l = text.length();
721             for (int i = 0; i < l; i++)
722             {
723                 char c = text.charAt(i);
724                 switch (c)
725                 {
726                     case '<' :
727                         buf.append("&lt;");
728                         break;
729                     case '>' :
730                         buf.append("&gt;");
731                         break;
732                     case '&' :
733                         buf.append("&amp;");
734                         break;
735                     default :
736                         buf.append(c);
737                 }
738             }
739         }
740
741         /**
742          *
743          * @param text
744          */

745         public void write(byte[] text)
746         {
747             // ### This may cause encoding complications for
748
// ### multi-byte characters. This should be properly
749
// ### fixed by implementing Bugzilla issue 28982.
750
for (int i = 0; i < text.length; i++)
751             {
752                 buf.append((char) text[i]);
753             }
754         }
755
756         /**
757          *
758          * @param text
759          */

760         public void write(char[] text)
761         {
762             buf.append(text);
763         }
764
765         /**
766          *
767          * @param text
768          */

769         public void write(String JavaDoc text)
770         {
771             buf.append(text);
772         }
773
774         /**
775          *
776          * @return
777          */

778         public String JavaDoc toString()
779         {
780             return buf.toString();
781         }
782
783         /**
784          *
785          * @return
786          * @throws UnsupportedEncodingException
787          */

788         public byte[] getBytes() throws UnsupportedEncodingException JavaDoc
789         {
790             return buf.toString().getBytes();
791         }
792     }
793 }
794
Popular Tags