An Android REST Client and Tomcat REST Webservice

Project Overview

This post will describe simple client-server communications between an Android app and a web service using REST techniques, particularly GET and POST. For this demo, we will be sending and receiving data for a simple Java class called Person, which includes a firstName, lastName and email address. The data stored in an instance of this class will be transmitted from our web service in JSON format.

NOTE: This post is a work in progress and will be updated and corrected, as time permits.

Prerequisites

This demo assumes that you have a version of Eclipse that can create a “Dynamic Web Project”, and that you have an instance of the Tomcat servlet container that you can control through Eclipse. This demo also assumes that you have the Android SDK, at least one Android Virtual Device (AVD), and have the Eclipse ADT plugin.

Another important requirement is that your Android device needs to be on the same network as your Tomcat server. The simplest way to do this would be to make sure you are running your emulator on the same computer that is running Tomcat.

About REST

REST – “Representational State Transfer” is a technique that makes use of standard web protocols for the implementation of a web service. A RESTful web service uses the standard HTTP GET PUT DELETE and POST actions to submit, retrieve or modify server-side data.

Commonly, in a REST web service, the standard HTTP actions are used as follows:

  • GET – retrieves or queries for data, usually using passed criteria
  • PUT – creates a new entry, record
  • DELETE – removes a resource
  • POST- updates a resource or creates a resource

The data that is transmitted uses standard MIME types, including images, video, text, html, XML and JSON.

A key feature of a REST service is its use of URI paths as query parameters. For example, a fictional web service for a book library might return a list of Civil War history books with this URI:

http://librarywebservice.com/books/history/CivilWar

Or, for magazines about tennis:

http://librarywebservice.com/magazines/sports/tennis

These are the absolute basics. REST has become a very popular replacement to SOAP for the development of web services.

Download the Jersey Jars

For the web server, this demo will use Tomcat, and will make use of a reference implementation of the JSR 311 Java library, otherwise known as “jersey”. The Jersey API can significantly streamline the development of a RESTful web service, and much of its ease comes from the use of annotations.

The home page, and the place to download the required jars for jersey can be found here. As of the writing of this post, the jars will be found in a zip called jersey-archive-1.12.zip . Please download this zip and expand it to a folder where you can find those jars.

Create a “Dynamic Web Project” in Eclipse

In Eclipse, select “File”->”New…”->”Project” and use the filter to find “Dynamic Web Project”
Eclipse Dynamic Web Project

I have Apache Tomcat 7 running on my laptop, and have previously configured a connection between Tomcat and Eclipse. The Dynamic web module version I am using is 3.0, but this tutorial can work with version 2.5.
Apache Tomcat and web module version

Click “Next” and define your context root. That is the starting-point URL for your web service. With the context root shown in the screenshot below, the resulting URL on my laptop will be http://localhost:8080/RestWebServiceDemo . Also, have Eclipse generate a web.xml deployment descriptor.
Context root and web.xml

Add Jersey Jars to Project

You will need to import the jars that you’ve downloaded from http://jersey.java.net into the WEB-INF/lib folder. Right click that folder (WebContent/WEB-INF/lib) and select Import…
Import jersey jars files

I’ve simply thrown in all the jars into that folder for the purposes of this tutorial. Crude, but effective. The smart thing would be to only use the jars you need. I’ll eventually circle back to that some day.
Jersey jar file list

Update Project Build Path to Include Jersey Jars

Now that the jars are in the WEB-INF/lib folder, you will need to configure the project to include these jars in its build path. From within your project in the Package Explorer on the left of your Eclipse screen, right click to select “Build Path”->”Configure Build Path…”
Configure build path

On the “Libraries” tab, click on “Add External JARS…” and select the jars that are now in your project’s WEB-INF/lib path.
Add external jars

Create the POJO Person class

Within the \src folder, create a package called com.avilyne.rest.model, and in that package, create a Java class called Person. Note, in the code shown below, the @XmlRootElement annotation. This tells Jersey that this would be the root object of any generated XML (or JSON) representation of this class. This might not be very useful for this class, but if you had a compound class, you’d be able to control what the XML or JSON output would look like by the addition of annotations like this.

package com.avilyne.rest.model;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Person {

	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	
	public Person() {
		
		id = -1;
		firstName = "";
		lastName = "";
		email = "";
		
	}

	public Person(long id, String firstName, String lastName, String email) {

		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
		this.email = email;
	}

	
	private long id;
	private String firstName;
	private String lastName;
	private String email;
		
}

Create the PersonResource class

Again, in the \src folder, create a new package called com.avilyne.rest.resource, and in that package create a class called PersonResource. (We are separating our data model from our controller).

The PersonResource class will be the interface between a Person object and the web. When a client requests a Person object, the PersonResource will handle the request and return the appropriate object.

Note, in the code below, the annotations. The @Path(“/person”) annotation allows us to define the URI that will have access to this resource. It will be something along the lines of http://RestServerDemo/rest/person, but this will be explained in more detail as we map this code in our web.xml file.

The @Context annotation allows us to inject, in this example, UriInfo and Request objects into our PersonResource object. Our code can inspect those injected objects for any desired Context information.

// The @Context annotation allows us to have certain contextual objects
// injected into this class.
// UriInfo object allows us to get URI information (no kidding).
@Context
UriInfo uriInfo;

// Another "injected" object. This allows us to use the information that's
// part of any incoming request.
// We could, for example, get header information, or the requestor's address.
@Context
Request request;

The @GET annotation lets us specify what method will be called when a client issues a GET to the webservice. Similar logic applies to the @POST annotation. Note that the actual method name will not be a part of the client’s URI.

Also note that more than one method has a @GET annotation. The first @GET is simply there so that, when we start our service, we can use a browser to retrieve some sort of response that indicates the service is running. Technically, instead of producing TEXT_PLAIN, the first GET could have produced a TEXT_HTML page.

	// Basic "is the service running" test
	@GET
	@Produces(MediaType.TEXT_PLAIN)
	public String respondAsReady() {
		return "Demo service is ready!";
	}

The @Path annotation lets us append a parameter onto our URI. Using the example from earlier, the hypothetical URI would be http://RestServerDemo/rest/person/sample .

	@GET
	@Path("sample")
	@Produces(MediaType.APPLICATION_JSON)
	public Person getSamplePerson() {
		
		System.out.println("Returning sample person: " + person.getFirstName() + " " + person.getLastName());
		
		return person;
	}

Note that paths do NOT need to be literal. One can have a path parameter as a variable, e.g. @Path(“{id}”) would refer to a person whose id value matched the passed URI value. http://RestServerDemo/rest/person/1 should return a person whose id is 1.

