The Networking Application BLACKBERRY

Create a new BlackBerry application project called Networking. Create the application and main screen classes in the com.beginningblackberry.networking package as follows:

NetworkingApplication.java:
package com.beginningblackberry.networking;
import net.rim.device.api.ui.UiApplication;
public class NetworkingApplication extends UiApplication {
public NetworkingApplication() {
NetworkingMainScreen scr = new NetworkingMainScreen();
pushScreen(scr);
}
public static void main(String[] args) {
NetworkingApplication application = new NetworkingApplication();
application.enterEventDispatcher();
}
}
NetworkingMainScreen.java:
package com.beginningblackberry.networking;
import net.rim.device.api.ui.container.MainScreen;
public class NetworkingMainScreen extends MainScreen {
public NetworkingMainScreen() {
}
}

Some Controls
The first functionality we’ll build is the capability to get images and web page text from the Web. We’ll add a few controls here. First, an edit field to enter the URL, and then a couple of fields to display resources that the application fetches: a BitmapField and another new control called RichTextField.Make the following changes to NetworkingMainScreen.java:

private EditField urlField;
private BitmapField imageOutputField;
private RichTextField textOutputField;
public NetworkingMainScreen() {
setTitle("Networking");
urlField = new EditField("URL:", "");
textOutputField = new RichTextField();
imageOutputField = new BitmapField();
add(urlField);
add(new SeparatorField());
add(new LabelField("Image retrieved:"));
add(imageOutputField);
add(new SeparatorField());
add(new LabelField("Text retrieved:"));
add(textOutputField);
}

RichTextField is a powerful control. It enables you to display a lot of text on multiple lines with built-in line wrapping and different styles for different parts of the text. For this application, we’ll take advantage of the multiline and line wrapping capabilities of RichTextField to display the text result of the network requests.We need to add a separate LabelField because RichTextField has no support for a built-in label. Next, add a menu item and skeleton method to initiate the HTTP request to NetworkingMainScreen:

private void getURL(){
}
protected void makeMenu(Menu menu, int instance) {
super.makeMenu(menu, instance);
menu.add(new MenuItem("Get", 10, 10) {
public void run() {
getURL();
}
});
}

Making an HTTP Connection
Now let’s get to the details of how to make the request. First, note one critical issue. Remember back in the early part of the book when we discussed the event thread? That information is important now. When networking on the BlackBerry, always remember the following:

Never perform a network operation on the event thread. Earlier, we said that it’s a bad idea to do anything that can take a lot of time on the event thread because that would have the effect of freezing the user interface, making the user think the application had hung or crashed. In the case of networking, the situation is worse. Depending on the device and configuration, the API often throws an exception if you try to initiate a network connection from the event thread.

The HttpRequestDispatcher Class
We need to create a separate thread for the HTTP request. Do this by creating a new class that extends java.lang.Thread. Call the class HttpRequestDispatcher:

public class HttpRequestDispatcher extends Thread {
private String url;
private String method; // GET or POST
private NetworkingMainScreen screen;
public HttpRequestDispatcher(String url, String method, NetworkingMainScreen screen)
{
this.url = url;
this.method = method;
this.screen = screen;
}
public void run() {
}
}

Notice we’re passing in an instance of the main screen.This is to give us a way to update the screen when a request succeeds or fails. We’ll need to add a couple of methods to let us send these notifications. Add the following method skeletons to NetworkingMainScreen. We’ll fill them in later:

public class NetworkingMainScreen extends MainScreen {
// ...
public void requestSucceeded(byte[] result, String contentType) {
}
public void requestFailed(String message) {
}

If we wanted to make our HttpRequestDispatcher more general purpose we’d define an interface containing the previous methods for NetworkingMainScreen to implement. Because we’re just illustrating basic concepts here, we’ve elected to use NetworkingMainScreen directly and eliminate the extra java file that an interface would require.

The Run Method

We will perform only GET requests at first. There will be an extra line or two for POST requests. All the work is done in the run method:

public void run() {
try {
HttpConnection connection = (HttpConnection)Connector.open(url);
connection.setRequestMethod(method);
int responseCode = connection.getResponseCode();
if (responseCode != HttpConnection.HTTP_OK) {
screen.requestFailed("Unexpected response code: " + responseCode);
connection.close();
return;
}
String contentType = connection.getHeaderField("Content-type");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream responseData = connection.openInputStream();
byte[] buffer = new byte[10000];
int bytesRead = responseData.read(buffer);
while(bytesRead > 0) {
baos.write(buffer, 0, bytesRead);
bytesRead = responseData.read(buffer);
}
baos.close();
connection.close();
screen.requestSucceeded(baos.toByteArray(), contentType);
} catch (IOException ex) {
screen.requestFailed(ex.toString());
}
}

The first couple of lines set up the connection parameters:

HttpConnection connection = (HttpConnection)Connector.open(url);
connection.setRequestMethod(method);

As mentioned previously, you get an HttpConnection back from Connector.open only if the URL starts with http://. Be sure to type that into the text field when using the application!

At this point in the run method, network activity has not occurred yet.The connection is still in the setup state. HttpConnection doesn’t actually start a connection until you ask for some data that it needs to request from the server. This is useful to remember because there’s often a noticeable delay in initiating a network connection, and in this case, it’ll happen when the next line is called:

int responseCode = connection.getResponseCode();

The response code is just the standard HTTP response code value: 200 if the request succeeded and some other value if it didn’t (it’s slightly more complicated,but for our purposes, that’s fine). Test the response code and if it’s not 200 (HttpConnection.HTTP_OK), notify the main screen and stop:

if (responseCode != HttpConnection.HTTP_OK) {
screen.requestFailed("Unexpected response code: " + responseCode);
connection.close();
return;
}

Next, retrieve the value of the Content-type response header. Getting a header is another method that would initiate the connection if we hadn’t already called getResponseCode:

String contentType = connection.getHeaderField("Content-type");

Finally, open the connection’s input stream and read the data into a buffer. We use a ByteArrayOutputStream as a convenient way to buffer the bytes from the input stream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream responseData = connection.openInputStream();
byte[] buffer = new byte[10000];
int bytesRead = responseData.read(buffer);
while(bytesRead > 0) {
baos.write(buffer, 0, bytesRead);
bytesRead = responseData.read(buffer);
}
baos.close();
connection.close();

Finally, we pass the data along with the content type back to the main screen to deal with the following:

screen.requestSucceeded(baos.toByteArray(), contentType);

Initiating the Connection

The getURL method requires only a couple of lines to initiate the connection:

private void getURL() {
HttpRequestDispatcher dispatcher = new HttpRequestDispatcher(urlField.getText(),
"GET", this);
dispatcher.start();
}

Displaying the Response Failed Notification

Now let’s start modifying NetworkingMainScreen to handle the results of the HTTP request. First, we’ll have requestFailed display a dialog box when called. Remember, this method is being called from a different thread than the event thread, so we can’t directly call Dialog.alert. Instead, we’ll use UiApplication.invokeLater to let the event thread display the dialog at the next available opportunity:

public void requestFailed(final String message){
UiApplication.getUiApplication().invokeLater(new Runnable(){
public void run() {
Dialog.alert("Request failed. Reason: " + message);
}
});
}

NOTE: If you’re paying close attention you’ll notice one change to the method signature: we made the message parameter final. This is necessary because we use it inside an anonymous inner class (the Runnable that we create).Final just means that we’re not allowed to do something like this in the body of the requestFailed method:

message = ""; // ERROR – will not work!

Testing It

We’re now at a stage where we can see a network connection happen: by typing in a URL to a page that doesn’t exist, we’ll get a failed notification and see a dialog. Before we run the application in the simulator, there’s an additional topic you need to know—the MDS simulator.

The MDS Simulator

By default, the BlackBerry makes connections using the BES/MDS method. To let you test this, the JDE and Eclipse Plug-in include an MDS simulator. This needs to be running to make MDS-enabled connections from your simulator.Fortunately, running the MDS simulator is easy. In the JDE Plug-in for Eclipse, open your debug configuration, and on the Simulator tab, ensure Launch Mobile Data SystemConnection Service (MDS-CS) with simulator is enabled as shown in Figure.

Enabling the MDS simulator from the Eclipse plug-in

Enabling the MDS simulator from the Eclipse plug-in

Similarly, from the JDE, open the Preferences dialog and under the Simulator/General tab, select the same option;for the JDE Preferences dialog.

Enabling the MDS simulator from the JDE

Enabling the MDS simulator from the JDE

NOTE: Some versions of the JDE use the term MDS and some use MDS-CS. For our purposes, they are the same thing.

Launching the MDS Manually

If you have the standalone JDE installed, you can also launch the MDS simulator from the Start menu. It’s common to have to restart the MDS simulator from time to time when debugging your applications; sometimes, it can take a few attempts to get it to connect. If you cannot perform a network connection, simply stop and restart both the MDS and Device simulators. The MDS that comes with the JDE will work fine with the MDS that comes with the JDE Plug-in for Eclipse, so if you have both environments installed, you can run the MDS from the JDE and run your simulator from Eclipse.

Running the Application

Start your debug session. Along with the simulator, you’ll see a command prompt window open running the MDS.

The MDS simulator running

The MDS simulator running

Let’s test the requestFailed method by making a request for a page that doesn’t exist. Start the application, in the URL field and then open the menu and click Get, as shown in Figure.

Type a URL to a nonexistent page, and then click Get.

Type a URL to a nonexistent page, and then click Get.

After a momentary delay, you should see a dialog informing you that the request failed with a response code of 404, which is HTTP speak for “not found,” as shown in Figure.

URL not found

URL not found

Believe it or not, this is a good result.The 404 error is sent by the server, meaning we’ve successfully made a network connection! Next, we’ll fill in the details to handle a request for a resource that actually exists.

Handling Successful Requests

We’ll do the following things in the requestSucceeded method:

