redstone.xmlrpc.XmlRpcClient.java Source code

Java tutorial

Introduction

Here is the source code for redstone.xmlrpc.XmlRpcClient.java

Source

/*
Copyright (c) 2007 Redstone Handelsbolag
    
This library is free software; you can redistribute it and/or modify it under the terms
of the GNU Lesser General Public License as published by the Free Software Foundation;
either version 2.1 of the License, or (at your option) any later version.
    
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
    
You should have received a copy of the GNU Lesser General Public License along with this
library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA  02111-1307  USA
 */

package redstone.xmlrpc;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnRoutePNames;
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.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;

import cn.wiz.sdk.util.WizMisc;

import android.content.Context;

/**
 * An XmlRpcClient represents a connection to an XML-RPC enabled server. It
 * implements the XmlRpcInvocationHandler so that it may be used as a relay to
 * other XML-RPC servers when installed in an XmlRpcServer.
 * 
 * @author Greger Olsson
 */
@SuppressWarnings({ "unchecked", "rawtypes" })
public class XmlRpcClient extends XmlRpcParser implements XmlRpcInvocationHandler {
    /**
     * Creates a new client with the ability to send XML-RPC messages to the the
     * server at the given URL.
     * 
     * @param url
     *            the URL at which the XML-RPC service is locaed
     * 
     * @throws MalformedURLException
     */

    private Context mContext;

    public XmlRpcClient(Context ctx, String url) throws MalformedURLException {
        this(new URL(url));
        mContext = ctx;
    }

    /**
     * @see XmlRpcClient(String,boolean)
     */

    public XmlRpcClient(URL url) {
        this.url = url;
        writer = new StringWriter(2048);
    }

    /**
     * Sets the HTTP request properties that the client will use for the next
     * invocation, and any invocations that follow until setRequestProperties()
     * is invoked again. Null is accepted and means that no special HTTP request
     * properties will be used in any future XML-RPC invocations using this
     * XmlRpcClient instance.
     * 
     * @param requestProperties
     *            The HTTP request properties to use for future invocations made
     *            using this XmlRpcClient instance. These will replace any
     *            previous properties set using this method or the
     *            setRequestProperty() method.
     */

    public void setRequestProperties(Map requestProperties) {
        this.requestProperties = requestProperties;
    }

    /**
     * Sets a single HTTP request property to be used in future invocations.
     * 
     * @see setRequestProperties()
     * 
     * @param name
     *            Name of the property to set
     * @param value
     *            The value of the property
     */

    public void setRequestProperty(String name, String value) {
        if (requestProperties == null) {
            requestProperties = new HashMap();
        }

        requestProperties.put(name, value);
    }

    /**
     * Invokes a method on the terminating XML-RPC end point. The supplied
     * method name and argument collection is used to encode the call into an
     * XML-RPC compatible message.
     * 
     * @param method
     *            The name of the method to call.
     * 
     * @param arguments
     *            The arguments to encode in the call.
     * 
     * @return The object returned from the terminating XML-RPC end point.
     * 
     * @throws XmlRpcException
     *             One or more of the supplied arguments are unserializable.
     *             That is, the built-in serializer connot parse it or find a
     *             custom serializer that can. There may also be problems with
     *             the socket communication.
     * @throws
     */

    public synchronized Object invoke(String method, List arguments) throws XmlRpcException, XmlRpcFault {
        beginCall(mContext, method);

        if (arguments != null) {
            Iterator argIter = arguments.iterator();

            while (argIter.hasNext()) {
                try {
                    writer.write("<param>");
                    serializer.serialize(argIter.next(), writer);
                    writer.write("</param>");
                } catch (IOException ioe) {
                    throw new XmlRpcNetworkException(XmlRpcMessages.getString("XmlRpcClient.NetworkError"), ioe);
                }
            }
        }

        return endCall(mContext, method);
    }

    /**
     * Invokes a method on the terminating XML-RPC end point. The supplied
     * method name and argument vector is used to encode the call into XML-RPC.
     * 
     * @param method
     *            The name of the method to call.
     * 
     * @param arguments
     *            The arguments to encode in the call.
     * 
     * @return The object returned from the terminating XML-RPC end point.
     * 
     * @throws XmlRpcException
     *             One or more of the supplied arguments are unserializable.
     *             That is, the built-in serializer connot parse it or find a
     *             custom serializer that can. There may also be problems with
     *             the socket communication.
     */
    final static public String ApiMethodName_DownloadAttachmentData = "attachment.getMobileData";

