Using embedded worklight browser simulator

The Worklight 50 studio comes with a very nice mobile browser simulator, you can access it by going to http://localhost:8080/console and clicking on the device related link It opens up a nice mobile browser simulator like this, you can use it to simulate multiple devices, use different Cordova functions.

Notes about Embedded Jetty server inside Worklight Studio

The Worklight 50 simplifies development by embedding the Worklight server as plugin. It uses embedded Jetty server for running the worklight application. This makes starting, stopping server and deploying application to server much faster compared to before. I noticed one problem with the Worklight embedded server which is that as soon as i start my IDE it starts embedded jetty on port 8080, which conflicts with others server application and so far i have not found a way to change this port number. Also when you try to stop the server from within worklight studio it does not actually stop the server. When you right click on your application and say "Build All and Deploy" it actually just copies the application in WorklightServerHome\<appname> directory. In that directory you can see few Jetty server related directories, there is also a log directory which has Jetty server related logs You can take a look at <workspace>\WorklightServerHome\<appname>\log\server.log for more details on the problems on server.

Worklight 50 release

IBM recently released release WorkLight 5, which is available for download from here. If you have IBM partner world account like me then you can download the Worklight server from partner world. Looks like IBM Worklight 5.0 has quite few new features In my case i already have a PhoneGap development environment setup, so all i had to do was add http://public.dhe.ibm.com/ibmdl/export/pub/software/mobile-solutions/wor as Available site in Eclipse and it took care of installing Worklight studio and it comes with embedded Worklight server in it, so no need to install either Worklight server or database server separately.

Using jQUery .load(), ajax wrapper method

In the Posting data to REST service in PhoneGap/Cordova i talked about how to use jQuery to make a HTTP POST call to a REST service. In that case i used $.post() method, But jQuery also provides one wrapper method load() that makes making AJAX call even simpler. I wanted to try that so i changed the sample to use the load(). The load() method works on jQuery selector, so it first makes AJAX request and then takes the response of AJAX method and inserts it in all the elements returns by the selector. In my case first i did create a HTTP form and when i click on it, i want to collect all the values entered by the user on the form and use them to make HTTP POST call and once the result it returned i want to insert it into html div with id equal to result. This single line of code does everything $('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact', $("#insertContact :input")); First i am selecting a div with id equal to result by using $('div#result') selector, the http://192.168.1.101:9000/ManageContact/rest/contact parameter is the URL to which the XHR request should be made. Second paramater $("#insertContact :input") says that select the form insertContact and return all the inputs on it in array format and submit them to the URL.

<!DOCTYPE html>
<html>
<head>
<title>Manage Contact</title>
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script type="text/javascript" charset="utf-8">
  $(document).ready(function(){
    $("#submit").click(insertContact);
    function insertContact(){
      console.log("Entering insertContact()");
      $('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact',
   $("#insertContact :input")); 
      return false;
    }
  });
</script>
</head>
<body>
  <form id="insertContact">
    <table>
      <tr>
        <td>Contact Id</td>
        <td><input type="text" name="contactId" /></td>
      </tr>
      <tr>
        <td>First Name</td>
        <td><input type="text" name="firstName" /></td>
      </tr>
      <tr>
        <td>Last Name</td>
        <td><input type="text" name="lastName" /></td>
      </tr>
      <tr>
        <td>Email</td>
        <td><input type="text" name="email" /></td>
      </tr>
      <tr>
        <td><input type="submit" id="submit" name="submit" 
  value="Submit" /></td>
      </tr>
    </table>
  </form>
  
  <div id='result'></div>
 
</body>
</html>
The load() method looks at the second parameter to figure out if the request should be made using HTTP GET or POST. If i wanted to submit the form using HTTP GET i should have used $('div#result').load('http://192.168.1.101:9000/ManageContact/rest/contact', $("#insertContact").serialize());

Adding support for back button in jQuery Mobile page in PhoneGap/Cordova application

When your building PhoneGap/Cordova application you might have noticed that it does not get browser back button. But if your using JQuery mobile you can take care of this situation by adding data-add-back-btn attribute at page level, I did that and this is screen show of how my page looks This is the index.html file that i used for my application.

<!DOCTYPE html>
<html>
<head>
<title>Device Properties Example</title>

<script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>
<link rel="stylesheet"
  href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
<script
  src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>

<script type="text/javascript" charset="utf-8">