  • heck the contentType.
  • f the content type is an image (image/png, image/jpeg, mage/gif), decode the image and display it.
  • if the content type is text (text/plain, text/html, or text/anything), display the text in the RichTextField.
  • Otherwise, display an error message.

The code for this is actually imple. Again, this method is called from outside the event thread so you have to be sure to get the event lock before you manipulate the UI:

public void requestSucceeded(byte[] result, String contentType) {
if (contentType.equals("image/png") ||
contentType.equals("image/jpeg") ||
contentType.equals("image/gif")) {
Bitmap bitmap = Bitmap.createBitmapFromBytes(result, 0, result.length, 1);
synchronized (UiApplication.getEventLock()) {
imageOutputField.setBitmap(bitmap);
}
}
else if (contentType.startsWith("text/")) {
String strResult = new String(result);
synchronized (UiApplication.getEventLock()) {
textOutputField.setText(strResult);
}
}
else {
synchronized (UiApplication.getEventLock()) {
Dialog.alert("Unknown content type: " + contentType);
}
}
}

Everything is straightforward. The only thing to note is that we used three separate synchronized blocks instead of making the entire method synchronized. Generally, this is a good UI programming principle. We minimize the amount of work done in the synchronized blocks so we can minimize the impact to the user experience. In this case, it probably wouldn’t be noticeable, but if we encoded or scaling scaled a very large image or large amount of text, putting all that into the synchronized block would lock up the event thread for longer.

Try It

Now, run the application again. First, try the URL for the main test web application: Enter the URL, and in the menu, click Get. You should see a lot of HTML in the text area.

Retrieving the HTML of the test web application

Retrieving the HTML of the test web application

When we opened an HttpConnection the server sent back “text/html” as the content type, so the method interpreted the data as a string and put the text into the RichTextField. Now let’s verify that the code for retrieving an image works, too. Because you can see the URL to the apress_logo image in the HTML—img/apress_logo.png—you just need to add that to the end of the URL Then, select Get again and you’ll see a result like what you see in Figure.

Retrieving the logo from the web application

Retrieving the logo from the web application

In this case, the server sent back image/png as the content-type, so the code interpreted the data from the input stream as the bytes for an image and successfully decoded and displayed it.

Two-Way Interaction: Sending Data via HTTP POST

Now we’ll complete our exploration of HTTP using the BlackBerry by sending some data to the web application using an HTTP POST. Remember that the web application takesa series of words separated by spaces and returns the same list of words in reverse order but separated by new lines and HTML break (br) tags.

How an HTML Form Works

You might already know this, but let’s review how a POST from an HTML form in a browser works. We will duplicate this functionality in the Networking application. The web application contains this HTML:

<form action="/" method="POST">
<input type="text" name="content"></input>
<input type="submit" value="Go!"/>
</form>

This defines a form that the browser uses to send data to the web application.Specifically, the first line says to send the data via HTTP POST to the URL “/,” which is just the base URL of the web application. The input type=”submit” defines the Go button as the button that invokes the POST. Finally, the input type=”text” line defines the text box and gives it the name content. The web application expects the body of the POST request to contain something like the following: content=ONE+TWO+THREE The “+” characters are a way of encoding spaces in the input. We have to do this, too. In addition, the content type header in the request to the server should be application/xwww- form-urlencoded to indicate that the content is encoded in this way.

Modifying HttpRequestDispatcher

Most of the code to perform a POST is the same as to perform a GET, so we’ll just modify the run method of HttpRequestDispatcher to handle both. First, we need a way to pass the POST body to HttpRequestDispatcher.Create a new member variable called postData and a new constructor so we can initialize it:

private byte[] postData;
public HttpRequestDispatcher(String url, String method,
NetworkingMainScreen screen, byte[] postData) {
this.url = url;
this.method = method;
this.screen = screen;
this.postData = postData;
}

Next, we need to check if we have post data to send before initiating the connection. If we do, we’ll set the content-type header by using Http Connection. set Request Property, and then open an output stream for the connection and write the data. Modify the run method by adding the following lines:

if (method.equals("POST") && postData != null) {
connection.setRequestProperty("Content-type", "application/xwww-
form-urlencoded");
OutputStream requestOutput = connection.openOutputStream();
requestOutput.write(postData);
requestOutput.close();
}

Everything else should stay the same. We’ll handle the response the same way by calling requestSucceeded in NetworkingMainScreen.

Modifying NetworkingMainScreen

We need two things in our screen: an edit field to enter the post data and a way to invoke the Post request (a menu item and method). Add the edit field first. Declare a new EditField called postDataField: private EditField postDataField; Then, initialize it and position it right below the URL field:

add(urlField);
add(new SeparatorField());
postDataField = new EditField("Post data:", "");
add(postDataField);
add(new SeparatorField());
add(new LabelField("Image retrieved:"));
add(imageOutputField);

Next, define the postURL method. It does the same thing as the getURL method with the additional functionality of taking the text from the post data edit field and encoding it for the body of the post.We’ll use the class net.rim.blackberry.api.browser.URLEncodedPostData to do the actual encoding and formatting of the data for the request body:

private void postURL() {
String postString = postDataField.getText();
URLEncodedPostData encodedData = new URLEncodedPostData(null, false);
encodedData.append("content", postString);
HttpRequestDispatcher dispatcher = new HttpRequestDispatcher(urlField
.getText(), "POST", this, encodedData.getBytes());
dispatcher.start();
}

If postDataField contains the text “A B C”, the byte[] output from encodedData will be “content=A+B+C”.Adding the menu item is exactly the same as the Get menu item. Add the following lines to makeMenu:

menu.add(new MenuItem("Post", 10, 10) {
public void run() {
postURL();
}
});

We’re done. We don’t need to make modifications to requestSucceeded because we want the same functionality, which is to display the text. Let’s try it out. Enter in the URL field and ONE TWO THREE FOUR FIVE in the Post data field,and then click Post from the menu. You should see the words, one per line, in the output as shown in Figure.

The result of posting ONE TWO THREE FOUR FIVE

The result of posting ONE TWO THREE FOUR FIVE

Making Secure HTTP (HTTPS) Connections

To make a connection to a secure HTTP server, replace the Connector.open method. Fortunately, the web application also supports HTTPS connections, so simply substitute into the URL and then click Get. The result will look almost the same as the non-secure HTTP connection.

Retrieving the web application over HTTPS

Retrieving the web application over HTTPS

Performing the POST works in a similar way. We haven’t had to change the connection-handling code because Connector.open returns an HttpsConnection instead of HttpConnection, and HttpsConnection derives from HttpConnection. We can, however, detect this and display some information about the connection (in this case,the issuer of the TLS certificate). Add the following lines to the run method of HttpRequestDispatcher right after getting the response code:

if (connection instanceof HttpsConnection){
HttpsConnection secureConnection = (HttpsConnection)connection;
final String issuer =
secureConnection.getSecurityInfo().getServerCertificate().getIssuer();
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run(){
Dialog.inform("Secure Connection, certificate
issued by: " + issuer);
}
}

Now, if we enter the https:// URL, we’ll get a dialog with some info as shown in Figure.

Information about the security of the connection

Information about the security of the connection

NOTE: Something to be aware of with HTTPS connections—and this applies to secure socket (TLS and SSL) connections, too—is that things are simple only when the certificate provided by the server is known to the BlackBerry, or, in the case of a BES/MDS connection, known to the BES. In the case of an unknown certificate such as a self-signed certificate, a prompt is displayed to the user asking them to verify the connection. If the connection is a BES/MDS connection, this prompt is displayed only if certificate verification is done on the device by adding the EndToEndRequired=true parameter to the end of the URL to force certificate verification to happen on the device. Otherwise, the connection just fails. If you stick with certificates from known certification authorities, you shouldn’t have to worry about any of this.

Summary: HTTP Networking

You’ve learned the basics of HTTP networking, and created an application that performed both an HTTP GET and HTTP POST. The application performed the requests in a separate thread, which is necessary for all BlackBerry networking.We decoded the responses by first looking at the Content-type header to determine what the server was sending back, and then decoding either an image or text data. We also encoded our POST request body and set the Content-type header for the request appropriately. Finally, we peformed an HTTPS connection simply by switching the scheme of the URL,and using the HttpsConnection interface got some information about the connection.


All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

BLACKBERRY Topics