RESTful Web Services with AngularJS, Bootstrap and Java using JAX-RS and Jersey

RESTful Web Service using JAX-RS and Jersey

JAX-RS stands for Java API for RESTful Web Services and by using this powerful API developers can easily build REST services. JAX-RS is part of the Java 6 Enterprise Edition.

Jersey RESTful Web Services is an open source framework for developing RESTful Web Services in Java that provides support for JAX-RS APIs and serves as the Official Reference Implementation (JSR 311 and JSR 339).

What is REST?

REST (REpresentational State Transfer) is an architectural style, and an approach to communications that is usually used when developing Web services. REST has gained in popularity over its contender SOAP (Simple Object Access Protocol) because REST is lighter in terms of bandwidth usage. RESTful services are much easier to implement and scale than SOAP. Thus REST is the chosen architecture by service providers like Facebook, Twitter, Amazon, Microsoft, and Google.

REST architecture describes six constraints. These constraints were described in Roy Fielding’s dissertation as Uniform Interface, Stateless, Cacheable, Client-Server, Layered-System, and Code On Demand.

  • Uniform Interface – Resources are manipulated via CRUD (create, read, update, delete) operations. CRUD operations are managed via PUT, GET, POST, and DELETE request methods.
  • Stateless – In REST the state is contained within the request itself, or as part of the URI, query-string parameters, body or in the headers. After processing the request, the state may be communicated back via the headers, status or response body.
  • Cacheable – Responses from the web service to its clients are explicitly labeled as cacheable or non-cacheable. This way, the service, the consumer, or one of the intermediary middleware components can cache the response for reuse in later requests.
  • Client Server – This is a key constraint, as it based on separations of concerns. The client/server requirement ensures that a distributed environment exists. It requires the client, that sends requests and a server component that receives the requests. After processing the request, the server may return a response to the client. Error responses may be transmitted as well, which requires the client to be responsible for taking any corrective action.
  • Layered System – A client should may not be able to tell whether it is connected directly to the end server, or to an intermediary along the way. Intermediary servers may add security policies, or improve scalability.
  • Code On Demand – This is an optional constraint. It allows a client to have logic locally via the ability to download and execute code from a remote server.

Our Application

The application used in this post is a Actor Management application. You are allowed to search for actors by name, add new actors, update and delete existing actors. I am using AngularJS and BootStrap to add a little flair to the application and give you some valuable insights into these javascript/css technologies as well.

jax-rs Delete Actor

RESTful Web Services for CRUD Operations

RESTFul web services define the base URI (Universal Resource Identifier) for the web service, it also defines the end points of the service via links on the web. Resources are manipulated via CRUD (create, read, update, delete) operations. CRUD operations are managed via PUT, GET, POST, and DELETE request methods.

  • GET to retrieve and search data
  • POST to add new data
  • PUT to update existing data
  • DELETE to remove data

Getting Started

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

Required Libraries

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

asm-3.1.jar
jackson-core-asl-1.9.2.jar
jackson-jaxrs-1.9.2.jar
jackson-mapper-asl-1.9.2.jar
jackson-xc-1.9.2.jar
jersey-client-1.18.jar
jersey-core-1.18.jar
jersey-json-1.18.jar
jersey-server-1.18.jar
jersey-servlet-1.18.jar
jsr311-api-1.1.1.jar
log4j-1.2.17.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.

jax-rs_project_structure

RESTful Web Service End Points

This REST API example will accept the data in the payload to be in either XML or JSON format. Subsequently, it will also return all results in either XML or JSON format.

#URIMethodDescription
1/rest/actorsGETReturns a list of all the actors available
2/rest/actors/{id}GETReturns actor based on the id
3/rest/actors/search/{query}GETReturns all of the actors matching the query anywhere in the name
4/rest/actors/{id}DELETEDelete the actor in the data store based on the id
5/rest/actors/{id}PUTUpdates the actor in the data store based on the id
6/rest/actors/addPOSTInserts the actor into the data store based on the contents of the form

Implement your Service Class using the JAX-RS API

Implementing a RESTful service requires nothing more than creating a POJO and annotating using the javax.ws.rs.* annotations. Additionally, you will need to ensure your class is under the package you defined in your web descriptor as Jersey will use this package to scan your classes for the existence RESTful resources.

