Implementing Basic and Advanced Search using Angular Material Design, Grid-UI, Spring MVC REST API and MongoDB Example

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. 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 AngularJS Grid-UI 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 User Interface (UI) components like AngularJS, Angular Material, JQuery, HTML, CSS and Javascript called, “Designing Basic and Advanced Dynamic Search Forms using Angular Material and Grid-UI Example”.

What is Covered in this Tutorial ?

  1. What is Angular Material and Google’s Material Design ?
  2. Getting Started
  3. Complete Project Overview
  4. RESTful Web Service End Points
  5. The Employee Model
  6. Our Controller Class
  7. EmployeeRepository Data Access Object (DAO) for MongoDB
  8. The SelectionCriteria Class
  9. The Field Class
  10. LOG4J Configuration File
  11. Web Deployment Descriptor
  12. Configure Spring Web DispatcherServlet
  13. MongoDB Employee Collection
  14. 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 Angular Material 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. AngularJS supports dynamic form creating using ng-repeat and ng-form directives. We will discuss these in more details in our other tutorial.

associateSearchAdvanced

What is Angular Material and Google’s Material Design ?

Angular Material is a reference implementation of Google’s Material Design specification but it is also a user interface (UI) component framework. Google’s Material Design has made it a priority to utilize good design principles with their goals stated in their specifications document. These goals are encompassed in the following concepts:

MATERIAL DESIGN GOALS

  • Develop a single underlying system that allows for a unified experience across platforms and devices.
  • Mobile precepts are fundamental.
  • Touch, voice, mouse, and keyboard are all first-class input methods.

Angular Material Associate Search Application

Getting Started

In order to run this tutorial yourself, you will need the following:

  • Java JDK 1.6 or greater
  • 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)
  • Tomcat 7 or greater or other popular container (Weblogic, Websphere, Glassfish, JBoss, VMWare vFabric, etc). For this tutorial I am using VMware vFabric tc Server Developer Edition which is essentially an enhanced Tomcat instance integrated with Spring STS
  • Angular Material – reference implementation of Google’s Material Design using AngularJS.
  • Angular UI Grid – Native AngularJS implementation of dynamic grid with standard support of sorting, filtering, column pinning, grouping, in-place editing, expandable rows, internationalization, customizable templates,large set support and plug-in support.
  • Font Awesome provides over 600 icons all in one font. No javascript required as Font-Awesome utilizes CSS. I am using for some icons. If you like you can use material icons instead but code (html) changes will be required.
  • JQuery is a fast, small, and feature-rich JavaScript library. In this example, we are using for simple document traversal.
  • MongoDB is an open-source document database designed for ease of development and scaling.
  • Jackson Mapper for Object to JSON and vice-versa serialization/deserialization
  • log4J (for logging purposes)

Required Libraries

Copy all of the following jars to WebContent->WEB-INF->lib folder.

aopalliance-1.0.jar
aspectjrt-1.6.10.jar
commons-logging-1.2.jar
jackson-core-asl-1.9.13.jar
jackson-mapper-asl-1.9.13.jar
log4j-1.2.17.jar
mongo-java-driver-3.0.1.jar
slf4j-api-1.7.10.jar
slf4j-simple-1.7.10.jar
spring-aop-4.0.6.RELEASE.jar
spring-aspects-4.0.6.RELEASE.jar
spring-beans-4.0.6.RELEASE.jar
spring-context-4.0.6.RELEASE.jar
spring-core-4.0.6.RELEASE.jar
spring-data-commons-1.10.0.RELEASE.jar
spring-data-mongodb-1.6.1.RELEASE.jar
spring-expression-4.0.6.RELEASE.jar
spring-test-4.0.6.RELEASE.jar
spring-tx-4.0.6.RELEASE.jar
spring-web-4.0.6.RELEASE.jar
spring-webmvc-4.0.6.RELEASE.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.

associateSearch_proj_struct

RESTful Web Service End Points

