Spring Data Rest + AngularJS + MongoDB

Hello everybody, this time i took little long time to post an article here. Here i came up with a very powerful integration pack called “Integration of Spring data rest with AngularJS and MongoDB”. Now a days this combination is best suited for SPA – “Single Page Application” with light weight back end support with mongodb. Now we will have a Jump start into the tutorial. this tutorial will assume the prior basic knowledge of

  • Java8
  • Spring & Spring Data Rest
  • MongoDB

the project structure looks as below.
SpringAngularJSProj

Before proceeding with the Spring integration with angularjs and mongodb, please install mongodb and start the mongodb server. if everything goes well server startup should look as below.
mongodb_server_start

Now install any GUI client for mongodb for easy access, we can even access mongodb with out client but this is for better user experience. I personally use “Robomongo” and once you connect Robomongo to MongoDB server that will look as below.
robomongo

Once everything is ready and inplace, let us start with writing the code. I have used Maven to manage the dependencies and hence the pom.xml looks as below.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.spark.ng.spring</groupId>
	<artifactId>AngularSpring</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<name>AngularSpring</name>
	<description>AngularSpring</description>

	<properties>
		<spring-version>4.1.2.RELEASE</spring-version>
		<jdk-version>1.8</jdk-version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring-version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-rest-core</artifactId>
			<version>2.3.0.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-mongodb</artifactId>
			<version>1.5.2.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-rest-webmvc</artifactId>
			<version>2.3.0.RELEASE</version>
		</dependency>

		<!-- mongodb java driver -->
		<dependency>
			<groupId>org.mongodb</groupId>
			<artifactId>mongo-java-driver</artifactId>
			<version>2.11.0</version>
		</dependency>
		
		<!-- Servlet 3.x api -->
		 <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        
	</dependencies>

	<build>
		<finalName>AngularSpring</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>2.4</version>
				<configuration>
					<warSourceDirectory>src/main/webapp</warSourceDirectory>
					<warName>AngularSpring</warName>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Once the dependencies are downloaded let us start with the configuration. In this tutorial i am moving with NoXml style of configuration.
ApplicationConfig.java

/**
 * 
 */
package com.spark.ng.spring.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.mongodb.MongoDbFactory;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

import com.mongodb.MongoClient;

/**
 * @author Mantha Pavan Kumar
 *
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages={"com.spark.ng.spring"})
@EnableMongoRepositories(basePackages={"com.spark.ng.spring.data.repository"})
@Import(RepositoryRestMvcConfiguration.class)
public class ApplicationConfig {
	
	public @Bean
    MongoDbFactory mongoDbFactory() throws Exception {
		MongoClient mongoClient = new MongoClient("localhost",27017);
        return new SimpleMongoDbFactory(mongoClient, "Customer");
    }

    public @Bean
    MongoTemplate mongoTemplate() throws Exception {
        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
        return mongoTemplate;
    }
    
	@Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".html");
 
        return viewResolver;
    }
}

WebApplicationConfig.java

/**
 * 
 */
package com.spark.ng.spring.config;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

/**
 * @author Mantha Pavan Kumar
 *
 */
public class WebApplicationConfig implements WebApplicationInitializer{

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		
		AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
		annotationConfigWebApplicationContext.register(ApplicationConfig.class);
		annotationConfigWebApplicationContext.setServletContext(servletContext);
		
		ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(annotationConfigWebApplicationContext));
		servlet.setLoadOnStartup(1);
		servlet.addMapping("/rest/*");
	}

}

With the above two files we are done with the configuration part. Let us talk about ApplicationConfig.java a little more in deep. At the top of the class please take a close look, i have annotated the class with “@EnableWebMvc”, which will enable all “@Component” based class and scans them when the application is loaded by the web container. The annotation “@EnableWebMvc” is equivalent to “”. Moving little forward “@ComponentScan” will scan all the @Component based classes. Now comes the “@EnableMongoRepositories” which will load all the repositories. Finally we have imported a core class from the Spring API called “RepositoryRestMvcConfiguration” this is the class responsible for converting the domain objects back and forth to JSON format and acts as a communication bridge between clint(browser) & backend. In this class we have innitialised two beans called “MongoDBFactory” & “MongoTemplate” since if we have any operation to do apart from Repository implementation then we can use of “MongoTemplate” object to fulfill that.

Let us have a look at the domain object Customer.java

/**
 * 
 */
package com.spark.ng.spring.domain.objects;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;


/**
 * @author PavanKumar Mantha
 *
 */
@Document(collection="customer")
public class Customer {

	@Id
	@Field
	private String id;
	@Field
	private String customerId;
	@Field
	private String firstName;
	@Field
	private String lastName;
	@Field
	private String contactAddr;
	@Field
	private String mobileNumber;
	

	/**
	 * @return the customerId
	 */
	public String getCustomerId() {
		return customerId;
	}

	/**
	 * @param customerId
	 *            the customerId to set
	 */
	public void setCustomerId(String customerId) {
		this.customerId = customerId;
	}

	/**
	 * @return the firstName
	 */
	public String getFirstName() {
		return firstName;
	}

	/**
	 * @param firstName
	 *            the firstName to set
	 */
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	/**
	 * @return the lastName
	 */
	public String getLastName() {
		return lastName;
	}

	/**
	 * @param lastName
	 *            the lastName to set
	 */
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	/**
	 * @return the contactAddr
	 */
	public String getContactAddr() {
		return contactAddr;
	}

	/**
	 * @param contactAddr
	 *            the contactAddr to set
	 */
	public void setContactAddr(String contactAddr) {
		this.contactAddr = contactAddr;
	}

	/**
	 * @return the mobileNumber
	 */
	public String getMobileNumber() {
		return mobileNumber;
	}

	/**
	 * @param mobileNumber
	 *            the mobileNumber to set
	 */
	public void setMobileNumber(String mobileNumber) {
		this.mobileNumber = mobileNumber;
	}

}

A generic Response object with which we can send messages back to browser.

/**
 * 
 */
package com.spark.ng.spring.domain.objects;

/**
 * @author Sony
 *
 */
public class ResponseObject {

	private String message;

	/**
	 * @return the message
	 */
	public String getMessage() {
		return message;
	}

	/**
	 * @param message
	 *            the message to set
	 */
	public void setMessage(String message) {
		this.message = message;
	}

}

The class “ApplicationRepositoryUtil.java will get us all the objects that are required using the @Autowired annotation.

/**
 * 
 */
package com.spark.ng.spring.data.repository;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

/**
 * @author Sony
 *
 */
@Service
public class ApplicationRepositoryUtil {

	@Autowired
	private MongoTemplate mongoTemplate;
	@Autowired
	private CustomerRepository customerRepository;

	/**
	 * @return the mongoTemplate
	 */
	public MongoTemplate getMongoTemplate() {
		return mongoTemplate;
	}

	/**
	 * @return the customerRepository
	 */
	public CustomerRepository getCustomerRepository() {
		return customerRepository;
	}

}

Here comes the exact repository that we were talking from little while. Please observe the repository carefully. Its an interface extending one more interface called “MongoRepository”.The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores.The central interface in Spring Data repository abstraction is Repository (probably not that much of a surprise). It takes the domain class to manage as well as the id type of the domain class as type arguments. This interface acts primarily as a marker interface to capture the types to work with and to help you to discover interfaces that extend this one. The CrudRepository provides sophisticated CRUD functionality for the entity class that is being managed. MongoRepository extends the basic CrudRepository.

/**
 * 
 */
package com.spark.ng.spring.data.repository;

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import com.spark.ng.spring.domain.objects.Customer;

/**
 * @author Sony
 *
 */
@RepositoryRestResource(collectionResourceRel="customer",path="customer")
public interface CustomerRepository extends MongoRepository<Customer, Integer> {

}

Here is a simple controller that will receive the requests from clients and delegates them to appropriate layers.

/**
 * 
 */
package com.spark.ng.spring.controller;

import java.util.function.Supplier;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.spark.ng.spring.data.repository.ApplicationRepositoryUtil;
import com.spark.ng.spring.domain.objects.ResponseObject;

/**
 * @author Sony
 *
 */
@RestController
@RequestMapping(value = "/app")
public class ApplicationController {

	@Autowired
	ApplicationRepositoryUtil applicationRepositoryUtil;

	@RequestMapping("/test")
	public ResponseObject handleRequest() {
		System.out
				.println("---------------------Testing method called---------------- Mongo Template :"
						+ applicationRepositoryUtil.getMongoTemplate()
						+ " Repository Object : "
						+ applicationRepositoryUtil.getCustomerRepository());
		Supplier<ResponseObject> supplier = ResponseObject::new;
		ResponseObject responseObject = supplier.get();

		responseObject.setMessage("Sample Data Object");
		return responseObject;

	}
}

Now we done with the java part and now its time for us to write the Html and java script. please download latest AngularJS library and include the “angular.min.js” inside your project, after including please write a file called “mongo.module.js” and out the following content into it.

/**
 * 
 */
var myApp = angular.module('myApp', ['controllers']);

Now write an AngularJs controller which is responsible for firing the Ajax request to the Spring and get the data in REST format. create a folder called “controllers” and create a file “controllers.js” and put the following content into it.

/**
 * 
 */

var controllersModule = angular.module('controllers', []);

controllersModule.controller('SampleController', function($scope) {
	console.log('Sample Load done !');
});

controllersModule.controller('ajaxController', function($scope, $http) {
	$http.get('/AngularSpring/rest/customer/').success(function(data) {
		console.log(data);
	});
});

Let us create an Html file “index.html” with the below content

<!DOCTYPE html>
<html lang="en" ng-app="myApp">
<head>
<meta charset="ISO-8859-1">
<title>Index Page</title>
<script type="text/javascript" src="jsLib/angular.min.js"></script>
</head>
<body>
	<div ng-controller="SampleController">Hello AngularJs</div>
	<div ng-controller="ajaxController">Hello Spring Data Rest & MongoDB</div>

</body>
<script type="text/javascript" src="mongo.module.js"></script>
<script type="text/javascript" src="controllers/controllers.js"></script>
</html>

That is it guys we are done from coding part and its time for us to build the application and deploy it to server.
Once we deploy the application into the server, please install an Add on to firefox or chrome called “RESTClient” from which we can test our repositories are working or not.
RestClient

Once you fire the request as shown in the above screen shot, we should get the response from the service as below.
REST_Response

Now try to open our page “index.html” and in firebug console we could see the same response as above which means pur angularjs is firing the request and our Spring repositories are responding to the request.

index

Happy Spring Data REst + AngularJS + MongoDB 🙂
Happy Coding 🙂

Advertisements