</script>
</head>
<body>
  <div data-role="page" id="page1" data-add-back-btn="true" 
  data-back-btn-text="Previous" data-title="First Page">
    <div data-theme="a" data-role="header">
      <h3>First Page</h3>
    </div>
    <div data-role="content">
      <a href="#page2">Go to page 2</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>
  
  <div data-role="page" id="page2" data-add-back-btn="true" 
  data-back-btn-text="Previous" data-title="Second Page">
    <div data-theme="a" data-role="header">
      <h3>Second Page</h3>
    </div>
    <div data-role="content">
      <a href="#page3">Go to page 3</a>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>  
    <div data-role="page" id="page3" data-add-back-btn="true" 
 data-back-btn-text="Previous" data-title="Third Page">
    <div data-theme="a" data-role="header">
      <h3>Third page</h3>
    </div>
    <div data-role="content">
      <h3>Hello from page 3</h3>
    </div>
    <div data-theme="a" data-role="footer">
      <h3>Copyright stuff</h3>
    </div>
  </div>  
</body>
</html>
My index.html page has 3 JQM pages inside it, each one of them has 2 attributes first is data-add-back-btn which says that you want Jquery Mobile to manage back button when their is page in the history, you enable it by setting its value to true. The data-back-btn-text attribute is used to set title of the back button, in my case i am setting it to Previous

Posting data to REST service in PhoneGap/Cordova

In the Consuming REST service from PhoneGap/Cordova entry i talked about how you can consume a REST service in PhoneGap/Cordova application using JQuery. In that application i was making a HTTP GET request to get data from REST service, i wanted to figure out how to make a HTTP POST call to REST service to create a new contact. You can download the index.html for my application from here This is screen shot of how my form looks like
This is screen shot of success message that i get if the contact insertion is successful

I followed these steps to build the CordovaManageContact application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i changed the index.html file for my application to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Manage Contact</title>
    <script type="text/javascript" 
    charset="utf-8" src="cordova-1.7.0.js"></script>
    <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script type="text/javascript" charset="utf-8">
      $(document).ready(function(){
        $("#submit").click(insertContact);
      });
      function insertContact(){
        console.log("Entering insertContact()");
        $.post("http://192.168.1.101:9000/ManageContact/rest/contact",
        $("#insertContact :input").serializeArray(), 
        function(json){
          if(json== null || json == 'undefined')
            alert("Insert failed");
          else
            alert("Insert successful");
        });
        return false;
      }
    </script>
    </head>
    <body>
      <h3>Insert Contact</h3>
      <form id="insertContact">
        <table>
          <tr>
            <td>Contact Id</td>
            <td><input type="text" name="contactId" /></td>
          </tr>
          <tr>
            <td>First Name</td>
            <td><input type="text" name="firstName" /></td>
          </tr>
          <tr>
            <td>Last Name</td>
            <td><input type="text" name="lastName" /></td>
          </tr>
          <tr>
            <td>Email</td>
            <td><input type="text" name="email" /></td>
          </tr>
          <tr>
            <td><input type="submit" 
      id="submit" name="submit" value="Submit" /></td>
          </tr>
        </table>
      </form>
    </body>
    </html>
    
    When the user clicks on submit button control goes to insertContact() method., In this method i am using jQuery $.post() call to submit the form to http://192.168.1.101:9000/ManageContact/rest/contact URL. I am using jQuery to collect all the values entered by the user into form and encode them by calling $("#insertContact :input").serializeArray() method. After the post request control goes to the anonymous function which is third parameter of the $.post() method. In that method i am checking if i got response if yes that means insert was successful if not that means insert failed, that is because my REST service is structured not to send anything back in case of insert failure.

Consuming REST service from PhoneGap/Cordova