The @Produces annotation allows us to define how the output from our resource should be transmitted to our client. Note that, for the getSamplePerson() method, we return a person object, and the annotation lets us tell Jersey to format and transmit that person as a JSON object.

The method with the @POST annotation also includes a @Consumes annotation. As you can guess, this method is called in response to a client’s POST request. The data for the “person” object being transmitted from the client is not a JSON object, but is a collection of Name-Value pairs. The @Consumes annotation allows us to specify that the data passed from the client is an array of these pairs, and we can pull out the values we want from that array.

	// Use data from the client source to create a new Person object, returned in JSON format.
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	public Person postPerson(
			MultivaluedMap<String, String> personParams
			) {
		
		String firstName = personParams.getFirst(FIRST_NAME);
		String lastName = personParams.getFirst(LAST_NAME);
		String email = personParams.getFirst(EMAIL);
		
		System.out.println("Storing posted " + firstName + " " + lastName + "  " + email);
		
		person.setFirstName(firstName);
		person.setLastName(lastName);
		person.setEmail(email);
		
		System.out.println("person info: " + person.getFirstName() + " " + person.getLastName() + " " + person.getEmail());
		
		return person;
						
	}

For each of the methods in this PersonResource, I’ve added a System.out.println() as a crude way of letting us see when a request is being processed. There are probably more elegant ways of doing this (Logging, for one), and one would almost never include a System.out.println in a production service. This is just a demo.

package com.avilyne.rest.resource;

import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.Request;

import com.avilyne.rest.model.Person;

@Path("/person")
public class PersonResource {

	private final static String FIRST_NAME = "firstName";
	private final static String LAST_NAME = "lastName";
	private final static String EMAIL = "email";
		
	private Person person = new Person(1, "Sample", "Person", "sample_person@jerseyrest.com");
	
	// The @Context annotation allows us to have certain contextual objects
	// injected into this class.
	// UriInfo object allows us to get URI information (no kidding).
	@Context
	UriInfo uriInfo;

	// Another "injected" object. This allows us to use the information that's
	// part of any incoming request.
	// We could, for example, get header information, or the requestor's address.
	@Context
	Request request;
	
	// Basic "is the service running" test
	@GET
	@Produces(MediaType.TEXT_PLAIN)
	public String respondAsReady() {
		return "Demo service is ready!";
	}

	@GET
	@Path("sample")
	@Produces(MediaType.APPLICATION_JSON)
	public Person getSamplePerson() {
		
		System.out.println("Returning sample person: " + person.getFirstName() + " " + person.getLastName());
		
		return person;
	}
		
	// Use data from the client source to create a new Person object, returned in JSON format.	
	@POST
	@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
	@Produces(MediaType.APPLICATION_JSON)
	public Person postPerson(
			MultivaluedMap<String, String> personParams
			) {
		
		String firstName = personParams.getFirst(FIRST_NAME);
		String lastName = personParams.getFirst(LAST_NAME);
		String email = personParams.getFirst(EMAIL);
		
		System.out.println("Storing posted " + firstName + " " + lastName + "  " + email);
		
		person.setFirstName(firstName);
		person.setLastName(lastName);
		person.setEmail(email);
		
		System.out.println("person info: " + person.getFirstName() + " " + person.getLastName() + " " + person.getEmail());
		
		return person;
						
	}
}

Create or Edit the WebContent\WEB-INF\web.xml file

In our web.xml file, we want to direct all requests to a servlet called Jersey REST Service. We will also tell this service where to find the resources that we want to make available to our client app.

Update the web.xml file (or create it, in WebContent\WEB-INF\web.xml, if it does not exist) to match the XML shown below.

The XML defines the Jersey REST Service from the com.sun.jersey.spi.container.ServletContainer class. That class, as you might guess, is in one of the Jersey Jars. The init-param section allows us to tell that servletcontainer to use the classes found in the com.avilyne.rest.resource package for the mapping of URIs to java code.

In the servlet-mapping section, we create a global url-pattern, which essentially says that any request that goes to /rest/ will attempt to be mapped to the appropriate methods.

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 
	id="WebApp_ID" 
	version="3.0">
  <display-name>JerseyRESTServer</display-name>
  <servlet>
  	<servlet-name>Jersey REST Service</servlet-name>
  	<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  	<init-param>
  		<param-name>com.sun.jersey.config.property.packages</param-name>
  		<param-value>com.avilyne.rest.resource</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  	<servlet-name>Jersey REST Service</servlet-name>
  	<url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Run the WebService

Run the completed project as a service on your Tomcat servlet container. The actual URL for the service will vary slightly, depending partly on the name of your project. I called my project JerseyRESTServer, and here is the URL for the web service on my computer:

http://localhost:8080/JerseyRESTServer/rest/person

Correction! Paul, a recent commenter, has pointed out that the URL should actually be:

http://localhost:8080/RestWebServiceDemo/rest/person

RestWebServiceDemo is the context root that we defined when we first started this project. I’ll mention that I built a few versions of this project before writing this post, so unfortunately some of the images show a URL from an earlier version of the project.

If I bring up this on a browser, it returns with this:
Service is Ready

If I add the “sample” path to the URL, like this:

http://localhost:8080/RestWebServiceDemo/rest/person/sample

The web service returns a JSON object:

{"email":"sample_person@jerseyrest.com","firstName":"Sample","id":"1","lastName":"Person"}

I would recommend that you make sure you can retrieve this sample person before you create the Android client app.

Create an Android Client App

Create a new Android project, calling it something like AndroidRESTClient. I used API level 10 for this project, but one can probably use a lower level API, if required. Android HttpClient requests have been around since the earliest days of the OS. Use com.avilyne.android as the package for the main activity.

Edit the \res\layout\main.xml File

The interface will be very simple, having three labels and three edit controls for firstName, lastName and email address. It will also have three buttons – one to GET, one to POST, and one to clear the controls.

The main.xml file should look like this:

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/tableLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:shrinkColumns="*"
    android:stretchColumns="*" >

    <TableRow
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            style=""
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/firstName" >
        </TextView>

        <EditText
            android:id="@+id/first_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textCapWords"
            android:layout_span="2" />
    </TableRow>

    <TableRow
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            style=""
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/lastName" >
        </TextView>

        <EditText
            android:id="@+id/last_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textCapWords" 
            android:layout_span="2" />
    </TableRow>

    <TableRow
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            style=""
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/email" >
        </TextView>

        <EditText
            android:id="@+id/email"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:inputType="textEmailAddress"
            android:layout_span="2" />
    </TableRow>

    <TableRow
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/bn_retrieve"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="retrieveSampleData"
            android:text="@string/retrieve" />

        <Button
            android:id="@+id/bn_post"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="postData"
            android:text="@string/post" />
        
        <Button
            android:id="@+id/bn_clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="clearControls"
            android:text="@string/clear" />
        
    </TableRow>