#URIMethodDescription
1/rest/statusGETDisplays the standard status message.
2/rest/employeesGETRetrieves all employee objects from MongoDB returning them as a JSON array.
3/rest/getemployeebyidGETRetrieves an employee given the ID, returning the employee as JSON object.
4/rest/standardSearchGETRetrieves all employee objects from MongoDB that match either the firstName, lastName or both, returning matches as a JSON array.
5/rest/advancedSearchGETRetrieves employee objects from MongoDB that match selection criteria returning matches as a JSON array.
6/rest/employee/addPOSTInserts the employee into our MongoDB data store based on the contents of the JSON object
7/rest/employee/updatePUTUpdates employee in our MongoDB data store based on the contents of the JSON object
8/rest/employee/deleteDELETEDelete employee in our MongoDB data store based on the ID

The Employee Model (Employee.java)

The Employee model is used as a basis for storing our Employee object into MongoDB as a document in the collection. This class contains two annotations. The first one, the @Document annotation identifies objects or entities that are going to be persisted to MongoDB. The next one, @Id is used to identify the field that will be used as an ID in MongoDB. This ID is labeled _id in MongoDB.

We will be performing advanced boolean and logical operations via the advanced search tab in our application.

package com.avaldes.model;

import java.util.Date;

import org.codehaus.jackson.annotate.JsonProperty;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import com.avaldes.util.JsonDateTimeSerializer;

@Document
public class Employee {

  @Id
  private String id;
  private String jobDesc;
  private String employeeType;
  private String employeeStatus;
  private String locationType;
  private String titleDesc;
  private String altTitle;
  private String costCenter;
  private Integer workingShift;
  private String firstName;
  private String preferredName;
  private String middle;
  private String lastName;
  private String fullName;
  private String country;
  private String companyName;
  private Date hireDate;
  private boolean isActive;

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public String getJobDesc() {
    return jobDesc;
  }

  public void setJobDesc(String jobDesc) {
    this.jobDesc = jobDesc;
  }

  public String getEmployeeType() {
    return employeeType;
  }

  public void setEmployeeType(String employeeType) {
    this.employeeType = employeeType;
  }

  public String getEmployeeStatus() {
    return employeeStatus;
  }

  public void setEmployeeStatus(String employeeStatus) {
    this.employeeStatus = employeeStatus;
  }

  public String getLocationType() {
    return locationType;
  }

  public void setLocationType(String locationType) {
    this.locationType = locationType;
  }

  public String getTitleDesc() {
    return titleDesc;
  }

  public void setTitleDesc(String titleDesc) {
    this.titleDesc = titleDesc;
  }

  public String getAltTitle() {
    return altTitle;
  }

  public void setAltTitle(String altTitle) {
    this.altTitle = altTitle;
  }

  public String getCostCenter() {
    return costCenter;
  }

  public void setCostCenter(String costCenter) {
    this.costCenter = costCenter;
  }

  public Integer getWorkingShift() {
    return workingShift;
  }

