Restful API J2EE using Jackson JAX-RS

What is Restful API?

REpresentational State Transfer (REST), or RESTful, web services provide interoperability between computer systems on the Internet. REST-compliant web services allow the requesting systems to access and manipulate textual representations of web resources by using a uniform and predefined set of stateless operations. Other kinds of web services, such as WSDL and SOAP, expose their own arbitrary sets of operations. [Wiki]

Architectural constraints

Client-server architecture

The principle behind the client-server constraints is the separation of concerns. Separating the user interface concerns from the data storage concerns improves the portability of the user interface across multiple platforms. It also improves scalability by simplifying the server components. Perhaps most significant to the Web, however, is that the separation allows the components to evolve independently, thus supporting the Internet-scale requirement of multiple organizational domains.

Statelessness

The client–server communication is constrained by no client context being stored on the server between requests. Each request from any client contains all the information necessary to service the request, and session state is held in the client. The session state can be transferred by the server to another service such as a database to maintain a persistent state for a period and allow authentication. The client begins sending requests when it is ready to make the transition to a new state. While one or more requests are outstanding, the client is considered to be in transition. The representation of each application state contains links that may be used the next time the client chooses to initiate a new state-transition.

Cacheability

As on the World Wide Web, clients and intermediaries can cache responses. Responses must therefore, implicitly or explicitly, define themselves as cacheable or not to prevent clients from reusing stale or inappropriate data in response to further requests. Well-managed caching partially or completely eliminates some client–server interactions, further improving scalability and performance.

Layered system

A client cannot ordinarily tell whether it is connected directly to the end server, or to an intermediary along the way. Intermediary servers may improve system scalability by enabling load balancing and by providing shared caches. They may also enforce security policies.

Code on demand (optional)

Servers can temporarily extend or customize the functionality of a client by transferring executable code. Examples of this may include compiled components such as Java appletsand client-side scripts such as JavaScript.

Uniform interface

The uniform interface constraint is fundamental to the design of any REST service.[2] It simplifies and decouples the architecture, which enables each part to evolve independently. The four constraints for this uniform interface are:

Resource identification in requests

Individual resources are identified in requests, for example using URIs in Web-based REST systems. The resources themselves are conceptually separate from the representations that are returned to the client. For example, the server may send data from its database as HTMLXML or JSON, none of which are the server’s internal representation.

Resource manipulation through representations

When a client holds a representation of a resource, including any metadata attached, it has enough information to modify or delete the resource.

Self-descriptive messages

Each message includes enough information to describe how to process the message. For example, which parser to invoke may be specified by an Internet media type (previously known as a MIME type).[2]

Hypermedia as the engine of application state (HATEOAS)

Having accessed an initial URI for the REST application—analogous to a human Web user accessing the home page of a website—a REST client should then be able to use server-provided links dynamically to discover all the available actions and resources it needs. As access proceeds, the server responds with text that includes hyperlinks to other actions that are currently available. There is no need for the client to be hard-coded with information regarding the structure or dynamics of the REST service.
[Source wiki]
Now let us create a Restful webservice.
Dependencies

Entity Class

User.java

  UserDAO.java

package com.mycompany.restfuljpacrud;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Persistence;
import javax.persistence.Query;

/**
 * ** @author Rashim12000
 */
public class UserDao {

    User u;
    ResultSet rs;
    public EntityManagerFactory emfactory;
    public EntityManager entitymanager;

    public UserDao() {
        u = new User();
        emfactory = Persistence.createEntityManagerFactory("myPU");
        entitymanager = emfactory.createEntityManager();
    }

    public List getAllUsers() {List & amp;amp;lt;User & amp;amp;gt;
        userList = entitymanager.createNamedQuery("Users").getResultList();
        return userList;
    }

    public boolean addUsers(User user) {
        entitymanager.getTransaction().begin();
        u.setName(user.getName());
        u.setMobile(user.getMobile());
        u.setAddress(user.getAddress());
        u.setPassword(user.getPassword());
        entitymanager.persist(u);
        entitymanager.getTransaction().commit();
        if (entitymanager.contains(u)) {
            entitymanager.close();
            emfactory.close();
            return true;
        }
        entitymanager.close();
        emfactory.close();
        return false;
    }

    public User getUser(int id) {
        u = entitymanager.find(User.class, id);
        return u;
    }

