processing.core.PRequest.java Source code

Java tutorial

Introduction

Here is the source code for processing.core.PRequest.java

Source

package processing.core;

/**
 * Android port of the Mobile Processing project - http://mobile.processing.org
 * 
 * The author of Mobile Processing is Francis Li (mail@francisli.com)
 *
 * 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
 *
 * @author Paul Gregoire (mondain@gmail.com)
 */

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;

import android.util.Log;

/**
 * The <b>PRequest</b> object represents an active network request. They are
 * returned by the methods in the <b>PClient</b> used to initiate network
 * requests. A request object can be in one of the states specified by the
 * constant values below. As the state of the request changes, library events
 * are fired to notify the sketch.
 * 
 * @category Net
 * @related PClient
 * 
 * @author Paul Gregoire (mondain@gmail.com)
 */
public class PRequest extends InputStream implements Runnable {

    private static final String tag = "PRequest";

    /**
     * Constant value representing that the request is being sent to the server,
     * waiting for reply
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int STATE_OPENED = 0;

    /**
     * Constant value representing that the request has been received and a
     * response is available.
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int STATE_CONNECTED = 1;

    /**
     * Constant value representing that the response is being fetched from the
     * server.
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int STATE_FETCHING = 2;

    /**
     * Constant value representing that the entire response has been read.
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int STATE_DONE = 3;

    /**
     * Constant value representing that an error occurred and the connection has
     * been closed.
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int STATE_ERROR = 4;

    /**
     * Constant value representing that connection has been closed and resources
     * have been released.
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int STATE_CLOSED = 5;

    /**
     * Event fired when the server has received the request and a response is
     * available.
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int EVENT_CONNECTED = 0;

    /**
     * Event fired when the entire response has been read and is available. The
     * data object will be an array of bytes (byte[]) containing the data.
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int EVENT_DONE = 1;

    /**
     * Event fired when an error has occurred. The data object will be a String
     * containing an error message.
     * 
     * @thisref PRequest
     * @thisreftext the PRequest class
     */
    public static final int EVENT_ERROR = 2;

    protected PMIDlet midlet;

    protected String url;
    protected String contentType;
    protected byte[] bytes;

    protected DefaultHttpClient client;
    protected HttpRequestBase request;

    protected InputStream is;

    protected String authorization;

    /**
     * The current state of the connection, as specified by the above constants
     * 
     * @thisref request
     * @thisreftext any variable of the type PRequest
     */
    public int state;

    /** @hidden */
    public PRequest(PMIDlet midlet, String url, String contentType, byte[] bytes, String authorization) {
        this.midlet = midlet;
        this.url = url;
        this.contentType = contentType;
        this.bytes = bytes;
        this.authorization = authorization;
    }

    /** @hidden */
    public void run() {
        try {
            if (client == null) {
                //instance the client
                client = new DefaultHttpClient();
                HttpParams params = client.getParams();
                //open connection to server
                //request type depends on content type var
                if (contentType != null) {
                    request = new HttpPost(url);
                    //which content types are posted?               
                    params.setParameter("Content-Type", contentType);
                    //create a byte array entity and add to post
                    if (bytes != null) {
                        ByteArrayEntity entity = new ByteArrayEntity(bytes);
                        ((HttpPost) request).setEntity(entity);
                        //we can release the request bytes and reuse the
                        // reference
                        bytes = null;
                    }
                } else {
                    request = new HttpGet(url);
                }
                if (authorization != null) {
                    params.setParameter("Authorization", authorization);
                }
                params.setParameter("Connection", "close");
                request.setParams(params);
                //
                client.execute(request, new StateResponseHandler<Integer>());
                // done, notify midlet
                boolean notify = false;
                synchronized (this) {
                    if (state == STATE_OPENED) {
                        state = STATE_CONNECTED;
                        notify = true;
                    }
                }
                if (notify) {
                    midlet.enqueueLibraryEvent(this, EVENT_CONNECTED, null);
                }
            } else {
                synchronized (this) {
                    if (state == STATE_CONNECTED) {
                        state = STATE_FETCHING;
                    } else {
                        throw new Exception("Not connected.");
                    }
                }
                // read the response
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int bytesRead = is.read(buffer);
                while (bytesRead >= 0) {
                    baos.write(buffer, 0, bytesRead);
                    bytesRead = is.read(buffer);
                }
                buffer = null;
                buffer = baos.toByteArray();
                // done, notify midlet
                boolean notify = false;
                synchronized (this) {
                    if (state == STATE_FETCHING) {
                        state = STATE_DONE;
                        notify = true;
                    }
                }
                if (notify) {
                    midlet.enqueueLibraryEvent(this, EVENT_DONE, buffer);
                }
            }
        } catch (Exception e) {
            boolean notify = false;
            synchronized (this) {
                if ((state == STATE_CONNECTED) || (state == STATE_FETCHING)) {
                    notify = true;
                }
            }
            close();
            if (notify) {
                synchronized (this) {
                    state = STATE_ERROR;
                }
                midlet.enqueueLibraryEvent(this, EVENT_ERROR, e.getMessage());
            }
        } finally {

        }
    }

    /**
     * Reads the next byte of data and returns it as an int.
     * 
     * @thisref request
     * @thisreftext any variable of the type PRequest
     * @return int
     */
    public int read() {
        try {
            return is.read();
        } catch (IOException ioe) {
            throw new PException(ioe);
        }
    }

    /**
     * Reads the next byte of data and returns it as a char.
     * 
     * @thisref request
     * @thisreftext any variable of the type PRequest
     * @return char
     */
    public char readChar() {
        return (char) read();
    }

    /**
     * Reads the rest of the response from the server. This method returns
     * immediately, and the download occurs in the background. While it is
     * downloading, the request will be in <b>STATE_FETCHING</b>. When it is
     * complete, the <b>EVENT_DONE</b> event will be fired back to the sketch.
     * 
     * @thisref request
     * @thisreftext any variable of the type PRequest
     * @return None
     */
    public void readBytes() {
        Thread t = new Thread(this);
        t.start();
    }

    /**
     * Closes the connection and releases the resources associated with this
     * request to the server.
     * 
     * @thisref request
     * @thisreftext any variable of the type PRequest
     * @return None
     */
    public void close() {
        synchronized (this) {
            state = STATE_CLOSED;
        }
        if (is != null) {
            try {
                is.close();
            } catch (IOException ioe) {
            }
            is = null;
        }
        if (client != null) {
            try {
                ClientConnectionManager mgr = client.getConnectionManager();
                mgr.closeExpiredConnections();
                mgr.closeIdleConnections(1, TimeUnit.SECONDS);
                mgr.shutdown();
            } catch (Exception ioe) {
            }
            client = null;
        }
    }

    @SuppressWarnings("hiding")
    private class StateResponseHandler<Integer> implements ResponseHandler<Integer> {

        @Override
        public Integer handleResponse(HttpResponse response) throws ClientProtocolException, IOException {
            //get the response code
            int statusCode = response.getStatusLine().getStatusCode();
            switch (statusCode) {
            case 200: //Ok
                //look for an entity
                HttpEntity entity = response.getEntity();
                if (entity instanceof ByteArrayEntity) {
                    bytes = EntityUtils.toByteArray(entity);
                    //create an input stream for reading our bytes
                    is = new ByteArrayInputStream(bytes);
                } else {
                    Log.d(tag, "Response: " + EntityUtils.toString(entity));
                }

                break;

            }
            return null;
        }

    }

}