JAX-RS: How to use BeanParam

BeanParam annotation is available in JAX-RS 2.0. It allows us to aggregate the parameters into java bean.

In this post, we are going to see how we can use with an example,

Below is my Resource class.

I have specified the annotation @BeanParam for the SearchParamBean class. Hence the query parameters those are passed to this endpoint will be mapped to the SearchParamBean model class.

TestResource.java


import javax.ws.rs.BeanParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("test")
public class TestResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @Path("search")
    public Response performSearchWithMultiParameters(@BeanParam SearchParamBean searchParamBean) {
        return Response.ok().entity(searchParamBean).type(MediaType.APPLICATION_JSON).build();
    }

}

Below is my BeanParam class.

I have given the necessary annotation for all the fields of this class. I have used QueryParam here. Hence the query parameters ‘query’, ‘sort’ and ‘filter’ are mapped to fields of SearchParamBean class.

We can also use PathParam, HeaderParam, CookieParam, MatrixParam and FormParam.

SearchParamBean.java


import javax.ws.rs.QueryParam;

public class SearchParamBean {

    @QueryParam("query")
    public String query;

    @QueryParam("sort")
    public String sort;

    @QueryParam("filter")
    public String filter;

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("SearchParamBean{");
        sb.append("query='").append(query).append('\'');
        sb.append(", sort='").append(sort).append('\'');
        sb.append(", filter='").append(filter).append('\'');
        sb.append('}');
        return sb.toString();
    }

}

Here is my request and response.

URL: /api/test/search?query=test&sort=test&filter=test
Response:


 {
query: "test",
sort: "test",
filter: "test"
}

Most of the time, we don’t need to use this annotation instead we can use Jackson to map the query parameters directly to a bean class.

For example, When the service accepts more than one query parameters and we want to do any validation and need to use those values further in service layer or DAO layer, then we can make use of this annotation.

Advertisements

Git: Pushing remote branch to Gerrit

If you use the Gerrit as your code review tool, then follow the below steps for pushing the remote branch to Gerrit.


1. Create a local branch to track for the Remote Branch. Run the below command for the same. 

 git branch LOCAL_BRANCH_NAME origin/REMOTE_BRANCH_NAME 

2. Then try to merge your local branch with master branch to avoid any conflicts. This will be helpful when we merge the master with REMOTE_BRANCH_NAME in the future.

 git merge origin/master 

3. Make the necessary changes in the code and run the below commands to add and commit it. 

 git add . 
 git commit -m "COMMIT_MESSAGE" 

4. Run the below command to push the code to gerrit for the remote branch 

git push gerrit LOCAL_BRANCH_NAME:refs/for/REMOTE_BRANCH_NAME 

How to get the name of the enclosing method

If you ever want to get the name of the enclosing method for logging purpose, then we can follow any one of the below approaches.

Get the method name from the exception stack trace


   private void methodNameFromExceptionTrace() {
        System.out.println("Method Name with Exception class:: " + new Exception().getStackTrace()[0].getMethodName());
    }

The main drawback in the above one is the unnecessary creation of Exception object.

Get the method name from the Thread



    private void methodNameFromCurrentThread() {
 Stream.of(Thread.currentThread().getStackTrace())
       .forEach(System.out::println);
 System.out.println("Method Name with Thread :: " + Thread.currentThread().getStackTrace()[1].getMethodName());     
    }

Refer below the entire code.



import java.util.stream.Stream;

public class Main {

    public static void main(String[] args) {
        Main main = new Main();
        // Get the method name From Exception trace
        main.methodNameFromExceptionTrace();
        // Get the method name From Thread
        main.methodNameFromCurrentThread();
      // Get the method name From Exception trace
        main.getMethodNameWithException();
    }

    private void methodNameFromExceptionTrace() {
        System.out.println("Method Name with Exception class:: " + new Exception().getStackTrace()[0].getMethodName());
    }

    private void methodNameFromCurrentThread() {
Stream.of(Thread.currentThread().getStackTrace()).forEach(System.out::println);
        System.out.println("Method Name with Thread :: " + Thread.currentThread().getStackTrace()[1].getMethodName());

    }

    private void getMethodNameWithException() {
        try {
            throw new Exception("Test");
        }
        catch (Exception e) {
            System.out.println("Exception happened in " + e.getStackTrace()[0].getMethodName());
        }
    }
}

Here in the getMethodNameWithException method, we are throwing an exception and in the catch block, we use that object to get back the method name. But if you call some other method and that method throws that exception, then the e.getStackTrace()[0] will be different so you need to change the index which should be 1. Refer the below example.



 private void getMethodNameWithException() {
        try {
            testMethod();
        }
        catch (Exception e) {
            Stream.of(e.getStackTrace()).forEach(System.out::println);
            System.out.println("Exception happened in " + e.getStackTrace()[1].getMethodName());
        }
    }
    private void testMethod() throws Exception {
        throw new Exception("Test");
    }


Docker container – health check

HEALTHCHECK instruction is used to tell the Docker how to test a container to see if its still working or not.  This is used to detect cases such as a web server that is stuck in an infinite loop and unable to handle new connections, even though the server is still running.

In this post, I am going to show an example how we can add this instruction in the Dockerfile and test this.

Refer my code @java-rest-docker

Here is my Dockerfile


FROM java:8

WORKDIR /opt

ADD hello-rest-${project.version}-jar-with-dependencies.jar /opt/helloRest.jar
ADD bin /opt/bin

RUN chmod +x /opt/helloRest.jar
RUN chmod +x /opt/bin/run.sh

CMD ["/opt/bin/run.sh"]

EXPOSE 8080

HEALTHCHECK --interval=1m --timeout=3s CMD curl -f http://localhost:8080/api/greeting || exit 1

Check the last line of my Dockerfile where I instruct the docker to access localhost:8080/api/greeting in every 1 min and do the health check and fail it if there are any issues. If the exit status 0 when the container is healthy. If it’s 1, then it’s not in healthy status.

My docker CE version is 17.06.2-ce.

Now Build the image and the run the docker container.Then run this command and check the health check output. Here replace the container name “elegant_bartik” with your local container name.


  docker inspect --format "{{json .State.Health }}" elegant_bartik

Output:
{“Status”:”healthy”,”FailingStreak”:0,”Log”:[{“Start”:”2017-09-19T18:23:54.612256172-04:00″,”End”:”2017-09-19T18:23:54.652678939-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0\r100 11 100 11 0 0 1022 0 –:–:– –:–:– –:–:– 1100\nHello World”},{“Start”:”2017-09-19T18:24:54.652833383-04:00″,”End”:”2017-09-19T18:24:54.696685407-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0\r100 11 100 11 0 0 842 0 –:–:– –:–:– –:–:– 916\nHello World”},{“Start”:”2017-09-19T18:25:54.696867514-04:00″,”End”:”2017-09-19T18:25:54.748451647-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0\r100 11 100 11 0 0 814 0 –:–:– –:–:– –:–:– 846\nHello World”},{“Start”:”2017-09-19T18:26:54.74862166-04:00″,”End”:”2017-09-19T18:26:54.798137953-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0\r100 11 100 11 0 0 663 0 –:–:– –:–:– –:–:– 687\nHello World”},{“Start”:”2017-09-19T18:27:54.798446904-04:00″,”End”:”2017-09-19T18:27:54.845142125-04:00″,”ExitCode”:0,”Output”:” % Total % Received % Xferd Average Speed Time Time Time Current\n Dload Upload Total Spent Left Speed\n\r 0 0 0 0 0 0 0 0 –:–:– –:–:– –:–:– 0Hello World\r100 11 100 11 0 0 991 0 –:–:– –:–:– –:–:– 1100\n”}]}

JAX-RS – How to bind Filters to JAX-RS resources with Name Binding

In my earlier post, I have given an example to know how to bind the Filters to JAX-RS resources with Dynamic Binding.

Refer it @ JAX-RS Dynamic Binding

In this post, I am going to show how we can do that with Name Binding.

Follow the below steps to do that.

1. Create a custom annotation which uses @NameBinding annotation.


package com.resource;

import javax.ws.rs.NameBinding;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@NameBinding
@Target({ ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface AppKeyVerifier {}

2. Create a filter and implement the app key verification logic. Then apply the custom annotation to it.


package com.resource;

import com.model.AppError;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
import java.io.IOException;

@Provider
@AppKeyVerifier
public class AppKeyVerifierFilter implements ContainerRequestFilter {

    private final static Logger LOGGER = LoggerFactory.getLogger(AppKeyVerifierFilter.class);

    private final static String APP_KEY = "994f4fe4-APP-KEY-f3d5af27e1ee";

    @Context
    private HttpServletRequest httpServletRequest;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        String appKeyValueFromHeader = requestContext.getHeaderString("APP-KEY");
        LOGGER.info("APP Key:{}", appKeyValueFromHeader);
        if (!StringUtils.equals(APP_KEY, appKeyValueFromHeader) ) {
            requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED)
                                             .entity(new AppError("APP-KEY is not available or wrong"))
                                             .build());

        }
    }
}

3. Then apply the custom annotation to the target resource classes or methods.


package com.resource;

import com.model.User;
import com.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;

@AppKeyVerifier
@Path("users")
public class UserResource {

    private final static Logger LOGGER = LoggerFactory.getLogger(UserResource.class);

    @Inject
    UserService userService;

    @GET
    @Path("get/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public User get(@PathParam("id") String id) throws Exception {
        LOGGER.debug("get method is called with id:{}", id);
        return userService.get(id);
    }
    @GET
    @Path("getAll")
    @Produces(MediaType.APPLICATION_JSON)
    public List getAll() throws Exception {
        LOGGER.debug("getAll method is called");
        return userService.getAll();
    }

    @POST
    @Path("add")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String save(User user) throws Exception {
        LOGGER.debug("save method is called with user:{}", user);
        return userService.save(user);
    }

    @DELETE
    @Path("delete/{id}")
    @Produces(MediaType.TEXT_PLAIN)
    public String remove(@PathParam("id") String id) throws Exception {
        LOGGER.debug("remove method is called with id:{}", id);
        return userService.remove(id);
    }

    @POST
    @Path("update")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String update(User user) throws Exception {
        LOGGER.debug("update method is called with user:{}", user);
        return userService.update(user);
    }
}

Refer my code @https://github.com/dkbalachandar/java-rest-docker

JAX-RS – How to bind Filters to JAX-RS resources with Dynamic Binding

JAX-RS provides multiple ways using which server side filters and interceptors can be bound to their target components.

By default, JAX-RS filters and interceptors are applied to all the methods of a resource class. This can be overridden with Named Binding or Dynamic binding. In this post, I am going to give an example to show how to do that.

Assume that we have a JAX-RS application which has two end points. They are “greeting” and “users”. Now we want to protect the “users” service from unauthorized access. So we want to intercept all the “users” service request and do some validation and then allow it if it’s valid. Assume that the client has to pass through an APP-KEY in the request header.

Here are my resource classes.

UserResource.java


package com.resource;

import com.model.User;
import com.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;

@Path("users")
public class UserResource {

    private final static Logger LOGGER = LoggerFactory.getLogger(UserResource.class);

    @Inject
    UserService userService;

    @GET
    @Path("get/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public User get(@PathParam("id") String id) throws Exception {
        LOGGER.debug("get method is called with id:{}", id);
        return userService.get(id);
    }
    @GET
    @Path("getAll")
    @Produces(MediaType.APPLICATION_JSON)
    public List getAll() throws Exception {
        LOGGER.debug("getAll method is called");
        return userService.getAll();
    }

    @POST
    @Path("add")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String save(User user) throws Exception {
        LOGGER.debug("save method is called with user:{}", user);
        return userService.save(user);
    }

    @DELETE
    @Path("delete/{id}")
    @Produces(MediaType.TEXT_PLAIN)
    public String remove(@PathParam("id") String id) throws Exception {
        LOGGER.debug("remove method is called with id:{}", id);
        return userService.remove(id);
    }

    @POST
    @Path("update")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public String update(User user) throws Exception {
        LOGGER.debug("update method is called with user:{}", user);
        return userService.update(user);
    }
}

HelloWorldResource.java



package com.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/greeting")
public class HelloWorldResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response sayHello(){
        System.out.println("sayHello");
        return Response.ok().entity("Hello World").build();
    }
}

Dynamic binding

It provides a way to add/bind the filters to resource methods/classes programmatically.

We have to add a Feature class which implements the DynamicFeature interface. Then override the configure method and provide our own logic to bind the filers to resource methods.

In my code, I am checking the resource class name with the APP_CLASS_LIST list. If that list contains the class name, then binding the AppRequestFilter to that class. I don’t want to filter the greeting service. Hence the APP_CLASS_LIST contains only the UserResource class name alone.

Refer the below example.


package com.resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
import java.util.Arrays;
import java.util.List;

@Provider
public class AppKeyFeature implements DynamicFeature {


    private final static Logger LOGGER = LoggerFactory.getLogger(AppKeyFeature.class);

    private static final List APP_CLASS_LIST = Arrays.asList(UserResource.class);

    @Override
    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
        LOGGER.info("resourceInfo.getResourceClass():{}", resourceInfo.getResourceClass());
        if (APP_CLASS_LIST.contains(resourceInfo.getResourceClass())) {
            context.register(AppRequestFilter.class);
        }
    }
}

Refer below my filter code. This filter will validate the APP_KEY available in the request header with the default one. If it does not match, then it will abort the request and throw an error message. Make sure that you should not add @provider annotation for AppRequestFilter.


package com.resource;

import com.model.AppError;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import java.io.IOException;

public class AppRequestFilter implements ContainerRequestFilter {

    private final static Logger LOGGER = LoggerFactory.getLogger(AppRequestFilter.class);

    private final static String APP_KEY = "994f4fe4-APP-KEY-f3d5af27e1ee";

    @Context
    private HttpServletRequest httpServletRequest;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        String appKeyValueFromHeader = requestContext.getHeaderString("APP-KEY");
        LOGGER.info("APP Key:{}", appKeyValueFromHeader);
        if (!StringUtils.equals(APP_KEY, appKeyValueFromHeader) ) {
            requestContext.abortWith(Response.status(Status.UNAUTHORIZED)
                                             .entity(new AppError("APP-KEY is not available or wrong"))
                                             .build());

        }
    }
}

Refer my code @https://github.com/dkbalachandar/java-rest-docker