    public List updateUser(int id, User user) {List & amp;amp;lt;User & amp;amp;gt;
        updatedUser = new ArrayList                   & amp;amp;lt; & amp;amp;gt;();
        entitymanager.getTransaction().begin();
        u = entitymanager.find(User.class, id);
        u.setName(user.getName());
        u.setMobile(user.getMobile());
        u.setAddress(user.getAddress());
        entitymanager.getTransaction().commit();
        u = new User(u.getId(), u.getName(), u.getAddress(), u.getMobile(), u.getPassword());
        entitymanager.close();
        emfactory.close();
        updatedUser.add(u);
        return updatedUser;
    }

    public boolean deleteUser(int id) {
        entitymanager.getTransaction().begin();
        u = entitymanager.find(User.class, id);
        entitymanager.remove(u);
        entitymanager.getTransaction().commit();
        entitymanager.close();
        emfactory.close();
        return true;
    }

    boolean auth(String uName, String uPassword) {
        Query query = entitymanager.createNamedQuery("UsersAuth").setParameter("uName", uName).setParameter("uPassword", uPassword);List & amp;amp;lt;User & amp;amp;gt;
        userList = query.getResultList();
        for (User us : userList) {
            if (uName.equalsIgnoreCase(us.getName()) && uPassword.equals(us.getPassword())) {
                return true;
            }
        }
        return false;
    }

    void insertToken(String uName, String token) {
        int userId = ((Number) entitymanager.createNamedQuery("UsersId").setParameter("uName", uName).getSingleResult()).intValue();
        entitymanager.getTransaction().begin();
        u = entitymanager.find(User.class, userId);
        u.setToken(token);
        entitymanager.getTransaction().commit();
        u = new User(u.getId(), u.getName(), u.getAddress(), u.getMobile(), u.getPassword());
        entitymanager.close();
        emfactory.close();
    }

    boolean checkToken(String uName, String login_token) {
        System.out.println("client " + login_token);
        String userToken;
        int userId = ((Number) entitymanager.createNamedQuery("UsersId").setParameter("uName", uName).getSingleResult()).intValue();
        try {
            userToken = ((String) entitymanager.createNamedQuery("UsersToken").setParameter("uToken", login_token).setParameter("uId", userId).getSingleResult());
            if (!(userToken.isEmpty())) {
                if (userToken.equals(login_token)) {
                    return true;
                }
            }
        } catch (NoResultException nre) {
        }
        return false;
    }

    public User getUserByToken(String login_token) {
        u = (User) entitymanager.createNamedQuery("UsersProfile").setParameter("uToken", login_token).getSingleResult();
        return u;
    }

    String checkTokenOnly(String login_token) {
        System.out.println("client " + login_token);
        String userToken;
        String userName;
        try {
            userToken = ((String) entitymanager.createNamedQuery("UsersTokenOnly").setParameter("uToken", login_token).getSingleResult());
            if (!(userToken.isEmpty())) {
                userName = ((String) entitymanager.createNamedQuery("UsersName").setParameter("uToken", login_token).getSingleResult());
                return userName;
            }
        } catch (NoResultException nre) {
        }
        return null;
    }

    List getRoles(String userName) {
        List userRole;
        userRole = (entitymanager.createNamedQuery("UsersRole").setParameter("uName", userName).getResultList());
        return userRole;
    }

    public User getUserByName(String uName) throws SQLException {
        u = (User) entitymanager.createNamedQuery("UsersObjectByName").setParameter("uName", uName).getSingleResult();
        return u;
    }

}

Persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
    <persistence-unit name="myPU" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>com.mycompany.restfuljpacrud.User</class>
        <class>Entity.UsersFaculties</class>
        <class>Entity.DeletedUsers</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/fonesoft?useSSL=false" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="root" />
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true" />
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
            <property name="hibernate.hbm2ddl.auto" value="validate" />
        </properties>
    </persistence-unit>
</persistence>

Rest Api

UserInfo.java

 package com.mycompany.restfuljpacrud;

import java.security.Principal;
import java.sql.SQLException;
import java.util.List;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.CookieParam;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PUT;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.SecurityContext;

/**
 * * REST Web Service** @author Rashim12000
 */


@Path("UserInfo")

public class UserInfo {

    @Contextprivate
    UriInfo context;

    UserDao userDao = new UserDao();

    @GET
    @Secured
    @Path("/users")
    @SecuredRole({Role.ADMIN})
    @Produces(MediaType.APPLICATION_XML)
    public List getUsers() throws SQLException {
        return userDao.getAllUsers();
    }