In the Using JPA in REST web application deployed in Jetty entry i blogged about how to build a ManageContact JPA service which will allow me to perform CRUD operations on CONTACT table using a REST service. I wanted to figure out how i can consume this service from PhoneGap/Cordova application so i built this sample application which will call http://localhost:9000/ManageContact/rest/contact rest service, read the contact list returned in XML format, parse it and display contact list to the user, you can download the sample application from here This screen shot displays the list of contacts that i got from the REST service I followed these steps to build the CordovaManageContact application
  1. I followed the instructions in Getting Started with Android to build application that points to index.html inside the application, i tried it once to make sure that it works
  2. Then i changed the index.html file for my application to look like this
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Device Properties Example</title>
    <script type="text/javascript" charset="utf-8" 
    src="cordova-1.7.0.js"></script>
    <script src="js/jquery.js"></script>
    
    <script type="text/javascript" charset="utf-8">
      $(document).ready(function(){
        $("#getContactBtn").click(getContactList);
      });
      function onDeviceReady() {
        console.log("Entering index.html.onDeviceReady");
        getContactList();
        console.log("Exiting index.html.onDeviceReady");
      }
      function getContactList(){ 
        console.log("Entering getContactList()");
        $.ajax({
          url : "http://192.168.1.101:9000/ManageContact/rest/contact",
          dataType:"xml",
          cache: false,
          error:function (xhr, ajaxOptions, thrownError){
            debugger;
                    alert(xhr.statusText);
                    alert(thrownError);
                },
          success : function(xml) {
            console.log("Entering getContactList.success()");
          
            $(xml).find("contact").each(function() {
              var html = '<li>' + $(this).find("firstName").text() 
              + ' ' + $(this).find("lastName").text() +'</li>';
              $('#contactList').append(html);
            });
            console.log("Exiting getContactList.success()");
          } 
        });
        console.log("Exiting getContactList()");
      }
    </script>
    </head>
    <body>
      <h3>Contact List</h3>
      <button id="getContactBtn">Get Contact</button>
      <ul id="contactList"></ul>
    </body>
    </html>
    
    When i click on the Get Contact button the getContactList() method gets called, it uses the jquery $.ajax() method to make a call and once the result is returned, it uses logic in the success method to parse the xml and get all the contact records and adds each one of them as list item in the contactList list. I have the REST service running on my machine along with the Android emulator which has the PhoneGap application but when i tried to access the service at http://localhost/ManageContact/rest/contact,http://127.0.0.1/ManageContact/rest/contact it did not work. I tried to map the ip address 192.168.1.101 to demohost.com in my host file but Android did not understand that mapping either. I had to use ipconfig command to figure out the ip address of the machine and then use it.

Enabling Request and Response logging in Jersey

I have been using Jersey as JAX-RS reference implementation for building REST services for last few days. And i wanted to enable logging so that it makes debugging easier. It seems that Jersey allows us to enable Request and Response logging and once you enable that it starts printing request and response info. In order to use that all you have to do is change web.xml to add LoggingFilter on both request and response like this

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <servlet>
    <servlet-name>Jersey Web Application</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>

    <init-param>
         <param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
         <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
     </init-param>
     <init-param>
         <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
         <param-value>com.sun.jersey.api.container.filter.LoggingFilter</param-value>
     </init-param>
   
 <init-param>
      <param-name>javax.ws.rs.Application</param-name>
      <param-value>com.webspherenotes.rest.ContactApplication</param-value>
    </init-param>
  
  </servlet>
  <servlet-mapping>
    <servlet-name>Jersey Web Application</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>

</web-app>
The com.sun.jersey.api.container.filter.LoggingFilter can be used to enable loggin on either request or response or both as i am doing in this case. Now if i hit a REST service by making GET call to http://localhost:9000/ManageContact/rest/contact/5 then the service returns JSON reso If you look into the generated log you can see both the request and response being printed like this

Securing REST service using annotations

