1

I am trying to send a json string , from my BlackBerry OS < 7.X application to my Server. I am trying to use an HTTP Post request. What i have done so far is :

String httpURL = "http://ip_of_my_server/phpServer/receiver2.php/" + jsonString;

try {
    HttpConnection httpConn;
    httpConn = (HttpConnection) Connector.open(httpURL + getConnectionString());
    httpConn.setRequestMethod(HttpConnection.POST);
    httpConn.setRequestProperty("Content-Type", "application/json; charset=utf-8");
    DataOutputStream _outStream = new DataOutputStream(httpConn.openDataOutputStream());
    byte[] request_body = httpURL.getBytes();
    for (int i = 0; i < request_body.length; i++) {
        _outStream.writeByte(request_body[i]);
    }
    DataInputStream _inputStream = new DataInputStream(httpConn.openInputStream());
    StringBuffer _responseMessage = new StringBuffer();
    int ch;
    while ((ch = _inputStream.read()) != -1) {
        _responseMessage.append((char) ch);
    }
    String res = (_responseMessage.toString());
    String response = res.trim();
    System.out.println("!!!!!!!!!!!!!! Response is: " + response);
    httpConn.close();
} catch (Exception e) {
    Dialog.alert("Error - " + e.toString());
}

The code works in a way that i dont fully understand. The author of the above code suggested to use as an httpURL the URL of the server + my json string. The final result is that on my server instead of arriving the json string , is arriving a string like that :

http://ip_of_my_server/phpServer/receiver2.php/ + jsonString

I am not familiar with java. I have previously done this for WP7 and iOS and in the httpUrl i put my servers URL and then with a command i was "appending" my json string to the http request and everything worked as expected.

How can i append the json string to the above HttpRequest , instead of adding it to the URL so that in the server arrives the JSON String only?

EDIT (providing the rest of the code that was used)


//used to specify connection type ( wifi - 3g - etc )
public static String getConnectionString() {
    String connectionString = null;
    // Wifi is the preferred transmission method
    if (WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED) {
        connectionString = ";interface=wifi";
    }
    // Is the carrier network the only way to connect?
    else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_DIRECT) == CoverageInfo.COVERAGE_DIRECT) {
        String carrierUid = getCarrierBIBSUid();
        if (carrierUid == null) {
            // Has carrier coverage, but not BIBS. So use the carrier's TCP network
            connectionString = ";deviceside=true";
        } else {
            // otherwise, use the Uid to construct a valid carrier BIBS request
            connectionString = ";deviceside=false;connectionUID="+carrierUid + ";ConnectionType=mds-public";
        }
    }
    // Check for an MDS connection instead (BlackBerry Enterprise Server)
    else if ((CoverageInfo.getCoverageStatus() & CoverageInfo.COVERAGE_MDS) == CoverageInfo.COVERAGE_MDS) {
        connectionString = ";deviceside=false";
    }
    // If there is no connection available abort to avoid hassling the user unnecessarily
    else if (CoverageInfo.getCoverageStatus() == CoverageInfo.COVERAGE_NONE) {
        connectionString = "none";
    }
    // In theory, all bases are covered by now so this shouldn't be reachable.But hey, just in case ...
    else {
        connectionString = ";deviceside=true";
    }
    System.out.println("!!!!!!!!!!!!!! Connection type is: " + connectionString);
    return connectionString;
    }

/**
 * Looks through the phone's service book for a carrier provided BIBS
 * network
 * 
 * @return The uid used to connect to that network.
 */
private synchronized static String getCarrierBIBSUid() {
    ServiceRecord[] records = ServiceBook.getSB().getRecords();
    int currentRecord;

    for (currentRecord = 0; currentRecord < records.length; currentRecord++) {
        if (records[currentRecord].getCid().toLowerCase().equals("ippp")) {
            if (records[currentRecord].getName().toLowerCase()
                    .indexOf("bibs") >= 0) {
                return records[currentRecord].getUid();
            }
        }
    }

    return null;
}
donparalias
  • 1,834
  • 16
  • 37
  • 60
  • It's not a problem, but in addition to the answer(s) below, you can change the line that opens the output stream to `DataOutputStream _outStream = httpConn.openDataOutputStream();`. No need to wrap another stream around it. – Nate Apr 23 '13 at 22:46
  • 3
    I think the code being used by you is quite bad - like unnecessary use of DataOutputStream instead of OutputStream, wrapping of DataOutputStream inside another DataOutputStream, Not sending the content length, writing data byte by byte when you can write the entire byte array in one shot, reading the data as characters which is not UTF compliant and finally not using a finally to close the connection. – Adwiv Apr 24 '13 at 04:29

