Events

The Observer Pattern is a very popular software design pattern in every object oriented programming language. The concept is that an object, the subject, will be monitored by one or more objects, the observer(s), which will be notified when specific state changes happen on the subject. The state change is called an event and this pattern is at the core of most event-handling systems.

Events are part of Java SE since its very beginning and have always been standard in common UI frameworks such as AWT, Swing, and JavaFX. By contrast, Java EE never had a specific JSR to attend to such requirements until the JSR 299 (Context and Dependency Injection for Java EE) release that defines an event-handling mechanism which is completely integrated with Java EE and easy to use.

In order to show an example of this mechanism, we're going to create an auditing module for the Store application, which is very similar to what has been accomplished by the logging interceptor in the previous section, illustrating key concepts of event handling in Java EE 6.

Tip

Check the Web Resources section of this chapter to find more documentation on Events and CDI.

Defining audit events

Defining auditing can be very tricky, but in the context of our example, it means displaying additional information for a specific function or method call. In a sense, it will be very similar to a log entry, but for the sake of the example, the audit entry will have more information, such as method parameters and possibly the response of the method call. The solution is illustrated by the following diagram:

Defining audit events

The details of the solution and how to create its components are given as follows:

  1. Start by creating a new Java package named com.packt.store.audit in the Store project.

    Tip

    Creating a single package that holds all the necessary classes for the solution will make it easier to extract and use it as a library in the future. All the further classes will be created in this package, unless explicitly said otherwise.

  2. Create a new class named AuditEvent. This class defines the event data's structure:
    package com.packt.store.audit;
    
    public class AuditEvent {
       private final long timestamp = System.currentTimeMillis();
       private String message;
       private Object[] params = null;
    
       public AuditEvent(String message, Object[] params) {
          this.message = message;
          this.params = params;
       }
    
       public String toString() {
          StringBuilder sb = new StringBuilder();
    
          sb.append("[").append(new Date(timestamp)).append("] - ")
                                  .append(message);
    
          if (getParams() != null) {
             sb.append("- Param value(s): ");
    
             for (Object o : params)
                sb.append(o).append(",");
                sb.deleteCharAt(sb.length() - 1);
             }
             return sb.toString();
          }
      // getters and setters
    }
  3. Create a new annotation named Audit. It will be the binder between the interceptor and our code, marking the classes or methods that must be audited. This is the same concept we saw when implementing the log interceptor:
    package com.packt.store.audit;
    
    //imports omitted for brevity
    
    @Inherited
    @InterceptorBinding
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.TYPE})
    public @interface Audit {
    }
  4. Let's create two more annotations, @Enter and @Exit, which will act as event qualifiers. A CDI qualifier is a special annotation that can be applied to a class or field to indicate the kind of bean we're working with. In our example, CDI qualifiers will differentiate the events and qualify them into two categories, representing the entry and exit points of a method:
    package com.packt.store.audit;
    
    //imports omitted for brevity
    
    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
    public @interface Enter {
    }
    
    package com.packt.store.audit;
    
    //imports omitted for brevity
    
    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
    public @interface Exit {
    }

    Tip

    Remember to create each annotation into separate files.

  5. Create a new class named AuditHandler. This class will simply print the audit message to the standard output, but a creative reader can actually implement anything here, such as publishing the message to a JMS queue or making a web service call. Note that we're using qualifiers to filter which event the methods should listen to:
    package com.packt.store.audit;
    
    // imports omitted for brevity
    
    @Stateless
    @Named
    public class AuditHandler {
       private static final String PREFIX = " [ AUDIT ] ";
       private static final String ENTER = "[ Entering ]";
       private static final String EXIT = "[ Exiting ]";
    
       public void logEnter(@Observes @Enter AuditEvent event) {
          System.out.println(PREFIX + ENTER + event);
       }
       public void logExit(@Observes @Exit AuditEvent event) {
          System.out.println(PREFIX + EXIT + event);
       }
    }
  6. Create a new class named AuditInterceptor, which will be the actual interceptor that traps the messages from the annotated classes or methods and forwards them as CDI events. The events are observed by AuditHandler, but there are no dependencies in compile or design time between the two classes:
    package com.packt.store.audit;
    
    //imports omitted for brevity
    
    @Audit
    @Interceptor
    public class AuditInterceptor implements Serializable {
       private static final long serialVersionUID = 1L;
       
       @Inject @Enter
       // The Event referenced here is javax.enterprise.event.Event
       Event<AuditEvent> enterEvent;
    	
       @Inject @Exit
       Event<AuditEvent> exitEvent;
    
       @AroundInvoke
       public Object auditMethod(InvocationContext ic) throws Exception {
          enterEvent.fire(new AuditEvent(ic.getMethod().toString(),     
                         (ic.getParameters().length > 0 ? ic.getParameters() : null)));
    
          Object obj = ic.proceed();
    
          exitEvent.fire(new AuditEvent(ic.getMethod().toString(),
                        (ic.getParameters().length > 0 ? 
                         ic.getParameters() : null)));
    
          return obj;
       }
    }

    Note

    Note the usage of the @Enter and @Exit qualifiers in the event objects.

    If you compare this interceptor implementation to the one created for the logging mechanism, you will notice that it isn't calling the handler directly as we did before, it just publishes events that will be consumed by components that the interceptor doesn't have to know about. This is one benefit of this approach, decoupling the producers and consumers and creating a more flexible structure.

  7. Modify the beans.xml file (under /WEB-INF/) to tell the container that the AuditInterceptor class must be loaded as an interceptor:
    <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><class>com.packt.store.audit.AuditInterceptor</class>
        </interceptors>
    </beans>

    Note

    Keep in mind that if you decorate an element with multiple interceptors, the sequence of execution will follow the order in which they were declared in the beans.xml file.

  8. Save all files, making sure you have no missing imports or build errors in the project.
  9. Open the SearchManager class and add the @Audit decorator to this class. This will perform the audit functionality on every method of the class:
    @Named("search")
    @SessionScoped
    @Audit
    public class SearchManager implements Serializable {
    …

    Tip

    In a real-world scenario, auditing the whole class can bring a serious performance overhead, but to test and demonstrate our implementation, it's just fine. You may want to remove the annotation after testing it, as we will use this class frequently throughout the book.

  10. Save and publish the Store application to the running server.
  11. Browse to http://localhost:7001/store/index.jsf and check the output of the server to see the audit entries. If the server was started from Eclipse, you can see them on the Console tab:
    <Nov 17, 2012 9:25:03 PM BRST> <Notice> <Stdout> <BEA-000000> <[ AUDIT ][ Exiting ][Sat Nov 17 21:25:03 BRST 2012] - public java.util.List com.packt.store.search.SearchManager.getTheaters()> 
    <Nov 17, 2012 9:25:03 PM BRST> <Notice> <Stdout> <BEA-000000> <[ AUDIT ][ Exiting ][Sat Nov 17 21:25:03 BRST 2012] - public int com.packt.store. search.SearchManager.getTheater()> 
    <Nov 17, 2012 9:25:03 PM BRST> <Notice> <Stdout> <BEA-000000> <[ AUDIT ][ Entering ][Sat Nov 17 21:25:03 BRST 2012] - public java.util.List com.packt.store. search.SearchManager.getMovies()> 
    <Nov 17, 2012 9:25:03 PM BRST> <Notice> <Stdout> <BEA-000000> <[ AUDIT ][ Exiting ][Sat Nov 17 21:25:03 BRST 2012] - public java.util.List com.packt.store. search.SearchManager.getMovies()> 
    <Nov 17, 2012 9:25:03 PM BRST> <Notice> <Stdout> <BEA-000000> <[ AUDIT ][ Entering ][Sat Nov 17 21:25:03 BRST 2012] - public int com.packt.store. search.SearchManager.getMovie()> 
    <Nov 17, 2012 9:25:03 PM BRST> <Notice> <Stdout> <BEA-000000> <[ AUDIT ][ Exiting ][Sat Nov 17 21:25:03 BRST 2012] - public int com.packt.store. search.SearchManager.getMovie()>

Let's review what we have done in this section. We created another interceptor in the Store application to handle audit entries based on a new annotation, @Audit, which can be applied to classes and methods. The interceptor uses CDI events to communicate with a simple handler, which, in this example, only writes a message to the standard output of WebLogic Server. These events can be listened to by multiple classes if needed, so based on what you've learned, you can create a JMS or a web service handler that can send specific audit messages to these components.

Tip

Note that the AuditHandler class in this example is an EJB, and that the processing of the @Observer decoration occurs by default in the same thread as the event publisher (our business class). In order to decouple the caller thread from the called object, we just need to add the @Asynchronous decoration to AuditHandler.

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

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