</TableLayout>

Note that an onClick method is defined for each button.

Edit the \res\values\strings.xml file

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="firstName">First Name</string>
    <string name="lastName">Last name</string>
    <string name="email">Email</string>
    <string name="retrieve">Retrieve</string>
    <string name="post">Post</string>
    <string name="clear">Clear</string>
    <string name="app_name">AndroidRESTClient</string>

</resources>

Edit the AndroidRESTClientActivity.java file

Discuss the WebServiceTask, and the handleResponse() method.

The full source code for the Android client is listed below, but I want to highlight a few items in the code that you should be aware of. On my home network, the computer running the service on Tomcat is at IP address 192.168.1.9. On your network, the address will most definitely be different. If you are running the Android client and the Tomcat service on the same computer, you can get away with using “localhost” in your Android code.

(CORRECTION: (Thanks ABa) Actually, you *can’t* use “localhost”. “Localhost” in this context would refer to the Android device itself. Use the IP address of the computer on your network that is running the Tomcat service. See this StackOverflow link for details.)

public class AndroidRESTClientActivity extends Activity {

	private static final String SERVICE_URL = "http://192.168.1.9:8080/RestWebServiceDemo/rest/person";

For this tutorial, the most important code is the internal “WebServiceTask” class, which is extended from an “AsyncTask” class. An AsyncTask class descendant allows a process to run in a separate thread. If our communication with our service were on the Android app’s main thread, the user interface would lock up as the process was waiting for results from the server.

One can define the types of parameters that are passed to one’s instance of the AsyncTask. (more on that later). The communication with the web service occurs in the WebServiceTask’s “doInBackground()” code. This code uses Android’s HttpClient object, and for the GET method, uses HttpGet, and for the POST method, uses HttpPost.

The AsyncTask class includes two other methods that one has the option to overwrite. One is onPreExecute(), which one can use to prepare for the background process, and the other is onPostExecute(), which one can use to do any required clean-up after the background process is complete. This code overrides those methods to display and remove a progress dialog.

The background task also includes two timeout options. One is a timeout period for the actual connection to the service, and the other is a timeout period for the wait for the service’s response.

package com.avilyne.android;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;

public class AndroidRESTClientActivity extends Activity {

	private static final String SERVICE_URL = "http://192.168.1.9:8080/RestWebServiceDemo/rest/person";

	private static final String TAG = "AndroidRESTClientActivity";
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	}

	public void retrieveSampleData(View vw) {

		String sampleURL = SERVICE_URL + "/sample";

		WebServiceTask wst = new WebServiceTask(WebServiceTask.GET_TASK, this, "GETting data...");
		
		wst.execute(new String[] { sampleURL });
		
	}

	public void clearControls(View vw) {

		EditText edFirstName = (EditText) findViewById(R.id.first_name);
		EditText edLastName = (EditText) findViewById(R.id.last_name);
		EditText edEmail = (EditText) findViewById(R.id.email);

		edFirstName.setText("");
		edLastName.setText("");
		edEmail.setText("");
				
	}
	
	public void postData(View vw) {

		EditText edFirstName = (EditText) findViewById(R.id.first_name);
		EditText edLastName = (EditText) findViewById(R.id.last_name);
		EditText edEmail = (EditText) findViewById(R.id.email);

		String firstName = edFirstName.getText().toString();
		String lastName = edLastName.getText().toString();
		String email = edEmail.getText().toString();

		if (firstName.equals("") || lastName.equals("") || email.equals("")) {
			Toast.makeText(this, "Please enter in all required fields.",
					Toast.LENGTH_LONG).show();
			return;
		}

		WebServiceTask wst = new WebServiceTask(WebServiceTask.POST_TASK, this, "Posting data...");

		wst.addNameValuePair("firstName", firstName);
		wst.addNameValuePair("lastName", lastName);
		wst.addNameValuePair("email", email);

		// the passed String is the URL we will POST to
		wst.execute(new String[] { SERVICE_URL });

	}

	public void handleResponse(String response) {
		
		EditText edFirstName = (EditText) findViewById(R.id.first_name);
		EditText edLastName = (EditText) findViewById(R.id.last_name);
		EditText edEmail = (EditText) findViewById(R.id.email);
		
		edFirstName.setText("");
		edLastName.setText("");
		edEmail.setText("");
		
		try {
			
			JSONObject jso = new JSONObject(response);
			
			String firstName = jso.getString("firstName");
			String lastName = jso.getString("lastName");
			String email = jso.getString("email");
			
			edFirstName.setText(firstName);
			edLastName.setText(lastName);
			edEmail.setText(email);			
			
		} catch (Exception e) {
			Log.e(TAG, e.getLocalizedMessage(), e);
		}
		
	}