1 Answers1

7

The the first line should be simply your URL

String httpURL = "http://ip_of_my_server/phpServer/receiver2.php";

And you should only send the json string to the server as request.

instead of byte[] request_body = httpURL.getBytes();

use byte[] request_body = jsonString.getBytes();

Here is the method for OS 5.0 and above

public static HttpConnection getHttpConnection(String url, byte[] postData) {
    HttpConnection conn = null;
    OutputStream out = null;
    try {
        conn = (HttpConnection) new ConnectionFactory().getConnection(url).getConnection();
        if (conn != null) {
            if (postData == null) {
                conn.setRequestMethod(HttpConnection.GET);
                conn.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0");
            } else {
                conn.setRequestMethod(HttpConnection.POST);
                conn.setRequestProperty("Content-Length", String.valueOf(postData.length));
                conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                conn.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0");

                out = conn.openOutputStream();
                out.write(postData);
                out.flush();
            }
            if (conn.getResponseCode() != 0) {
                return conn;
            }
        }
    } catch (Exception e) {
    } finally {
        try {
            out.close();
        } catch (Exception e2) {
        }
    }

    //Only if exception occurs, we close the connection.
    //Otherwise the caller should close the connection himself.
    try {
        conn.close();
    } catch (Exception e1) {
    }
    return null;
}

Here is the complete class if you want it to work with OS 4.2 and above. You may need to replace the constant COVERAGE_DIRECT by its value 1, if you want to compile it with < 4.5.

public class ConnectionHelper {
    /**
     * Returns the working connection type. The connection types can be BIS, BES, TCP, WAP2, TCPIP
     */
    public static HttpConnection getHttpConnection(String url, byte[] postData) {
        int[] preferredOrder = new int[] { CONNECTION_WIFI, CONNECTION_BIS, CONNECTION_BES, CONNECTION_UNITE, CONNECTION_WAP2, CONNECTION_TCPIP, };

        for (int i = 0; i < preferredOrder.length; i++) {
            int type = preferredOrder[i];
            if (isPresent(type)) {
                HttpConnection conn = null;
                OutputStream out = null;
                try {
                    conn = (HttpConnection) Connector.open(convertURL(type, url));
                    if (conn != null) {
                        if (postData == null) {
                            conn.setRequestMethod(HttpConnection.GET);
                            conn.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0");
                        } else {
                            conn.setRequestMethod(HttpConnection.POST);
                            conn.setRequestProperty(HttpProtocolConstants.HEADER_CONTENT_LENGTH, String.valueOf(postData.length));
                            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                            conn.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0");

                            out = conn.openOutputStream();
                            out.write(postData);
                            out.flush();
                        }
                        if (conn.getResponseCode() != 0) {
                            return conn;
                        }
                    }
                } catch (Exception e) {
                } finally {
                    try {
                        out.close();
                    } catch (Exception e2) {
                    }
                }
            }
        }
        // Only if exception occurs, we close the connection.
        // Otherwise the caller should close the connection himself.
        try {
            conn.close();
        } catch (Exception e1) {
        }

        return null;
    }

    /** Stores transport ServiceBooks if found. Otherwise, null */
    private static ServiceRecord srMDS, srWiFi, srBIS, srWAP2, srUnite;

    private static final int CONNECTION_DEFAULT = 0;
    private static final int CONNECTION_BIS = 1;
    private static final int CONNECTION_BES = 2;
    private static final int CONNECTION_TCPIP = 3;
    private static final int CONNECTION_WIFI = 4;
    private static final int CONNECTION_WAP2 = 5;
    private static final int CONNECTION_UNITE = 6;

    private static final int CONFIG_TYPE_BES = 1;

    private static final String UNITE_NAME = "Unite";

    private static void checkTransportAvailability() {
        initializeTransportAvailability();
    }