    public synchronized Object invoke(String method, Object[] arguments) throws XmlRpcException, XmlRpcFault {

        beginCall(mContext, method);

        if (arguments != null) {
            for (int i = 0; i < arguments.length; ++i) {
                try {
                    writer.write("<param>");
                    // hashMap?xml
                    // if (method.equals("attachment.postSimpleData")
                    // || method == "attachment.postSimpleData") {
                    //
                    // serializer.serializeattachment(arguments[i], writer);
                    // }else {

                    serializer.serialize(arguments[i], writer);
                    // }
                    writer.write("</param>");
                } catch (IOException ioe) {
                    throw new XmlRpcNetworkException(XmlRpcMessages.getString("XmlRpcClient.NetworkError"), ioe);
                }
            }
        }

        return endCall(mContext, method);
    }

    /**
     * A asynchronous version of invoke performing the call in a separate thread
     * and reporting responses, faults, and exceptions through the supplied
     * XmlRpcCallback. Determine on proper strategy for instantiating Threads.
     * 
     * @param method
     *            The name of the method at the server.
     * 
     * @param arguments
     *            The arguments for the call. This may be either a
     *            java.util.List descendant, or a java.lang.Object[] array.
     * 
     * @param callback
     *            An object implementing the XmlRpcCallback interface. If
     *            callback is null, the call will be performed but any results,
     *            faults, or exceptions will be ignored (fire and forget).
     */

    public void invokeAsynchronously(final String method, final Object arguments, final XmlRpcCallback callback) {
        if (callback == null) {
            new Thread() {
                public void run() {
                    try // Just fire and forget.
                    {
                        if (arguments instanceof Object[])
                            invoke(method, (Object[]) arguments);
                        else
                            invoke(method, (List) arguments);
                    } catch (XmlRpcFault e) { /* Ignore, no callback. */
                    } catch (XmlRpcException e) { /* Ignore, no callback. */
                    }
                }
            }.start();
        } else {
            new Thread() {
                public void run() {
                    Object result = null;

                    try {
                        if (arguments instanceof Object[])
                            result = invoke(method, (Object[]) arguments);
                        else
                            result = invoke(method, (List) arguments);

                        callback.onResult(result);
                    } catch (XmlRpcException e) {
                        callback.onException(e);
                    } catch (XmlRpcFault e) {
                        callback.onFault(e.getErrorCode(), e.getMessage());
                    }
                }
            }.start();
        }
    }

    /**
     * Initializes the XML buffer to be sent to the server with the XML-RPC
     * content common to all method calls, or serializes it directly over the
     * writer if streaming is used. The parameters to the call are added in the
     * execute() method, and the closing tags are appended when the call is
     * finalized in endCall().
     * 
     * @param methodName
     *            The name of the method to call.
     */

    // xml?
    private void beginCall(Context ctx, String methodName) throws XmlRpcException {

        writer.getBuffer().setLength(0);

        /**
         * xml
         */
        writer.write("<?xml version=\"1.0\" encoding=\"utf-8");
        writer.write("\"?>");

        writer.write("<methodCall><methodName>");
        writer.write(methodName);
        writer.write("</methodName><params>");
    }

    /**
     * Finalizaes the XML buffer to be sent to the server, and creates a HTTP
     * buffer for the call. Both buffers are combined into an XML-RPC message
     * that is sent over a socket to the server.
     * 
     * @return The parsed return value of the call.
     * @throws IOException
     * @throws ClientProtocolException
     * 
     * @throws XmlRpcException
     *             when some IO problem occur.
     */

    public HttpResponse doPost(DefaultHttpClient client, String url, String data)
            throws ClientProtocolException, IOException {
        HttpPost httppost = new HttpPost(url);
        httppost.addHeader("Content-Type", "text/xml");
        //
        httppost.setEntity(new StringEntity(data, "UTF-8"));
        HttpResponse response = client.execute(httppost);
        return response;
    }

