Network.java

/*
 * Copyright © 2012 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation.
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners.
 * See LICENSE.TXT for license information.
 */

package com.nokia.example.weatherapp.network;

import java.io.*;
import javax.microedition.io.*;

/**
 * Provides asynchronous HTTP GET and POST operations.
 */
public class Network
        implements Runnable {

    private NetworkListener listener;
    private String url = null;
    private String request = null;
    private String contentType = null;
    private static boolean allowed = false;
    private static boolean prompted = false;

    public Network(NetworkListener listener) {
        this.listener = listener;
    }

    /**
     * Sends a GET request synchronously.
     * @param url Target of the request.
     * @return Data received
     * @throws NetworkError
     */
    private String sendHttpGet(String url) throws NetworkError {
        HttpConnection hcon = null;
        DataInputStream dis = null;
        ByteArrayOutputStream response = new ByteArrayOutputStream();

        try {
            // A standard HttpConnection with READ access
            hcon = (HttpConnection) Connector.open(urlEncode(url));

            if (hcon == null) {
                throw new NetworkError("No network access");
            }

            // Obtain a DataInputStream from the HttpConnection
            dis = new DataInputStream(hcon.openInputStream());

            // Retrieve the response from the server
            int ch;
            while ((ch = dis.read()) != -1) {
                response.write((byte) ch);
            }
        }
        catch (Exception e) {
            if (e instanceof SecurityException) {
                prompted = true;
                allowed = false;
            }
            throw new NetworkError(e.getMessage());
        }
        finally {
            try {
                if (hcon != null) {
                    hcon.close();
                }
                if (dis != null) {
                    dis.close();
                }
            }
            catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
        return response.toString();
    }

    /**
     * Sends a POST request synchronously.
     * @param url Target for the request.
     * @param request Request body
     * @throws NetworkError
     */
    private String sendHttpPost(String url, String request) throws NetworkError {
        HttpConnection hcon = null;
        DataInputStream dis = null;
        DataOutputStream dos = null;
        StringBuffer responseMessage = new StringBuffer();

        try {
            // An HttpConnection with both read and write access
            hcon = (HttpConnection) Connector.open(urlEncode(url), Connector.READ_WRITE);
            if (hcon == null) {
                throw new NetworkError("No network access");
            }

            // Set the request method to POST
            hcon.setRequestMethod(HttpConnection.POST);
            // Content-Type is must to pass parameters in POST Request
            hcon.setRequestProperty("Content-Type", contentType);

            // Obtain DataOutputStream for sending the request string
            dos = hcon.openDataOutputStream();
            byte[] request_body = request.getBytes();

            // Send request string to server
            for (int i = 0; i < request_body.length; i++) {
                dos.writeByte(request_body[i]);
            }

            // Obtain DataInputStream for receiving server response
            dis = new DataInputStream(hcon.openInputStream());

            // Retrieve the response from server
            int ch;
            while ((ch = dis.read()) != -1) {
                responseMessage.append((char) ch);
            }
        }
        catch (Exception e) {
            if (e instanceof SecurityException) {
                prompted = true;
                allowed = false;
            }
            throw new NetworkError(e.getMessage());
        }
        finally {
            // Free up i/o streams and http connection
            try {
                if (hcon != null) {
                    hcon.close();
                }
                if (dis != null) {
                    dis.close();
                }
                if (dos != null) {
                    dos.close();
                }
            }
            catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
        return responseMessage.toString();
    }

    /**
     * Thread entry point. Network operation is run in a separate thread.
     */
    public void run() {
        try {
            if (!isAllowed()) {
                throw new NetworkError("Network connection not allowed");
            }
            if (request == null) {
                String response = sendHttpGet(url);
                listener.networkHttpGetResponse(response);
            }
            else {
                String response = sendHttpPost(url, request);
                listener.networkHttpPostResponse(response);
            }
        }
        catch (NetworkError e) {
            if (request == null) {
                listener.networkHttpGetResponse(null);
            }
            else {
                listener.networkHttpPostResponse(null);
            }
        }
        catch (Exception e) {
        }
    }

    /**
     * Sends a HTTP POST request asynchronously, returns immediately and
     * reports later using the listener.
     * @param url Target url
     * @param request Body
     * @param contentType Content type
     */
    void startHttpPost(String url, String request, String contentType) {
        this.url = url;
        this.request = request;
        this.contentType = contentType;
        Thread thread = new Thread(this);
        thread.start();
    }

    /**
     * Sends a HTTP POST request asynchronously, returns immediately and
     * reports later using the listener.
     * @param url Target url
     * @param request Body
     * @param contentType Content type
     */
    void startHttpPost(String url, String request) {
        startHttpPost(url, request, "application/x-www-form-urlencoded");
    }

    /**
     * Sends a HTTP POST request asynchronously, returns immediately and
     * reports later using the listener.
     * @param url Target url
     */
    void startHttpPost(String url) {
        startHttpPost(url, "");
    }

    /**
     * Sends a HTTP GET request asynchronously, returns immediately and
     * reports later using the listener.
     * @param url Target url
     */
    public void startHttpGet(String url) {
        this.url = url;
        Thread thread = new Thread(this);
        thread.start();
    }

    /**
     * Simple url encoder
     * @param url
     * @return Encoded string
     */
    public static String urlEncode(String url) {
        StringBuffer encoded = new StringBuffer();
        for (int i = 0; i < url.length(); i++) {
            char ch = url.charAt(i);
            if (ch == '<') {
                encoded.append("%3C");
            }
            else if (ch == '>') {
                encoded.append("%3E");
            }
            else if (ch == ' ') {
                encoded.append("%20");
            }
            else if (ch == '-') {
                encoded.append("%2D");
            }
            else {
                encoded.append(ch);
            }
        }
        return encoded.toString();
    }

    public static boolean isAllowed() {
        if (!prompted) {
            prompted = true;
            promptNetworkAccess();
        }
        return allowed;

    }

    /**
     * Opens a network connection just to trigger the network prompt
     */
    private static void promptNetworkAccess() {
        HttpConnection stimulus = null;
        try {
            stimulus = (HttpConnection) Connector.open("http://promptNetworkAccess.com");
        }
        catch (SecurityException se) {
            allowed = false;
            return;
        }
        catch (Exception e) {
            // Catch all the other exceptions
        }
        finally {
            try {
                if (stimulus != null) {
                    stimulus.close();
                }
            }
            catch (IOException ioe) {
            }
        }
        allowed = true;
    }
}