The objective of this article is to show a method for accessing a REST based database backend from an Android* app using MongoDB* and Spring IO*.
Table of Contents
- Introduction
- Tools Used
- Environment Setup
- Creating the Database
- Accessing the Database
- Setting up the Android Project & Permissions
- Parsing the JSON Response
- Manipulating the Database
- Summary
1. Introduction
As smartphone and tablet devices continue to grow in capabilities and popularity they are becoming a natural part of the everyday computing devices that we carry with us, know how to use, and rely on. This continues to provide opportunities for developers to innovate in software apps to take advantage of the unique portable platforms that mobile devices offer. Businesses are deploying tablet devices within their venues to enhance the customer experience and provide greater revenue opportunity. These devices have wireless connections that are capable of communicating to a backend server. A restaurant is an interesting example where tablet devices are being deployed to each table allowing the customer to browse the menu, order when they are ready, and pay their bill when they are finished. Having a centralized backend server that each tablet can communicate with allows for new opportunities in restaurant management, analytics, dynamic menus, and improved service. This article discusses a method for accessing a REST based database backend from an Android app. In particular, the first section of the article will discuss the tools used, environment setup, and how to create the restaurant database. The second section of the article will discuss accessing the database from an Android app.
2. Tools Used
When creating a proof of concept, having a flexible solution that allows you to get a server up and running quickly is desirable. The Spring IO platform is an open source solution that uses an Apache* Tomcat server and works with a variety of databases. Spring provides a framework that makes it easy to get an Android client and a Linux* server communicating over a REST interface. Components used for the server side consist of Ubuntu* Linux 14 for the operating system, MongoDB for the database, and the Spring IO platform for the REST service. The components used for the client side consist of Android Studio for the development IDE, the Spring REST Template API’s for talking to the server, the Jackson library for parsing the JSON response, and AVD for Android device emulation. Please see the links below to learn more about these components.
http://spring.io/understanding/REST
https://developer.android.com/sdk/installing/studio.html
http://wiki.fasterxml.com/JacksonHome
Environment Setup
To get the environment setup for the server and client sides, please follow the steps below to install Linux, packages, and Android Studio.
Install Ubuntu Linux 14 and connect to a local network
http://www.ubuntu.com/download
Install packages from the terminal
MongoDB
sudo apt-get update
sudo apt-get install –y mongodb-org
git
sudo apt-get install git
gradle
sudo apt-get install gradle
curl
sudo apt-get install curl
java
sudo apt-get update
sudo oracle-java8-installer
sudo oracle-java8-set-default
Install Android Studio
https://developer.android.com/sdk/index.html
3. Creating the Database
The Spring starter project provides a nice starting point for creating a persistent backend with MongoDB that uses a http JSON interface for accessing the database. It is easy to create a restaurant database that contains menu items and prices. In addition, queries can be defined in a few lines of code. See the steps below for creating the database, creating the queries, and running the service.
Get the Spring starter project
git clone https://github.com/spring-guides/gs-accessing-data-mongodb.git
Change into the initial source directory
cd gs-accessing-data-mongodb/initial/src/main/java/hello
Create the Restaurant Object (Restaurant.java)
import org.springframework.data.annotation.Id;
public class Restaurant {
@Id private String id;
private String menuCategoryName;
private String menuItemName;
private String menuItemPrice;
private Boolean isSpecial;
private String specialmenuItemPrice;
public String getmenuCategoryName() {
return menuCategoryName;
}
public void setmenuCategoryName(String menuCategoryName) {
this.menuCategoryName = menuCategoryName;
}
public String getmenuItemName() {
return menuItemName;
}
public void setmenuItemName(String menuItemName) {
this.menuItemName = menuItemName;
}
public String getmenuItemPrice() {
return menuItemPrice;
}
public void setmenuItemPrice(String menuItemPrice) {
this.menuItemPrice = menuItemPrice;
}
public Boolean getisSpecial(){
return isSpecial;
}
public void setisSpecial(Boolean isSpecial){
this.isSpecial = isSpecial;
}
public String getspecialmenuItemPrice(){
return specialmenuItemPrice;
}
public void setspecialmenuItemPrice(String specialmenuItemPrice){
this.specialmenuItemPrice = specialmenuItemPrice;
}
}
Create the Restaurant Database Repository with Queries (RestaurantRepository.java)
The path of the database root on the server will be http://localhost:8080/menu. Two queries are defined in the interface below. findByMenuItemName allows searches by menuItemName. findByMenuCategoryName allows searches by menuCategoryName.
import java.util.List;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
@RepositoryRestResource(collectionResourceRel = "menu", path = "menu")
public interface RestaurantRepository extends MongoRepository<Restaurant, String> {
List<Restaurant> findByMenuItemName(@Param("name") String name);
List<Restaurant> findByMenuCategoryName(@Param("name") String name);
}
Create the Application class (Application.java)
The application class shown below is the main entry point into the service and includes configuration for using Spring IO and MongoDB in the service.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
@Configuration
@EnableMongoRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Change into the initial directory
cd gs-accessing-data-mongodb/initial/
Build It with Gradle
./gradlew build
Run the server
java –jar build/libs/<project_name>.jar
Access the database
curl http://localhost:8080/menu
View the JSON response
In this example, the database contains a Tacos menu item
{
"_links" : {
"self" : {
"href" : "http://localhost:8080/menu{?page,size,sort}",
"templated" : true
},
"search" : {
"href" : "http://localhost:8080/menu/search"
}
},
"_embedded" : {
"menu" : [ {
"menuCategoryName" : "Mexican",
"menuItemName" : "Tacos",
"menuItemPrice" : "$15",
"isSpecial" : false,
"specialmenuItemPrice" : "$7.50",
"_links" : {
"self" : {
"href" : "http://localhost:8080/menu/5488c2ee44ae7e3fab758edd"
}
}
} ]
},
"page" : {
"size" : 20,
"totalElements" : 1,
"totalPages" : 1,
"number" : 0
}
}
4. Accessing the Database
4a. Setting up the Android project permissions & dependencies
Launch Android Studio and create a new project. Once the new project is created it is necessary to add the internet permission to the manifest, Spring Rest Template dependency, and Jackson JSON parsing dependency as shown below.
Add the Internet Permission to the manifest (AndroidManifest.xml)
<uses-permission android:name="android.permission.INTERNET">
Add the RestTemplate and Jackson library dependencies (build.gradle)
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1.1"
defaultConfig {
applicationId "com.example.test.myapplication"
minSdkVersion 7
targetSdkVersion 21
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'META-INF/ASL2.0'
exclude 'META-INF/LICENSE'
exclude 'META-INF/license.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/notice.txt'
}
}
dependencies {
compile 'com.android.support:appcompat-v7:+'
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'org.springframework.android:spring-android-rest-template:1.0.1.RELEASE'
compile 'com.fasterxml.jackson.core:jackson-databind:2.3.2'
}
4b. Parsing the JSON response
The app needs a way to understand the JSON response and be able to parse it. An approach is to create Java objects that will represent the response. The Jackson library is a convenient library that can be used to parse the JSON response and use the Java objects. To use this library, Jackson annotations are added to the Java objects. The Java objects can be manually created, but there is a handy tool out there that will assist with creating them for a given JSON response at http://www.jsonschema2pojo.org/. The Menu Class for this example is shown below with the Jackson annotations.
package com.example.test.myapplication;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import java.util.HashMap;
import java.util.Map;
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
"menuCategoryName",
"menuItemName",
"menuItemPrice",
"isSpecial",
"specialmenuItemPrice",
"_links"
})
public class Menu {
@JsonProperty("menuCategoryName")
private String menuCategoryName;
@JsonProperty("menuItemName")
private String menuItemName;
@JsonProperty("menuItemPrice")
private String menuItemPrice;
@JsonProperty("isSpecial")
private Boolean isSpecial;
@JsonProperty("specialmenuItemPrice")
private String specialmenuItemPrice;
@JsonProperty("_links")
private Links_ Links;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("menuCategoryName")
public String getMenuCategoryName() {
return menuCategoryName;
}
@JsonProperty("menuCategoryName")
public void setMenuCategoryName(String menuCategoryName) { this.menuCategoryName = menuCategoryName; }
@JsonProperty("menuItemName")
public String getMenuItemName() {
return menuItemName;
}
@JsonProperty("menuItemName")
public void setMenuItemName(String menuItemName) {
this.menuItemName = menuItemName;
}
@JsonProperty("menuItemPrice")
public String getMenuItemPrice() {
return menuItemPrice;
}
@JsonProperty("menuItemPrice")
public void setMenuItemPrice(String menuItemPrice) {
this.menuItemPrice = menuItemPrice;
}
@JsonProperty("isSpecial")
public Boolean getIsSpecial() {
return isSpecial;
}
@JsonProperty("isSpecial")
public void setIsSpecial(Boolean isSpecial) {
this.isSpecial = isSpecial;
}
@JsonProperty("specialmenuItemPrice")
public String getSpecialmenuItemPrice() {
return specialmenuItemPrice;
}
@JsonProperty("specialmenuItemPrice")
public void setSpecialmenuItemPrice(String specialmenuItemPrice) { this.specialmenuItemPrice = specialmenuItemPrice; }
@JsonProperty("_links")
public Links_ getLinks() {
return Links;
}
@JsonProperty("_links")
public void setLinks(Links_ Links) {
this.Links = Links;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) { this.additionalProperties.put(name, value); }
}
4c. Manipulating the database
The database is accessed through a simple REST based http interface for doing primitive operations such as POST, PUT, GET, and DELETE. The RestTemplate provides a nice API for performing these operations in an AsyncTask allowing the app to stay responsive. The code snippets below show an example of how to access and manipulate the restaurant database.
Creating a RestTemplate instance and server path
private RestTemplate rest = new RestTemplate();
private String url = "http://192.168.1.110:8080/menu/";
Enabling the Jackson library JSON parsing
RestTemplate rest = new RestTemplate();
rest.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
Creating a Menu Item instance
Menu myMenuItem = new Menu();
myMenuItem.setMenuCategoryName("Mexican");
myMenuItem.setMenuItemName("Tacos");
myMenuItem.setMenuItemPrice("$15");
myMenuItem.setIsSpecial(false);
myMenuItem.setSpecialmenuItemPrice("$7.50");
Adding a new menu item to the database (POST)
Adding a new menu item is a POST operation that passes a new menu item to the task
new Http_POST_Task().execute(myMenuItem);
private class Http_POST_Task extends AsyncTask<Menu,Void,Void> {
@Override
protected Void doInBackground(Menu... menuItem) {
try{
rest.postForObject(url,menuItem[0],Restaurant.class);
}
catch (Exception e) {
Log.e("MainActivity", e.getMessage(), e);
}
return null;
}
}
Deleting a menu item from the database (DELETE)
Deleting a menu item is a DELETE operation that passes the menu item to the task for deleting
new Http_DELETE_Task().execute(myMenuItem);
private class Http_DELETE_Task extends AsyncTask<Menu,Void,Void> {
@Override
protected Void doInBackground(Menu... menuItem) {
try{
//DELETE
String urlStr = menuItem[0].getLinks().getSelf().getHref();
rest.delete(new URI(urlStr));
}
catch (Exception e) {
Log.e("MainActivity", e.getMessage(), e);
}
return null;
}
}
Searching for a menu item in the database (GET)
Searching for a menu item performs the findByMenuItemName query to get the specified menu item:
try {
myMenuItem = new Http_findByMenuItemName_Task().execute("Tacos").get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
private class Http_findByMenuItemName_Task extends AsyncTask<String,Void,Menu> {
@Override
protected Menu doInBackground(String... menuItemName) {
try{
String queryURL = url+"search/findByMenuItemName?name="+menuItemName[0];
Restaurant restaurant = rest.getForObject(queryURL, Restaurant.class);
return restaurant.getEmbedded().getMenu().get(0);
}
catch (Exception e) {
Log.e("MainActivity", e.getMessage(), e);
}
return null;
}
}
Updating an existing menu item in the database (GET) (PUT)
Updating an existing menu item performs a GET query operation to get the existing menu item:
try {
myMenuItem = new Http_findByMenuItemName_Task().execute("Tacos").get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
The existing menu item is modified and then a PUT operation is performed
myMenuItem.setMenuCategoryName("Chinese");
myMenuItem.setMenuItemName("Crab Puffs");
myMenuItem.setMenuItemPrice("$7");
myMenuItem.setIsSpecial(true);
myMenuItem.setSpecialmenuItemPrice("$5");
new Http_PUT_Task().execute(myMenuItem);
private class Http_PUT_Task extends AsyncTask<Menu,Void,Void> {
@Override
protected Void doInBackground(Menu... menuItem) {
try{
String urlStr = menuItem[0].getLinks().getSelf().getHref();
rest.put(new URI(urlStr),menuItem[0]);
}
catch (Exception e) {
Log.e("MainActivity", e.getMessage(), e);
}
return null;
}
}
Summary
This article showed a method for accessing a REST based database backend from an Android app. The tools used and set up of the environment was first discussed. Creating an example restaurant database with queries using MongoDB and Spring IO was shown. The article concluded showing how to use the Spring IO framework in an Android app to parse the JSON response & manipulate the database.
++This sample source code is released under the Intel Sample Source License
About the Author
Mike Rylee is a Software Engineer with Intel Corporation. He currently works on app enabling for Android.