    /**
     * Initializes the ServiceRecord instances for each transport (if available). Otherwise leaves it null. Also determines if sufficient coverage is available for each transport
     * and sets coverage* flags.
     */
    private static void initializeTransportAvailability() {
        ServiceBook sb = ServiceBook.getSB();
        ServiceRecord[] records = sb.getRecords();

        for (int i = 0; i < records.length; i++) {
            ServiceRecord myRecord = records[i];
            String cid, uid;

            if (myRecord.isValid() && !myRecord.isDisabled()) {
                cid = myRecord.getCid().toLowerCase();
                uid = myRecord.getUid().toLowerCase();

                // BIS
                if (cid.indexOf("ippp") != -1 && uid.indexOf("gpmds") != -1) {
                    srBIS = myRecord;
                }

                // BES
                if (cid.indexOf("ippp") != -1 && uid.indexOf("gpmds") == -1) {
                    srMDS = myRecord;
                }
                // WiFi
                if (cid.indexOf("wptcp") != -1 && uid.indexOf("wifi") != -1) {
                    srWiFi = myRecord;
                }

                // Wap2.0
                if (cid.indexOf("wptcp") != -1 && uid.indexOf("wifi") == -1 && uid.indexOf("mms") == -1) {
                    srWAP2 = myRecord;
                }

                // Unite
                if (getConfigType(myRecord) == CONFIG_TYPE_BES && myRecord.getName().equals(UNITE_NAME)) {
                    srUnite = myRecord;
                }
            }
        }
    }

    /**
     * Gets the config type of a ServiceRecord using getDataInt below
     * 
     * @param record
     *            A ServiceRecord
     * @return configType of the ServiceRecord
     */
    private static int getConfigType(ServiceRecord record) {
        return getDataInt(record, 12);
    }

    /**
     * Gets the config type of a ServiceRecord. Passing 12 as type returns the configType.
     * 
     * @param record
     *            A ServiceRecord
     * @param type
     *            dataType
     * @return configType
     */
    private static int getDataInt(ServiceRecord record, int type) {
        DataBuffer buffer = null;
        buffer = getDataBuffer(record, type);

        if (buffer != null) {
            try {
                return ConverterUtilities.readInt(buffer);
            } catch (EOFException e) {
                return -1;
            }
        }
        return -1;
    }

    /**
     * Utility Method for getDataInt()
     */
    private static DataBuffer getDataBuffer(ServiceRecord record, int type) {
        byte[] data = record.getApplicationData();
        if (data != null) {
            DataBuffer buffer = new DataBuffer(data, 0, data.length, true);
            try {
                buffer.readByte();
            } catch (EOFException e1) {
                return null;
            }
            if (ConverterUtilities.findType(buffer, type)) {
                return buffer;
            }
        }
        return null;
    }

    private static String convertURL(int connectionType, String url) {
        switch (connectionType) {
        case CONNECTION_BES:
            url += ";deviceside=false";
            break;
        case CONNECTION_BIS:
            url += ";deviceside=false" + ";ConnectionType=mds-public";
            break;
        case CONNECTION_TCPIP:
            url += ";deviceside=true";
            break;
        case CONNECTION_WIFI:
            url += ";interface=wifi";
            break;
        case CONNECTION_WAP2:
            url += ";deviceside=true;ConnectionUID=" + srWAP2.getUid();
            break;
        case CONNECTION_UNITE:
            url += ";deviceside=false;ConnectionUID=" + srUnite.getUid();
            break;
        }

        return url;
    }

    private static boolean isPresent(int connectionType) {
        checkTransportAvailability();

        switch (connectionType) {
        case CONNECTION_BIS:
            return (srBIS != null && CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_BIS_B));

        case CONNECTION_BES:
            return (srMDS != null && CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_MDS));

        case CONNECTION_WIFI:
            return (srWiFi != null && CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_DIRECT, RadioInfo.WAF_WLAN, false));

        case CONNECTION_TCPIP:
            return (CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_DIRECT));

        case CONNECTION_WAP2:
            return (srWAP2 != null && CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_DIRECT));

        case CONNECTION_UNITE:
            return (srUnite != null && CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_MDS));

        case CONNECTION_DEFAULT:
            return true;
        }

        return false;
    }
}