  public void setWorkingShift(Integer workingShift) {
    this.workingShift = workingShift;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getPreferredName() {
    return preferredName;
  }

  public void setPreferredName(String preferredName) {
    this.preferredName = preferredName;
  }

  public String getMiddle() {
    return middle;
  }

  public void setMiddle(String middle) {

    this.middle = middle;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  @JsonSerialize(using=JsonDateTimeSerializer.class)
  public Date getHireDate() {
    return hireDate;
  }

  public void setHireDate(Date hireDate) {
    this.hireDate = hireDate;
  }

  public void setActive(boolean isActive) {
    this.isActive = isActive;
  }

  public String getFullName() {
    return fullName;
  }

  public void setFullName(String fullName) {
    this.fullName = fullName;
  }

  public String getCountry() {
    return country;
  }

  public void setCountry(String country) {
    this.country = country;
  }

  public String getCompanyName() {
    return companyName;
  }

  public void setCompanyName(String companyName) {
    this.companyName = companyName;
  }

  public boolean getIsActive() {
    return isActive;
  }

  public void setIsActive(boolean isActive) {
    this.isActive = isActive;
  }

  @Override
  public String toString() {
    return "Employee [id=" + id + ", jobDesc=" + jobDesc
        + ", employeeType=" + employeeType + ", employeeStatus="
        + employeeStatus + ", locationType=" + locationType
        + ", titleDesc=" + titleDesc + ", altTitle=" + altTitle
        + ", costCenter=" + costCenter + ", workingShift="
        + workingShift + ", firstName=" + firstName
        + ", preferredName=" + preferredName + ", middle=" + middle
        + ", lastName=" + lastName + ", fullName=" + fullName
        + ", country=" + country + ", companyName=" + companyName
        + ", hireDate=" + hireDate + ", isActive=" + isActive + "]";
  }
}

Our Controller Class (RestController.java)

Our RestController class is the main class that contains all web service mapping end points defined in our table above. The @Controller 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.stereotype.Controller;
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 com.avaldes.dao.EmployeeRepository;
import com.avaldes.model.Employee;
import com.avaldes.model.SelectionCriteria;

/**
 * Handles requests for the application home page.
 */
@Controller
public class RestController {

  private static final Logger logger = LoggerFactory
      .getLogger(RestController.class);
  public static final String APPLICATION_JSON = "application/json";
  public static final String APPLICATION_XML = "application/xml";
  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() {
    return "application OK...";
  }

  @RequestMapping(value = "/employees", method = RequestMethod.GET)
  public @ResponseBody List<Employee> getAllEmployees() {
    logger.info("Inside getAllEmployees() method...");

    List<Employee> allEmployees = employeeRepository
        .getAllEmployees();

    return allEmployees;
  }

  @RequestMapping(value = "/getemployeebyid",
      method = RequestMethod.GET, produces = {
      APPLICATION_JSON, APPLICATION_XML })
  public @ResponseBody Employee getEmployeeById(
      @RequestParam("id") String id) {
    Employee employee = employeeRepository.getEmployeeById(id);

    if (employee != null) {
      logger.info(
          "Inside getEmployeeById, returned: " + employee.toString());
    } else {
      logger
          .info("Inside getEmployeeById, ID: " + id + ", NOT FOUND!");
    }

    return employee;
  }

  @RequestMapping(value = "/standardSearch", method=RequestMethod.POST)
  public @ResponseBody List<Employee> standardSearch(
      @RequestParam("firstName") String firstName,
      @RequestParam("lastName") String lastName) {
    logger.info("Inside standardSearch() method...");
    logger.info("firstName....: " + firstName);
    logger.info("lastName.....: " + lastName);

    List<Employee> filteredAssociates = employeeRepository
        .getEmployeesStandardSearch(firstName, lastName);

    return filteredAssociates;
  }

  @RequestMapping(value = "/advancedSearch", method=RequestMethod.POST)
  public @ResponseBody List<Employee> advancedSearch(
      @RequestBody List<SelectionCriteria> criteriaList) {
    logger.info("Inside advancedSearch() method...");

    /*
     * for (SelectionCriteria criteria: criteriaList) {
     * logger.info(criteria.toString()); }
     */

    List<Employee> filteredAssociates = employeeRepository
        .getEmployeesBySelectionCriteria(criteriaList);

    return filteredAssociates;
  }

  @RequestMapping(value = "/employee/delete",
      method = RequestMethod.DELETE, produces = {
      APPLICATION_JSON, APPLICATION_XML })
  public @ResponseBody RestResponse deleteEmployeeById(
      @RequestParam("id") String id) {
    RestResponse response;

    Employee employee = employeeRepository.deleteEmployee(id);

    if (employee != null) {
      logger.info("Inside deleteEmployeeById, deleted: "
          + employee.toString());
      response = new RestResponse(true,
          "Successfully deleted employee: " + employee.toString());
    } else {
      logger.info(
          "Inside deleteEmployeeById, ID: " + id + ", NOT FOUND!");
      response = new RestResponse(false,
          "Failed to delete ID: " + id);
    }

    return response;
  }

  @RequestMapping(value = "/employee/update",
      method = RequestMethod.PUT, consumes = {
      APPLICATION_JSON, APPLICATION_XML }, produces = {
      APPLICATION_JSON, APPLICATION_XML })
  public @ResponseBody RestResponse updateEmployeeById(
      @RequestParam("id") String id, @RequestBody Employee employee) {
    RestResponse response;

    Employee myEmployee = employeeRepository.updateEmployee(id,
        employee);

    if (myEmployee != null) {
      logger.info("Inside updateEmployeeById, updated: "
          + myEmployee.toString());
      response = new RestResponse(true,
          "Successfully updated ID: " + myEmployee.toString());
    } else {
      logger.info(
          "Inside updateEmployeeById, ID: " + id + ", NOT FOUND!");
      response = new RestResponse(false,
          "Failed to update ID: " + id);
    }

    return response;
  }

  @RequestMapping(value = "/employee/add",
      method = RequestMethod.POST, consumes = {
      APPLICATION_JSON, APPLICATION_XML }, produces = {
      APPLICATION_JSON, APPLICATION_XML })

  public @ResponseBody RestResponse addEmployee(
      @RequestParam("id") String id, @RequestBody Employee employee) {
    RestResponse response;

    logger.info("Inside addEmployee, model attribute: "
        + employee.toString());

    if (id == null) {
      response = new RestResponse(false, "ID may not be null.");
      return response;
    }

    if (id != null && id.isEmpty()) {
      response = new RestResponse(false, "ID may not be empty.");
      return response;
    }

    Employee myEmployee = employeeRepository.getEmployeeById(id);
    if (myEmployee != null) {
      if (myEmployee.getId() != null
          && myEmployee.getId().equalsIgnoreCase(id)) {
        response = new RestResponse(false,
            "ID already exists in the system.");
        return response;
      }
    }

    if (employee.getId() != null && employee.getId().length() > 0) {
      logger
          .info("Inside addEmployee, adding: " + employee.toString());
      employeeRepository.addEmployee(employee);
      response = new RestResponse(true,
          "Successfully added Employee: " + employee.getId());
    } else {
      logger.info("Failed to insert...");
      response = new RestResponse(false, "Failed to insert...");
    }

    return response;
  }
}

RestResponse Class (RestResponse.java)

package com.avaldes.tutorial;

public class RestResponse {
  private boolean success;
    private String message;

    public RestResponse(boolean success, String message) {
      this.success = success;
      this.message = message;
    }

    public boolean isSuccess() {
      return success;
    }

    public void setSuccess(boolean success) {
      this.success = success;
    }

    public String getMessage() {
      return message;
    }

    public void setMessage(String message) {
      this.message = message;
    }
}

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.

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 = "employee";
  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("firstName").regex(firstName, "i");
      andCriteriaList.add(c1);
      ok = true;
    }
    if (lastName != null && lastName.length() > 0) {
      Criteria c1 = Criteria.where("lastName").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("jobDesc", Employee.getJobDesc());
    update.set("employeeType", Employee.getEmployeeType());
    update.set("employeeStatus", Employee.getEmployeeStatus());
    update.set("locationType", Employee.getLocationType());
    update.set("titleDesc", Employee.getTitleDesc());
    update.set("altTitle", Employee.getAltTitle());
    update.set("costCenter", Employee.getCostCenter());
    update.set("workingShift", Employee.getWorkingShift());
    update.set("firstName", Employee.getFirstName());
    update.set("preferredName", Employee.getPreferredName());
    update.set("middle", Employee.getMiddle());
    update.set("lastName", Employee.getLastName());
    update.set("fullName", Employee.getFullName());
    update.set("country", Employee.getCountry());
    update.set("companyName", Employee.getCompanyName());
    update.set("hireDate", Employee.getHireDate());
    update.set("isActive", Employee.getIsActive());

    mongoTemplate.updateFirst(query, update, Employee.class);

    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 + "]";
  }
}