uri structure

Annotations used in this class:

  • @Path(“/actors”) – This first path annotation is defined as a class-level annotation so it gets appended after the servlet-mapping URL pattern in the URI.
  • @GET, @POST, @PUT, @DELETE – HTTP method the path corresponds to. These annotations will only appear on the method level annotations.
  • @Consumes – The @Consumes annotation is used to specify which MIME media types resource can consume from the client. @Consumes may be used at the class level which will cause all the consuming methods to accept the specified MIME types by default. If @Consumes is applied at the method level, it will overrides any @Consumes annotations applied at the class level. Since we are using the POJOMappingFeature in Jersey we are allowed to pass JSON and XML in the payload and the data will automatically be deserialized into the Actor Java object.
  • @Produces – The @Produces annotation is used to specify the MIME media types a resource can produce and send back to the client. If @Produces is applied at the class level, all the methods in a resource can produce the specified MIME types by default. If it is applied at the method level, it overrides any @Produces annotations applied at the class level. Since we are using the POJOMappingFeature in Jersey our Java objects are serialized into JSON or XML prior to sending the response back to the client.

Note

For the sake of simplicity, I have refrained from creating separate DAO objects and instead created a Hashmap which will represent our Datastore. All CRUD (Create, Read, Update, Delete) operations will work perfectly fine on this lightweight object.

The JAX-RS Controller (RestfulWSExample.java) (Text wrapped for blog purposes only)

package com.avaldes;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.avaldes.model.Actor;

@Path("/actors")
public class RestfulWSExample {
  static final String api_version = "1.01A rev.18729";
  static Logger logger = Logger.getLogger(RestfulWSExample.class);
  static String xmlString = null;
  static Map<String, Actor> actors = new HashMap<String, Actor>();
  
