RESTful Web Services - JAX-RS 2.0 - InshortsView

|
Divnesh BLOG
RESTful Web Services
 course introduces the basic concepts of RESTful web services. It also deals with concepts and how to create and consume RESTful web services using Jersey and AngularJS frameworks.

After completing this course, you will be able to:

  • Expose a resource using RESTful web service
  • Consume a RESTful web service using AngularJS
  • Exposing BoatAPI as RESTful web service and consuming it using AngularJS in HouseBoats application
Presence of Web Services
You might have used Google account for logging in to several websites or apps.
When you launch Quora, you can observe  that it allows the user to login either with Google account credentials or facebook credentials
This is achieved with the help of Web Services.
Web services are modular applications, used to expose business logic as services that can be published, discovered, and invoked over the internet.
Google has exposed its login authentication mechanism as a web service, which can be consumed by other websites or app.
Websites can use this exposed web service for recognizing a user based on his/her google credentials.
The advantage of this mechanism is that all the websites or apps, need not have their own login mechanism, they can reuse the login web service exposed by Google.
Let us work on creating a web service for HouseBoats application. 
Requirement 1: Adding Web Services in HouseBoats Application
HouseBoats provide one of the best ways to explore and enjoy the backwaters. Currently the booking process for house boats is manual and we want to automate this process. 
HouseBoats wants to expose some of its resources as a RESTful web service.
HouseBoats application also requires the web page booking.html,  to consume the exposed web services:
1
R.1.1 BoatAPI resource should be exposed as "Boat"
R.1.2 fetchBoatingTypeList() of BoatAPI should be exposed as "fetchboatingtypes" for HTTP GET request
R.1.3 fetchBoatingTypeList() should produce in JSON format
R.1.4 fetchBoatingTypeList() should fetch the boating types from getBoatingTypes() of HouseBoatService, and the response should sent in JSON format with appropriate status code
R.1.5 booking.html should consume web service with URI "Boat/fetchboatingtypes", on successful execution radio buttons will generated 
Implementation of Requirement 1
booking.html :
2
BoatAPI.java :
@Path("Boat")
public class BoatAPI {


 @Path("fetchboatingtypes")
 @GET
 @Produces(MediaType.APPLICATION_JSON)
 public Response fetchBoatingTypeList() {
  String returnValue = null;
  Response response = null;
  try {
   HouseBoatService houseBoatService = Factory
     .createHouseBoatService();
   Map boatingTypes = houseBoatService
     .getBoatingTypes();
   returnValue = JSONParser.toJson(boatingTypes);
   response = Response.status(Status.OK).entity(returnValue).build();
  } catch (Exception e) {
   String returnString = "{\"message\":\""
     + AppConfig.PROPERTIES.getProperty(e.getMessage()) + "\"}";
   response = Response.status(Status.SERVICE_UNAVAILABLE)
     .entity(returnString).build();
  }
  return response;
 }
Controller.js :
application.controller("BookingController",
  function($scope, $http) {
   $scope.bookingForm = {};
   $scope.boatingMap = null;
   $scope.bookingForm.location = null;
   $scope.bookingForm.boatingType = null;
   $scope.bookingForm.dateOfRide = null;
   $scope.bookingForm.noOfPeople = null;
   $scope.bookingForm.contactNo = null;
   $scope.bookingForm.message = null;
   
   $scope.generateList = function() {
    $http.get(URI + "Boat/fetchboatingtypes").then(
      function(response) {      
       $scope.boatingMap = response.data;
       $scope.bookingForm.message = null;
      }, function(response) {
       $scope.bookingForm.message = response.data.message;
       $scope.boatingMap = null;
      });
   };


  });
 Exposing BoatAPI as Root Resource Class
In order to solve R.1.1 i.e to expose BoatAPI as Boat, @Path annotation is added for BoatAPI.java  as shown below:
@Path("Boat")
public class BoatAPI {

  . . . 

}
@Path is used to map the path in the URI (Boat) with resources (BoatAPI) or sub resources (methods).
Its value(Boat) denotes the relative URI that must be added to the context root of the application.
http://<>:<>/<>/api/<<@Path Value>>
Its value can also denote template parameters received in the request.
@Path("/{boatid}")
Exposing fetchBoatingTypeList() as fetchboatingtypes
In order to solve R.1.2 i.e. to expose fetchBoatingTypeList() of BoatAPI as fetchboatingtypes , @Path and @Get annotation are added to fetchBoatingTypeList() as shown below:
@Path("Boat")
public class BoatAPI {

