Cross-cutting concerns

Enterprise applications require some technically motivated cross-cutting concerns. Examples of these are transactions, logging, caching, resilience, monitoring, security, and other non-functional requirements. Even for systems that solely target business, use cases need some amount of technical plumbing.

We just saw in the handling of transactions, an example of a non-functional cross-cutting concern. Java EE doesn't require much time and effort spent by engineers to integrate transactional behavior. The same is true for other cross-cutting concerns.

Java EE interceptors is a prime example for cross-cutting concerns. Following the concept of aspect-oriented programming, the implementation of the cross-cutting concern is separated from the decorated functionality. Methods of managed beans can be decorated to define interceptors, which interrupt the execution and perform the desired task. Interceptors have full control over the execution of the intercepted method including returned values and thrown exceptions. To match the idea of other APIs, interceptors are integrated in a lightweight fashion, not setting many constraints on the decorated functionality.

The previous example of transparently handling errors in a HTTP client class showed the usage of an interceptor. Business methods also can be decorated using custom interceptor bindings. The following demonstrates a business motivated process tracking aspect realized via custom annotations:

@Stateless
public class CarManufacturer {

    ...

    @Tracked(ProcessTracker.Category.MANUFACTURER)
    public Car manufactureCar(Specification spec) {
        ...
    }
}

The Tracked annotation defines a so-called interceptor binding. The annotation parameter represents a non-binding value that configures the interceptor:

import javax.enterprise.util.Nonbinding;
import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Inherited
@Documented
@Target({TYPE, METHOD})
@Retention(RUNTIME)
public @interface Tracked {

    @Nonbinding
    ProcessTracker.Category value();
}

The interceptor is activated via the binding annotation:

import javax.annotation.Priority;

@Tracked(ProcessTracker.Category.UNUSED)
@Interceptor
@Priority(Interceptor.Priority.APPLICATION)
public class TrackingInterceptor {

    @Inject
    ProcessTracker processTracker;

    @AroundInvoke
    public Object aroundInvoke(InvocationContext context) throws Exception {
        Tracked tracked = resolveAnnotation(context);

        if (tracked != null) {
            ProcessTracker.Category category = tracked.value();
            processTracker.track(category);
        }

        return context.proceed();
    }

    private Tracked resolveAnnotation(InvocationContext context) {
        Function<AnnotatedElement, Tracked> extractor = c -> c.getAnnotation(Tracked.class);
        Method method = context.getMethod();

        Tracked tracked = extractor.apply(method);
        return tracked != null ? tracked : extractor.apply(method.getDeclaringClass());
    }
}

By default, interceptors bound via interceptor bindings are not enabled. An interceptor must either be explicitly enabled via specifying a priority via @Priority, like demonstrated in this example. Another possibility is to activate it in the beans.xml descriptor.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
        http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
        bean-discovery-mode="all">
    <interceptors>
        <class>com.example.cars.processes.TrackingInterceptor</class>
    </interceptors>
</beans>

Interceptors can use reflection to retrieve potential annotation parameters, such as the process tracking category in the example. Interceptor bindings can be placed either on method or class level.

Interceptors decorate behavior on methods without tightly coupling them. They especially make sense for scenarios where the cross-cutting aspects need to be added to a lot of functionality.

Interceptors are similar to CDI decorators. Both concepts decorate managed beans with custom behavior that is encapsulated in a different place. The difference is that decorators are meant to be used for decorating business logic, which is also mostly specific to the decorated bean. Interceptors, however, are mostly used for technical concerns. They offer a broader usage, making it possible to annotate all kind of beans. Both concepts are a helpful functionality to realize cross-cutting aspects.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.37.250