	private void hideKeyboard() {

		InputMethodManager inputManager = (InputMethodManager) AndroidRESTClientActivity.this
				.getSystemService(Context.INPUT_METHOD_SERVICE);

		inputManager.hideSoftInputFromWindow(
				AndroidRESTClientActivity.this.getCurrentFocus()
						.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
	}
	
	
	private class WebServiceTask extends AsyncTask<String, Integer, String> {

		public static final int POST_TASK = 1;
		public static final int GET_TASK = 2;
		
		private static final String TAG = "WebServiceTask";

		// connection timeout, in milliseconds (waiting to connect)
		private static final int CONN_TIMEOUT = 3000;
		
		// socket timeout, in milliseconds (waiting for data)
		private static final int SOCKET_TIMEOUT = 5000;
		
		private int taskType = GET_TASK;
		private Context mContext = null;
		private String processMessage = "Processing...";

		private ArrayList<NameValuePair> params = new ArrayList<NameValuePair>();

		private ProgressDialog pDlg = null;

		public WebServiceTask(int taskType, Context mContext, String processMessage) {

			this.taskType = taskType;
			this.mContext = mContext;
			this.processMessage = processMessage;
		}

		public void addNameValuePair(String name, String value) {

			params.add(new BasicNameValuePair(name, value));
		}

		private void showProgressDialog() {
			
			pDlg = new ProgressDialog(mContext);
			pDlg.setMessage(processMessage);
			pDlg.setProgressDrawable(mContext.getWallpaper());
			pDlg.setProgressStyle(ProgressDialog.STYLE_SPINNER);
			pDlg.setCancelable(false);
			pDlg.show();

		}

		@Override
		protected void onPreExecute() {

			hideKeyboard();
			showProgressDialog();

		}

		protected String doInBackground(String... urls) {

			String url = urls[0];
			String result = "";

			HttpResponse response = doResponse(url);

			if (response == null) {
				return result;
			} else {

				try {

					result = inputStreamToString(response.getEntity().getContent());

				} catch (IllegalStateException e) {
					Log.e(TAG, e.getLocalizedMessage(), e);

				} catch (IOException e) {
					Log.e(TAG, e.getLocalizedMessage(), e);
				}

			}

			return result;
		}

		@Override
		protected void onPostExecute(String response) {
			
			handleResponse(response);
			pDlg.dismiss();
			
		}
		
		// Establish connection and socket (data retrieval) timeouts
		private HttpParams getHttpParams() {
			
			HttpParams htpp = new BasicHttpParams();
			
			HttpConnectionParams.setConnectionTimeout(htpp, CONN_TIMEOUT);
			HttpConnectionParams.setSoTimeout(htpp, SOCKET_TIMEOUT);
			
			return htpp;
		}
		
		private HttpResponse doResponse(String url) {
			
			// Use our connection and data timeouts as parameters for our
			// DefaultHttpClient
			HttpClient httpclient = new DefaultHttpClient(getHttpParams());

			HttpResponse response = null;

			try {
				switch (taskType) {

				case POST_TASK:
					HttpPost httppost = new HttpPost(url);
					// Add parameters
					httppost.setEntity(new UrlEncodedFormEntity(params));

					response = httpclient.execute(httppost);
					break;
				case GET_TASK:
					HttpGet httpget = new HttpGet(url);
					response = httpclient.execute(httpget);
					break;
				}
			} catch (Exception e) {

				Log.e(TAG, e.getLocalizedMessage(), e);

			}

			return response;
		}
		
		private String inputStreamToString(InputStream is) {

			String line = "";
			StringBuilder total = new StringBuilder();

			// Wrap a BufferedReader around the InputStream
			BufferedReader rd = new BufferedReader(new InputStreamReader(is));

			try {
				// Read response until the end
				while ((line = rd.readLine()) != null) {
					total.append(line);
				}
			} catch (IOException e) {
				Log.e(TAG, e.getLocalizedMessage(), e);
			}

			// Return full string
			return total.toString();
		}

	}
}

(Code discussion, with excerpts, here)

Update the AndroidManifest.xml

Since this app needs to communicate via the internet, one must give the app the appropriate permissions.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.avilyne.android"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />
    <uses-permission android:name="android.permission.INTERNET" />
    
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".AndroidRESTClientActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Running the Android Client App

This screenshot shows the Android client app with its blank screen.
Android client blank

If one presses the “Retrieve” button, the web service will respond:
Server returning person

The Android client’s controls should get populated with the retrieved Person:
Android client person

Here I’ve modified the values on my Android client screen, and pressed the “Post” button:
Android client test human

The web service responds to the post:
Storing test human

Hopefully, this will give you some idea of how to build an Android client that communicates with a web service using REST.

83 thoughts on “An Android REST Client and Tomcat REST Webservice

    1. admin Post author

      Thanks for the comment regarding the use of “localhost” within the emulator. The link you posted is correct, of course, in that the emulator runs within its own VM, so “localhost” would refer to the Android device itself.

      For the purposes of this demo, it might be best to point to the IP of the computer on one’s network that is functioning as the server.

      I’ll update this post accordingly.

      Mark

      1. diego

        Thank you for this comment.
        I did not realize that ‘localhost’ was referencing the smartphone itself.
        Now my web service is working :D

  1. Paul MacInnis

    For what it’s worth: I had to change all the URL’s to use the Web Module’s Context Root name to get this to work, e.g. change from “http://localhost:8080/JerseyRESTServer/rest/person” to “http://localhost:8080/RestWebServiceDemo/rest/person”.

    Otherwise a great tutorial … a big help getting started!!! Thanks a lot for posting this.
    Paul

    1. admin Post author

      Paul: Great catch on that context root issue. I had built several versions of this little project before writing up this post. It is now apparent that my screen captures were from more than one version.

      I’ll update this post to reflect the corrections you’ve mentioned.

      Mark

    1. admin Post author

      404 is “not found”, which means the server is up, but it can’t find anything at that url. What is your project’s “Context root”? We’ll start from there.

      Also, if you’d be willing to post your web.xml, I might be able to help by looking at that.

      1. @AdityaSetyadi

        My “Context Root” is Luna, I’d already try the same way with http://localhost:8080/Luna/rest/person

        Here is my web.xml, I try to code the same as yours. and so about the other java class Person and PersonResource.

        Luna

        Jersey REST Service
        com.sun.jersey.spi.container.servlet.ServletContainer

        com.sun.jersey.config.property.packages
        com.avilyne.rest.resource

        1

        Jersey REST Service
        /rest/*

        I think this might be the problem, when I try to run on Tomcat, by right click my Project “Luna” then Run on Server, then I choose the Tomcat 7.0 that I’d already create.
        It appears diaolog box like this.
        “Starting tomcat has encountered a problem, server tomcat failed to start”
        and in my console it appears like this

        Jun 21, 2012 7:49:11 AM org.apache.catalina.core.AprLifecycleListener init
        INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jre7\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:/Program Files/Java/jre7/bin/client;C:/Program Files/Java/jre7/bin;C:/Program Files/Java/jre7/lib/i386;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\ATI Technologies\ATI.ACE\Core-Static;C:\Program Files\WIDCOMM\Bluetooth Software\;C:\Program Files\Java\jdk1.6.0_16\bin;C:\Program Files\Java\jdk1.7.0_01\bin;C:\opencv\build\common\tbb\ia32\vc10\;C:\opencv\build\x86\vc10\bin\;C:\Server\apache-tomcat-6.0.26\bin;;D:\Progima\Eclipse JEE Indigo\eclipse;;.
        Jun 21, 2012 7:49:11 AM org.apache.tomcat.util.digester.SetPropertiesRule begin
        WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property ‘source’ to ‘org.eclipse.jst.jee.server:Luna’ did not find a matching property.
        Jun 21, 2012 7:49:12 AM org.apache.coyote.AbstractProtocolHandler init
        INFO: Initializing ProtocolHandler ["http-bio-8080"]
        Jun 21, 2012 7:49:12 AM org.apache.coyote.AbstractProtocolHandler init
        INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
        Jun 21, 2012 7:49:12 AM org.apache.catalina.startup.Catalina load
        INFO: Initialization processed in 612 ms
        Jun 21, 2012 7:49:12 AM org.apache.catalina.core.StandardService startInternal
        INFO: Starting service Catalina
        Jun 21, 2012 7:49:12 AM org.apache.catalina.core.StandardEngine startInternal
        INFO: Starting Servlet Engine: Apache Tomcat/7.0.12
        java.lang.IllegalAccessError: class com.sun.media.sound.AbstractPlayer cannot access its superclass com.sun.media.sound.AbstractMidiDevice
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$100(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1591)
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1521)
        at org.apache.catalina.startup.ContextConfig.checkHandlesTypes(ContextConfig.java:1956)
        at org.apache.catalina.startup.ContextConfig.processAnnotationsStream(ContextConfig.java:1919)
        at org.apache.catalina.startup.ContextConfig.processAnnotationsJar(ContextConfig.java:1806)
        at org.apache.catalina.startup.ContextConfig.processAnnotationsUrl(ContextConfig.java:1765)
        at org.apache.catalina.startup.ContextConfig.processAnnotations(ContextConfig.java:1751)
        at org.apache.catalina.startup.ContextConfig.webConfig(ContextConfig.java:1255)
        at org.apache.catalina.startup.ContextConfig.configureStart(ContextConfig.java:882)
        at org.apache.catalina.startup.ContextConfig.lifecycleEvent(ContextConfig.java:317)
        at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:119)
        at org.apache.catalina.util.LifecycleBase.fireLifecycleEvent(LifecycleBase.java:89)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5081)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1033)
        at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:774)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
        at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:1033)
        at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:291)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
        at org.apache.catalina.core.StandardService.startInternal(StandardService.java:443)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
        at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:727)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
        at org.apache.catalina.startup.Catalina.start(Catalina.java:620)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:303)
        at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:431)

        1. admin Post author

          I might be chasing the wrong thing, but in the trace that you’ve posted, I find this:
          “java.lang.IllegalAccessError: class com.sun.media.sound.AbstractPlayer cannot access its superclass com.sun.media.sound.AbstractMidiDevice

          Maybe there’s an incorrect jar file in your project?

          Perhaps the best way to diagnose this would be for me to see the other project components.

  2. google apps free

    Howdy! This is my first visit to your blog!

    We are a group of volunteers and starting a new project
    in a community in the same niche. Your blog provided us beneficial information to work
    on. You have done a marvellous job!

  3. Michael Denham

    If you want to access the Tomcat service which running on your PC from your Android device via your wireless network, first run the command ipconfig on your the command window of your PC (in Windows 7 use run (Windows logo + R), cmd, ipconfig).

    Note the IPv4 address: (it should be 192.168.0.x) for some x. Use this as the server IP address, together with the port number, i.e. 192.168.0.x:8080, in your code, for example
    private static final String SERVICE_URL = “http://192.168.0.7:8080/RestWebServiceDemo/rest/person”;

    Your Android device will then access the server via your wireless network router.

  4. Andrew Luxner

    This is the best example I’ve found on dealing with RESTful services from the server side using Java with an Android client, and it was really helpful. Thanks a lot!

    1. Mark Post author

      Does your layout include edit controls with those names? That’s the first thing I’d check, but I’ll also mention that if there’s an error *anywhere else* in your layout, your XML won’t compile, and the Android toolkit will not generate the required “R” values. This *might* account for the missing R.xxx error messages you are receiving.

  5. Ranjith

    i want to send the image to server through RESTFUL get method how to send when i send image by converting to string it is not taking my input how to send image to server please help me

    1. Mark Post author

      You should probably convert your image to a byte [] array and create an encoded string from that array:

      You could then upload that to your server via a POST. On the server side, you’d need to convert the received encoded string to a byte array and create an image from that.

      I’m sure there are examples on StackOverflow.

      1. William Kinaan

        I did that, but the problem is the encoded string hasn’t been sent, is that because the size of the string? its size is about 10000 chars

  6. Eyüp Alemdar

    Really awesome tutorial . We are bening to an android project using web service and it helps me a lot . Thank you very much

  7. giha

    hi
    tnx a lot for this tutorial,but i don’t know why i have this exception :java.lang.String cannot be converted to JSONObject in the line : JSONObject jso = new JSONObject(response);
    can you help me to fix this problem pleaze?

    1. Mark Post author

      It *might* be that the response value you’re getting back is not a String. I’d do two things:
      1.) Step through this code with a debugger to find out what is in your returned response value.
      2.) try: JSONObject jso = new JSONObject(response.toString())

      You shouldn’t have to do item 2, but that might get you over the hump. I think the real issue is the format of the “response” value you’re receiving.

      1. tl

        Hi,
        what should I do, when my return response value like this:

        Strict Standards: Redefining already defined constructor for class Object in C:\xampp-portable\htdocs\cakephp-rest\cake\libs\object.php on line 54

        Strict Standards: Non-static method Configure::getInstance() should not be called statically in C:\xampp-portable\htdocs\cakephp-rest\cake\bootstrap.php on line 38

        Strict Standards: Non-static method CakeLog::getInstance() should not be called statically, assuming $this from incompatible context in C:\xampp-portable\htdocs\cakephp-rest\cake\libs\cake_log.php on line 290
        [{"Post":{"id":"1","title":"The title","body":"This is the post body.","created":"2013-06-11 10:57:35","modified":null}},{"Post":{"id":"2","title":"A title once again","body":"And the post body follows.","created":"2013-06-11 10:57:35","modified":null}},{"Post":{"id":"3","title":"Title strikes back","body":"This is really exciting! Not.","created":"2013-06-11 10:57:35","modified":null}},{"Post":{"id":"4","title":"Love is in the air","body":"bla bla","created":"2013-06-11 11:02:06","modified":"2013-06-11 11:02:06"}},{"Post":{"id":"5","title":"Love is in the air","body":"bla bla","created":"2013-06-11 11:02:40","modified":"2013-06-11 11:02:40"}},{"Post":{"id":"6","title":"Love","body":"iiiiiiiii","created":"2013-06-11 11:03:31","modified":"2013-06-11 11:03:31"}},{"Post":{"id":"7","title":"bluemontains","body":"dgasga","created":"2013-06-11 11:39:34","modified":"2013-06-11 11:39:34"}}]

        I use your tutorial for my android REST client to connect with my Xampp Server using cakephp Rest Webservice.
        Hope you can help me. Thank you in advance

        1. Mark Post author

          This sounds like a server-side PHP issue, and I see references to the cakephp framework. You might need to post your question at a relevant PHP forum.

  8. sandeep

    I have copied the exact code for restful web service, while runnning it, if (http://localhost:8080/RestWebService/rest/person) i am also getting the same result:Demo service is ready!

    but when i am trying (http://localhost:8080/RestWebService/rest/person/sample) i am getting the error : com.sun.jersey.api.MessageException: A message body writer for Java class com.polaris.webservice.Person, and Java type class com.polaris.webservice.Person, and MIME media type application/json was not found

    Returning sample person: Sample Person

  9. sandeep

    Well, I got the solution, i didn’t add @XmlRootElement previously..now it’s showing perfectly..and thanks for this wonderful tutorial.

  10. Knirpsi

    Great tutorial, thx!
    I’ve got one question:
    ‘Post’ should change the attributes of person with the entered values, but when I click on ‘Retrieve’ afterwards I get the original Values (Sample, Person, sample_person@jerseyrest.com) again.
    What have I not considered?

    1. Mark Post author

      Excellent question. In short, the updated data is not being stored. A complete implementation would include a persistence mechanism that would retrieve and store your updates in a database. It is also worth noting that each call to the servlet brings up a separate live instance of that servlet, which in this demo includes a Person object with hard-coded values, rather than being retrieved from a database. Now, if two people access the same Person object with the intent to update its values, there’s a potential for conflict there which would have to be handled on the server-side code.

      The other day I encountered an interview question that asked how to handle that conflict. I stumbled through the question, but a solution would involve the use of ConcurrentHashMap. Among other features, it provides the equivalent of a relational database’s row locking mechanism.

      This might be a great subject for a separate tutorial, slightly outside of the original scope of this mobile-centric blog, but worth considering. Scalable server side development for mobile apps is an area that I think needs more discussion.

  11. Pooja Desai

    Hi,

    Thanks for a great but I am getting the 404 resource not found error. I’ve checked that the context root and the URL is the same i.e both contain RestWebserviceDemo.

    The following is the error message I get when I run Tomcat Server:
    11-Jan-2013 13:44:25 org.apache.catalina.core.AprLifecycleListener init
    INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jre6\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:/Program Files/Java/jre6/bin/client;C:/Program Files/Java/jre6/bin;C:/Program Files/Java/jre6/lib/i386; C:\Program Files\Java\jdk1.6.0_26/bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Users\Pooja\AppData\Local\Smartbar\Application\;C:\Users\Pooja\AppData\Local\Smartbar\Application\;C:\apache-ant-1.8.4\bin;C:\apache-ant-1.8.4\bin;C:\Program Files\Bitvise Tunnelier;C:\Program Files\MATLAB1\R2010b\runtime\win32;C:\Program Files\MATLAB1\R2010b\bin;C:\Program Files\Bitvise SSH Client;C:\Users\Pooja\Documents\eclipse;;.
    11-Jan-2013 13:44:25 org.apache.tomcat.util.digester.SetPropertiesRule begin
    WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property ‘source’ to ‘org.eclipse.jst.jee.server:MyDMULibraryServer’ did not find a matching property.
    11-Jan-2013 13:44:25 org.apache.tomcat.util.digester.SetPropertiesRule begin
    WARNING: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property ‘source’ to ‘org.eclipse.jst.jee.server:RestWebservice’ did not find a matching property.
    11-Jan-2013 13:44:25 org.apache.coyote.AbstractProtocol init
    INFO: Initializing ProtocolHandler ["http-bio-8080"]
    11-Jan-2013 13:44:25 org.apache.coyote.AbstractProtocol init
    INFO: Initializing ProtocolHandler ["ajp-bio-8009"]
    11-Jan-2013 13:44:25 org.apache.catalina.startup.Catalina load
    INFO: Initialization processed in 719 ms
    11-Jan-2013 13:44:25 org.apache.catalina.core.StandardService startInternal
    INFO: Starting service Catalina
    11-Jan-2013 13:44:25 org.apache.catalina.core.StandardEngine startInternal
    INFO: Starting Servlet Engine: Apache Tomcat/7.0.30
    11-Jan-2013 13:44:27 com.sun.jersey.api.core.PackagesResourceConfig init
    INFO: Scanning for root resource and provider classes in the packages:
    com.example.rest.resource
    11-Jan-2013 13:44:27 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
    INFO: Initiating Jersey application, version ‘Jersey: 1.12 02/15/2012 04:51 PM’
    11-Jan-2013 13:44:27 com.sun.jersey.server.impl.application.RootResourceUriRules
    SEVERE: The ResourceConfig instance does not contain any root resource classes.
    11-Jan-2013 13:44:27 org.apache.catalina.core.ApplicationContext log
    SEVERE: StandardWrapper.Throwable
    com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.
    at com.sun.jersey.server.impl.application.RootResourceUriRules.(RootResourceUriRules.java:99)
    at com.sun.jersey.server.impl.application.WebApplicationImpl._initiate(WebApplicationImpl.java:1308)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.access$700(WebApplicationImpl.java:171)
    at com.sun.jersey.server.impl.application.WebApplicationImpl$13.f(WebApplicationImpl.java:777)
    at com.sun.jersey.server.impl.application.WebApplicationImpl$13.f(WebApplicationImpl.java:773)
    at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:193)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:773)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:768)
    at com.sun.jersey.spi.container.servlet.ServletContainer.initiate(ServletContainer.java:488)
    at com.sun.jersey.spi.container.servlet.ServletContainer$InternalWebComponent.initiate(ServletContainer.java:318)
    at com.sun.jersey.spi.container.servlet.WebComponent.load(WebComponent.java:607)
    at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:208)
    at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)
    at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)
    at javax.servlet.GenericServlet.init(GenericServlet.java:160)
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1266)
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1185)
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1080)
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5027)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5314)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
    11-Jan-2013 13:44:27 org.apache.catalina.core.StandardContext loadOnStartup
    SEVERE: Servlet /RestWebserviceDemo threw load() exception
    com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.
    at com.sun.jersey.server.impl.application.RootResourceUriRules.(RootResourceUriRules.java:99)
    at com.sun.jersey.server.impl.application.WebApplicationImpl._initiate(WebApplicationImpl.java:1308)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.access$700(WebApplicationImpl.java:171)
    at com.sun.jersey.server.impl.application.WebApplicationImpl$13.f(WebApplicationImpl.java:777)
    at com.sun.jersey.server.impl.application.WebApplicationImpl$13.f(WebApplicationImpl.java:773)
    at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:193)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:773)
    at com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:768)
    at com.sun.jersey.spi.container.servlet.ServletContainer.initiate(ServletContainer.java:488)
    at com.sun.jersey.spi.container.servlet.ServletContainer$InternalWebComponent.initiate(ServletContainer.java:318)
    at com.sun.jersey.spi.container.servlet.WebComponent.load(WebComponent.java:607)
    at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:208)
    at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:373)
    at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:556)
    at javax.servlet.GenericServlet.init(GenericServlet.java:160)
    at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1266)
    at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1185)
    at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:1080)
    at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:5027)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5314)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1559)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1549)
    at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
    11-Jan-2013 13:44:28 org.apache.coyote.AbstractProtocol start
    INFO: Starting ProtocolHandler ["http-bio-8080"]
    11-Jan-2013 13:44:28 org.apache.coyote.AbstractProtocol start
    INFO: Starting ProtocolHandler ["ajp-bio-8009"]
    11-Jan-2013 13:44:28 org.apache.catalina.startup.Catalina start
    INFO: Server startup in 3083 ms

    And the here is my web.xml:

    RestWebserviceDemo

    Jersey REST Service
    com.sun.jersey.spi.container.servlet.ServletContainer

    com.sun.jersey.config.property.packages
    com.example.rest.resource

    1

    Jersey REST Service
    /rest/*

    Can you please help?

    Thanks :)

    1. Mark Post author

      This probably has to do with the mapping of the servlet in your web.xml file. When I first built this project I had a similar issue. Can you compare your servlet mapping to your package name?

      It’s also possible that your code is not being copied to the right destination when you kick off a Tomcat run.

  12. te

    Hi Nice post. if i want to implement secure link (https)between the android and server how do I implement this? Thanks

  13. Pingback: android access server database out of two choices

  14. Pingback: android access server database out of two choices : Android Community - For Application Development

  15. Carlos Calla

    Hi!

    I have a question, when you first retrieve the information you get the information of the sample person. Then I post information changing the info of the sample and when I retrieve after posting the new information, should I get the new information I just posted or is it ok if I am getting the information of the sample person. When I do it I am getting the information of the sample person but I thought you were using the post to update the info of the sample person.

    Warm regards, was a very useful post :D

    1. Mark Post author

      That’s a great question. Once your data is posted to the server, that data should actually be stored, mostly commonly in a database. I bypassed implementing a storage mechanism for the purposes of brevity. As you have probably realized, each time you interact with the server, a separate instance of that servlet is instantiated, and in my implementation, that instance creates a “hard coded” Person instance. In a real implementation, the servlet would retrieve the data from a database to populate that Person object.

      1. Carlos Calla

        Thank you, I figured that out adding a constructor to the PersonResource and printing something just to know if everytime I do a GET or a POST the constructor prints the line I wrote and it did it so I realized that a separate instance of the servlet is instantiated in each interaction. I also did updated the object Person with the POST and got the information stored with the GET by converting the object in singleton just for testing purposes and having a better understanding :D

  16. PankS

    Hi, this is very wonderful tutorial it helped me alot.
    Bt i hv 1 doubt. .though m using “id As in LONG Format its returning in STRING format “I m newbie to tis so if anythng i missd correct me pls…
    thankyou

    1. Mark Post author

      The ID value that’s generated from the GCM registration process might look like a long number, but it’s definitely a string. Make sure it’s enclosed in ["] quote characters.

  17. Carlos Calla

    Hi, I have recently added an API to my android client called RestEasy mobile, do you know how to work with HTTPS in web services? Does JAX-RS allow HTTPS?

  18. Ernesto Alonso

    Hi! Nice tutorial, thanks!

    I just want to mention that you’re missing a slash “/” in the Path annotation of the getSamplePerson method. So that it would be like this: @Path(“/sample”). I had errors because of that missing.

    Regards!

      1. Ernesto Alonso

        Maybe that could be an obvious issue for experienced people, but for those who we are learning isn’t.
        BTW, This tutorial helped me a lot. All is working fine. Thanks again!

  19. Asmaa

    Hello

    Thank you Mark for this great Tutorial.
    I’m trying to implement the Post Button. And send data from my android to my web service.
    I read your code to see what are the part that concern the Post event, but I don’t see any clicklistner on the bn_post button?
    Please Help!!

    1. Mark Post author

      Take a look at the main.xml layout file, and you’ll see that the bnPost button has an “onClick” property. This property allows a developer to assign a method to respond to an onClick event (of course!).

      I put this in my example, but I am ambivalent about using this XML mapping approach. My thought is that a layout XML file is for layouts, and Java code is for – executing java code. Nevertheless, this XML approach is part of the Android spec, and there you have it.

  20. TJ

    First of all, I really appreciate you effort. This tutorial is terrific !!
    I have one problem thought :
    The “response” given as a parameter to the OnPostExecute method contains this :

    response value is : Apache Tomcat/7.0.12 – Rapport d”erreur Etat HTTP 415 – Unsupported Media Typetype Rapport d”étatmessage Unsupported Media Typedescription Le serveur a refusé cette requête car l’entité de requête est dans un format non supporté par la ressource demandée avec la méthode spécifiée (Unsupported Media Type).Apache Tomcat/7.0.12

    I don’t understand why does it contain html. I’m not able to fill my json object in the handleResponse method.

    Do u have any idea what the problem might be ??

    1. Mark Post author

      I’d have to see more of your project’s code and configuration to give you a decent answer. For starters, what is the content of the URL that you are sending to the server? Put a Debug.Log message there, and let’s see what you are actually sending.

  21. Prathyusha

    Hi, This is an excellent example for beginners like me.. I tried to follow the guidelines to build an android app that refers to RESTFul web service. As I debug through, HTTPGet returns a success but then I get AsyncTask.class source not found and all other threadpool related errors. How do I fix that?

    1. Mark Post author

      What you are seeing is probably not really an error. When you are stepping through your code through the debugger, there will be places where Android’s own code will be called. So the debugger is looking for the source code for AsyncTask, but can’t find it.

      Two ways to “fix”, or at least work around, the error. If you recognize that the code you are about to step into is a built-in Android class, or a class from a library or jar where you don’t have the source code, step *over* that class instantiation instead. That’s the workaround.

      The other way to “fix” this would be to download Google’s Android source code, and you can do this via the Android SDK Manager. For every version of the Android SDK, there is an option to download the source, but note that this will take a little time to download, and will of course take up a good amount of disk space. But once you have the source code for the SDK version you are using in your project, you will not get that “source not found” error, and you’ll be able to step *into* classes like AsyncTask (if you dare!).

      Mark

  22. minoch

    hi !!when i create my android application the class R.java dissapear !!dont know why !!aty first it was built automatically and now i have the problem R cant be resolved to a variable !!can any boby help me plzzz :)

    1. Mark Post author

      If your R.java file has disappeared, it is most likely because there is an issue in one of your XML files. If you are using Eclipse, check your project’s RES folder to see if any errors are listed. Most common place would be in a layout file.

      Once you find and fix any XML resource errors, Eclipse (and the ADT) will automatically regenerate the required R.java file.

  23. kelly

    Hi,
    Thanks for such a good tutorial. Can you please tell me how to delete from server through client? Actually i want to delete data which i posted on rest service. How can i do that?
    Please help!!!

    Thanks in advance.

    1. Mark Post author

      Officially, in addition to GET and POST, the http standard includes a DELETE command. But the truth is, I’ve seen very few services written to respond to a DELETE submission. A more common approach would be to send a POST that would include, as part of the attached set of key-value pairs, a key that could be called “COMMAND” or “OPTION” or “ACTION” or “TASK”, etc. The basic idea is that the web service would look for that key value to find out what action is being requested.

      So, one could create a web service that responds to a POST, with a “COMMAND” key that has the value “DELETE”. The other key values (possibly combined with REST URL mapping) would determine what data to delete. And the web service would delete the data that matched the passed values.

      Something like that.

  24. wayne

    Really appreciate the effort. The tutorial is concise and helpful.

    Is there any tutorial on how to implement authentication? or at least, could you point me to the right direction?
    Since its a RESTful service, it wouldn’t make sense to mantain a session right?
    Many thanks.

  25. Santiago

    From the bottom of my heart, I love you men. I’ve been struggling with this for ages! Thank you so much!

  26. java-ai

    Awesome post! It’s good to find tutorials that go a step further than hello world, less than giving the kitchen sink but just enough to see how things combine to work together. Hope you do more

    1. Mark Post author

      Very kind words. I’ve been doing mostly iOS work for the past several months, so no Android posts for a while. But I hope to get back to writing again.

  27. Ramzi

    Hi, I tried to test this code but an exception has occurred
    com.sun.jersey.spi.service.ServiceConfigurationError: jersey-server-components: A dependent class, org/jvnet/mimepull/MIMEParsingException, of the class com.sun.jersey.multipart.impl.MultiPartReader implementing the provider class java.lang.Object is not found. The provider implementation is ignored.
    com.sun.jersey.spi.service.ServiceFinder.fail(ServiceFinder.java:397)
    com.sun.jersey.spi.service.ServiceFinder.access$200(ServiceFinder.java:149)
    com.sun.jersey.spi.service.ServiceFinder$LazyClassIterator.next(ServiceFinder.java:604)
    com.sun.jersey.spi.service.ServiceFinder$LazyClassIterator.next(ServiceFinder.java:580)
    com.sun.jersey.spi.service.ServiceFinder.toClassArray(ServiceFinder.java:383)
    com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:606)
    com.sun.jersey.server.impl.application.WebApplicationImpl.initiate(WebApplicationImpl.java:589)
    com.sun.jersey.spi.container.servlet.ServletContainer.initiate(ServletContainer.java:429)
    com.sun.jersey.spi.container.servlet.ServletContainer$InternalWebComponent.initiate(ServletContainer.java:278)
    com.sun.jersey.spi.container.servlet.WebComponent.load(WebComponent.java:566)
    com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:211)
    com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:333)
    com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:497)
    javax.servlet.GenericServlet.init(GenericServlet.java:160)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    java.lang.Thread.run(Unknown Source)
    Please an you tell me the origin of this exception .Thank you a lot

    1. Mark Post author

      By the errors in your log, it looks like Tomcat can not find the jars that are in the jersey library. Check your paths.

  28. Markus

    Excellent example. I used it as reference, it works with JBoss and Maven integration too.

  29. chris dimoulas

    You must insert to Manifest.xml this line

    thats because you target your ip machine (like 192.168.1.9)

  30. yousss

    please kindly help with this problem. i used your code but when i click post, it gives me problem like this:

    07-01 14:44:10.445: E/AndroidRESTClientActivity(1257): Value Apache of type java.lang.String cannot be converted to JSONObject
    07-01 14:44:10.445: E/AndroidRESTClientActivity(1257): org.json.JSONException: Value Apache of type java.lang.String cannot be converted to JSONObject
    07-01 14:44:10.445: E/AndroidRESTClientActivity(1257): at org.json.JSON.typeMismatch(JSON.java:111)
    07-01 14:44:10.445: E/AndroidRESTClientActivity(1257): at org.json.JSONObject.(JSONObject.java:158)
    07-01 14:44:10.445: E/AndroidRESTClientActivity(1257): at org.json.JSONObject.(JSONObject.java:171)
    07-01 14:44:10.445: E/AndroidRESTClientActivity(1257): at com.example.testandroid.MainActivity.handleResponse(MainActivity.java:126)
    07-01 14:44:10.445: E/AndroidRESTClientActivity(1257): at com.example.testandroid.MainActivity$WebServiceTask.onPostExecute(MainActivity.java:238)
    07-01 14:44:10.445: E/AndroidRESTClientActivity(1257): at com.example.testandroid.MainActivity$WebServiceTask.onPostExecute(MainActivity.java:1)

    1. Mark Post author

      “Value Apache…” seems a bit odd. In a debugger, I’d put a stop at the line where this error occurs, and I’d look at the results being passed that’s failing the conversion to JSON.

  31. Charlie Tap

    Could you shed some light on how you would integrate this with database, using JPA presumably?
    What database to use etc?

    Rgds

    Charlie

    1. Mark Post author

      Integrating with a database would certainly be the next big step. In a relational database like MySQL, I’d create a table that would have at least two fields:
      user_id (probably an email address)
      device_id (a field large enough to hold that device_id that you captured manually)

      When the Android app successfully receives a device_id, I’d have it POST that id, along with a user_id or email address to a web service. That web service would collect those two parameters, verify that the user_id exists, and save that device_id to that database table.

      Then I might rewrite the server side broadcast method to allow an adminstrator to select users via user_id, finding each selected user’s corresponding device_id instead of hard-coding a target device_id.

      That’s a high-level overview, but I’m sorry I don’t have any code yet to demonstrate this.

  32. Pingback: Guide in Java web services needed : Android Community - For Application Development

  33. Manjeet singh goyal

    thanks Alot i have run this sample code at my end successfully, easy for learning java rest web service

Comments are closed.