 @Path("fetchboatingtypes")
 @GET
 public Response fetchBoatingTypeList() {
  . . .
 }
 
}
@Get annotation is used to map the method to be invoked in the Java API class (fetchBoatingTypeList()) with the HTTP method in the request.
Creating the Response
In order to solve R.1.4 i.e. to fetch the boating types from getBoatingTypes() of HouseBoatService and to create the response in JSON with appropriate status code, following code is placed in fetchBoatingTypeList() as shown below: 
@Path("fetchboatingtypes")
 @GET
 @Produces(MediaType.APPLICATION_JSON)
 public Response fetchBoatingTypeList() {
  String returnValue = null;
  Response response = null;
  try {
                        // Code to invoke DAO 
   HouseBoatService houseBoatService = Factory
     .createHouseBoatService();
   Map boatingTypes = houseBoatService
     .getBoatingTypes();

                        // Sending the response as JSON string
   returnValue = JSONParser.toJson(boatingTypes);
   response = Response.status(Status.OK).entity(returnValue).build();
  } catch (Exception e) {
   String returnString = "{\"message\":\""
     + AppConfig.PROPERTIES.getProperty(e.getMessage()) + "\"}";
   response = Response.status(Status.SERVICE_UNAVAILABLE)
     .entity(returnString).build();
  }
  return response;
 }
 Consuming Web Service
In order to solve R.1.5 i.e. to consume the web service with given URI and to display the radio buttons on proper execution, $http is used in BookingController as shown below:
application.controller("BookingController",
  function($scope, $http) {
   $scope.bookingForm = {};
   $scope.boatingMap = null;
   $scope.bookingForm.location = null;
   $scope.bookingForm.boatingType = null;
   $scope.bookingForm.dateOfRide = null;
   $scope.bookingForm.noOfPeople = null;
   $scope.bookingForm.contactNo = null;
   $scope.bookingForm.message = null;
   
                        // Consuming web service with URI (Boat/fetchboatingtypes) using $http 
   $scope.generateList = function() {
    $http.get("http://localhost:2002/HouseBoats/api/" + "Boat/fetchboatingtypes").then(
      function(response) {      
       $scope.boatingMap = response.data;
       $scope.bookingForm.message = null;
      }, function(response) {
       $scope.bookingForm.message = response.data.message;
       $scope.boatingMap = null;
      });
   };

  });
$http is an angular provided service for communication over HTTP. 
Exposing deleteEmployee() for HTTP DELETE Request
HouseBoats wants to expose the deleteEmployee() functionality as web service, in order to delete the details of its previous employees.
HouseBoats application also requires the web page empDetails.html,  to consume the exposed web service:
R.1.a deleteEmployee() of BoatAPI should be exposed for HTTP DELETE request by accepting a parameter
R.1.b deleteEmployee() should produce the appropriate response in PLAIN TEXT format 
R.1.c deleteEmployee() should generate the response with appropriate status code
R.1.d empDetails.html should consume web service with URI "Employees/{empid}"

Solving the requirements :
In order to solve R.1.a @Delete,@Path and @PathParam annotation is added for deleteEmployee() as shown below:
BoatAPI.java:
    @DELETE
 @Path("/{empid}")
 public Response deleteEmployee(@PathParam("empid") int empId) {
  . . .
      }
@PathParam is used to indicate the parameter received in the URI. It injects the value received in the URI as an argument to the method. 
In order to solve R.1.b @Produces annotation is added for deleteEmployee() as shown below:
    @DELETE
 @Path("/{empid}")
 @Produces(MediaType.TEXT_PLAIN)
 public Response deleteEmployee(@PathParam("empid") int empId) {
   . . .
    }
In order to solve R.1.c the response object is created as shown below:
    @DELETE
 @Path("/{empid}")
 @Produces(MediaType.TEXT_PLAIN)
 public Response deleteEmployee(@PathParam("empid") int empId) {
  String message = null;
  Response result = null;
  try {
   // CODE TO CALL SERVICE CLASS METHOD FOR DELETING EMPLOYEE DETAILS
   message = "SERVICE.DELETE_EMPLOYEE_SUCCESS from DELETE for EMPLOYEE ID : "
     + empId;

   result = Response.status(Status.OK).entity(message).build();

  } catch (Exception e) {
   if (e.getMessage().contains("DAO")) {
    result = Response.status(Status.SERVICE_UNAVAILABLE).entity(e.getMessage()).build();
   } else {
    result = Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
   }
   
  }
  return result;
      }    
In order to solve R.1.4 the $http is used in deleteEmpDetails as shown below:
empDetails.html:
app.controller('deleteEmpDetails',function($http,$scope) {
 $http.delete('http://localhost:1002/001-EmployeeWebService/api/Boat/2').  // Parameter passed along with URI : 2
 then(function(response)
   {
    $scope.message = response.data;
   },function(response)
   {
    $scope.message = response.data;
   }) });
 Mapping API in web.xml
In order to map the request to the corresponding resource, ServletContainer and the location of the resource is configured in web.xml as shown below:
3
Note:
  • org.glassfish.jersey.servlet.ServletContainer – Provided by Jersey
  • jersey.config.server.provider.packages  - It indicates package where API classes are placed
  • com.infy.api - Resources package
  • /api/* - Pattern to be matched so that requests having this pattern in the URI are routed to the Jersey Servlet container
@SuppressWarnings({ "rawtypes", "unchecked" })
public class JSONParser {

 public static Object jsonToBean(String jsonInput, Class class1)
   throws Exception {
  JsonObject jsonObj = Json.createReader(new StringReader(jsonInput))
    .readObject();

  Object object = null;
  try {

   object = class1.newInstance();
   Field fieldArray[] = class1.getDeclaredFields();

   for (Field field : fieldArray) {
    // To know the Json type of the field
    // System.out.println(jsonObj.get(field.getName())+"  "+jsonObj.get(field.getName()).getValueType());
    field.setAccessible(true);
    if (jsonObj.containsKey(field.getName())) {
     // Sets the value for field type
     // Number,String,boolean,Object[Calendar,AnyUserdefined
     // Object]
     setFieldData(jsonObj, field, object);

     if (jsonObj.get(field.getName()).getValueType()
       .equals(ValueType.ARRAY)) {

      System.out.println(field.getName() + " "
        + field.getType().getCanonicalName());

      if (field.getType().getCanonicalName()
        .contains("java.util.Map")) {
       // JsonArray
       // jsonArray=jsonObj.getJsonArray(field.getName());

       ParameterizedType listType = (ParameterizedType) field
         .getGenericType();
       Class keyClass = (Class) listType
         .getActualTypeArguments()[0];
       Class valueClass = (Class) listType
         .getActualTypeArguments()[1];
       System.out.println("hi" + keyClass + " "
         + valueClass);

      }
      // If object type is list
      else if (field.getType().getCanonicalName()
        .contains("java.util.List")) {

       // Get the ParameterizedType of List
       ParameterizedType listType = (ParameterizedType) field
         .getGenericType();
       Class listClass = (Class) listType
         .getActualTypeArguments()[0];

       JsonArray jsonArray = jsonObj.getJsonArray(field
         .getName());
       List list = new ArrayList();
       // List of String
       if (listClass.getCanonicalName().contains("String")) {
        for (int i = 0; i < jsonArray.size(); i++) {
         list.add(jsonArray.getString(i));
        }
       }
       // List of Integer
       else if (listClass.getCanonicalName().contains(
         "Integer")) {
        for (int i = 0; i < jsonArray.size(); i++) {
         list.add(jsonArray.getInt(i));
        }
       }
       // list of user defined object
       else {
        for (int i = 0; i < jsonArray.size(); i++) {
         JsonObject jsonObject = jsonArray
           .getJsonObject(i);
         Object obj = getUserDefinedObject(listClass, jsonObject, field);
         list.add(obj);
        }
       }
       field.set(object, list);
      }
     }
    }
   }
   System.out.println(object);
  } catch (Exception e) {
   e.printStackTrace();
   throw new Exception("Problem in input data", e);
  }
  return object;
 }

 private static Object getUserDefinedObject(Class listClass,
   JsonObject jsonObject, Field field) throws Exception {

  Object obj = Class.forName(listClass.getName()).newInstance();
  Field fieldBean[] = listClass.getDeclaredFields();

  for (Field field2 : fieldBean) {
   field2.setAccessible(true);
   setFieldData(jsonObject, field2, obj);
  }
  return obj;
 }

 // Getting Calender object from JsonObject
 private static Calendar getCalendarFromJSON(JsonObject jsonObj, Field field) {
  JsonObject calJsonObj = jsonObj.getJsonObject(field.getName());

  Calendar actualCalObject = Calendar.getInstance();
  actualCalObject.set(Calendar.DATE, calJsonObj.getInt("DATE"));
  actualCalObject.set(Calendar.MONTH, calJsonObj.getInt("MONTH"));
  actualCalObject.set(Calendar.YEAR, calJsonObj.getInt("YEAR"));
  return actualCalObject;
 }

 // For setting primitive types,Calendar, User defined
 private static void setFieldData(JsonObject jsonObj, Field field,
   Object object) throws Exception {
  // System.out.println(jsonObj.get(field.getName())+"  "+jsonObj.get(field.getName()).getValueType());

  if (jsonObj.get(field.getName()).getValueType().equals(ValueType.STRING)) {
   
   field.set(object, jsonObj.getString(field.getName()));
  } else if (jsonObj.get(field.getName()).getValueType().equals(ValueType.NUMBER)) {
   if (field.getType().getCanonicalName().contains("Integer"))
    field.set(object, jsonObj.getInt(field.getName()));
   else if (field.getType().getCanonicalName().contains("Long")) {
    Long value = Long.parseLong(jsonObj.get(field.getName())
      .toString());
    field.set(object, value);
   } else if (field.getType().getCanonicalName().contains("Double")) {
    Double value = Double.parseDouble(jsonObj.get(field.getName())
      .toString());
    field.set(object, value);
   } else if (field.getType().getCanonicalName().contains("Float")) {
    Float value = Float.parseFloat(jsonObj.get(field.getName())
      .toString());
    field.set(object, value);
   } else if (field.getType().getCanonicalName().contains("Short")) {
    Short value = Short.parseShort(jsonObj.get(field.getName())
      .toString());
    field.set(object, value);
   } else if (field.getType().getCanonicalName().contains("Byte")) {
    Byte value = Byte.parseByte(jsonObj.get(field.getName())
      .toString());
    field.set(object, value);
   }
  } else if (jsonObj.get(field.getName()).getValueType()
    .equals(ValueType.TRUE)) {
   field.set(object, jsonObj.getBoolean(field.getName()));
  } else if (jsonObj.get(field.getName()).getValueType()
    .equals(ValueType.FALSE)) {
   field.set(object, jsonObj.getBoolean(field.getName()));
  } else if (jsonObj.get(field.getName()).getValueType()
    .equals(ValueType.OBJECT)) {
   // Object of Calendar type with only date,month,year
   if (field.getType().getCanonicalName()
     .contains("java.util.Calendar")) {
    Calendar actualCalObject = getCalendarFromJSON(jsonObj, field);
    field.set(object, actualCalObject);
   }

   else {
    JsonObject jsonObject2 = jsonObj.getJsonObject(field.getName());
    Object resultObj = getUserDefinedObject(field.getType(),
      jsonObject2, field);
    field.set(object, resultObj);
   }
  }
 }

 // Convert given object into Json String
 public static String beanToJson(Object input) {
  String jsonString = null;

  JsonObjectBuilder mainObjectBuilder = Json.createObjectBuilder();
  
  try {
   Class class1 = input.getClass();
   
   
   if(class1.getCanonicalName().contains("List")|| class1.getCanonicalName().contains("ArrayList")){
    List list=(List)input;
    jsonString=addListObject( list.get(0).getClass(), input).toString();
   }
   else{
   Field fields[] = input.getClass().getDeclaredFields();
    for (Field field : fields) {
     field.setAccessible(true);
     Object fieldValue = field.get(input);

     if (fieldValue != null) {
      // System.out.println(field.getType().getCanonicalName()+field.getName());
      addFieldData(mainObjectBuilder, field.getType()
        .getCanonicalName(), field.getName(),
        fieldValue, field);

     } else {
      mainObjectBuilder.add(field.getName(), JsonValue.NULL);
     }
    }
    
   
   StringWriter stringWriter = new StringWriter();
   JsonWriter jsonWriter = Json.createWriter(stringWriter);
   jsonWriter.writeObject(mainObjectBuilder.build());
   jsonString = stringWriter.getBuffer().toString();
  }
  } catch (IllegalArgumentException | IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return jsonString;
 }

 private static void addFieldData(JsonObjectBuilder builder, String type,
   String name, Object input, Field field) {

  if (type.contains("String")) {
   builder.add(name, input.toString());
  } else if (type.contains("Integer")) {
   builder.add(name, Integer.parseInt(input + ""));
  } else if (type.contains("Long")) {
   builder.add(name, Long.parseLong(input.toString()));
  } else if (type.contains("Double")) {
   builder.add(name, Double.parseDouble(input.toString()));
  } else if (type.contains("Float")) {
   builder.add(name, Float.parseFloat(input.toString()));
  } else if (type.contains("Short")) {
   builder.add(name, Short.parseShort(input.toString()));
  } else if (type.contains("Byte")) {
   builder.add(name, Byte.parseByte(input.toString()));
  } else if (type.equalsIgnoreCase("Boolean")) {
   builder.add(name, Boolean.parseBoolean(input.toString()));
  } else if (type.equalsIgnoreCase("Character")
    || type.equalsIgnoreCase("char")) {
   builder.add(name, input.toString().charAt(0));
  } else if (type.contains("java.util.Calendar")) {
   addCalendarObject(builder, name, input);
  } else if (type.contains("java.util.ArrayList")
    || type.contains("java.util.List")) {
   ParameterizedType listType = (ParameterizedType) field.getGenericType();
   Class listClass = (Class) listType.getActualTypeArguments()[0];
   JsonArray array=addListObject(listClass, input);
   builder.add(field.getName(), array);
  } else {
   builder.add(name, generateJsonObject(input));
  }

 }

 private static JsonArray addListObject( Class listClass,
   Object input) {
  List list = (List) input;

  JsonArrayBuilder arrayBuilder = Json.createArrayBuilder();

  // System.out.println("Type "+listClass.getCanonicalName());
  String name = listClass.getCanonicalName();
  for (Object object : list) {
   if (name.contains("String")) {
    arrayBuilder.add(object.toString());
   } else if (name.contains("Integer")) {
    arrayBuilder.add(Integer.parseInt(object.toString()));
   } else if (name.contains("Long")) {
    arrayBuilder.add(Long.parseLong(object.toString()));
   } else if (name.contains("Float")) {
    arrayBuilder.add(Float.parseFloat(object.toString()));
   } else if (name.contains("Double")) {
    arrayBuilder.add(Double.parseDouble(object.toString()));
   } else if (name.contains("Byte")) {
    arrayBuilder.add(Byte.parseByte(object.toString()));
   }

   else {
    arrayBuilder.add(generateJsonObject(object));
   }

  }
  return arrayBuilder.build();

 }

 private static void addCalendarObject(JsonObjectBuilder builder,
   String name, Object input) {
  Calendar calendar = (Calendar) input;

  JsonObjectBuilder calObjBuilder = Json.createObjectBuilder();
  calObjBuilder.add("DATE", calendar.get(Calendar.DATE))
    .add("MONTH", calendar.get(Calendar.MONTH))
    .add("YEAR", calendar.get(Calendar.YEAR));
  builder.add(name, calObjBuilder.build());

 }

 private static JsonObject generateJsonObject(Object input) {
  JsonObject jsonObject = null;

  Field fields[] = input.getClass().getDeclaredFields();

  JsonObjectBuilder jsonObjectBuilder = Json.createObjectBuilder();
  try {
   for (Field field : fields) {
    field.setAccessible(true);
    Object fieldValue = field.get(input);

    if (fieldValue != null) {
     addFieldData(jsonObjectBuilder, field.getType()
       .getCanonicalName(), field.getName(), fieldValue,
       field);
    } else {
     jsonObjectBuilder.add(field.getName(), JsonValue.NULL);
    }
   }
   jsonObject = jsonObjectBuilder.build();
  } catch (IllegalArgumentException | IllegalAccessException e) {
    e.printStackTrace();
  }

  // System.out.println(jsonObject);
  return jsonObject;
 }

}
Requirement 2 : Adding Web Services in HouseBoats Application
HouseBoats wants to expose the booking() functionality as web service, in order to save the booking details of HouseBoats.
HouseBoats application also requires the web page booking.html,  to consume the exposed web service:
R.2.1 booking() of BoatAPI should be exposed as "book" for HTTP POST request
R.2.2 booking() should consume and produce data in JSON format
R.2.3 booking() should convert JSON to HouseBoatBooking bean object and later send the bean object using bookHouseBoat() of HouseBoatService
R.2.4 booking.html should consume web service with URI "Boat/booking", on successful execution a success message is shown as below: 
5
booking.html : @Path annotation with "book" URI path template and @POST annotation
6
BoatAPI.java : @Consumes annotation indicates the request representation.
@Path("Boat")
public class BoatAPI {
    @Path("book")
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response booking(String dataRecieved) throws Exception {
        Response response = null;
        String string = dataRecieved;
        try {
            // Converting JSON into Bean
            HouseBoatBooking houseBoatBooking = JSONParser.fromJson(string,HouseBoatBooking.class);
            
           // Code to invoke DAO 
            HouseBoatService houseBoatService = Factory
                    .createHouseBoatService();
            houseBoatBooking = houseBoatService.bookHouseBoat(houseBoatBooking);

            String successMessage = this.getBookingSuccessMessage(houseBoatBooking);            
            houseBoatBooking = new HouseBoatBooking();
            houseBoatBooking.setMessage(successMessage);
            
            // Sending the response as JSON string
            String returnString = JSONParser.toJson(houseBoatBooking);
            response = Response.status(Status.OK).entity(returnString).build();
            
        } catch (Exception e) {
            e.printStackTrace();
            String errorMessage = AppConfig.PROPERTIES.getProperty(e.getMessage());
            
            HouseBoatBooking houseBoatBooking = new HouseBoatBooking();
            houseBoatBooking.setMessage(errorMessage);
            
            String returnString = JSONParser.toJson(houseBoatBooking);

            response = Response.status(Status.SERVICE_UNAVAILABLE)
                    .entity(returnString).build();
        }
        return response;
    }

@Path("fetchboatingtypes")
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response fetchBoatingTypeList() {
    . . .
    }

 }
Controller.js :
application.controller("BookingController",
        function($scope, $http) {
            $scope.bookingForm = {};
            $scope.boatingMap = null;
            $scope.bookingForm.location = null;
            $scope.bookingForm.boatingType = null;
            $scope.bookingForm.dateOfRide = null;
            $scope.bookingForm.noOfPeople = null;
            $scope.bookingForm.contactNo = null;
            $scope.bookingForm.message = null;
            
            $scope.generateList = function() {
               ...
            };

            $scope.bookingForm.submitTheForm = function() {
                $scope.bookingForm.message = null;
                $scope.bookingForm.dateOfRide = $scope.bookingForm.dateOfRide.toLocaleString()
                var data = angular.toJson($scope.bookingForm);
                $http.post(URI + "Boat/book", data).then(function(response) {
                    $scope.bookingForm.message = response.data.message;
                }, function(response) {
                    $scope.bookingForm.message = response.data.message;
                });
            };
        }
Exposing updateEmployee() for HTTP PUT Request
HouseBoats wants to expose the updateEmployee() functionality as web service, in order to update the details of its existing employees.
HouseBoats application also requires the web page empDetails.html,  to consume the exposed web service:
R.2.a updateEmployee() of BoatAPI should be exposed for HTTP PUT request 
R.2.b updateEmployee() should consume and produce data in JSON format
R.2.c updateEmployee() should convert JSON to Employee bean object and later send the bean object to appropriate service method. It should produce the response with appropriate status code
R.2.d empDetails.html should consume the exposed web service
Solving the requirements :
In order to solve R.2.a @PUT annotation is added for updateEmployee() as shown below:
BoatAPI.java:
@PUT
public Response updateEmployee(String emp) {
 . . .
}
In order to solve R.2.b @Produces and @Consumes annotation is added for deleteEmployee() as shown below:
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateEmployee(String emp) {
     . . . 
}
In order to solve R.2.c the response object is created as shown below:
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response updateEmployee(String emp) {
  String message = null;
  Response result = null;
  try {
   // Converting JSON string into appropriate bean object
   Employee input = (Employee) JSONParser.jsonToBean(emp,
     Employee.class);

   // CODE TO CALL DAO CLASS METHOD FOR UPDATING EMPLOYEE DETAILS
   message = "SERVICE.UPDATE_EMPLOYEE_SUCCESS from PUT";

   String jsonMessage = "{\"message\":\"" + message + "\"}";
   result = Response.status(Status.OK).entity(jsonMessage).build();

  } catch (Exception e) {
   if (e.getMessage().contains("DAO")) {
    String error = "{\"message\":\"" + e.getMessage() + "\"}";
    result = Response.status(Status.SERVICE_UNAVAILABLE).entity(error).build();
   } else {
    String error = "{\"message\":\"" + e.getMessage() + "\"}";
    result = Response.status(Status.BAD_REQUEST).entity(error).build();
   }
  }
  return result;
}
In order to solve R.2.d the $http is used in updateEmpDetails as shown below:
empDetails.html:
app.controller('updateEmpDetails',function($http,$scope){
 $scope.employee= {};
    var data = angular.toJson($scope.employee); // JSON.Stringify();
 
 $http.put('http://localhost:1002/WebService/api/Boat',data).
 then(function(response)
   {
    $scope.message = response.data.message;
   },function(response)
   {
    $scope.message = response.data.message;
   })
});
URI for Identifying the Resources
We have prefixed all the URIs with  :  
http://<>:<>/<>/api
Shortcut Methods for $http Service API
You would have seen the below code in BoatAPI.java while solving the requirement:
$http.get(". . ./Boat/fetchboatingtypes").then(function(response){...},function(response){...});
$http.put('. . ./Boat',data).then(function(response){...},function(response){...});
$http.post(". . ./Boat/book", data).then(function(response) {...}, function(response) {...});
$http.delete('.../Boat/2').then(function(response){...},function(response){...});
These are nothing but shortcut methods provided by the $http service API.
$http internally uses the browser’s XMLHttpRequest object to make the request.
These methods can have an optional config parameters as shown below:
$http.get(url,config).then(function(response){...},function(response){...})
$http.post(url,data,config).then(function(response){...},function(response){...})
$http.put(url,data,config).then(function(response){...},function(response){...})
$http.delete(url,config).then(function(response){...},function(response){...})
The config object contains the following key value pairs among others:
  • method : HTTP method – GET, POST, PUT, DELETE etc.
  • url: URL of the resource on the server
  • params: key value pairs containing parameters to be sent over HTTP GET method. These get appended to the URL
  • data: Data to be sent to the server over POST/PUT
The response object that is received by these functions as argument contains the following properties among others:
  • data: The body of the HTTP response
  • status: HTTP status code sent by the server
  • headers: HTTP headers in the response
  • statusText: text received in the status of the response
$httpProvider can be used to change the behavior of the $http service of angular i.e.
  • headers: Configure headers such as content-type, accept etc. for different requests
  • cache: used for caching response of requests
One to one mapping of HTTP methods with CRUD operations are as shown below:
8
Status Codes for Response
By now, you would have seen the code highlighted below in BoatAPI.java while solving the requirement:
 Response.status(Status.OK).entity(returnValue).build();
 Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build()
 Response.status(Status.SERVICE_UNAVAILABLE).entity(returnString).build();
Whenever we are generating the response, we also need to send the appropriate response status code along with the response.
Every status code is represented by with 3-digit code. Out of which the first digit of the code defines the class of the response.
Response codes are classified into following classes :
  • 1xx: Informational
  • 2xx: Success
  • 3xx: Redirection
  • 4xx: Client Error
  • 5xx: Server Error
Some important response status codes are as follows :
1xx: Informational
 "100" : Continue
2xx: Success 
 "200" : OK
 "204" : No Content
4xx: Client Error
 "400" : Bad Request
 "404" : Not Found
5xx: Server Error
 "500" : Internal Server Error
 "503" : Service Unavailable
SOAP Based Web Services
SOAP web service is interoperable and Firewall friendly
We have seen creation of RESTful Web Service.
REpresentational State Transfer(REST) is an architectural style to create web services. REST based web services are fast and permits different data formats(HTML, XML, JSON etc.).
Another type of web service is SOAP based web service. SOAP based web services contain
  • SOAP protocol
    • Simple Object Access Protocol(SOAP) is an XML-based messaging protocol for exchanging information.
  • XML/JSON message
    • Data can be interchanged in XML or JSON format
  • WSDL
    • Web Services Description Language(WSDL) that describes form of message, the location where message needs to be sent and response type of each message.
  • UDDI
    • Universal Description, Discovery and Integration(UDDI) is a registry which provides a mechanism to register and locate web service applications.
Although SOAP based web services are highly secure and reliable, they are slower than the RESTful web services as more time is needed to parse the XML data.
Also SOAP only uses WSDL and doesnt have any other mechanism to discover the service, which is another major disadvantage of SOAP based web services.
JAX-RS 2.0 API Annotations and Jersey Framework
You would have seen the below annotations while exposing the resource as web service in BoatAPI.java of HouseBoats application. Let us understand the classification of these annotations.
@Path("Boat")
public class BoatAPI {

 @Path("fetchboatingtypes")
 @GET
 @Produces(MediaType.APPLICATION_JSON)
 public Response fetchBoatingTypeList() {
         ...
        }


        @DELETE
 @Path("/{empid}")
 @Produces(MediaType.TEXT_PLAIN)
 public Response deleteEmployee(@PathParam("empid") int empId) {
   . . .
        }

        @Path("book")
        @POST
        @Consumes(MediaType.APPLICATION_JSON)
        @Produces(MediaType.APPLICATION_JSON)
        public Response booking(String dataRecieved) throws Exception {
         . . .
         }


        @PUT
        @Consumes(MediaType.APPLICATION_JSON)
        @Produces(MediaType.APPLICATION_JSON)
        public Response updateEmployee(String emp) {
             . . . 
        }
}

These are the annotations provided by JAX-RS 2.0 API, in order to expose resource as RESTful web services in Java.
JAX-RS 2.0 is an annotation based simplified API, which provides
  • features to manage URI patterns and HTTP operations using annotation ( i.e. mapping /fetchboatingtypes to $http.get() )
  • methods to convert Java objects into JSON/XML and vice versa
  • access to web services using REST client API ( i.e consuming the RESTful web services using Java client)
These annotations can be classified into four categories as shown below:
  • URI matching annotations
@javax.ws.rs.Path
  • HTTP method matching annotations
@javax.ws.rs.GET
@javax.ws.rs.POST
@javax.ws.rs.PUT
@javax.ws.rs.DELETE
  • Injection annotations : help in mapping the parameter from http request with the parameter of a Java method 
@javax.ws.rs.PathParam
  • Resource representation annotations
@Produces
@Consumes
Jersey is open source and serves as a reference implementation of JAX-RS specification.
  • It supports a servlet based implementation of RESTful web service (org.glassfish.jersey.servlet.ServletContainer in web.xml)
  • It also provides client components to test the RESTful services that are created
Web Service Vulnerabilities
Login page of Quora requires user's Email and Password. When entered, this Email and Password is validated against Emails and Passwords stored in Quora's database or authentication server. This is known as Authentication.
All the actions after login, like liking a post or commenting on it etc. should be specific to the user's account, which is taken care by session management.
Failure to implement these two functionalities in a web application leads to vulnerability known as broken authentication and session management. It is one of the top 10 vulnerabilities listed by OWASP(Open Web Application Security Project).
Authentication is said to be broken when an attacker is able to find out the credentials of a user and impersonate them on the application.
Causes of occurrence of such a vulnerability could be:
9
Implementing Authentication and Session Management
Authentication and Session management may be implemented in several ways
Some of the authentication mechanisms:
  1. Basic authentication – Uses username and password
  2. OAuth authentication – A token based authentication
  3. OpenId – Authenticated using an identity provider and can be used for SSO
Some of the session management mechanisms:
  1. Server provided session management
  2. URL rewriting
  3. Cookies
Let us implement authentication using basic authentication and session management using cookies
  • Basic authentication means accepting user name and password from the user when the user connects to the web site. This is done using an HTML form
  • Once the user provides the credential, we send the credentials to the server for validation
  • Once validated, the server responds with a unique token in the cookie object (contains key-value pairs)
  • The cookie is received at the client and will be used for all subsequent requests
AngularJS has ngCookies module which needs to be injected in your module as shown below:
var email = angular.module('EmailApp', [ 'ngRoute', 'ngCookies' ]);
It exposes $cookies service which is used to manage the cookies as shown below:
var cookieId = $cookies.get("cookieId");
Once fetched, various methods of $cookies which can be used are listed below:

Controller : App.js
var email = angular.module('EmailApp', [ 'ngRoute', 'ngCookies' ]);

// Added an error route for errors related to cookies
email.config([ '$routeProvider', function($routeProvider) {
 $routeProvider.when('/login', {
  templateUrl : 'partials/login.html',
  controller : 'LoginController'
 }).when('/inbox/', {
  templateUrl : 'partials/inbox.html',
  controller : 'InboxController'
 }).when('/messages/:msgid', {
  templateUrl : 'partials/message.html',
  controller : 'MessageController'
 }).when('/error', {
  templateUrl : 'partials/error.html'
 }).otherwise({
  redirectTo : '/login'
 });
} ]);

//Config to allow cookies
/*email.config(['$httpProvider',function($httpProvider){
 $httpProvider.defaults.withCredentials=true;
 //$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
}]);*/
user=null;
email.controller('LoginController', [ '$scope', '$location', 
  'DataService', function($scope, $location, service) {
   // Logic to validate credentials
   $scope.validateCredentials = function() {
    $scope.errorMessage = null;
    var username = $scope.username;
    var password = $scope.password;
    service.validateUser(username, password, function(isValid) {
     if (isValid) {
      path = "/inbox";
      user=username;
      $location.path(path);
     } else {
      $scope.errorMessage = "Invalid Credentials. Try again";
      user=null;
     }
    });

   }
  } ]);

email.controller('InboxController', [ '$scope', '$location',
  'DataService', function($scope, $location, service) {
   $scope.pNum = 1;
   $scope.pSize = 2;

   $scope.username = user;

   service.getUserMessages( function(userMessages) {
    if (userMessages != "error")
     $scope.messageList = userMessages;
    else
     $location.path("/error");
   });

  } ]);

email.controller('MessageController', [ '$scope', '$routeParams', '$location',
  'DataService', function($scope, $routeParams, $location, service) {
   user_id = $routeParams.username;
   msg_id = $routeParams.msgid;
   service.getMessage(msg_id, function(msg) {
    if (msg != "error")
     $scope.msg = msg;
    else
     $location.path("/error");
   });
  } ]);

email.filter('paginate', function() {
 return function(msgList, pageNo, pageSize) {
  var start = (pageNo - 1) * pageSize;
  var end = start + pageSize;
  console.log(start + " " + end);
  return msgList.slice(start, end)
 };
});

email.factory('DataService', [ '$http',  function($http) {
 var uri = "http://localhost:9644/EmailAppWebService/api/users";
 return {

  validateUser : function(uname, pwd, successCallback) {
   var valid = false;
   var validateuri = uri + "/authenticate";
   credentials={username:uname,password:pwd};
   data=angular.toJson(credentials);
   console.log(data)
   $http.post(validateuri,data).then(function(response) {
    console.log("data"+response.data)
    data=response.data;
    if (data == "false")
     valid = false;
    else{
     valid=true;
    }
    successCallback(valid);
   },function(){});
  },
  // Changed this method to get the username from the cookies automatically sent by browser
  getUserMessages : function(successCallback) {
   var userMessages;
   var allMessagesUri = uri + "/user/messages";
   $http.get(allMessagesUri).then(function(response) {
    successCallback(response.data);
   });
  },
  // Changed this method to get the username from cookies
  getMessage : function(msgid, successCallback) {
   //var cookieId = $cookies.get("cookieId");
   var msg;
   var messageUri = uri + "/user/messages/" + msgid;
   $http.get(messageUri).then(function(response) {
    successCallback(response.data);
   });
  }
 }
} ]);












Featured Post

HTML cheetsheet

List: Link tgs Dropdown

Popular Post

(C) Copyright 2018, All rights resrved InShortView. Template by colorlib