    @GET
    @Secured
    @SecuredRole({Role.ADMIN})
    @Path("/users/{id}")
    @Produces(MediaType.APPLICATION_XML)
    public Response getSingleUser(@PathParam("id") int id, @Context Request request, @Context SecurityContext securityContext) throws SQLException {
        User getSingleUser = userDao.getUser(id);
        CacheControl cc = new CacheControl();
        cc.setMaxAge(60);
        cc.setPrivate(true);
        try {
            EntityTag etag = new EntityTag(Integer.toString(getSingleUser.hashCode()));
            ResponseBuilder builder = request.evaluatePreconditions(etag);
            Principal principal = securityContext.getUserPrincipal();
            String username = principal.getName();
            if (builder != null) {
                builder.cacheControl(cc);
                return builder.build();
            }
            builder = Response.ok(getSingleUser + " asked by " + username);
            builder.tag(etag);
            return builder.build();
        } catch (Exception e) {
            return Response.ok("User Not Found").build();
        }
    }

    @PUT
    @Consumes(MediaType.APPLICATION_XML)
    public void putXml(String content) {
    }

    @POST
    @Path("/post")
    @SecuredRole({Role.ADMIN, Role.MANAGER})
    @Produces(MediaType.APPLICATION_JSON)
    public String postJson() {
        return "post works";
    }

    @POST
    @Path("/postUser")
    @Produces(MediaType.TEXT_PLAIN)
    @Consumes(MediaType.APPLICATION_XML)
    public String addUser(User user) throws SQLException {
        boolean adduser = userDao.addUsers(user);
        if (adduser) {
            return "Saved";
        } else {
            return "Not Saved";
        }
    }

    @POST
    @Secured
    @Path("/postUser/{id}")
    @Produces(MediaType.APPLICATION_XML)
    @Consumes(MediaType.APPLICATION_XML)
    public List updateUser(@PathParam("id") int id, User user) throws SQLException {
        return userDao.updateUser(id, user);
    }

    @PUT
    @Secured
    @Path("/deleteUser/{id}")
    @Produces(MediaType.APPLICATION_XML)
    @Consumes(MediaType.APPLICATION_XML)
    public String deleteUser(@PathParam("id") int id) throws SQLException {
        boolean delete = userDao.deleteUser(id);
        if (delete) {
            return "deleted";
        } else {
            return "This service in not available currently";
        }
    }

    @POST
    @Path("/authenticate")
    @Produces(MediaType.TEXT_PLAIN)
    public Response authenticate(@FormParam("username") String username, @FormParam("password") String password, @CookieParam("login_token") String login_token) {
        if (login_token != null) {
            if (userDao.checkToken(username, login_token)) {
                return Response.ok("Already Logged In").build();
            } else {
                boolean authUser = userDao.auth(username, password);
                if (authUser) {
                    String token = LoginTokenGenerator.issueToken();
                    userDao.insertToken(username, token);
                    NewCookie cookie = new NewCookie("login_token", token);
                    return Response.ok("UserLoggedIn").cookie(cookie).build();
                }
            }
        } else {
            boolean authUser = userDao.auth(username, password);
            if (authUser) {
                String token = LoginTokenGenerator.issueToken();
                userDao.insertToken(username, token);
                NewCookie cookie = new NewCookie("login_token", token);
                return Response.ok("UserLoggedIn").cookie(cookie).build();
            }
        }
        return Response.ok("NotLoggedIn").build();
    }

    @GET
    @Secured
    @Path("/profile")
    @Produces(MediaType.APPLICATION_XML)
    public Response getLoginUserProfile(@CookieParam("login_token") String login_token) throws SQLException {
        if (login_token != null) {
            User u = userDao.getUserByToken(login_token);
            return Response.ok(u).build();
        }
        return Response.status(Response.Status.UNAUTHORIZED).build();
    }

    @GET
    @Secured
    @Path("/logout")
    @Produces(MediaType.TEXT_PLAIN)
    public Response logout(@CookieParam("login_token") Cookie cookie) {
        if (cookie != null) {
            NewCookie login_cookie = new NewCookie(cookie, null, 0, false);
            return Response.ok("OK").cookie(login_cookie).build();
        }
        return Response.ok("OK - No session").build();
    }
}

LoginTokenGenerator.java

package com.mycompany.restfuljpacrud;

import java.security.SecureRandom;

public class LoginTokenGenerator {

    static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    static SecureRandom rnd = new SecureRandom();

    static String issueToken() {
        int len = 10;
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            sb.append(AB.charAt(rnd.nextInt(AB.length())));
        }
        return sb.toString();
    }
}