+

Search Tips   |   Advanced Search

Implement a JAX-RS resource with decorators and method interceptors

Use Java Contexts and Dependency Injection (JCDI) to write interceptors and decorators for Java API for RESTful Web Services (JAX-RS) resource types. For example, we can use the interceptor and decorator capabilities from JCDI to log calls to a particular class or to complete a security check before invoking a method when using JCDI-enabled web applications with JAX-RS.

Two methods exist for adding cross-cutting concerns to the application. Interceptor bindings need a custom annotation. The annotation is used to mark which methods should be intercepted. Decorators implement the base type. Then, using an @javax.decorator.Decorator injected instance, we can implement your special logic around calls to the injected delegate.


Tasks

  1. Create an annotation to indicate that a method is intercepted. This SecurityChecked annotation indicates a method where some security information is verified. The following example illustrates a custom annotation with an intercepted method:
    package com.example.jaxrs;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    import javax.interceptor.InterceptorBinding;
    
    @Inherited
    @InterceptorBinding
    @Target( {ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface SecurityChecked {
    
    }
    

  2. Create the JAX-RS resource class, and annotate any methods to intercept with the @com.example.jaxrs.SecurityChecked annotation. The following example illustrates a JAX-RS resource class with an intercepted method:
    package com.example.jaxrs;
    
    import javax.enterprise.context.RequestScoped;
    import javax.ws.rs.Path;
    import javax.ws.rs.GET;
    
    @Path("exampleInterceptor")
    @RequestScoped
    public class MyResourceBean {
    
        @GET
        @com.example.jaxrs.SecurityChecked
        public String echo(String hello) {
            return "Hello world!";
        }
    }
    
  3. Write an interceptor class that is annotated with the @javax.interceptor.Interceptor annotation and with the @com.example.jaxrs.SecurityChecked interceptor marker. Then add a method annotated with the @javax.interceptor.AroundInvoke annotation with the javax.interceptor.InvocationContext parameter. Use the InvocationContext methods to inspect the method call, as well as determine whether the call should proceed. The following example illustrates an interceptor:
    package com.example.jaxrs;
    
    import javax.interceptor.AroundInvoke;
    import javax.interceptor.Interceptor;
    import javax.interceptor.InvocationContext;
    
    @Interceptor
    @com.example.jaxrs.SecurityChecked
    public class SecurityCheckInterceptor {
    
        @AroundInvoke
        public Object checkSecurity(InvocationContext context) throws Exception {
            /* check the parameters or do a generic security check before invoking the            original method */
            Object[] params = context.getParameters();
    
            /* if security validation fails, we can throw an exception */
    
            /* invoke the proceed() method to call the original method */
            Object ret = context.proceed();
    
            /* perform any post method call work */
            return ret;
        }
    }
    

  4. Add a beans.xml deployment descriptor to the web application (WAR) in the WEB-INF directory. The existence of the WEB-INF/beans.xml file indicates that the archive is a JCDI-enabled archive. We are required to add some information for this instance. The following example illustrates a WEB-INF/beans.xml file with an interceptor listed:
    <?xml version="1.0 encoding="UTF-8"?>
    <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/
    XMLSchema-instance" xsi:schemeLocation="http://java.sun.com/xml/ns/javaee http://
    java.sun.com/xml/ns/javaee/beans_1_0.xsd">
        <interceptors>
            <class>com.example.jaxrs.SecurityCheckInterceptor</class>
        </interceptors>
    </beans>
    
    In the previous steps, we configured a web application to use JCDI, added a custom annotation to mark methods for interception, and wrote an interceptor. When a marked method is called, the interceptor is used to determine whether the marked method should be called and can perform additional logic. We must list the interceptors in your beans.xml deployment descriptor.

    In the remaining steps, we create and enable a decorator in a JCDI-enabled web application. Interceptor bindings require an annotation to mark methods for interception. Decorators implement the common base type and can wrap the calls.

  5. Find or create the base type that the decorator must implement. For instance, the base type might be an interface named Item. The following example illustrates a common interface used by a decorator:
    package com.example.jaxrs;
    
    import javax.ws.rs.GET;
    
    public interface Item {
    
        @GET
        public String getInformation();
    }
    

  6. Create a standard JAX-RS resource class that implements the base type. The following example illustrates a JAX-RS resource class that will be decorated:
    package com.example.jaxrs;
    
    import javax.enterprise.context.RequestScoped;
    import javax.ws.rs.Path;
    
    @Path("decoratedresource")
    @RequestScoped
    public class MyItem implements Item {
    
        public String getInformation() {
            /* return some information */
        }
    
    }
    

  7. Create a decorator class. The decorator class must implement the base type that it wrapa all calls to. The decorator must be annotated with the @javax.decorator.Decorator annotation.

    The decorator must have a special field, called a delegate injection point, with the base type. The field can have any variable name, and should have an @javax.inject.Inject and an @javax.decorator.Delegate annotation. This field will be the original object being used decorated. We can then use this decorated object in your implementation calls.

    The following example illustrates a basic decorator class:

    package com.example.jaxrs;
    
    import javax.decorator.Decorator;
    import javax.decorator.Delegate;
    import javax.inject.Inject;
    
    @Decorator
    public class MyItemDecorator implements Item {
    
        @Inject
        @Delegate
        private Item decoratedItem;
    
        public String getInformation() {
            /* perform some logging */
            String info = decoratedItem.getInformation();
            /* perform some more logging */
            return info;
        }
    }
    

  8. Add a beans.xml deployment descriptor to the web application (WAR) file in the WEB-INF directory. The existence of the WEB-INF/beans.xml file indicates that the archive is a JCDI-enabled archive. We are required to add information regarding the active decorator classes for this instance. The following example illustrates a WEB-INF/beans.xml file with a decorator listed:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/
    XMLSchema-instance" xsi:schemeLocation="http://java.sun.com/xml/ns/javaee http://
    java.sun.com/xml/ns/javaee/beans_1_0.xsd">
        <interceptors>
            <class>com.example.jaxrs.SecurityCheckInterceptor</class>
        </interceptors>
        <decorators>
            <class>com.example.jaxrs.MyItemDecorator</class>
        </decorators>
    </beans>
    

We have configured a web application to use JCDI, created a method interceptor for one resource, created a decorator, and enabled it to decorate your method calls for a second resource.

  • Implement JAX-RS resources with different lifecycle scopes
  • Implement JAX-RS resources with dependency injection
  • Web services specifications and APIs