Build an Async Restful Webservice Client in Android in 5min and read BA flight data

In an earlier blog post we build a Restful WS running on Wildfly, now lets build the client part for Android. Thanks to the okHttp, an Apache 2.0 licensed java and Android library, this becomes a very easy challenge. In a mobile application we definitely want to implement a async call as we cannot rely on the response time being fast and a blocked application is not a pleasant user experience.

To make it a bit more interesting for the aviation context of my blog, we will take a real airline webservice and show operational flight data  in the second step.

Step 1: The Basic Webservice Client

Lets get started, I skip the project creation steps, you can create any basic empty Android application.

Add the dependency

..
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:24.1.1'
    testCompile 'junit:junit:4.12'
compile 'com.squareup.okhttp3:okhttp:3.9.0'
}

Implement the WS call with a call back

(we use the serverside we implemented previously with 2 parameters)

    private void testOKHttp() {

        OkHttpClient httpClient = new OkHttpClient();
        String callURL = "http://www.yourservername.com/DemoRandomizer/randomservice/random/numberrange";

        HttpUrl.Builder urlBuilder = HttpUrl.parse(callURL).newBuilder();
        urlBuilder.addQueryParameter("min", "10");
        urlBuilder.addQueryParameter("max", "100");
        callURL = urlBuilder.build().toString();

        Request request = new Request.Builder().url(callURL).build();
        httpClient.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e) {
                System.out.println(e.getMessage());
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                ResponseBody responseBody = response.body();
                if (!response.isSuccessful()) {
                    throw new IOException("Error response " + response);
                } else {
                    String str = new String(responseBody.bytes());
                    System.out.println(str);
                }
            }
        });

    }

It is good practice to block UI features (buttons, etc) that rely on the WS response or use a progress dialogm which is in some way blocking but the user is aware of the ongoing request.

 

// Set progress dialog befor the call
        prgDialog = new ProgressDialog(this);
        prgDialog.setMessage("Please wait...");
        prgDialog.setCancelable(false);
        prgDialog.show();

// remove the call after successful response or error
        prgDialog.dismiss();

Step 2: Client to read British Airways data

device-2017-10-14-092750

Lets gets our hand on some real data and tap into one of the open API’s offered by some airlines and airports, though I have to say that only very few players offer their data for public consumption. I will list some API’s in another blog post. Today we will make use of the British Airways API running on the Tibco Mashery platform. BA offers a variety of flight related data and we look at the flight status webservice. We can use the service for evaluation purpose up to 1 call a second and 500 calls a day, good enough to play with their data. We will collect the status of outbound flights from LHR (London Heathrow) for +/- 2hours. Before jumping into sourcode I recommend using a tool to test the webservice manually, eg. the Chrome Browser Rest Web Service Client extension.

2017-10-14 09_24_58-Rest Web Service Client

Using Chrome extension to get data from BA Webservice

One of the challenges, every API offering might support JSON and XML as WS GET, but the attributes and response format is different, we have to implement for every airline or airport we want to use the data, or add some kind of mapping layer.

For the BA service we have to change to call from step 1 above because we need to add the API key that you apply for under your account, also the parameter are not handled as query parameters but as parameter matrix.