LOG4J Configuration File (log4j.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC
  "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration debug="true"
  xmlns:log4j="http://jakarta.apache.org/log4j/">

  <!-- Appenders -->
  <appender name="console" class="org.apache.log4j.ConsoleAppender">
    <param name="Target" value="System.out" />
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern"
        value="%d{dd-MMM-yyyy HH:mm:ss.SSS} %-5p %-5l: %c - %m%n" />
    </layout>
  </appender>

  <appender name="fileAppender"
    class="org.apache.log4j.RollingFileAppender">
     <param name="append" value="false" />
     <param name="maxFileSize" value="10MB" />
     <param name="maxBackupIndex" value="10" />
     <param name="File"
      value="/local1/EmployeeDirectory/logs/EmployeeDirectory.log" />
     <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern"
        value="%d{dd-MMM-yyyy HH:mm:ss.SSS} %-5p %-5l: %c - %m%n" />
     </layout>
  </appender>

  <!-- Application Loggers -->
  <logger name="com.avaldes.tutorial.*">
    <level value="DEBUG" />
    <appender-ref ref="fileAppender" />
  </logger>

  <logger name="org.springframework.beans.*">
    <level value="DEBUG" />
    <appender-ref ref="fileAppender" />
  </logger>

  <!-- Root Logger -->
  <root>
    <priority value="debug" />
    <appender-ref ref="fileAppender" />
  </root>
</log4j:configuration>

Web Deployment Descriptor (web.xml)

This is a pretty straight forward deployment descriptor file containing the location of log4j.xml file and location of context confiuration parameters for our dispatcher servlet.xml file. Please ensure you add it to the web.xml file as shown below.

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.5"
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name>EmployeeDirectory</display-name>

  <!-- The context params that are read by Log4jConfigListener -->
  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.xml</param-value>
  </context-param>

  <listener>
    <listener-class>
      org.springframework.web.util.Log4jConfigListener
    </listener-class>
  </listener>

  <!-- The context params that are read by ContextLoaderListener  -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/root-context.xml</param-value>
  </context-param>

  <listener>
    <listener-class>
      org.springframework.web.context.ContextLoaderListener
    </listener-class>
  </listener>

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>
      org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Configure Spring Web DispatcherServlet (dispatcher-servlet.xml)

Modify the dispatcher-servlet.xml and add the necessary MongoDB configurations. You will notice that I have added MongoTemplate which is used for the mongo operations and MongoFactoryBean which creates the mongo instance to our dispatcher-servlet.xml. MongoTemplate is configured to use the database settings via MongoFactoryBean.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:beans="http://www.springframework.org/schema/beans"
  xmlns:context="http://www.springframework.org/schema/context"
  xsi:schemaLocation="http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd">

  <!-- DispatcherServlet Context: defines this servlet's -->
  <!-- request-processing infrastructure -->

  <!-- Enables the Spring MVC @Controller programming model -->
  <!-- <beans:bean annotation-driven /> -->
  <annotation-driven />

  <!-- Handles HTTP GET requests for /resources/** by efficiently -->
  <!-- serving up static resources in the ${webappRoot}/resources -->
  <!-- directory -->
  <resources mapping="/resources/**" location="/resources/" />

  <!-- Define MongoTemplate which handles connectivity w/ MongoDB -->
  <beans:bean id="mongoTemplate"
    class="org.springframework.data.mongodb.core.MongoTemplate">
    <beans:constructor-arg name="mongo" ref="mongo" />
    <beans:constructor-arg name="databaseName" value="employees" />
  </beans:bean>

  <!-- Factory bean that creates the MongoDB instance -->
  <beans:bean id="mongo"
    class="org.springframework.data.mongodb.core.MongoFactoryBean">
    <beans:property name="host" value="localhost" />
  </beans:bean>

  <!-- Use this post processor to translate any -->
  <!-- MongoExceptions thrown in @Repository annotated classes -->
  <beans:bean
    class="org.springframework.dao.annotation.
           PersistenceExceptionTranslationPostProcessor" />

  <beans:bean
    class="org.springframework.web.servlet.mvc.annotation.
           AnnotationMethodHandlerAdapter">
    <beans:property name="messageConverters">
      <beans:list>
        <beans:bean
          class="org.springframework.http.converter.json.
                 MappingJacksonHttpMessageConverter" />
      </beans:list>
    </beans:property>
  </beans:bean>

  <context:component-scan base-package="com.avaldes" />

</beans:beans>

MongoDB Employee Collection

{
    "id" : "00001",
    "jobDesc" : "IT Development",
    "employeeType" : "permanent",
    "employeeStatus" : "active",
    "locationType" : "domestic",
    "titleDesc" : "Senior Developer",
    "altTitle" : "",
    "costCenter" : "1025",
    "workingShift" : 1,
    "firstName" : "Amaury",
    "preferredName" : "Amaury",
    "middle" : "",
    "lastName" : "Valdes",
    "fullName" : "Amaury Valdes",
    "country" : "USA",
    "companyName" : "Lark Productions",
    "hireDate" : "2012-05-18T04:00:00.0001Z",
    "isActive" : false
}
{
    "id" : "00002",
    "jobCode" : "IT Management",
    "employeeType" : "permanent",
    "employeeStatus" : "active",
    "locationType" : "domestic",
    "titleDesc" : "Senior Manager",
    "altTitle" : "",
    "costCenter" : "1025",
    "workingShift" : 1,
    "firstName" : "Steven",
    "preferredName" : "Steve",
    "middle" : "J",
    "lastName" : "Adelson",
    "fullName" : "Steven Adelson",
    "country" : "USA",
    "companyName" : "Lark Productions",
    "hireDate" : "2010-03-02T04:00:00.0001Z",
    "isActive" : true
}
{
    "id" : "00003",
    "jobDesc" : "Senior Management",
    "employeeType" : "permanent",
    "employeeStatus" : "active",
    "locationType" : "domestic",
    "titleDesc" : "Senior Group Manager",
    "altTitle" : "",
    "costCenter" : "1025",
    "workingShift" : 1,
    "firstName" : "Robert",
    "preferredName" : "Bob",
    "middle" : "",
    "lastName" : "Paterson",
    "fullName" : "Robert Paterson",
    "country" : "USA",
    "companyName" : "Lark Productions",
    "hireDate" : "2010-09-04T04:00:00.0001Z",
    "isActive" : true
}
{
    "id" : "00004",
    "jobDesc" : "Receptionist",
    "employeeType" : "temp",
    "employeeStatus" : "active",
    "locationType" : "domestic",
    "titleDesc" : "Front Desk Reception",
    "altTitle" : "",
    "costCenter" : "1025",
    "workingShift" : 1,
    "firstName" : "Sandra",
    "preferredName" : "Sandy",
    "middle" : "",
    "lastName" : "Jeffries",
    "fullName" : "Sandra Jeffries",
    "country" : "USA",
    "companyName" : "Kelly Temps",
    "hireDate" : "2008-12-23T04:00:00.0001Z",
    "isActive" : true
}
{
    "id" : "00005",
    "jobDesc" : "Developer",
    "employeeType" : "permanent",
    "employeeStatus" : "active",
    "locationType" : "domestic",
    "titleDesc" : "Front-End Developer",
    "altTitle" : "",
    "costCenter" : "982",
    "workingShift" : 1,
    "firstName" : "Christopher",
    "preferredName" : "Chris",
    "middle" : "",
    "lastName" : "Smith",
    "fullName" : "Christopher Smith",
    "country" : "USA",
    "companyName" : "Lark Productions",
    "hireDate" : "2010-05-02T04:00:00.0001Z",
    "isActive" : true
}
{
    "id" : "00006",
    "jobDesc" : "Developer",
    "employeeType" : "consultant",
    "employeeStatus" : "active",
    "locationType" : "domestic",
    "titleDesc" : "Front-End Developer",
    "altTitle" : "",
    "costCenter" : "982",
    "workingShift" : 1,
    "firstName" : "Christa",
    "preferredName" : "Chrissy",
    "middle" : "",
    "lastName" : "Barnes",
    "fullName" : "Christa Barnes",
    "country" : "USA",
    "companyName" : "Sapient Technologies",
    "hireDate" : "2012-07-13T04:00:00.0001Z",
    "isActive" : true
}
{
    "id" : "00007",
    "jobDesc" : "Developer",
    "employeeType" : "permanent",
    "employeeStatus" : "active",
    "locationType" : "domestic",
    "titleDesc" : "Java Developer",
    "altTitle" : "",
    "costCenter" : "960",
    "workingShift" : 1,
    "firstName" : "Christine",
    "preferredName" : "Christine",
    "middle" : "",
    "lastName" : "Verde",
    "fullName" : "Christine Verde",
    "country" : "USA",
    "companyName" : "Lark Productions",
    "hireDate" : "2006-03-15T04:00:00.0001Z",
    "isActive" : 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 the Complete Source Code

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!!!

advanced_search_backend

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 Example
    In 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 GUI
    Good 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

Facebooktwittergoogle_plusredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *