Walter has been developing applications since the 80's, and professionally since the late 90's. He has been an early adopter of Java technology in the dot com days, and has managed and led software development teams across the globe. Walter has posted 4 posts at DZone. View Full User Profile

Android App to Monitor Hudson Rest API

02.06.2010
| 14426 views |
  • submit to reddit
The android platform provides a compeling solution for Java developers. It is deployable in the new generation of smart phones, it leverages the Java language in syntax, and it integrates in eclipse. This reduces the barrier to entry even more so than J2ME.

Take this technology and mingle it with a pragmatic development environment with continuous integration server. Often, development teams ignore a continuous integrations rants of broken build emails simply and seamlessly to be sent to the junk email filter. Hudson is a continuous build server, which provides remote api for JSON. This article will discus how to develop an Android smart phone application that will allow viewing summary build status.

If you haven't installed eclipse Android development environment it's pretty simple. This project can be started from the simple HelloWorld example. This application will demonstrate how to develop an Android application that has menu options, makes HTTP calls, marshalls data using JSON, and stores states in an application preference.

The first step is creating the entry point of the application. The first screen is a listing of the Hudson jobs, and last build status as a red, blue, yellow ball.

Android platform development offers two choices in designing the user interface. Either via code, or XML. XML code can be modified with the Android application runtime. Depending on your comfort level XML is easy to read, and the eclipse Android plugin gives a preview view. Android has a number of “layout managers” that give a variety of options for the UI developer. The most basic and common default is the LinearLayout. Take and modify the main.xml in the android res/layout/main.xml. Add a list view to the linear layout.

The result should be the following:

 res/layout/main.xml
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<ListView android:id="@+id/android:list"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>


</LinearLayout>


 

We need to create the main class entry point that the Android platform will use. Activity classes are a UI screens within an Android application, similar if an application had multiple screens to Android this would be multiple activities. The main activity in this case is the SpyHudson class which will extends from ListActivity.

 SpyHudson.java
public class SpyHudson extends ListActivity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

}

}

 

The onCreate calls setContentView(). If you notice R.layout.main is called and references the main.xml resource this is an index to resource definitions. The R. class is auto generated by the android eclipse development kit. As you properly update the xml files in the “res” directory, the generated R.java file is updated in eclipse. The operative word is properly since if you make formating or reference errors in your resource xmls then the R.java will not generate.

Like mentioned previously, Hudson supports remote calls via Xml, JSON and Python. The Android platform has basic JSON api support. This makes creating a restful client simple as making a http call, streaming the results of the information to JSONObjects, which can then be returned as ArrayList of items.

The first step is enable internet access for the android application. Because of the security and permissions required by the Android platform for applications, it is not automatically enabled. To enable this feature for your application, edit the AndroidManifest.xml and adding the following entry before the <application> tags.

<uses-permission
android:name="android.permission.INTERNET" />

 

 Create a class HudsonRestful, that will take a specified URL and parse the returning information into JSON objects. This class is more of a utility helper class in the context of this application. The Android SDK uses the apache http api and provides it's basic functionality. First, establish a HttpClient, do a HttpGet and read the response in the input stream.

 HudsonRestful.java
private String queryRESTurl(String url) {

// URLConnection connection;

HttpClient httpclient = new DefaultHttpClient();

HttpGet httpget = new HttpGet(url);

HttpResponse response;

try {

response = httpclient.execute(httpget);

Log.i(TAG, "Status:[" + response.getStatusLine().toString() + "]");

HttpEntity entity = response.getEntity();


if (entity != null) {

InputStream instream = entity.getContent();

String result = convertStreamToString(instream);

Log.i(TAG, "Result of converstion: [" + result + "]");

instream.close();

return result;

}

} catch (ClientProtocolException e) {

Log.e("REST", "There was a protocol based error", e);

} catch (IOException e) {

Log.e("REST", "There was an IO Stream related error", e);

}

return null;

}

 

 

 

 

You may have noticed not much difference in standard http requests using the apache http package. Again the method call to convertStreamToString() is nothing more than your standard BufferedReader writing to a string builder till it reaches the end of the stream:

 

 HudsonRestful.java (continued)
private String convertStreamToString(InputStream is) {

BufferedReader reader = new BufferedReader(new InputStreamReader(is));

StringBuilder sb = new StringBuilder();

String line = null;

try {

while ((line = reader.readLine()) != null) {

sb.append(line + "\n");

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

is.close();

} catch (IOException e) {

e.printStackTrace();

}

}

return sb.toString();

}

 

 

The main point is to take the string information that is returned and parse it to a JSONObject. Again the Android development platform has JSON api built into it. This makes developing restful clients easy. This is done as follows:

 

 HudsonRestful.java (continued)
 