And finally post your data.

public void sendJson(String jsonString) {
    String httpURL = "http://ip_of_my_server/phpServer/receiver2.php";
    HttpConnection httpConn = null;

    try {
        httpConn = getHttpConnection(httpURL, jsonString.getBytes());

        if(httpConn.getResponseCode() == 200) {
            //If you need the output, then read it. Otherwise comment it.
            byte[] data = IOUtilities.streamToBytes(httpConn.openInputStream());
            String response = new String(data);
            System.out.println("!!!!!!!!!!!!!! Response is: " + response);
        }
    } catch (Exception e) {
    }
    finally {
        try {
            httpConn.close();
        } catch (Exception e2) {
        }
    }
}
Adwiv
  • 1,283
  • 9
  • 15
  • I don't know if it's strictly **necessary**, but the code I have that does this also includes the line `httpConn.setRequestProperty(HttpProtocolConstants.HEADER_CONTENT_LENGTH, String.valueOf(request_body.length));` before writing to `_outStream`. – Nate Apr 23 '13 at 22:44
  • Yes, It is a good practice to provide Content-Length since some servers would not accept the post request without it. However, the OP's server seems to work without the it. – Adwiv Apr 24 '13 at 04:24
  • Thank you very much for your answer adwiv , and for your useful comment @Nate , really appreciate it. Yes now works perfectly and is exactly what i was looking for. One more tip though if you like. The response variable that you see in the code is always null. Is it supposed to behave like this? Also i saw this answer here http://stackoverflow.com/questions/8969666/http-post-blackberry-null-response/8981963#8981963 where they take different kind of actions according to the response. Should i do smth like that here too? If yes how would it look like? – donparalias Apr 24 '13 at 08:01
  • @adwiv i also just read your comment , that the code is quite bad. I am very new to BB developing and i found most of that code. Could you provide a better one as you think it better , to do exactly the same thing? Thank you very much for your time :) – donparalias Apr 24 '13 at 08:04
  • I also edited my answer with the rest of the code that i used. Actually these are the functions that find the connection type. I guess these are ok , but you can take a look at them too just to be sure – donparalias Apr 24 '13 at 08:27
  • @donparalias, you should clarify what **minimum** OS version you need to support. If you don't have to support older than 5.0 (which is itself quite old), then [ConnectionFactory](http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/io/transport/ConnectionFactory.html) can make this easier. – Nate Apr 24 '13 at 08:36
  • well i need OS < 7.X and as lower as possible. I am not even sure the code i provided if is working for < 5.X – donparalias Apr 24 '13 at 08:47
  • Added a sample implementation that works with < 5.0 However, as nate said, in general it is not required to to below 5.0 OS nowdays. – Adwiv Apr 24 '13 at 14:33
  • Thank you very much adwiv! I was just told , that we ll need to support OS 5 minimum finally. If you have ready code for OS 5 , you could post it too , so that we have a general answer on the matter. Is is really easier as Nate suggested? – donparalias Apr 24 '13 at 15:12
  • @donparalias Added the code for OS5+ and the code to use it. Please note that you will have to change the `Content-Type` parameter as per your need. Currently the methods send the content type used for form data. – Adwiv Apr 24 '13 at 16:16
  • Incredible answer adwiv. Everything works perfectly. 2 more things. Do you know why the response variable seems to be empty when i print it? Should be smth there? And when my server is down , the app freezes for like 20secs , trying to connect , without success. I think thats the default timeOut time of the connection. Do you know how i could change that to , lets say 5 secs , so that the user wont have to see the application hung like that in the case my server is down? – donparalias Apr 25 '13 at 07:37
  • @donparalias First thing you should not run this connection in the event dispatcher Thread (eg on the click of a button) but you should rather start a new `Thread`. This way the the app will not freeze. The latency over the air can be sometimes very high even if your server is up. Regarding the response variable, are you sending any data from the server in response? – Adwiv Apr 25 '13 at 08:04
  • hmm i guess this is the problem then. I am trying to connect as soon as the app starts , but i am not sure how to make the connection on another thread , i should look on that. Hmm , not really i dont send smth back. I thought that the 200 OK response would be printed or smth like that. – donparalias Apr 25 '13 at 08:07