Chapter 6. Using Events, Interceptors, and Logging Services

In this chapter, we are going to briefly introduce two concepts of Java EE development: interceptors and events. We will also see how to integrate these concepts with WebLogic services. It's a common misunderstanding that these technologies are complex and difficult to use, but after working with examples of this chapter, it will become clear that they are powerful yet easy to use. Along the way, we will cover WebLogic Server's logging services, which shows us how to configure the framework, how to write messages to it, and how to read them using the administration console.

Understanding interceptors

Interceptors are defined as part of the EJB 3.1 specification (JSR 318), and are used to intercept Java method invocations and lifecycle events that may occur in Enterprise Java Beans (EJB) or Named Beans from Context Dependency Injection (CDI).

The three main components of interceptors are as follows:

  • The Target class: This class will be monitored or watched by the interceptor. The target class can hold the interceptor methods for itself.
  • The Interceptor class: This interceptor class groups interceptor methods.
  • The Interceptor method: This method will be invoked according to the lifecycle events.

As an example, a logging interceptor will be developed and integrated into the Store application. Following the hands-on approach of this book, we will see how to apply the main concepts through the given examples without going into a lot of details.

Note

Check the Web Resources section to find more documentation about interceptors.

Creating a log interceptor

A log interceptor is a common requirement in most Java EE projects as it's a simple yet very powerful solution because of its decoupled implementation and easy distribution among other projects if necessary. Here's a diagram that illustrates this solution:

Creating a log interceptor

Log and LogInterceptor are the core of the log interceptor functionality; the former can be thought of as the interface of the interceptor, it being the annotation that will decorate the elements of SearchManager that must be logged, and the latter carries the actual implementation of our interceptor. The business rule is to simply call a method of class LogService, which will be responsible for creating the log entry.

Here's how to implement the log interceptor mechanism:

  1. Create a new Java package named com.packt.store.log in the project Store.
  2. Create a new enumeration named LogLevel inside this package. This enumeration will be responsible to match the level assigned to the annotation and the logging framework:
    package com.packt.store.log;
    
    public enum LogLevel {
       // As defined at java.util.logging.Level
       SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST;
    
       public String toString() {
          return super.toString();
       }
    }

    Tip

    We're going to create all objects of this section—LogLevel, Log, LogService, and LogInterceptor—into the same package, com.packt.store.log. This decision makes it easier to extract the logging functionality from the project and build an independent library in the future, if required.

  3. Create a new annotation named Log. This annotation will be used to mark every method that must be logged, and it accepts the log level as a parameter according to the LogLevel enumeration created in the previous step:
    package com.packt.store.log;
    
    @Inherited
    @InterceptorBinding
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    public @interface Log {
       @Nonbinding 
       LogLevel value() default LogLevel.FINEST;
    }

    Note

    As this annotation will be attached to an interceptor, we have to add the @InterceptorBinding decoration here. When creating the interceptor, we will add a reference that points back to the Log annotation, creating the necessary relationship between them.

    Also, we can attach an annotation virtually to any Java element. This is dictated by the @Target decoration, where we can set any combination of the ElementType values such as ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, and TYPE (mapping classes, interfaces, and enums), each representing a specific element. The annotation being created can be attached to methods and classes or interface definitions.

  4. Now we must create a new stateless session bean named LogService that is going to execute the actual logging:
    @Stateless
    public class LogService {
       // Receives the class name decorated with @Log 
       public void log(final String clazz, final LogLevel level, final String message) {
          // Logger from package java.util.logging
          Logger log = Logger.getLogger(clazz);
          log.log(Level.parse(level.toString()), message);
       }
    }
  5. Create a new class, LogInterceptor, to trap calls from classes or methods decorated with @Log and invoke the LogService class we just created—the main method must be marked with @AroundInvoke—and it is mandatory that it receives an InvocationContext instance and returns an Object element:
    @Log
    @Interceptor
    public class LogInterceptor implements Serializable {
       private static final long serialVersionUID = 1L;
    
       @Inject
       LogService logger;
    
       @AroundInvoke
       public Object logMethod(InvocationContext ic) throws Exception 
       {
          final Method method = ic.getMethod();
    
          // check if annotation is on class or method
          LogLevel logLevel = method.getAnnotation(Log.class)!= null ? 
          method.getAnnotation(Log.class).value() : 
          method.getDeclaringClass().getAnnotation(Log.class).value();
    
          // invoke LogService
          logger.log(ic.getClass().getCanonicalName(),logLevel, method.toString());
          return ic.proceed();
       }
    }

    Note

    As we defined earlier, the Log annotation can be attached to methods and classes or interfaces by its @Target decoration; we need to discover which one raised the interceptor to retrieve the correct LogLevel value.

    When trying to get the annotation from the class shown in the method.getDeclaringClass().getAnnotation(Log.class) line, the engine will traverse through the class' hierarchy searching for the annotation, up to the Object class if necessary. This happens because we marked the Log annotation with @Inherited. Remember that this behavior only applies to the class's inheritance, not interfaces.

    Finally, as we marked the value attribute of the Log annotation as @Nonbinding in step 3, all log levels will be handled by the same LogInterceptor function. If you remove the @Nonbinding line, the interceptor should be further qualified to handle a specific log level, for example @Log(LogLevel.INFO), so you would need to code several interceptors, one for each existing log level.

  6. Modify the beans.xml (under /WEB-INF/) file to tell the container that our class must be loaded as an interceptor—currently, the file is empty, so add all the following lines:
    <beans xmlns="http://java.sun.com/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
        <interceptors>
            <class>com.packt.store.log.LogInterceptor</class>
        </interceptors>
    </beans>
  7. Now decorate a business class or method with @Log in order to test what we've done. For example, apply it to the getTheaters() method in SearchManager from the project Store. Remember that it will be called every time you refresh the query page:
      @Log(LogLevel.INFO)
      public List<Theater> getTheaters() {
         ...
      }
  8. Make sure you have no errors in the project and deploy it to the current server by right-clicking on the server name and then clicking on the Publish entry.
  9. Access the theater's page, http://localhost:7001/theater/theaters.jsf, refresh it a couple of times, and check the server output. If you have started your server from Eclipse, it should be under the Console tab:
    Nov 12, 2012 4:53:13 PM com.packt.store.log.LogService log
    INFO: public java.util.List com.packt.store.search.SearchManager.getTheaters()

Let's take a quick overview of what we've accomplished so far; we created an interceptor and an annotation that will perform all common logging operations for any method or class marked with such an annotation. All log entries generated from the annotation will follow WebLogic's logging services configuration.

Interceptors and Aspect Oriented Programming

There are some equivalent concepts on these topics, but at the same time, they provide some critical functionalities, and these can make a completely different overall solution. In a sense, interceptors work like an event mechanism, but in reality, it's based on a paradigm called Aspect Oriented Programming (AOP). Although AOP is a huge and complex topic and has several books that cover it in great detail, the examples shown in this chapter make a quick introduction to an important AOP concept: method interception.

Note

Consider AOP as a paradigm that makes it easier to apply crosscutting concerns (such as logging or auditing) as services to one or multiple objects. Of course, it's almost impossible to define the multiple contexts that AOP can help in just one phrase, but for the context of this book and for most real-world scenarios, this is good enough.

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

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