public ArrayList<JSONObject> retrieveJSONArray(String urlString) {

String result = queryRESTurl(urlString);

ArrayList<JSONObject> JOBS = new ArrayList<JSONObject>();

if (result != null) {

try {

JSONObject json = new JSONObject(result);

JSONArray jobsArray = json.getJSONArray("jobs");

for (int a = 0; a < jobsArray.length(); a++) {

JSONObject jobitem = jobsArray.getJSONObject(a);

JOBS.add(jobitem);

}

return JOBS;

} catch (JSONException e) {

Log.e("JSON", "There was an error parsing the JSON", e);

}

}

JSONObject myObject = new JSONObject();

try {

myObject.put("name","No hudson Jobs found");

myObject.put("color", "grey");

JOBS.add(myObject);

} catch (JSONException e1) {

Log.e("JSON", "There was an error creating the JSONObject", e1);

}

return JOBS;

}

 

 

 

 

 

Going back to the main SpyHudson view, we want the list to display more than just text. The idea is to display an image and a text element. The way to do this is create a image and text adapter to the list. The adapter will look at the ArrayList of JSONObject passed to it and set the image and text description to each row element in the list.

The first step is to create a image and text layout resource. In resources layout, create a image_text_layout.xml. The linear layout will do, and the property for orientation is set to horizontal in this case, since this is rows of information being created. Then add two elements, a ImageView element, and TextView element. The result is as follows:

 res/layout/image_text_layout.xml
 
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="wrap_content">

<ImageView android:id="@+id/last_build_stat"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:src="@drawable/grey"/>

<TextView android:id="@+id/job_text"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

</LinearLayout>

 

 

 

Notice, in the ImageView element android:src references “@drawable/grey” this is an image in res/drawable/ folder called grey.gif.

The adapter class to be created that will use this layout resource will extend the class from the ArrayAdapter in the android.widgets. The ArrayAdapter has a getView() method that will be overridden by this implementation.

The class constructor uses the main activity, and a list of JSONObjects.

 ImageTextListAdapter.java
 
public class ImageTextListAdapter extends ArrayAdapter<JSONObject> {

public ImageTextListAdapter(Activity activity, List<JSONObject> imageAndTexts) {

super(activity, 0, imageAndTexts);

}

 

Override the getView method of the Array adapter. The fist section will be getting the view from the resource. Then because it's a listing, the current position pulls out the JSONObject from the passed array via the getItem(position) method call.
 ImageTextListAdapter.java
 
@Override

public View getView(int position, View convertView, ViewGroup parent) {

Activity activity = (Activity) getContext();

LayoutInflater inflater = activity.getLayoutInflater();

// Inflate the views from XML

View rowView = inflater.inflate(R.layout.image_text_layout, null);

JSONObject jsonImageText = getItem(position);

 

Acquiring a widget from the Android xml resources such as ImageView object by using the findViewById() call and calling the id in the resource xml. This is the id found as recalled earlier as:

<ImageView android:id="@+id/last_build_stat"

 You can use the findViewById() reference to any widget for the xml view and programatically manipulate the view at run time.

Next step, Query the JSONObject's “color” attribute for the color string, and set the Image view resource to the appropriate color. Notice, that the “grey” status image was excluded. That is because in the layout resource it was already declared in the layout xml, and therefore becomes the default image source.

 ImageTextListAdapter.java(continued from getView)
// Load the image and set it on the ImageView

ImageView imageView = (ImageView) rowView.findViewById(R.id.last_build_stat);

String color = "";

try {

color = (String) jsonImageText.get("color");

} catch (JSONException e) {

color = "grey";

}

if (color.equalsIgnoreCase("yellow")) {

imageView.setImageResource(R.drawable.yellow);

} else if(color.equalsIgnoreCase("blue")) {

imageView.setImageResource(R.drawable.blue);

} else if (color.equalsIgnoreCase("red")) {

imageView.setImageResource(R.drawable.red);

}

 

The TextView element is done the same way, but here it is simply get the JSONObject's “name” attribute and place it into the text. The List adapter finally returns the rowView back to the main view.

  ImageTextListAdapter.java(continued from getView)
 
// Set the text on the TextView

TextView textView = (TextView) rowView.findViewById(R.id.job_text);

try {

textView.setText((String)imageAndText.get("name"));

} catch (JSONException e) {

textView.setText("JSON Exception");

}

return rowView;

}

 


 

Combine the adapter to the main SpyHudson class go back to the onCreate() method and add the following.

 SpyHudson.java
 
public class SpyHudson extends ListActivity {

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

spyRest = new HudsonRestful();

String remoteUrl = "http://localhost:8080/hudson/api/json";

ArrayList<JSONObject> jobs = spyRest.retrieveJSONArray(remoteUrl);

setListAdapter(new ImageTextListAdapter(this,jobs));

}

}

 


When you launch the android emulator via eclipse you should see one of the following. The first is default when there is no hudson server or jobs found. The second is a listing of hudson jobs and appropriate colored status health for your builds:

 job listinglistingview

Naturally, you'll have to point the URL string to your own Hudson server's api. You can read more on hudson's api at http://wiki.hudson-ci.org/display/HUDSON/Remote+access+API.

This simple Android application demonstrate how to create a quick simple android application that makes a restful call to Hudson's Rest JSON api.

AttachmentSize
SimpleHudson.zip64.08 KB
Published at DZone with permission of its author, Walter Bogaardt.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Damon Young replied on Wed, 2010/07/07 - 3:52pm

Thanks for this. I've been looking for a great RESTful Android primer, and this is just what the doctor ordered

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.