    private Object endCall(Context ctx, String method) throws XmlRpcException, XmlRpcFault {
        try {
            writer.write("</params>");
            writer.write("</methodCall>");

            StringBuffer buffer = ((StringWriter) writer).getBuffer();
            //         
            //         try {
            //            FileUtil.saveTextToFile(FileUtil.getCacheRootPath(ctx) + method + ".xml", buffer.toString(), "UTF-8");
            //         } catch (Exception e) {
            //         }
            //
            // 
            DefaultHttpClient client = getConnection(ctx);
            //
            HttpResponse response = doPost(client, this.url.toString(), buffer.toString());
            if (response == null) {
                String errorMessage = XmlRpcMessages.getString("XmlRpcClient.NetworkError");
                throw new XmlRpcNetworkException(errorMessage);
            }
            //
            int retCode = response.getStatusLine().getStatusCode();
            if (retCode != 200) {
                String errorMessage = XmlRpcMessages.getString("XmlRpcClient.NetworkErrorTimeout");
                errorMessage = errorMessage.replaceAll("%1", Integer.toString(retCode));
                throw new XmlRpcNetworkException(errorMessage);
            }
            //
            HttpEntity httpEntity = response.getEntity();
            //
            InputStream is = httpEntity.getContent();
            try {
                handleResponse(method, is);
            } finally {
                if (is != null) {
                    is.close();
                }
            }

        } catch (ClientProtocolException err) {
            err.printStackTrace();
            throw new XmlRpcNetworkException(err.getMessage());
        } catch (java.net.SocketTimeoutException err) {
            throw new XmlRpcNetworkException(XmlRpcMessages.getString("XmlRpcClient.NetworkErrorTimeout"));
        } catch (org.apache.http.conn.ConnectTimeoutException err) {
            throw new XmlRpcNetworkException(err.getMessage());
        } catch (IOException err) {
            err.printStackTrace();
            throw new XmlRpcNetworkException(err.getMessage());
        } finally {
        }

        return returnValue;
    }

    /**
     * Handles the response returned by the XML-RPC server. If the server
     * responds with a "non-200"-HTTP response or if the XML payload is
     * unparseable, this is interpreted as an error in communication and will
     * result in an XmlRpcException.
     * <p>
     * 
     * If the user does not want the socket to be kept alive or if the server
     * does not support keep-alive, the socket is closed.
     * 
     * @param inout
     *            The stream containing the server response to interpret.
     * 
     * @throws IOException
     *             If a socket error occurrs, or if the XML returned is
     *             unparseable. This exception is currently also thrown if a
     *             HTTP response other than "200 OK" is received.
     * @throws XmlRpcFault
     */

    private void handleResponse(String method, InputStream is) throws XmlRpcFault {
        try {
            BufferedInputStream bis = new BufferedInputStream(is, 1024 * 8);
            parse(method, bis);
        } catch (XmlRpcException e) {
            throw e;
        } catch (XmlRpcFault e) {
            throw e;
        } catch (Exception e) {
            throw new XmlRpcException(XmlRpcMessages.getString("XmlRpcClient.ParseError"), e);
        }

    }

    /**
     * Stores away the one and only value contained in XML-RPC responses.
     * 
     * @param value
     *            The contained return value.
     */

    protected void handleParsedValue(Object value) {
        returnValue = value;
    }

    /**
     * Opens a connection to the URL associated with the client instance. Any
     * HTTP request properties set using setRequestProperties() are recorded
     * with the internal HttpURLConnection and are used in the HTTP request.
     * 
     * @throws IOException
     *             If a connection could not be opened. The exception is
     *             propagated out of any unsuccessful calls made into the
     *             internal java.net.HttpURLConnection.
     */

    private static final int REQUEST_TIMEOUT = 30 * 1000;// 30
    private static final int SO_TIMEOUT = 30 * 1000; // ?30

    static private DefaultHttpClient _client = null;

    static private synchronized DefaultHttpClient getConnection(Context ctx) {
        if (_client == null) {
            BasicHttpParams httpParams = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(httpParams, REQUEST_TIMEOUT);
            HttpConnectionParams.setSoTimeout(httpParams, SO_TIMEOUT);

            SchemeRegistry schemeRegistry = new SchemeRegistry();
            schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
            ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager(httpParams, schemeRegistry);

            _client = new DefaultHttpClient(manager, httpParams);
            //
            if (WizMisc.isCMWAP(ctx)) {
                HttpHost proxy = new HttpHost("10.0.0.172", 80);
                _client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
            }
        }
        //
        return _client;

    }

    public void disconnect() {
        if (writer != null) {
            try {
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /** The server URL. */
    private URL url;

    /**
     * HTTP request properties, or null if none have been set by the
     * application.
     */
    private Map requestProperties;

    /** The parsed value returned in a response. */
    private Object returnValue;

    /** Writer to which XML-RPC messages are serialized. */
    private StringWriter writer;

    /**
     * Indicates wheter or not we shall stream the message directly or build
     * them locally?
     */
    /** Indicates whether or not the incoming response is a fault response. */

    /** The serializer used to serialize arguments. */
    private XmlRpcSerializer serializer = new XmlRpcSerializer();
}