The first code block only lists the relevant parts and the complete code block below adds some extras to handle GUI access, exception handling and the parameter creation.

 private void testWSCall() {

        OkHttpClient httpClient = new OkHttpClient();
        String callURL = "https://api.ba.com/rest-v1/v1/flights;departureLocation=LHR;startTime=12:00;endTime=18:00;";

        Request request = new Request.Builder().url(callURL)
                .addHeader("Content-Type", "application/json")
                .addHeader("client-key", "YOUR_KEY_HERE")
                .build();

        httpClient.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e) {
                System.out.println("WS Call failed (1):" + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) {
                ResponseBody responseBody = response.body();
                if (!response.isSuccessful()) {
                    final String errRep = "WS Call failed (2):" + response.toString();
		    System.out.println(errRep);
                } else {
                    String str = new String(responseBody.bytes());
		    System.out.println(str);
                }
            }
        });

}
 private void testOKHttp() {

        OkHttpClient httpClient = new OkHttpClient();

        DateFormat dateFormatShort = new SimpleDateFormat("HH:mm");
        Date date = new Date();
        long timeNow = date.getTime();
        long timePlus = timeNow + 120 * 60 * 1000;
        long timeMinus = timeNow - 120 * 60 * 1000;
        Date datePlus = new Date(timePlus);
        Date dateMinus = new Date(timeMinus);

        String callURL = "https://api.ba.com/rest-v1/v1/flights;departureLocation=LHR;startTime=" + dateFormatShort.format(dateMinus) +";endTime=" + dateFormatShort.format(datePlus) +";";
        System.out.println(callURL);

        Request request = new Request.Builder().url(callURL)
                .addHeader("Content-Type", "application/json")
                .addHeader("client-key", "YOUR_KEY_HERE<span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>")
                .build();

        httpClient.newCall(request).enqueue(new Callback() {

            @Override
            public void onFailure(Call call, IOException e) {
                System.out.println("WS Call failed (1):" + e.getMessage());
                prgDialog.dismiss();
            }

            @Override
            public void onResponse(Call call, Response response) {
                ResponseBody responseBody = response.body();
                if (!response.isSuccessful()) {
                    final String errRep = "WS Call failed (2):" + response.toString();
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            editTextResult.setText(errRep);
                        }
                    });
                    prgDialog.dismiss();
                } else {
                    try {
                        System.out.println("Response " + response.toString());
                        String str = new String(responseBody.bytes());
                        prgDialog.dismiss();

                        final JSONObject svcresponse = new JSONObject(str);
                        int spacesToIndentEachLevel = 2;
                        final String prettyPrintString = svcresponse.toString(spacesToIndentEachLevel);
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                editTextResult.setText(prettyPrintString);
                            }
                        });

                    } catch (Exception e) {
                        e.printStackTrace();
                        final String errStr = e.getMessage();
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                editTextResult.setText(errStr);
                            }
                        });
                        prgDialog.dismiss();
                    }
                }
            }
        });

    }

D3.js Getting started (again..)

The same questions as some years back: How to get started with it ?

The learning curve for beginners is certainly a bit steep if you dont have a basic background in at least HTML, CSS and JavaScript, in this case you rather go for a visualization tool like Tableau if you look for fast results.
The good news: There is plenty of material out there about D3, books, websites, tutorials. I purchased a couple of books from Packt Books and O’Reilly as PDF version, I think they have the most books, though most of them based on earlier D3 versions.

Packt Books

Packt Books

O'Reilly Books

O’Reilly Books

I wont recommend a specific one, most follow the same principles, a short intro to the DOM, explain the D3 basics followed by more or less complex samples. As with any other technology or programming language you learn most hands-on, there so many examples and good tutorials available. I never attempted to understand everything, understand the concepts, dissect existing visualizations and get creative !

Continue reading

Glassfish V3.1.2 and SSL

After almost 3 years (see previous post) I revisit the topic this time using the latest version og Glassfish 3.1.2 and GoDaddy as certificate provider. I created a certificate for a sub-domain (sub.whateverdomain.com) this time and make use of the extremly cheap 5.99 U$/year offer (no wildcard included)

Let me summarize the key steps here: Continue reading

Tutorial: Reading from the DB with Netbeans and ZK

After the first very basic tutorial that gets you started with the required plugins and settings (link), I will summarize in this tutorial how display data with zul using the JPA.

Requirements:

  • Netbeans 6.5 with a running derby DB and the sample database (customer table)
  • Installed ZK 3.6.0 plugin

Tutorial:

Content:
1. Start a new project
2. Create a new Entity Class from Database
3. Create a Controller
4. Create the table

Continue reading