In the Security REST service using web.xml entry i talked about how you can protect a REST service by adding security-constraint element in the web.xml. But JAXRS provides annotations that would give you more granular control over the REST services. I wanted to try that feature so i changed the same ManageContactApp to use the javax.annotation.security annotations. You can download the sample application from here When the user tries to insert a new record he will get prompted for basic authentication like this I followed these steps to build the sample application
  1. First i did download the basic ManageContactApp.zip that provides REST interface and i tested it to make sure that it works
  2. Next i used the instructions in Securing web application deployed in Jetty to make changes in Maven build file(pom.xml) to enable loginService in Jetty
    
    <build>
      <finalName>JettySecurity</finalName>
      <plugins>
        <plugin>
          <groupId>org.mortbay.jetty</groupId>
          <artifactId>jetty-maven-plugin</artifactId>
          <version>7.4.5.v20110725</version>
          <configuration>
            <scanIntervalSeconds>10</scanIntervalSeconds>
            <webAppConfig>
              <contextPath>/JettySecurity</contextPath>
            </webAppConfig>
            <loginServices>
              <loginService implementation="org.eclipse.jetty.security.HashLoginService">
                <name>Default</name>
                <config>${basedir}/src/main/resources/realm.properties</config>
              </loginService>
            </loginServices> 
            <connectors>
              <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
                <port>9000</port>
                <maxIdleTime>60000</maxIdleTime>
              </connector>
            </connectors>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  3. Next create realm.properties file in ${basedir}/src/main/resources directory which looks like this
    
    guest:guest
    admin:admin,ADMIN
    
    This file has only 2 users first is guest and second is admin the admin user has ADMIN role.
  4. Next change the web.xml file to declare the ADMIN role and define BASIC authentication scheme for the web application like this.
    
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
      <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>
    	com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
          <param-name>javax.ws.rs.Application</param-name>
          <param-value>com.webspherenotes.rest.ContactApplication</param-value>
        </init-param>
    
        <init-param>
          <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name>
          <param-value>
    	  com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory</param-value>
        </init-param>
       
     <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/rest/*</url-pattern>
      </servlet-mapping>
    
    
      <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Default</realm-name>
      </login-config>
    
      <security-role>
        <role-name>ADMIN</role-name>
      </security-role>
    </web-app>
    
    By default the Jersey implementation does not look for security annotations in your REST service, in order for that to work you must set value of com.sun.jersey.spi.container.ResourceFilters init parameter to com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory this filter takes care of parsing and understanding PermitAll, RolesAllowed and DenyAll annotations
  5. The last step is to change the ContactService.java to use the security related annotations at individual class and method level
    
    package com.webspherenotes.rest;
    
    import java.util.List;
    
    import javax.annotation.security.PermitAll;
    import javax.annotation.security.RolesAllowed;
    import javax.persistence.EntityManager;
    import javax.persistence.EntityManagerFactory;
    import javax.persistence.Query;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.DELETE;
    import javax.ws.rs.FormParam;
    import javax.ws.rs.GET;
    import javax.ws.rs.POST;
    import javax.ws.rs.PUT;
    import javax.ws.rs.Path;
    import javax.ws.rs.PathParam;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    @Path("/contact")
    
    public class ContactService {
    
      Logger logger = LoggerFactory.getLogger(ContactService.class);
      
      EntityManagerFactory entityManagerFactory;
      public ContactService(EntityManagerFactory entityManagerFactory){
        this.entityManagerFactory=entityManagerFactory;
      }
    
      @GET
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      public List getContactList() {
        logger.debug("Entering ContactService.getContactList()");
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Query q = entityManager.createQuery("SELECT x from Contact x");
        logger.debug("Exiting ContactService.getContactList()");
    
        return (List) q.getResultList();
      }
    
      @GET
      @Path("/{contactId}")
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      public Contact getContact(@PathParam("contactId") int contactId) {
        logger.debug("Entering ContactService.getContact() contactId" + contactId);
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = entityManager.find(Contact.class, contactId);
        logger.debug("Exiting ContactService.getContact()" );
    
        return contact;
      }
      
      @POST
      @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      @RolesAllowed("ADMIN")
      public Contact insertContact(@FormParam("contactId") int contactId,
          @FormParam("firstName") String firstName,
          @FormParam("lastName") String lastName,
          @FormParam("email") String email) {
        logger.debug("Entering ContactService.insertContact()");
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = new Contact();
        contact.setContactId(contactId);
        contact.setFirstName(firstName);
        contact.setLastName(lastName);
        contact.setEmail(email);
        try{
        entityManager.getTransaction().begin();
        
        entityManager.persist(contact);
        entityManager.getTransaction().commit();
        }catch(Throwable t){
          if(entityManager.getTransaction().isActive())
            entityManager.getTransaction().rollback();
          contact = null;
        }finally{
          entityManager.close();
        }
        logger.debug("Exiting ContactService.insertContact()");
        return contact;
      }
    
    
      @PUT
      @Path("/{contactId}")
      @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
      @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
      @RolesAllowed("ADMIN")
      public Contact updateContact(@PathParam("contactId") int contactId,
          @FormParam("firstName") String firstName,
          @FormParam("lastName") String lastName,
          @FormParam("email") String email) {
        logger.debug("Entering ContactService.update() contactId" + contactId);
    
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Contact contact = new Contact();
        contact.setContactId(contactId);
        contact.setFirstName(firstName);
        contact.setLastName(lastName);
        contact.setEmail(email);
        try{
        entityManager.getTransaction().begin();
        entityManager.merge(contact);
        entityManager.getTransaction().commit();
        }catch(Throwable t){
          if(entityManager.getTransaction().isActive())
            entityManager.getTransaction().rollback();
          contact = null;
        }finally{
          entityManager.close();
        }
        logger.debug("Exiting ContactService.updateContact()");
    
        return contact;
      }
    
      @DELETE
      @Path("/{contactId}")
      @RolesAllowed("ADMIN")
      public void deleteContact(@PathParam("contactId") int contactId) {
        logger.debug("Entering ContactService.deleteContact() contactId " + contactId);
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try{
          entityManager.getTransaction().begin();
          Contact contact = entityManager.find(Contact.class, contactId);
          logger.debug("remove contact " + contact);
          entityManager.remove(contact);
          logger.debug("After removing " + contact);
          entityManager.getTransaction().commit();
          }catch(Throwable t){
            if(entityManager.getTransaction().isActive())
              entityManager.getTransaction().rollback();
    
          }finally{
            entityManager.close();
          }
        logger.debug("Exiting ContactService.deleteContact()");
      }
    
    }
    
    By default all the methods are accessible to user. But i did add @RolesAllowed("ADMIN") to insertContact(), updateContact() and deleteContact() method to say that only users who have admin rights can access these methods.

Security REST service using web.xml

In the Using JPA in REST web application deployed in Jetty entry i talked about how to create a REST service which uses JPA to interact with database. This service allows you to list, insert, update and delete records from CONTACT table. When you create a service that allows you to update your back end you might want to protect it so that only authorized user can update the database. I wanted to figure out how to protect the service so that every user can get list of contacts but only ADMIN user is able to modify the contacts by inserting, updating and deleting them. YOu can download the sample application that i developed from here When the user tries to insert a new record he will get prompted for basic authentication like this I followed these steps to build the sample application
  1. First i did download the basic ManageContactApp.zip that provides REST interface and i tested it to make sure that it works
  2. Next i used the instructions in Securing web application deployed in Jetty to make changes in Maven build file(pom.xml) to enable loginService in Jetty
    
    <build>
      <finalName>JettySecurity</finalName>
      <plugins>
        <plugin>
          <groupId>org.mortbay.jetty</groupId>
          <artifactId>jetty-maven-plugin</artifactId>
          <version>7.4.5.v20110725</version>
          <configuration>
            <scanIntervalSeconds>10</scanIntervalSeconds>
            <webAppConfig>
              <contextPath>/JettySecurity</contextPath>
            </webAppConfig>
            <loginServices>
              <loginService implementation="org.eclipse.jetty.security.HashLoginService">
                <name>Default</name>
                <config>${basedir}/src/main/resources/realm.properties</config>
              </loginService>
            </loginServices> 
            <connectors>
              <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
                <port>9000</port>
                <maxIdleTime>60000</maxIdleTime>
              </connector>
            </connectors>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  3. Next create realm.properties file in ${basedir}/src/main/resources directory which looks like this
    
    guest:guest
    admin:admin,ADMIN
    
    This file has only 2 users first is guest and second is admin the admin user has ADMIN role.
  4. The next step is to make changes in web.xml to protect the appropriate HTTP method calls in web.xml like this
    
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
      <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>
    	com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param> 
              <param-name>javax.ws.rs.Application</param-name> 
              <param-value>
    		  com.webspherenotes.rest.ContactApplication</param-value> 
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/rest/*</url-pattern>
      </servlet-mapping>
    
    
      <security-constraint>
        <web-resource-collection>
          <web-resource-name>Create Contact</web-resource-name>
          <url-pattern>/rest/*</url-pattern>
          <http-method>POST</http-method>
          <http-method>PUT</http-method>
          <http-method>DELETE</http-method>
        </web-resource-collection>
        <auth-constraint>
          <role-name>ADMIN</role-name>
        </auth-constraint>
      </security-constraint>
    
      <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>Default</realm-name>
      </login-config>
    
      <security-role>
        <role-name>ADMIN</role-name>
      </security-role>
    
    </web-app>
    
    The most important change in web.xml is defining the security constraints for the /rest URL, which is the base URL for the REST service. The security constraint says that only allow those users who have ADMIN role access to POST, PUT, DELETE HTTP methods on this URL. The login-config element says that use Basic authentication
Now run the web application by executing mvn jetty:run and you will notice that you can get list of contacts but when you try to either insert a new contact or delete existing contact then you will get prompted for userid and password