  static { 
    System.out.println("Initializing Internal DataStore...");
    actors.put("123", new Actor(123, "Hugh Jackson", "Hugh Michael Jackman", 
    "October 12, 1968", "hughjackman@mail.com", "https://stockland
    martelblog.files.wordpress.com/2013/07/nino-muncc83oz_hugh-
    jackman_page_3.jpg", true));
    actors.put("124", new Actor(124, "Jennifer Lawrence", 
    "Jennifer Shrader Lawrence", "August 15, 1990", "jennifer@mail.com", 
    "http://www.siempre-lindas.cl/wp-content/uploads/2014
    /11/jennifer-lawrence-164522_w1000.jpg", true));
    actors.put("345", new Actor(345, "Jennifer Lopez", "Jennifer Lynn Lopez",
    "July 24, 1969", "jlo@verizon.com", "http://media1.popsugar-assets.com/
    files/2013/01/01/5/192/1922398/e0bd827287eb8c5f_145351598.
    xxxlarge_2.jpg", true));
    actors.put("333", new Actor(333, "Jennifer Anniston", "Jennifer Joanna
    Aniston", "February 11, 1969", "jennifer.anniston@eonline.com", 
    "http://media1.popsugar-assets.com/files/2013/01/01/5/192/1922398
    /e59ab35359063e7c_139123757.xxxlarge_2.jpg", true));
    actors.put("444", new Actor(444, "Julia Roberts", "Julia Fiona Roberts",
    "October 28, 1967", "julia.roberts@att.com", 
    "http://img2.timeinc.net/people/i/2013/specials/beauties
    /then-now/julia-roberts-4-435.jpg", true));
    actors.put("777",new Actor(777,"Chris Evans","Christopher Robert Evans",
    "June 13, 1981", "chris.evans@comcast.com", 
    "http://assets-s3.usmagazine.com/uploads/assets/celebrities/28454-
    chris-evans/1311353993_chris-evans-bio-402.jpg", true));
    actors.put("654", new Actor(654, "Robert Downey Jr.", "Robert John
    Downey Jr", "April 4, 1965", "robertdowney@verizon.com", 
    "http://cdn.playbuzz.com/cdn/3bfbcced-5435-4fb0-898a-91c8146f0a37
    /2c641acc-e33d-4f59-8fe9-f7c0cc11a951.jpg", true));
    actors.put("255",new Actor(255,"Johnny Depp","John Christopher Depp II",
    "June 9, 1963", "johndepp@hollywood.com", 
    "http://images.latinpost.com/data/images/full/9536/johnny-depp-at-
    transcendence-los-angeles-premiere.jpg?w=600", true));    
    actors.put("989", new Actor(989, "Scarlet Johansson", "Scarlett 
    Ingrid Johansson", "November 22, 1984", "scarjo@mail.com", 
    "http://static.comicvine.com/uploads/original/11111/111117228
    /4087439-2415305600-Scarl.jpg", true));
  }
    
  @Path("/version") 
  @GET
  @Produces(MediaType.TEXT_HTML)
  public String returnVersion() {
    return "<p>Version: " + api_version + "</p>";
  }

  // This is the default @PATH
  @GET
  @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  public ArrayList<Actor> getAllActors() {
    System.out.println("Getting all actors...");
    ArrayList<Actor> actorList = new ArrayList<Actor>(actors.values());
    return actorList;
  }
  
  @Path("{id}")
  @GET
  @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  public Actor getActorById(@PathParam("id") String id) {
    System.out.println("Getting actor by ID: " + id);

    Actor actor = actors.get(id);
    if (actor != null) {
    logger.info("Inside getActorById, returned: " + actor.toString());
    } else {
    logger.info("Inside getActorById, ID: " + id + ", NOT FOUND!");
    }
    return actor;
  }
  
  @Path("{id}")
  @PUT
  @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  public Actor updateActor(Actor actor) {
    actors.put(""+actor.getId(), actor);
    
    System.out.println("updateActor with ID: " + actor.getId());
    if (actor != null) {
    logger.info("Inside updateActor, returned: " + actor.toString());
    } else {
    logger.info("Inside updateActor, ID: " + actor.getId() 
                                              + ", NOT FOUND!");
    }
    return actor; 
  }
  
  @Path("/search/{query}")
  @GET
  @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  public ArrayList<Actor> searchActorByName(@PathParam("query") 
                                                    String query) {
      System.out.println("Searching actor by Name: " + query);
    
      ArrayList<Actor> actorList = new ArrayList<Actor>();    
    for (Actor c: actors.values()) {
      if (c.getName().toUpperCase().contains(query.toUpperCase()))
        actorList.add(c);
    }
    return actorList;
  }
  
  @Path("/add")
  @POST
  @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  public Actor addActor(Actor actor) {
    System.out.println("Adding actor with ID: " + actor.getId());
    
    if (actor != null) {
    System.out.println("Inside addActor, returned: " + actor.toString());
    actors.put(""+actor.getId(), actor);
    System.out.println("# of actors: " + actors.size());
    System.out.println("Actors are now: " + actors);
    } else {
    System.out.println("Inside addActor, Unable to add actors...");
    } 
    return actor;
  }
  
  @Path("{id}")
  @DELETE
  @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
  public Actor deleteActorById(@PathParam("id") String id) {
    System.out.println("Deleting actor with ID: " + id);
    
    Actor actor = actors.remove(id);
    if (actor != null) {
    logger.info("Inside deleteActorById, returned: " + actor.toString());
    } else {
    logger.info("Inside deleteActorById, ID: " + id + ", NOT FOUND!");
    }
    return actor;
  }
}

Model Class (Actor.java)

Another point to mention about annotations can be seen in our Actor Model Class. Jersey also has built in support for JAXB (Java Architecture for XML Binding). By adding the annotations @XmlRootElement and @XmlElement we are able to serialize the model from Java object to XML and deserialize the XML back to Java object model.

Warning

Without the proper annotations in place you would see the following error message on your console:

SEVERE: Mapped exception to response: 500 (Internal Server Error)
javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException: A message body writer for Java class com.avaldes.model.Actor, and Java type class com.avaldes.model.Actor, and MIME media type application/xml was not found.
at com.sun.jersey.spi.container.ContainerResponse….

package com.avaldes.model;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "actor")
public class Actor {
  private int id;
  private String name;
  private String birthName;
  private String birthDate;
  private String email;
  private String image;
  private boolean active;
  
  public Actor(int id, String name, String birthName, String birthDate, 
                String email, String image, boolean active) {
    this.id = id;
    this.name = name;
    this.birthName = birthName;
    this.birthDate = birthDate;
    this.email = email;
    this.image = image;
    this.active = active;
  }
  
  @XmlElement
  public int getId() {
    return id;
  }

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

  @XmlElement
  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  @XmlElement
  public String getBirthName() {
    return birthName;
  }

  public void setBirthName(String birthName) {
    this.birthName = birthName;
  }

  @XmlElement
  public String getBirthDate() {
    return birthDate;
  }

  public void setBirthDate(String birthDate) {
    this.birthDate = birthDate;
  }

  @XmlElement
  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  @XmlElement
  public boolean isActive() {
    return active;
  }

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

  @XmlElement
  public String getImage() {
    return image;
  }

  public void setImage(String image) {
    this.image = image;
  }

  public Actor() {
  }

  @Override
  public String toString() {
    return "Actor [id=" + id + ", name=" + name + ", birthName="
        + birthName + ", birthDate=" + birthDate + ", email=" + email
        + ", image=" + image + ", active=" + active + "]";
  }
}

The AngularJS Client (index.jsp) (Text wrapped for blog purposes only)

<html ng-app="app">
 <head>
  <title>Show Actors</title>
  <link href="include/styles.css" rel="stylesheet">
  <!-- Use Bootstrap -->
  <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap
   /3.2.0/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1
   /jquery.min.js"></script>
  <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js
   /bootstrap.min.js"></script>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax
   /libs/angularjs/1.2.16/angular.min.js"></script>  
  
  <script type="text/javascript" src="include/app.js"></script>
  <meta name="viewport" content="width=device-width, initial-scale=1">
 </head>
 
 <body ng-controller="HttpCtrl as app">
   <div class="container">
  <div class="header">
   <h1 class="custom">Actor Management Interface</h1>
  </div>

  <div class="leftPanel">
   <div class="LeftPanelHeader">{{navTitle}}</div>
   <table class="side">
     <tr ng-repeat="a in actors" ng-click="getActor(a.id)" >
       <td class="side">{{a.name}}</td>
     </tr>
   </table>
  </div>

  <div class="RightPanel">
   <image src="{{actor.image}}" width="220">
  </div>
  
  <div class="MainBody">
      <form>
       <table>
     <tr>
      <td><input type="text" ng-model="searchName" size="30"></td>
      <td>
         <button type="button" ng-click="searchActor(searchName)" 
         class="btn btn-primary btn-sm">
         <span class="glyphicon glyphicon-search"></span>Search</button>
      </td>
      <td><button ng-click="addNew()" class="btn btn-primary btn-sm">
      <span class="glyphicon glyphicon-plus"></span> Add New </button></td>
      <td><button ng-click="resetSearch()"  class="btn btn-info btn-sm">
      <span class="glyphicon glyphicon-refresh"></span> Reset Search
      </button></td>
     </tr>
    </table>
   </form>
      
   <form id="main">
    <table>
     <tr>
      <td class="display_bold"><label for="actor.name">ID:</label></td>
     </tr>
     <tr>
      <td class="display"><input id="id" type="text" 
         ng-model="actor.id" size="4"></td>
     </tr>
     <tr>
      <td class="display_bold">
        <label for="name">Name:</label>
      </td>
     </tr>
     <tr>
      <td class="display"><input type="text" 
         ng-model="actor.name" size="30"></td>
     </tr>
     <tr>
      <td class="display_bold">
        <label for="name">Birth Name:</label>
      </td>
     </tr>
     <tr>
      <td class="display"><input type="text" 
        ng-model="actor.birthName" size="40"></td>
     </tr>
     <tr>
      <td class="display_bold">
        <label for="name">Birth Date:</label>
       </td>
     </tr>
     <tr>
      <td class="display"><input type="text" 
        ng-model="actor.birthDate" size="20"></td>
     </tr>
     <tr>
      <td class="display_bold">
        <label for="name">Email:</label>
      </td>
     </tr>
     <tr>
      <td class="display"><input type="text" 
         ng-model="actor.email" size="30"></td>
     </tr>
     <tr>
      <td class="display_bold">
        <label for="name">Image:</label>
      </td>
     </tr>
     <tr>
      <td class="display">
        <input type="text" ng-model="actor.image" size="80">
      </td>
     </tr>
     <tr>
      <td class="display_bold">
        <label for="name">IsActive:</label>
      </td>
     </tr>
     <tr>
      <td class="display">
        <input type="text" ng-model="actor.active" size="10">
      </td>
     </tr>
     
     <tr>
      <td>&nbsp;</td>
     </tr>
     <tr>
      <td>
        <table>         
         <tr>
           <td><button ng-click="saveActor(actor.id)" 
           class="btn btn-success btn-sm" 
           title="Save actor's details..." ng-disabled="isSaveDisabled">
           <span class="glyphicon glyphicon-plus"></span> 
             Save </button>
         </td>
         <td>
           <button ng-click="deleteActor(actor.id)" 
           class="btn btn-danger btn-sm" ng-disabled="isDeleteDisabled">
         <span class="glyphicon glyphicon-trash"></span> 
            Delete </button></td>     
         </tr>
        </table>
      </td>
     </tr>
     
    </table>
   </form>
  </div>
  <div class="footer">AngularJS Demo - Copyright © avaldes.com</div>
 </div>
 </body>
</html>

The AngularJS Controller (app.js)

(function() {
  
  var app = angular.module("app", []);

  
  app.controller("HttpCtrl", function($scope, $http) {
    $scope.navTitle = 'All Stars';
    $scope.operation="";
    $scope.isSaveDisabled = true;
    $scope.isDeleteDisabled = true;
    
    var response = $http.get('/RestfulWebServiceExample/rest/actors/');
    response.success(function(data) {
      $scope.actors = data;
      console.log("[main] # of items: " + data.length)
      angular.forEach(data, function(element) {
        console.log("[main] actor: " + element.name);
      });
    })
    response.error(function(data, status, headers, config) {
      alert("AJAX failed to get data, status=" + status);
    })
    
    
    $scope.getActor = function(id) {
      var response=$http.get('/RestfulWebServiceExample/rest/actors/'+ id );
      
      response.success(function(data) {
        console.log("getActor data: " + angular.toJson(data, false));
        $scope.actor = data;
        $scope.operation="update";
        $scope.isSaveDisabled = false;
        $scope.isDeleteDisabled = false;
      })
      
      response.error(function(data, status, headers, config) {
        alert("AJAX failed to get data, status=" + status);
      })
    };
    
    $scope.searchActor = function(name) {
      $scope.navTitle = 'Search Criteria';
      
      var response = $http.get(
         '/RestfulWebServiceExample/rest/actors/search/' + name);
      response.success(function(data) {
        $scope.actors = data;
        $scope.$apply();

        console.log("[searchActor] # of items: " + data.length)
        angular.forEach(data, function(element) {
          console.log("[searchActor] actor: " + element.name);
        });

      });
      
      response.error(function(data, status, headers, config) {
        alert("AJAX failed to get data, status=" + status);
      })
    };
    
    $scope.clearForm = function() {
      $scope.actor = {
        id:'',
        name:'',
        birthName:'',
        birthDate:'',
        email:'',
        image:'',
        active:''
      };
    }
    
    $scope.addNew = function(element) {
      $scope.operation="create";
      $scope.clearForm();
      main.id.focus();
      $scope.isSaveDisabled = false;
      $scope.isDeleteDisabled = true;
    }
    
    $scope.saveActor = function(id) {
      $scope.jsonObj = angular.toJson($scope.actor, false);
      console.log("[update] data: " + $scope.jsonObj);

      if ($scope.operation == "update") {
        var response = $http.put('/RestfulWebServiceExample/rest/actors/' 
         + id, $scope.jsonObj);
        response.success(function(data, status) {
          console.log("Inside update operation..." 
         + angular.toJson(data, false) + ", status=" + status);
          $scope.resetSearch();
        });
        
        response.error(function(data, status) {
          alert("AJAX failed to get data, status=" + status);
        })
      } else if ($scope.operation == "create") {
        var response=$http.post('/RestfulWebServiceExample/rest/actors/add',
         $scope.jsonObj);
        response.success(function(data, status) {
          console.log("Inside create operation..." 
           + angular.toJson(data, false) + ", status=" + status);
          $scope.resetSearch();
        });
        
        response.error(function(data, status) {
          alert("AJAX failed to get data, status=" + status, scope);
        })  
      }
    };
    
    $scope.deleteActor = function(id) {
      var response = $http.delete(
        '/RestfulWebServiceExample/rest/actors/' + id);
      response.success(function(data, status) {
        console.log("Inside delete operation..." 
         + angular.toJson(data, false) + ", status=" + status);
        $scope.resetSearch();
      });
        
      response.error(function(data, status) {
        alert("AJAX failed to get data, status=" + status);
      })
    };
    
    $scope.resetSearch = function(name) {
      $scope.operation="";
      $scope.clearForm();
      $scope.isSaveDisabled = true;
      $scope.isDeleteDisabled = true;
      $scope.navTitle = 'All Stars';
      $scope.searchName = '';
      
      var response = $http.get('/RestfulWebServiceExample/rest/actors/');
      response.success(function(data) {
        $scope.actors = data;
        //$scope.$apply();
        console.log("[resetSearch] # of items: " + data.length)
      });
      
      response.error(function(data, status, headers, config) {
        alert("AJAX failed to get data, status=" + status);
      })
    };
  }); 
})();

Web Deployment Descriptor (web.xml)

In order to facilitate ease of use in the conversion of your Java Objects to JSON I am making use of the Jersey JSON module which is based on Jackson Library. Please ensure you add it to the web,.xml file as shown below.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns
/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>Restful WebService Example</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <servlet-name>Jersey REST Service</servlet-name>
    <servlet-class>
      com.sun.jersey.spi.container.servlet.ServletContainer
    </servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.avaldes</param-value>
    </init-param>
    <init-param>
      <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
      <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey REST Service</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

Testing out the Web Services

Because of the limited functionality a web browser affords us given the fact that we would only be allowed to test out the GET methods from a standard web browser as a client. I have decided to use a Google Chrome Application called Postman.

We will be testing the following URI’s:

  • http://localhost:8080/RestfulWebServiceExample/rest/actors
  • http://localhost:8080/RestfulWebServiceExample/rest/actors/124 – Using GET, will return actor with id=124
  • http://localhost:8080/RestfulWebServiceExample/rest/actors/124 – Using DELETE, will remove actor with id=124
  • http://localhost:8080/RestfulWebServiceExample/rest/actors/124 – Using PUT, will perform an update where id=124, need to provide payload with actor data (XML/JSON)
  • http://localhost:8080/RestfulWebServiceExample/rest/actors/search/Jennifer
  • http://localhost:8080/RestfulWebServiceExample/rest/actors/add

Images from Postman Rest Client

Using the AngularJS/Bootstrap Client

Testing the web service by using our web browser (for GET operations) or some third party tool like Postman is cool, but there is nothing quite like using a real application to see how the service will operate. For this I have decided to use AngularJS with Bootstrap to give it a more professional look and feel. I hope you like how it turned out.

Getting All Actors

Upon launching the UI, it will make a call to the web service to retrieve all actors. This list of actors will be displayed on the left hand navigation panel. Clicking on any name will display the appropriate information in the form for review or modification. This request will be made using the GET method.

Editing a Record

Clicking on any name will display the appropriate information in the form for review or modification. At this point the record is in UPDATE mode. Clicking on the save button will make a call to the web service using the PUT method.

Adding a Record

Clicking the Add New button cause the form to clear out and cursor focus will automatically jump to the ID field where you can provide all of the actor’s details. Additionally, the record will be in CREATE mode. Clicking on the save button will make a call to the web service using the POST method.

Deleting a Record

In order for you to be able to delete a record you need to be viewing a particular record. You will notice that the DELETE button will become enabled. Clicking the delete button will make a call to the web service using the DELETE method. In this example, I deleted several actors as is evidenced by the list on the left showing only 3 remaining in our Datastore.

Searching all Records

Clicking the search button will make a call to the web service using the GET method and return all records in the Datastore whose name matches a part of the search string you used. The navigation list on the left will change mode to SEARCH CRITERIA and display all the records matching your query. Clicking on the RESET SEARCH button will reset the criteria and bring back all of the records in our Datastore.

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

java jaxrs

Please Share Us on Social Media

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply

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