Click here to Skip to main content
Click here to Skip to main content

A Simple Mobile Enterprise Application

, 30 May 2012 CPOL
Rate this:
Please Sign up or sign in to vote.
How to make a simple, end-to-end, mobile, Java enterprise application including a RESTful web service and an Android client.

Introduction

This article shows how to make a simple, end-to-end, mobile enterprise application for developers who are just learning how to make enterprise applications or are learning how to use a combination of the components described here in an application. Our example includes a RESTful web service that provides access to Java entity beans on Oracle’s Glassfish application server and a simple Android client to consume it.

Serialization of the entity beans between Android and Glassfish is encoded through JSON. All code is in Java unless otherwise specified.

Background

Earlier this year (2012), during a student assignment at Humber College in Etobicoke, Ontario, Canada, I was suddenly struck by the clever idea that one could simultaneously demonstrate the consumption of our RESTful web services with an Android application and the serialization of Java entity beans through JSON. It was only a few weeks later that my Java instructor informed me to my dismay that this was not an original idea even with his own past classes. Nonetheless, I did not find another article on the web that duplicates all that this one does (although my search may not have been thorough enough); therefore, I bestow the precious insight herein, gleaned by the sweat of my brow, as a warm light through all mankind to share. (Self-deprecating, Avengers-inspired sarcasm intended.)

The code shown has been extracted from my assignment and simplified for this presentation.

Using the code

We assume that our audience knows how to make simple examples of a database, an EJB-hosted RESTful web service, and an Android Activity. However, one should be able to get the gist of the code with just a little programming experience.

For the NetBeans web service project, I added the free, current, jettison.jar from jettison.codehaus.org. It includes the JSON STaX implementation, which contains the BadgerFish mapping of JSON to XML.

Data Model

Database

The underlying database table has just enough fields for the purposes of this article: an identifier, a label, and an info field to update. Here is the MySQL DDL script snippet that describes it:

CREATE TABLE `simpleuser` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

Entity

I generated the corresponding entity class with the “New > Entity Classes from Database” command in NetBeans.

public class SimpleUser implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @NotNull
    @Column(name = "id")
    private Integer id;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 255)
    @Column(name = "name")
    private String name;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 255)
    @Column(name = "email")
    private String email;
...
}

Business Logic

The RESTful web service provides the basic CRUD access operations to the entity beans, which are the resources in the REST paradigm: create, retrieve, update, delete.

Glassfish includes Jersey, the Reference Implementation for building RESTful web services in Java. We also add the JSON/XML mapping provided by BadgerFish in the Jettison library available at jettison.codehaus.org.

Our web service is implemented by a stateless Enterprise JavaBean, SimpleUserResource. We have also set the REST ApplicationPath attribute to be “rest”.

Create

Our adopted REST paradigm calls for resources to be created by the HTTP request POST. This snippet includes the start of our implementation class.

import java.net.URI;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.*;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.xml.bind.JAXBElement;
import org.codehaus.jettison.json.JSONArray;
@Path("/appuser")
@Produces(MediaType.WILDCARD)
@Consumes(MediaType.WILDCARD)
@Stateless
public class SimpleUserResource {
    @PersistenceContext(unitName = "SimpleUserRESTService-warPU")
    private EntityManager entityManager;
    @Context
    private UriInfo uriInfo;
    @POST
    @Consumes({MediaType.APPLICATION_JSON})
    public Response createSimpleUser(JAXBElement<SimpleUser> userJAXB) {
        // Save the new user to the db.
        SimpleUser user = userJAXB.getValue();
        user.setId(0); // set the id to nonesense to generate a new id
        entityManager.persist(user);
        // Refresh the persistence context for the newly generated id.
        // Can't just use EntityManager.merge() because the managed instance
        // that it returns does not contain the newly generated id, either.
        // (Pesist() makes the argument instance managed.)
        entityManager.flush();
        // Return the HTTP 201 Created response with the new user id.
        URI userURI = uriInfo.getAbsolutePathBuilder().path(user.getId().
                toString()).build();
        return Response.created(userURI).build();
    }
...

The @Path attribute specifies the URL path component that indicates to Glassfish that containing HTTP requests should be directed to this EJB.

The @Consumes and @Produces attributes specify the acceptable MIME types of the content of the HTTP requests that are received and responses that are sent, respectively. (WILDCARD indicates all types, of course.) An attribute specified for a method overrides a corresponding one for its class.

The JAXBElement parameters wraps the posted entity bean after BadgerFish has converted the bean’s HTTP JSON representation to XML and Project JAXB has converted the XML representation to a SimpleUser class instance.

Retrieval

Our adopted REST paradigm calls for resources to be retrieved by the HTTP request GET.

There are two basic types of retrieval. One is to retrieve a specific resource.

    @GET
    @Path("{id}/")
    @Produces({MediaType.APPLICATION_JSON})
    public SimpleUser retrieveSimpleUser(@PathParam("id") int id) {
        URI userURI = uriInfo.getAbsolutePathBuilder().build();
        if (id < 1) {
            ResponseBuilder rBuild = Response.status(
                Response.Status.NOT_FOUND).entity(
                userURI.toASCIIString());
            throw new WebApplicationException(rBuild.build());
        }
        SimpleUser user = entityManager.find(SimpleUser.class, id);
        if (user == null) {
            ResponseBuilder rBuild = Response.status(
                    Response.Status.NOT_FOUND).entity(
                    userURI.toASCIIString());
            throw new WebApplicationException(rBuild.build());
        }
        return user;
    }

The {id} template in @Path specifies that an URL in a HTTP GET request with an extra path component after “appuser” indicates that retrieveSimpleUser() will be invoked to handle the request with the text of that extra component being passed as an integer in the parameter named id.

The returned SimpleUser instance will be converted to XML by JAXB and JSON by BadgerFish before being sent to the client in the resulting HTTP response.

Another type of retrieval is to fetch the list of resources located at a specific directory.

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public JSONArray retrieveSimpleUsers() {
        // Perform query.
        Query query = entityManager.createNamedQuery("SimpleUser.findAll");
        List<SimpleUser> userList = query.getResultList();
        // Translate result list to JSON array.
        JSONArray resUriArray = new JSONArray();
        for (SimpleUser user : userList) {
            UriBuilder ub = uriInfo.getAbsolutePathBuilder();
            URI resUri = ub.path(user.getId().toString()).build();
            resUriArray.put(resUri.toASCIIString());
        }
        return resUriArray;
    }

The returned array will contain the URIs of the entities as JSON strings, not the entities themselves.

Update

Our adopted REST paradigm calls for resources to be updated by the HTTP request PUT.

 
    @PUT
    @Consumes({MediaType.APPLICATION_JSON})
    public Response updateSimpleUser(JAXBElement<SimpleUser> userJAXB) {
        SimpleUser user = userJAXB.getValue();
        URI userURI = uriInfo.getAbsolutePathBuilder().
                path(user.getId().toString()).build();
        try {
            // Ensure that this is an update, not an insert, for merge()
            // can create a new entity, too.
            SimpleUser matchU = (SimpleUser) entityManager.find(
                    SimpleUser.class, user.getId());
            if (matchU == null) {
                String msg = "PUT is the wrong HTTP request for creating
                        new user " + user.getId() + ".";
                ResponseBuilder rBuild = Response.status(
                        Response.Status.BAD_REQUEST).entity(msg);
                throw new WebApplicationException(rBuild.build());
            }
            // Save the current properties of the specified user to the db.
            entityManager.merge(user);
            return Response.ok().build();
        } catch (IllegalArgumentException ex) {
            ResponseBuilder rBuild = Response.status(
                    Response.Status.NOT_FOUND).entity(
                    userURI.toASCIIString());
            return rBuild.build();
        }
    }

Delete

Our adopted REST paradigm calls for resources to be removed by the HTTP request DELETE.

    @DELETE
    @Path("{id}/")
    public Response deleteSimpleUser(@PathParam("id") int id) {
        URI userURI = uriInfo.getAbsolutePathBuilder().build();
        if (id < 1) {
            ResponseBuilder rBuild = Response.status(
                    Response.Status.NOT_FOUND).entity(
                    userURI.toASCIIString());
            return rBuild.build();
        }
        // Find the specified user to remove.
        SimpleUser user = entityManager.find(SimpleUser.class, id);
        if (user == null) {
            ResponseBuilder rBuild = Response.status(
                    Response.Status.NOT_FOUND).entity(
                    userURI.toASCIIString());
            return rBuild.build();
        }
        entityManager.remove(user);
        // Status 204 No Content means that deletion has occurred.
        ResponseBuilder rBuild = Response.status(Response.Status.NO_CONTENT);
        return rBuild.build();
    }

The {id} template in @Path works the same way as for retrieveSimpleUser().

Android Client

Now, brace yourself for the stunning beauty of this Android client application.

Android client view 1

Displayed in this UI is the entity bean with id=3, name=”Dustin Penner”, and email="dustin.penner@simpleuser.org".

Now, a consideration in building this app is how to serialize the bean. A reflexive instinct of the typical contemporary software developer, trained to use application frameworks, is to reach for a library off the internet that will handle the serialization to JSON automatically, e.g. Jackson at jackson.codehaus.org. You know the thinking – don’t reinvent the wheel; don’t create errors by, heaven forbid, programming your own code; outsource the responsibility to the library maker. However, I think that, in this case, adding a library to the client is overkill. The SimpleUser entity is *simple* - we can handle it. We can include our own conversion methods in the entity class, which needs to be imported as a library into the Android client project anyway. We don’t have to be burdened with the overhead of including and updating the extra library in multitudes of client apps. And, for the purpose of enlightenment, we also get to show JSON serialization in action in this article.

Let’s create an entity to represent the great, young Canadian hockey defenseman, Drew Doughty. The method in the client sends out a HTTP POST request over a raw HttpURLConnection as follows:

private void createUser(SimpleUser user) {
    URL url;
    HttpURLConnection connection = null;
    try {
        // Create connection.
        String wsUri = getBaseContext().getResources().getString(
                R.string.rest_web_service_uri);
        url = new URL(wsUri);
        connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("Content-Type", "application/json");
        String userSer = user.getJSONSerialization();
        connection.setRequestProperty("Content-Length", Integer
                .toString(userSer.getBytes().length));
        connection.setUseCaches(false);
        connection.setDoInput(true);
        connection.setDoOutput(true);
        // Send request.
        DataOutputStream wr = new DataOutputStream(connection
                .getOutputStream());
        wr.writeBytes(userSer);
        wr.flush();
        wr.close();
...

This method also displays the HTTP response from the server with a Toast pop-up, but we’ll dismiss that as unimportant for this article.

Now let’s look at SimpleUser’s serialization method.

    public String getJSONSerialization() {
        StringBuilder sb = new StringBuilder();
        sb.append("{");
        sb.append(serializeJSONField("id", Integer.toString(id)) + ",");
        sb.append(serializeJSONField("name", name) + ",");
        sb.append(serializeJSONField("email", email) + ",");
        sb.append("}");
        return sb.toString();
    }
    private String serializeJSONField(String name, String value) {
        StringBuilder sb = new StringBuilder();
        sb.append("\"");
        sb.append(name);
        sb.append("\":\"");
        sb.append(value);
        sb.append("\"");
        return sb.toString();
    }

Elegant, eh? That’s the nice thing about JSON serialization in comparison with a JAXB XML serialization – little overhead.

Thus, when an Android-wielding hockey fan wants to record forever the name and email of a certain slick-skating L.A. King, his press of the Create User button will send a textual request like the following, bypassing any typical firewalls, directly to our web service:

POST /SimpleUserRESTService-war/rest/appuser HTTP/1.1
Host: localhost:8080
Accept: */*
Content-Type: application/json
Content-Length: 70
{"id":"0","name":"Drew Doughty","email":"drew.doughty@simpleuser.org"}

After clicking Retrieve All Users, the fan can select the last user id in the spinner and click Retrieve User Details in order to check his new addition.

private void retrieveUserDetails() {
    URL url;
    HttpURLConnection connection = null;
    try {
        // Create connection with the selected user. 
        String wsUri = getBaseContext().
                getResources().getString(
                R.string.rest_web_service_uri);
        Spinner spinner = (Spinner) findViewById(
                R.id.retrieveAllSpinner);
        String userId = (String) spinner.getSelectedItem();
        wsUri += userId;
        url = new URL(wsUri);
        connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        connection.setRequestProperty("Accept", "application/json");
        connection.setUseCaches(false);
        connection.setDoInput(true);
        connection.setDoOutput(true);
        // Send request.
        connection.connect();
        int rspCode = connection.getResponseCode();
        // Load in the response body.
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(connection.getInputStream()));
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        String rspBody = sb.toString();
        if (rspCode == HttpURLConnection.HTTP_OK) {
            // Deserialize the SimpleUser.
            currentUser = new SimpleUser(rspBody);
            // Load the detail text view.
            TextView detailsTextView = (TextView) findViewById(
                    R.id.detailsTextView);
            String userLabel = currentUser.getName() + " (" +
                    currentUser.getId() + ")";
            detailsTextView.setText(userLabel);    
            // Load the email edit view.
            EditText emailEditText = (EditText) findViewById(
                    R.id.emailEditText);
            emailEditText.setText(currentUser.getEmail());
        }
...

This causes the following HTTP request to and response from the server:

GET /SimpleUserRESTService-war/rest/appuser/4 HTTP/1.1
Host: localhost:8080
Accept: application/json

HTTP/1.1 200 OK
X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.1 Java/Oracle Corporation/1.7)
Server: GlassFish Server Open Source Edition 3.1.1
Content-Type: application/json
Transfer-Encoding: chunked
Date: Tue, 29 May 2012 19:28:14 GMT
{"email":"drew.doughty@simpleuser.org","id":"4","name":"Drew Doughty"}*

And this response is deserialized in the SimpleUser constructor as follows:

    public SimpleUser(String jSONSerialization) {
        try {
            String valStr = extractJSONFieldValue(jSONSerialization, "id");
            this.id = Integer.parseInt(valStr);
            this.name = extractJSONFieldValue(jSONSerialization, "name");
            this.email = extractJSONFieldValue(jSONSerialization, "email");
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(SimpleUser.class.getName()).
                    log(Level.SEVERE, null, ex);
            ex.printStackTrace();
        }
    }
    private String extractJSONFieldValue(String jSONSerialization, 
            String field) throws UnsupportedEncodingException {
        String fieldLab = '"' + field + '"' + ':';
        int i = jSONSerialization.indexOf(fieldLab);
        if (i < 0)
            throw new IllegalArgumentException(
                    "The JSON serialization is missing field label:" +
                    fieldLab);
        i = jSONSerialization.indexOf('"', i + fieldLab.length());
        if (i < 0)
            throw new IllegalArgumentException("The JSON serialization " +
                    "is missing the opening quote for the value for field " +
                    field);
        int j = jSONSerialization.indexOf('"', ++i);
        if (j < 0)
            throw new IllegalArgumentException("The JSON serialization " +
                    "is missing the closing quote for the value for field " + 
                    field);
        String valStr = jSONSerialization.substring(i, j);
        return URLDecoder.decode(valStr, "UTF-8");
    }

You should be able to extrapolate from here the necessary methods for retrieving all entities, updating an entity, and deleting one, so I won’t overburden you with more code.

Here is the Android form after the fan has checked his new entry:

Android client view 2

Points of Interest

Implementing your own JSON serialization of your database objects over HTTP is fun because it is simple and can pass through just about any network barriers between your client and server.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

I'm a software developer in Toronto, Ontario who has programmed and studied mostly Java off and on since 2000.

Comments and Discussions

 
QuestionMobile Enterprise Application PinmemberVarun85518-Jul-12 2:39 
AnswerRe: Mobile Enterprise Application PinmemberVarun85518-Jul-12 2:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.1411022.1 | Last Updated 30 May 2012
Article Copyright 2012 by Murray Brooks Davis
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid