Implementing Basic and Advanced Search using Sencha ExtJS 6.5, Spring MVC, Spring Boot, RESTful API and MongoDB Example
Implementing Basic and Advanced Search using Sencha ExtJS 6.5, Spring MVC, Spring Boot, RESTful API and MongoDB Example
In this tutorial we will discuss how to implement basic and advanced search techniques in MongoDB using ExtJS 6.5 with Spring Boot, Spring Data and Spring MVC REST API backend. The advanced search user interface (UI) will use logical operators and build a JSON object which contains the search field name, boolean or logical operator and the search value. You are then able to add many more additional fieldSets to search for. The created JSON object will be used in the RESTful API POST call. This tutorial will also be using ExtJS Grid component for our search results and we will discuss User Interface (UI) components and how they integrate with our RESTful API backend. We have another tutorial which focuses on the Front-End User Interface (UI) components using ExtJS version 6.5.x and Sencha Cmd (Command Line Interface).
The focus of this project is create a similar and fully working application as was created using AngularJS / GridUI but with an ExtJS implementation. The look and feel will be slightly different but the functionality will be pretty close to identical. If you would like to look at my reference implementation, please refer to my original tutorial “Implementing Basic and Advanced Search using Angular Material Design, Grid-UI, Spring MVC REST API and MongoDB Example”
What is Covered in this Tutorial ?
- What is ExtJS ?
- What is Spring Boot ?
- Getting Started
- Complete Project Overview
- RESTful Web Service End Points
- Spring Boot Application
- Application Properties
- The Employee Model
- Our Controller Class
- EmployeeRepository Data Access Object (DAO) for MongoDB
- The SelectionCriteria Class
- The Field Class
- JSON Date/Time Serializer
- LogBack Configuration File
- Spring Boot Actuator
- MongoDB Employee Collection
- Testing out the Web Services
Although our Spring MVC RESTful API backend code supports the other three CRUD operations (Create, Update and Delete) our UI application will NOT focus on that aspect in this tutorial. I have focused the ExtJS User Interface (UI) on the search aspects alone. I have tested out these operations using POSTMAN and have provided screenshots below.
In our advanced tab, I have added the ability to add rows dynamically to the form. ExtJS supports dynamic form creating using Ext.Container and insert(index, item) method which will allows child components to be added at a given index. We will discuss these in more details in our other tutorial.
What is ExtJS ?
ExtJS is one of the premiere Javascript frameworks available for building data intensive, cross platform web applications. ExtJS features more than 100 UI components are high-performance and have been pre-tested. These include Buttons, Panels, Forms, Tabs, Grids, Trees, Charts, Calendar and many many more…
ExtJS started it’s life under the BSD license and gradually changed to a more restrictive LGPL license model. Over time the authors gradually modified the license terms and nowadays ExtJS is released under the dual license structure of GNU General Public License version 3 (GPLv3) or the proprietary commercial pay per seat license.
Advantages of ExtJS
- ExtJS has one of the largest component libraries of any javascript framework with hundreds of components
- It has been battle tested for over 10 years and used in thousands of applications worldwide
- ExtJS includes rich framework for building web based applications using MVC and MVVM.
- Continued legacy browser support
- ExtJS makes it easy to develop web based applications without the need to learn Typescript
- ExtJS is supported by a suite of excellent design tools developed by Sencha
What is Spring Boot ?
Spring Boot was created by the folks at Pivotal to simply the configuration in Spring-based applications in order to get you up and running in minimal time and minimum effort. In order to accomplish this goal, Spring Boot takes an opinionated view and pre-configures the default settings for Spring Boot applications.
Spring Boot Features
- Ability to easily create stand-alone Spring based applications
- Ability to Embed Tomcat, Jetty or Undertow application web servers
- Automatically configure Spring whenever possible
- Provides the ability to monitor and manage your application with professional-level metrics and health checks
- No requirement for XML configurations
Spring Boot / Spring MVC Search Application
Getting Started
In order to run this tutorial yourself, you will need the following:
- Java JDK 1.7 or greater (although I used JDK 1.8)
- Favorite IDE Spring Tool Suite (STS), Eclipse IDE or NetBeans (I happen to be using STS because it comes with a Tomcat server built-in)
- Spring Boot open source solution for creating stand-alone spring applications using opinionated configuration parameters for no-fuss/quick starting applications.
- ExtJS – ExtJS is one of the premiere Javascript frameworks available for building data intensive, cross platform web applications.
- MongoDB is an open-source document database designed for ease of development and scaling.
- Maven is an open-source project management and comprehension tool designed for assisting in the application build lifecycle. Maven simplifies the build process by enforcing standards and favoring convention over configuration.
Required Libraries
For this project we are using Maven, however to make things easier for you, please refer to the following list of libraries.
spring-boot-starter-web-1.5.2.RELEASE.jar spring-boot-starter-1.5.2.RELEASE.jar spring-boot-1.5.2.RELEASE.jar spring-boot-autoconfigure-1.5.2.RELEASE.jar spring-boot-starter-logging-1.5.2.RELEASE.jar logback-classic-1.1.11.jar logback-core-1.1.11.jar jul-to-slf4j-1.7.24.jar log4j-over-slf4j-1.7.24.jar spring-core-4.3.7.RELEASE.jar snakeyaml-1.17.jar spring-boot-starter-tomcat-1.5.2.RELEASE.jar tomcat-embed-core-8.5.11.jar tomcat-embed-el-8.5.11.jar tomcat-embed-websocket-8.5.11.jar hibernate-validator-5.3.4.Final.jar validation-api-1.1.0.Final.jar jboss-logging-3.3.0.Final.jar classmate-1.3.3.jar jackson-databind-2.8.7.jar jackson-annotations-2.8.0.jar jackson-core-2.8.7.jar spring-web-4.3.7.RELEASE.jar spring-aop-4.3.7.RELEASE.jar spring-beans-4.3.7.RELEASE.jar spring-context-4.3.7.RELEASE.jar spring-webmvc-4.3.7.RELEASE.jar spring-expression-4.3.7.RELEASE.jar spring-boot-starter-actuator-1.5.2.RELEASE.jar spring-boot-actuator-1.5.2.RELEASE.jar commons-pool-1.6.jar commons-lang-2.6.jar spring-boot-starter-data-mongodb-1.5.2.RELEASE.jar mongodb-driver-3.4.2.jar bson-3.4.2.jar mongodb-driver-core-3.4.2.jar spring-data-mongodb-1.10.1.RELEASE.jar spring-tx-4.3.7.RELEASE.jar spring-data-commons-1.13.1.RELEASE.jar slf4j-api-1.7.24.jar jcl-over-slf4j-1.7.24.jar spring-boot-starter-thymeleaf-1.5.2.RELEASE.jar thymeleaf-spring4-2.1.5.RELEASE.jar thymeleaf-2.1.5.RELEASE.jar ognl-3.0.8.jar javassist-3.21.0-GA.jar unbescape-1.1.0.RELEASE.jar thymeleaf-layout-dialect-1.4.0.jar groovy-2.4.9.jar mongo-java-driver-3.4.2.jar jstl-1.2.jar junit-3.8.1.jar
Complete Project Overview
I have added the project overview to give you a full view of the structure and show you all files contained in this sample project.
As you can see from the tree view below, Maven standardizes the Java directory structure such that all Java sources will be compiled from src/main/java. Additionally, resources folder will reside in src/main/resources and all test case source files will reside in the src/test/java folder.

Spring Maven Dependencies (pom.xml)
For this application we will be using Maven to manage our build. Maven uses a pom.xml file to hold all of our project dependencies. POM stands for “Project Object Model“. This pom.xml file will contain all of the necessary information about our project as well as the configurations for any plugins uses during our build process.
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.avaldes</groupId> <artifactId>directoryservices</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>EmployeeDirectoryBoot Maven Webapp</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>commons-pool</groupId> <artifactId>commons-pool</artifactId> <version>1.6</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <finalName>directoryservices</finalName> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <!-- <version>3.0</version> --> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <executable>true</executable> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> <archive> <manifest> <addDefaultImplementationEntries> true </addDefaultImplementationEntries> <addDefaultSpecificationEntries> true </addDefaultSpecificationEntries> </manifest> <manifestEntries> <Implementation-Build> ${buildNumber} </Implementation-Build> <Build-Timestamp> ${maven.build.timestamp} </Build-Timestamp> </manifestEntries> </archive> </configuration> </plugin> </plugins> </build> </project>
RESTful Web Service End Points
In keeping with RESTful endpoint standards I am providing the ability to use either Path Parameters or Request Parameters with all of the CRUD endpoint URIs.
/directoryservices/v1/employees/{id} will be available for QUERY using the GET method.
/directoryservices/employees will be available for ADD using the POST method.
/directoryservices/employees/{id} will be available for UPDATE using the PUT method.
/directoryservices/employees/{id} will be available for DELETE using the DELETE method.
# | URI | Method | Description |
---|---|---|---|
1 | /directoryservices/status | GET | Displays the standard status message. |
2 | /directoryservices/v1/employees | GET | Retrieves all employee objects from MongoDB returning them as a JSON array or XML object. |
3 | /directoryservices/v1/employees/{id} | GET | Retrieves an employee given the ID, returning the employee as JSON or XML object. |
4 | /directoryservices/v1/search/std | GET | Retrieves all employee objects from MongoDB that match either the firstName, lastName or both, returning matches as a JSON array or XML document. |
5 | /directoryservices/v1/search/adv | GET | Retrieves employee objects from MongoDB that match selection criteria returning matches as a JSON array or XML document. |
6 | /directoryservices/v1/employees | POST | Inserts the employee into our MongoDB data store based on the contents of the JSON or XML object |
7 | /directoryservices/v1/employees/{id} | PUT | Updates employee in our MongoDB data store based on the contents of the JSON or XML object |
8 | /directoryservices/v1/employees/{id} | DELETE | Delete employee in our MongoDB data store based on the ID |
Spring Boot Application (EmployeeDirectoryApplication.java)
The Spring Boot entry point follows the same conventions as a standard Java application with the main method. This main method in turn, starts the Spring application by running SpringApplication.run. SpringApplication bootstraps out application and starts Spring which in turns starts the embedded Tomcat, Jetty or Undertow applications web servers.
package com.avaldes; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class EmployeeDirectoryApplication { public static void main(String[] args) { SpringApplication.run(EmployeeDirectoryApplication.class, args); } }
Our Controller Class (DirectoryServicesController.java)
Our DirectoryServicesController class is the main class that contains all web service mapping end points defined in our table above. The @RestController annotation indicates that this particular class is playing the role of a controller.
package com.avaldes.tutorial; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.avaldes.dao.EmployeeRepository; import com.avaldes.model.Employee; import com.avaldes.model.Employees; import com.avaldes.model.SelectionCriteria; import com.avaldes.model.StatusMessage; /** * Handles requests for the application home page. */ @RestController public class DirectoryServicesController { private static final Logger logger = (Logger) LoggerFactory .getLogger(DirectoryServicesController.class); public static final String APPLICATION_JSON = "application/json; charset=UTF-8"; public static final String APPLICATION_XML = "application/xml; charset=UTF-8"; public static final String APPLICATION_HTML = "text/html"; @Autowired private EmployeeRepository employeeRepository; /** * Simply selects the home view to render by returning its name. * */ @RequestMapping(value = "/status", method = RequestMethod.GET, produces = APPLICATION_HTML) public @ResponseBody String status() { logger.info("Inside status() method..."); return "application OK..."; } @RequestMapping(value = "/error", method = RequestMethod.GET) public @ResponseBody String getErrorMessage() { return "error"; } @RequestMapping(value = "/v1/employees", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<?> getAllEmployeesJson() { logger.info("Inside getAllEmployees() method..."); List<Employee> allEmployees = employeeRepository .getAllEmployees(); return new ResponseEntity<>(allEmployees, HttpStatus.OK); } @RequestMapping(value = "/v1/employees", method = RequestMethod.GET, produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity<?> getAllEmployeesXml() { logger.info("Inside getAllEmployees() method..."); Employees allEmployees = new Employees(employeeRepository .getAllEmployees()); return new ResponseEntity<>(allEmployees, HttpStatus.OK); } @RequestMapping(value="/v1/employees/{id}", method=RequestMethod.GET) public ResponseEntity<?> getEmployeeById( @PathVariable(value = "id", required = false) String id) { if (id == null || id.isEmpty()) { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.BAD_REQUEST.value()); statusMessage.setMessage("'id' is a required field for this request"); if (logger.isInfoEnabled()) { logger.info("'id' is a required field for this request"); } return new ResponseEntity<>(statusMessage, HttpStatus.BAD_REQUEST); } Employee employee = employeeRepository.getEmployeeById(id); if (employee == null) { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.NOT_FOUND.value()); statusMessage.setMessage("'id' is a required field for this request"); if (logger.isInfoEnabled()) { logger.info("Inside getEmployeeById, ID: " + id + ", NOT FOUND!"); } return new ResponseEntity<>(statusMessage, HttpStatus.NOT_FOUND); } if (logger.isInfoEnabled()) { logger.info("Inside getEmployeeById, returned: "+employee.toString()); } return new ResponseEntity<>(employee, HttpStatus.OK); } @RequestMapping(value = "/v1/search/std", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<?> standardSearchJson( @RequestParam(value = "firstName", required = false) String firstName, @RequestParam(value = "lastName", required = false) String lastName) { logger.info("Inside standardSearchJson() method..."); logger.info("firstName....: " + firstName); logger.info("lastName.....: " + lastName); if (firstName == null && lastName == null) { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.BAD_REQUEST.value()); statusMessage .setMessage("Both firstName and lastName may not be empty."); logger.error( "Both firstName and lastName may not be empty. Search aborted!!!"); return new ResponseEntity<>(statusMessage, HttpStatus.BAD_REQUEST); } else { List<Employee> filteredAssociates = employeeRepository .getEmployeesStandardSearch(firstName, lastName); return new ResponseEntity<>(filteredAssociates, HttpStatus.OK); } } @RequestMapping(value = "/v1/search/std", method = RequestMethod.POST, produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity<?> standardSearchXML( @RequestParam(value = "firstName", required = false) String firstName, @RequestParam(value = "lastName", required = false) String lastName) { logger.info("Inside standardSearchXML() method..."); logger.info("firstName....: " + firstName); logger.info("lastName.....: " + lastName); if (firstName == null && lastName == null) { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.BAD_REQUEST.value()); statusMessage .setMessage("Both firstName and lastName may not be empty."); logger.error( "Both firstName and lastName may not be empty. Search aborted!!!"); return new ResponseEntity<>(statusMessage, HttpStatus.BAD_REQUEST); } else { Employees filteredAssociates = new Employees(employeeRepository .getEmployeesStandardSearch(firstName, lastName)); return new ResponseEntity<>(filteredAssociates, HttpStatus.OK); } } @RequestMapping(value = "/v1/search/adv", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<?> advancedSearchJson( @RequestBody List<SelectionCriteria> criteriaList) { logger.info("Inside advancedSearchJson() method..."); List<Employee> filteredAssociates = employeeRepository .getEmployeesBySelectionCriteria(criteriaList); return new ResponseEntity<>(filteredAssociates, HttpStatus.OK); } @RequestMapping(value = "/v1/search/adv", method = RequestMethod.POST, produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity<?> advancedSearchXml( @RequestBody List<SelectionCriteria> criteriaList) { logger.info("Inside advancedSearchXml() method..."); Employees filteredAssociates = new Employees(employeeRepository .getEmployeesBySelectionCriteria(criteriaList)); return new ResponseEntity<>(filteredAssociates, HttpStatus.OK); } @RequestMapping(value="/v1/employees/{id}", method=RequestMethod.DELETE) public ResponseEntity<?> deleteEmployeeById( @PathVariable(value = "id", required = false) String id) { if (id == null || id.isEmpty()) { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.BAD_REQUEST.value()); statusMessage.setMessage("'id' is a required field for this request"); if (logger.isInfoEnabled()) { logger.info("'id' is a required field for this request"); } return new ResponseEntity<>(statusMessage, HttpStatus.BAD_REQUEST); } Employee employee = employeeRepository.deleteEmployee(id); if (employee == null) { if (logger.isInfoEnabled()) { logger.info( "Inside deleteEmployeeById, ID: " + id + ", NOT FOUND!"); } StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.NOT_FOUND.value()); statusMessage.setMessage("Unable to delete employee ID: " + id); if (logger.isInfoEnabled()) { logger.info("Inside getEmployeeById, ID: " + id + ", NOT FOUND!"); } return new ResponseEntity<>(statusMessage, HttpStatus.NOT_FOUND); } if (logger.isInfoEnabled()) { logger.info("Inside deleteEmployeeById, deleted: " + employee.toString()); } return new ResponseEntity<>(employee, HttpStatus.OK); } @RequestMapping(value="/v1/employees/{id}", method=RequestMethod.PUT) public ResponseEntity<?> updateEmployeeById( @PathVariable(value = "id", required = false) String id, @RequestBody Employee employee) { if (id == null || id.isEmpty()) { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.BAD_REQUEST.value()); statusMessage.setMessage("'id' is a required field for this request"); if (logger.isInfoEnabled()) { logger.info("'id' is a required field for this request"); } return new ResponseEntity<>(statusMessage, HttpStatus.BAD_REQUEST); } Employee myEmployee = employeeRepository.updateEmployee(id, employee); if (myEmployee == null) { if (logger.isInfoEnabled()) { logger.info( "Unable to update employee. ID: " + id + ", NOT FOUND!"); } StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.NOT_FOUND.value()); statusMessage.setMessage("Unable to delete employee ID: " + id); return new ResponseEntity<>(statusMessage, HttpStatus.NOT_FOUND); } if (logger.isInfoEnabled()) { logger.info("Inside updateEmployeeById, updated: " + myEmployee.toString()); } return new ResponseEntity<>(myEmployee, HttpStatus.OK); } @RequestMapping(value = "/v1/employees", method = RequestMethod.POST) public ResponseEntity<?> addEmployee( @RequestBody Employee employee) { logger.info("Inside addEmployee, model attribute: " + employee.toString()); if (employee.getId() == null || employee.getId().isEmpty()) { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.BAD_REQUEST.value()); statusMessage.setMessage("'id' is a required field for this request"); if (logger.isInfoEnabled()) { logger.info("'id' is a required field for this request"); } return new ResponseEntity<>(statusMessage, HttpStatus.BAD_REQUEST); } Employee myEmployee = employeeRepository .getEmployeeById(employee.getId()); if (myEmployee != null) { if (myEmployee.getId() != null && myEmployee.getId().equalsIgnoreCase(employee.getId())) { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.CONFLICT.value()); statusMessage.setMessage("ID already exists in the system."); if (logger.isInfoEnabled()) { logger.info("'id' is a required field for this request"); } return new ResponseEntity<>(statusMessage, HttpStatus.CONFLICT); } } if (employee.getId() != null && employee.getId().length() > 0) { logger.info("Inside addEmployee, adding: " + employee.toString()); employeeRepository.addEmployee(employee); } else { StatusMessage statusMessage = new StatusMessage(); statusMessage.setStatus(HttpStatus.NOT_MODIFIED.value()); statusMessage.setMessage("Failed to add employee"); if (logger.isInfoEnabled()) { logger.info("Failed to insert..."); } return new ResponseEntity<>(statusMessage, HttpStatus.NOT_MODIFIED); } return new ResponseEntity<>(employee, HttpStatus.OK); } }
Employees Class (Employees.java)
This class is used as a container or wrapper class when using XML responses for list of Employees.
package com.avaldes.model; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "employees") public class Employees { private List<Employee> employees; public Employees() {} public Employees(List<Employee> employees) { super(); this.employees = employees; } @XmlElement(name = "employee") public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } @Override public String toString() { return "Employees []"; } }
StatusMessage Class (StatusMessage.java)
This class is used to display JSON or XML responses in a standard format with status_code and a message component.
package com.avaldes.model; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; @JsonPropertyOrder({"status", "message"}) @XmlType (propOrder={"status", "message"}) @XmlRootElement(name = "statusmessage") public class StatusMessage { private Integer status; private String message; public StatusMessage() { } @JsonProperty(value = "status") public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } @JsonProperty(value = "message") public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "StatusMessage [status=" + status + ", message=" + message + "]"; } }
DateUtility Class (DateUtility.java)
package com.avaldes.util; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateUtility { public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; private DateUtility() { } public static Date getDate(String dateStr) { final DateFormat formatter = new SimpleDateFormat(DATETIME_FORMAT); try { return formatter.parse(dateStr); } catch (ParseException e) { return null; } } public static Date getDate(String dateStr, String format) { final DateFormat formatter = new SimpleDateFormat(format); try { return formatter.parse(dateStr); } catch (ParseException e) { return null; } } }
EmployeeRepository Data Access Object (DAO) for MongoDB (EmployeeRepository.java)
In this class you will notice two annotations being used. The first one, @Repository indicates that the class EmployeeRepository fulfills the role of a Data Access Object of a repository. This class will handle all of the persistence of Employee objects and database access for us.
The second annotation, @Autowired indicates that MongoTemplate is autowired from the Spring configuration, in this case our dispatcher-servlet.xml file.
In this scenario, we use Spring Boot’s auto-configuration features and opinionated defaults for configuring and setting up our Mongo Repository.
package com.avaldes.dao; import java.util.ArrayList; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Repository; import org.springframework.web.bind.annotation.RequestParam; import com.avaldes.model.Employee; import com.avaldes.model.SelectionCriteria; import com.avaldes.util.DateUtility; @Repository public class EmployeeRepository { public static final String COLLECTION_NAME = "associate"; private static final Logger logger = LoggerFactory .getLogger(EmployeeRepository.class); @Autowired private MongoTemplate mongoTemplate; public void addEmployee(Employee employee) { if (!mongoTemplate.collectionExists(Employee.class)) { mongoTemplate.createCollection(Employee.class); } mongoTemplate.insert(employee, COLLECTION_NAME); } public Employee getEmployeeById(String id) { return mongoTemplate.findOne( Query.query(Criteria.where("id").is(id)), Employee.class, COLLECTION_NAME); } public List<Employee> getAllEmployees() { return mongoTemplate.findAll(Employee.class, COLLECTION_NAME); } public List<Employee> getEmployeesStandardSearch( @RequestParam("firstName") String firstName, @RequestParam("lastName") String lastName) { List<Criteria> andCriteriaList = new ArrayList<Criteria>(); boolean ok = false; Query query = new Query(); if (firstName != null && firstName.length() > 0) { Criteria c1 = Criteria.where("first_name").regex(firstName, "i"); andCriteriaList.add(c1); ok = true; } if (lastName != null && lastName.length() > 0) { Criteria c1 = Criteria.where("last_name").regex(lastName, "i"); andCriteriaList.add(c1); ok = true; } if (ok) { query.addCriteria(new Criteria().andOperator(andCriteriaList .toArray(new Criteria[andCriteriaList.size()]))); return mongoTemplate.find(query, Employee.class, COLLECTION_NAME); } else { return null; } } public List<Employee> getEmployeesBySelectionCriteria( List<SelectionCriteria> criteriaList) { List<Criteria> andCriteriaList = new ArrayList<Criteria>(); Query query = new Query(); for (SelectionCriteria criteriaElem : criteriaList) { if (criteriaElem.getOperator().getId().equals("equalTo")) { Criteria c1 = Criteria.where(criteriaElem.getField().getId()) .is(criteriaElem.getValue()); andCriteriaList.add(c1); } else if (criteriaElem.getOperator().getId().equals("like")) { Criteria c1 = Criteria.where(criteriaElem.getField().getId()) .regex(criteriaElem.getValue(), "i"); andCriteriaList.add(c1); } else if (criteriaElem.getOperator().getId() .equals("notEqualTo")) { Criteria c1 = Criteria.where(criteriaElem.getField().getId()) .ne(criteriaElem.getValue()); andCriteriaList.add(c1); } else if (criteriaElem.getOperator().getId() .equals("greaterThan")) { Criteria c1 = Criteria.where(criteriaElem.getField().getId()) .gt(DateUtility.getDate(criteriaElem.getValue())); andCriteriaList.add(c1); } else if (criteriaElem.getOperator().getId() .equals("lessThan")) { Criteria c1 = Criteria.where(criteriaElem.getField().getId()) .lt(DateUtility.getDate(criteriaElem.getValue())); andCriteriaList.add(c1); } logger.info(criteriaElem.toString()); } query.addCriteria(new Criteria().andOperator(andCriteriaList .toArray(new Criteria[andCriteriaList.size()]))); return mongoTemplate.find(query, Employee.class, COLLECTION_NAME); } public Employee deleteEmployee(String id) { Employee Employee = mongoTemplate.findOne( Query.query(Criteria.where("id").is(id)), Employee.class, COLLECTION_NAME); mongoTemplate.remove(Employee, COLLECTION_NAME); return Employee; } public Employee updateEmployee(String id, com.avaldes.model.Employee Employee) { Query query = new Query(); query.addCriteria(Criteria.where("id").is(id)); Update update = new Update(); update.set("id", Employee.getId()); update.set("short_name", Employee.getShort_name()); update.set("first_name", Employee.getFirst_name()); update.set("last_name", Employee.getLast_name()); update.set("job_desc", Employee.getJob_desc()); update.set("employee_type", Employee.getEmployee_type()); update.set("employee_status", Employee.getEmployee_status()); update.set("location_type", Employee.getLocation_type()); update.set("title_desc", Employee.getTitle_desc()); update.set("alt_title", Employee.getAlt_title()); update.set("cost_center", Employee.getCost_center()); update.set("working_shift", Employee.getWorking_shift()); update.set("preferred_name", Employee.getPreferred_name()); update.set("middle", Employee.getMiddle()); update.set("full_name", Employee.getFull_name()); update.set("country", Employee.getCountry()); update.set("company_name", Employee.getCompany_name()); update.set("company_code", Employee.getCompany_code()); update.set("department", Employee.getDepartment()); update.set("region", Employee.getRegion()); update.set("district", Employee.getDistrict()); update.set("building", Employee.getBuilding()); update.set("floor", Employee.getFloor()); update.set("section", Employee.getSection()); update.set("section_num", Employee.getSection_num()); update.set("phone", Employee.getPhone()); update.set("extension", Employee.getExtension()); update.set("manager_id", Employee.getManager_id()); update.set("manager_name", Employee.getManager_name()); update.set("email", Employee.getEmail()); update.set("hire_date", Employee.getHire_date()); update.set("is_active", Employee.getIs_active()); mongoTemplate.updateFirst(query, update, Employee.class, COLLECTION_NAME); return Employee; } }
The SelectionCriteria Class (SelectionCriteria.java)
package com.avaldes.model; public class SelectionCriteria { private Field field; private Operator operator; private String value; public Field getField() { return field; } public void setField(Field field) { this.field = field; } public Operator getOperator() { return operator; } public void setOperator(Operator operator) { this.operator = operator; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } @Override public String toString() { return "SelectionCriteria [field="+field+", operator="+operator + ", value=" + value + "]"; } }
The Operator Class (Operator.java)
package com.avaldes.model; public class Operator { private String id; private String name; private String type; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String toString() { return "Operator [id="+id+", name="+name+", type="+type+"]"; } }
The Field Class (Field.java)
package com.avaldes.model; public class Field { private String id; private String name; private String type; private String selected; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSelected() { return selected; } public void setSelected(String selected) { this.selected = selected; } @Override public String toString() { return "Field [id=" + id + ", name=" + name + ", type=" + type + ", selected=" + selected + "]"; } }
JSON Data/Time Serializer (JsonDateTimeSerializer.java)
In our example, we use the @JsonSerialize Jackson annotation in order to specify how to serialize the date field in the Employee class.
package com.avaldes.util; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.context.annotation.ComponentScan; import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; @Component @ComponentScan("com.avaldes.util") public class JsonDateTimeSerializer extends JsonSerializer<Date> { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSZ"); @Override public void serialize(Date date, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException { String formattedDate = dateFormat.format(date); gen.writeString(formattedDate); } }
LogBack Configuration File (logback.xml)
<?xml version="1.0" encoding="UTF-8"?> <configuration> <property name="USER_HOME" value="/local1/directoryservices/logs" /> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <layout class="ch.qos.logback.classic.PatternLayout"> <Pattern> %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n </Pattern> </layout> </appender> <appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${USER_HOME}/DirectoryService.log</file> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <Pattern> %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n </Pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- rollover daily --> <fileNamePattern> ${USER_HOME}/archived/archive.%d{yyyy-MM-dd}.%i.log </fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>10MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> </appender> <logger name="org.springframework" level="info" additivity="false"> <appender-ref ref="STDOUT" /> </logger> <logger name="com.avaldes" level="info" additivity="false"> <appender-ref ref="STDOUT" /> <appender-ref ref="fileAppender" /> </logger> <root level="error"> <appender-ref ref="STDOUT" /> </root> </configuration>
Spring Boot Actuator
Spring Boot Actuator makes it easy to collect critical metrics from the application, its environment and monitor the application’s health. Without this insight you would be hard-pressed to really know what is going on in your spring-based application. By using the Actuator module available in Spring Boot we are able to implement production-grade features like metrics, health check, traces, security and administration.
Set up is extremely straight forward, you need only add the following dependency in your application’s pom.xml file.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
and add the following lines into the application.properties file.
management.port: 8181 management.context-path=/manage management.security.enabled=false
I added a few more lines to provide some additional application-level details that will be available when using the /info endpoint.
info.app.name=Spring Boot Directory Services Application info.app.description=Spring Boot Directory Services Application info.app.version=1.0.0 info.app.developer=Amaury Valdes
{ "app": { "version": "1.0.0", "developer": "Amaury Valdes", "description": "Spring Boot Directory Services Application", "name": "Spring Boot Directory Services Application" } }
{ "status": "UP", "diskSpace": { "status": "UP", "total": 499578830848, "free": 329006968832, "threshold": 10485760 }, "mongo": { "status": "UP", "version": "3.2.10" } }
{ "mem": 335731, "mem.free": 125347, "processors": 4, "instance.uptime": 2289729, "uptime": 2292650, "systemload.average": -1, "heap.committed": 283136, "heap.init": 131072, "heap.used": 157788, "heap": 1840640, "nonheap.committed": 53568, "nonheap.init": 2496, "nonheap.used": 52595, "nonheap": 0, "threads.peak": 39, "threads.daemon": 36, "threads.totalStarted": 46, "threads": 39, "classes": 7486, "classes.loaded": 7486, "classes.unloaded": 0, "gc.ps_scavenge.count": 8, "gc.ps_scavenge.time": 58, "gc.ps_marksweep.count": 2, "gc.ps_marksweep.time": 115, "httpsessions.max": -1, "httpsessions.active": 0 }
{ "/webjars/**": { "bean": "resourceHandlerMapping" }, "/**": { "bean": "resourceHandlerMapping" }, "/**/favicon.ico": { "bean": "faviconHandlerMapping" }, "{[/status],methods=[GET],produces=}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.String com.avaldes.tutorial.DirectoryServicesController.status()" }, "{[/error],methods=[GET]}": { "bean": "requestMappingHandlerMapping", "method": "public java.lang.String com.avaldes.tutorial.DirectoryServicesController.getErrorMessage()" }, "{[/v1/employees],methods=[GET],produces=[application/json]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<?> com.avaldes.tutorial.DirectoryServicesController.getAllEmployeesJson()" }, "{[/v1/employees/{id}],methods=[PUT]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<?> com.avaldes.tutorial.DirectoryServicesController.updateEmployeeById(java.lang.String,com.avaldes.model.Employee)" }, "{[/v1/employees],methods=[GET],produces=[application/xml]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<?> com.avaldes.tutorial.DirectoryServicesController.getAllEmployeesXml()" }, "{[/v1/employees/{id}],methods=[DELETE]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<?> com.avaldes.tutorial.DirectoryServicesController.deleteEmployeeById(java.lang.String)" }, "{[/v1/employees],methods=[POST]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<?> com.avaldes.tutorial.DirectoryServicesController.addEmployee(com.avaldes.model.Employee)" }, "{[/v1/employees/{id}],methods=[GET]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<?> com.avaldes.tutorial.DirectoryServicesController.getEmployeeById(java.lang.String)" }, "{[/v1/search/std],methods=[POST]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<?> com.avaldes.tutorial.DirectoryServicesController.standardSearch(java.lang.String,java.lang.String)" }, "{[/v1/search/adv],methods=[POST]}": { "bean": "requestMappingHandlerMapping", "method": "public java.util.List<com.avaldes.model.Employee> com.avaldes.tutorial.DirectoryServicesController.advancedSearch(java.util.List<com.avaldes.model.SelectionCriteria>)" }, "{[/error]}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)" }, "{[/error],produces=}": { "bean": "requestMappingHandlerMapping", "method": "public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)" } }
MongoDB Employee Collection
On MongoDB, please make sure you insert the following records in order to test this application locally otherwise you won’t have any data stored in your MongoDB local instance.
MongoDB Prerequiste
- Have an instance on MongoDB running locally on your machine (run the mongod command)
- Use a MongoDB client tool like RoboMongo or MongoChef (alternately you can simply use mongo command line interface)
For detailed MongoDB shell basics, please visit MongoDB Shell Basics – Insert, Update, Find, Delete and Indexing tutorial.
{ "_id" : "00001", "short_name": "avaldes", "job_desc" : "IT Development", "employee_type" : "permanent", "employee_status" : "active", "location_type" : "domestic", "title_desc" : "Senior Developer", "alt_title" : "developer", "cost_center" : "1025", "working_shift" : 1, "first_name" : "Amaury", "preferred_name" : "Amaury", "middle" : "", "last_name" : "Valdes", "full_name" : "Amaury Valdes", "country" : "USA", "company_name" : "Lark Productions", "company_code" : 121, "department" : "Product Development", "region": "NorthEast", "district": "NJEast", "building": "800B", "floor": "2", "section": "C", "section_num": "302", "phone": "800-555-1212", "extension": "x4555", "manager_id": "pmcneal", "manager_name": "Paul McNeal", "email": "amaury@lark.com", "hire_date" : ISODate("2012-05-18T04:00:00.0001Z"), "is_active" : false } { "_id" : "00002", "short_name": "sadelson", "job_desc" : "IT Management", "employee_type" : "permanent", "employee_status" : "active", "location_type" : "domestic", "title_desc" : "Senior Manager", "alt_title" : "manager", "cost_center" : "1025", "working_shift" : 1, "first_name" : "Steven", "preferred_name" : "Steve", "middle" : "J", "last_name" : "Adelson", "full_name" : "Steven Adelson", "country" : "USA", "company_name" : "Lark Productions", "company_code" : 121, "department" : "Product Development", "region": "NorthEast", "district": "NJEast", "building": "800B", "floor": "1", "section": "B", "section_num": "102", "phone": "800-555-1212", "extension": "x3223", "manager_id": "khenderson", "manager_name": "Keith Henderson", "email": "sadelson@lark.com", "hire_date" : ISODate("2010-03-02T04:00:00.0001Z"), "is_active" : true } { "_id" : "00003", "short_name": "rpaterson", "job_desc" : "Senior Management", "employee_type" : "permanent", "employee_status" : "active", "location_type" : "domestic", "title_desc" : "Senior Group Manager", "alt_title" : "manager", "cost_center" : "1025", "working_shift" : 1, "first_name" : "Robert", "preferred_name" : "Bob", "middle" : "", "last_name" : "Paterson", "full_name" : "Robert Paterson", "country" : "USA", "company_name" : "Lark Animation, LLC", "company_code" : 122, "department" : "Animation Studio", "region": "MidWest", "district": "Ch08", "building": "3902F", "floor": "3", "section": "DD", "section_num": "344", "phone": "800-849-8878", "extension": "x4904", "manager_id": "ganderson", "manager_name": "Greg Anderson", "email": "ganderson@larkAnimation.com", "hire_date" : ISODate("2010-09-04T04:00:00.0001Z"), "is_active" : true } { "_id" : "00004", "short_name": "sjefferies", "job_desc" : "Receptionist", "employee_type" : "temp", "employee_status" : "active", "location_type" : "domestic", "title_desc" : "Front Desk Reception", "alt_title" : "receptionist", "cost_center" : "1025", "working_shift" : 1, "first_name" : "Sandra", "preferred_name" : "Sandy", "middle" : "", "last_name" : "Jeffries", "full_name" : "Sandra Jeffries", "country" : "USA", "company_name" : "Kelly Temps", "company_code" : 322, "department" : "Office Support", "region": "South", "district": "Tx5", "building": "TT8800", "floor": "1", "section": "1B", "section_num": "200", "phone": "888-263-3222", "extension": "x203", "manager_id": "rwilliams", "manager_name": "Roger Williams", "email": "rwilliams@kelly.com", "hire_date" : ISODate("2008-12-23T04:00:00.0001Z"), "is_active" : true } { "_id" : "00005", "short_name": "csmith", "job_desc" : "Developer", "employee_type" : "permanent", "employee_status" : "active", "location_type" : "domestic", "title_desc" : "Front-End Developer", "alt_title" : "developer", "cost_center" : "982", "working_shift" : 1, "first_name" : "Christopher", "preferred_name" : "Chris", "middle" : "", "last_name" : "Smith", "full_name" : "Christopher Smith", "country" : "USA", "company_name" : "Lark Productions", "company_code" : 121, "department" : "Development Support", "region": "NorthEast", "district": "NJEast", "building": "800B", "floor": "4", "section": "WW", "section_num": "700", "phone": "800-555-1212", "extension": "x3738", "manager_id": "ltillnow", "manager_name": "Larry Tillnow", "email": "csmith@lark.com", "hire_date" : ISODate("2010-05-02T04:00:00.0001Z"), "is_active" : true } { "_id" : "00006", "short_name": "cbarnes", "job_desc" : "Developer", "employee_type" : "consultant", "employee_status" : "active", "location_type" : "domestic", "title_desc" : "Front-End Developer", "alt_title" : "developer", "cost_center" : "982", "working_shift" : 1, "first_name" : "Christa", "preferred_name" : "Chrissy", "middle" : "", "last_name" : "Barnes", "full_name" : "Christa Barnes", "country" : "USA", "company_name" : "Sapient Technologies", "company_code" : 572, "department" : "Development Support", "region": "West", "district": "CaWest", "building": "650", "floor": "3", "section": "G", "section_num": "540", "phone": "866-433-2393", "extension": "x400", "manager_id": "bclark", "manager_name": "Brian Clark", "email": "cbarnes@lark.com", "hire_date" : ISODate("2012-07-13T04:00:00.0001Z"), "is_active" : true } { "_id" : "00007", "short_name": "cverde", "job_desc" : "Developer", "employee_type" : "permanent", "employee_status" : "active", "location_type" : "domestic", "title_desc" : "Java Developer", "alt_title" : "developer", "cost_center" : "960", "working_shift" : 1, "first_name" : "Christine", "preferred_name" : "Christine", "middle" : "", "last_name" : "Verde", "full_name" : "Christine Verde", "country" : "USA", "company_name" : "Lark Technologies", "company_code" : 120, "department" : "Development Support", "region": "West", "district": "SVWest", "building": "32000", "floor": "55", "section": "ZZ", "section_num": "644", "phone": "888-999-2020", "extension": "x2343", "manager_id": "msalvano", "manager_name": "Michael Salvano", "email": "cverde@larkTech.com", "hire_date" : ISODate("2006-03-15T04:00:00.0001Z"), "is_active" : true }
Testing out the Web Services
In addition to using our AngularJS/Angular Material/Grid-UI web application to test out our restful services I used Postman which is a Google Chrome Application. Using this tool I validated each of the REST API calls. Please review the screen shots below:
Testing Application and POSTMAN Chrome Extension
Download Complete Source Code for Employee Directory Boot / Sencha ExtJS Application
That’s It!
I hope you enjoyed this tutorial. It was certainly a lot of fun putting it together and testing it out. Please continue to share the love and like us so that we can continue bringing you quality tutorials. Happy Coding!!!

Related Spring Posts
- Creating Hello World Application using Spring MVC on Eclipse IDE
In this tutorial we will go into some detail on how to set up your Eclipse IDE environment so that you can develop Spring MVC projects. In this post, we will create our first Spring MVC project with the all to familiar “Hello World” sample program. - Spring MVC Form Handling Example
The following tutorial will guide you on writing a simple web based application which makes use of forms using Spring Web MVC framework. With this web application you will be able to interact with the customer entry form and enter all of the required values and submit them to the backend processes. I have taken the liberty of using CSS to beautify and transform the HTML page from a standard drab look and feel to a more appealing view. - Spring @RequestHeader Annotation ExampleIn this tutorial, we will discuss the different ways that Spring MVC allow us to access HTTP headers using annotation. We will discuss how to access individual header fields from the request object as well accessing all the headers by supplying Map and then iterating through the LinkedHashMap collection. We will also show you how to set the headers in the response object.
- Spring MVC Exception Handling using @ExceptionHandler with AngularJS GUIGood exception handling is a essential part of any well developed Application Framework and Spring MVC is no exception — pardon the pun. Spring MVC provides several different ways to handle exceptions in our applications. In this tutorial, we will cover Controller Based Exception Handling using the @ExceptionHandler annotation above the method that will handle it.
- Spring RESTful Web Service Example with JSON and Jackson using Spring Tool Suite
For this example, I will be using Spring Tool Suite (STS) as it is the best integrated development environment for building the Spring framework projects. Spring is today's leading framework for building Java, Enterprise Edition (Java EE) applications. One additional feature that makes Spring MVC so appealing is that it now also supports REST (REpresentational State Transfer) for build Web Services. - Spring MVC RESTful Web Service Example with Spring Data for MongoDB and ExtJS GUI
This post will show another example of how to build a RESTful web service using Spring MVC 4.0.6, Spring Data for MongoDB 1.6.1 so that we can integrate the web application with a highly efficient datastore (MongoDB 2.6). In this tutorial we will walk you through building the web service and NoSQL database backend and show you how to implement CRUD (Create, Read, Update and Delete) operations. - Building DHTMLX Grid Panel User Interface with Spring MVC Rest and MongoDB Backend
In this tutorial we will show how easy it is to use DHTMLX dhtmlxGrid component while loading JSON data with Ajax pulling in data from the Spring MVC REST web service from our MongoDB data source. You will see how simple it is to create a visually appealing experience for your client(s) with minimal javascript coding. - Spring MVC with JNDI Datasource for DB2 on AS/400 using Tomcat
In this tutorial we will discuss how to set up Spring MVC web services and configure a JNDI Datasource using Tomcat and connect to IBM DB2 Database on a AS/400. JNDI (Java Naming and Directory Interface) provides and interface to multiple naming and directory services. - Java Spring MVC Email Example using Apache Velocity
In this tutorial we will discuss how to set up a Java Spring MVC RESTful Webservice with Email using Apache Velocity to create a Velocity template that is used to create an HTML email message and embed an image, as shown below, using MIME Multipart Message. - Implementing Basic and Advanced Search using Angular Material Design, Grid-UI, Spring MVC REST API and MongoDB Example
In this tutorial we will discuss how to implement basic and advanced search techniques in MongoDB using AngularJS and Google’s Material Design with Spring MVC REST API backend. The advanced search user interface (UI) will use logical operators and build a JSON object which contains the search field name, boolean or logical operator and the search value. - Spring MVC Interceptor using HandlerInterceptorAdapter Example
In this tutorial we will discuss how to use the HandlerInterceptorAdapter abstract class to create a Spring MVC interceptor. These interceptors are used to apply some type of processing to the requests either before, after or after the complete request has finished executing.
Please Share Us on Social Media






Leave a Reply