HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

This is the first in a two-part series on Event management in large distributed applications built on top of Enterprise JavaBeans (EJB). This installment will cover the architecture and the implementation of a local (single VM) version of the event distribution system. The second article will implement an EJB version of the system that will handle distributed events from remote VMs.

One of the goals of Enterprise JavaBeans is to make it easy to write applications. Application developers won't have to understand low-level transaction and state management details, multithreading, resource pooling and other complex low-level APIs. In the nominal case this extrapolation of the complexities of building large, complex distributed applications has been accomplished well. It doesn't take much more effort to implement an interface to a database table object using EJB than it does to implement it locally. If the EJB object is deployed using a high-end EJB server, the object can be replicated among multiple machines, providing load balancing and fault tolerance with little or no effort on the bean developer's part.

The model that EJB is predicated on, transaction-based processing, works well in many circumstances, the most prominent being client/server and n-tiered systems in which the server layer manages some form of persistent store. A number of other distributed application needs, however, don't fit as well into the EJB model. One of them is event-based processing.

EJB still provides many benefits to applications that don't directly match the transaction-based EJB model. It just takes a bit more understanding of what EJB provides for you and what you have to do to get it to work. Event processing happens to be an application that begs for a middleware server but doesn't fall into the transactional model.

Events happen on a regular basis in our daily lives. We get up, go to work and so on. The same can be said of our software - events happen regularly in our software systems. The user logs into the application, requests a new client's data, etc. Some events, such as handling an order, are handled in the regular course of processing; others, such as an I/O failure, are considered exceptional processing.

In large distributed systems a frequent problem is that events often need to be processed by a component that resides far from the component that detected the event. Maybe the middle tier detected that the logon attempt failed, but a logging component on the server needs to know about this failure. On the other hand, the component that detected the problem shouldn't need to know what components are interested in this specific event. For distributed applications the event distributor needs to handle both the receiving and the transmitting of events to remote components.

In reviewing the strengths and weaknesses of EJB and how they fit in an event distribution subsystem, we can determine the following:

  • EJB allows remote components to send events to the event distribution system.
  • EJB doesn't provide a mechanism for the event distribution system to inform the remote components that an event has occurred (no callback mechanism).
  • The EJB server provides all the features required for a highly scalable, fault-tolerant, load-balanced application.
  • EJB's transactional model doesn't help us in the event distribution subsystem, since events are not transactional in nature.
This article and its sequel will show you one possible way to handle these vast requirements in a simple, straightforward framework so that your application will be able to handle events as easily as it handles normal, transaction-oriented, multitiered processing.

What Is an Event, Anyway?
In most Java development circles, when event handling is discussed it's normally in association with the AWT event model. In the AWT model the component interested in the event (consumer) simply registers with the producer of events (producer). The producer then iterates its list of interested parties (consumers), sending the event to all who have registered with it. This is a classic implementation of the observer pattern, and it works well in an application that runs in a single VM.

When an application begins to span multiple VMs, and components are created on the fly without notification to other components, the AWT model of tight coupling between the consumer of events and the producer of events gets to be unworkable. How does the consumer know that a producer of events has started on a machine somewhere else on the network?

Consumers of events don't care who generated the event, just that the event occurred. If a database is out of space, the event consumer doesn't care what process detected that the database needs more disk space. It just knows that its job is to notify the administrator of this fact. Conversely, the event producer doesn't care who needs the event, or what they're going to do with it, just that the event was detected and that consumers may be interested in it.

The mediator pattern can be used to alleviate the tight coupling in the basic event distribution model described above. To alleviate the tight coupling, a third component - the event distributor - is added to act as a mediator between the publisher and the consumer. The producer of events publishes the events to the distributor and thus doesn't know what objects are consuming the events. It just knows that they have been handed off to the proper component.

Consumers of events, on the other hand, are notified when events they're interested in are received by the event distributor. The consumers don't know what object produced the event, just that it occurred.

Thus we have three roles that exist in an event distribution system:

  1. Event Producers: Components that detect events and so inform the world
  2. Event Consumers: Components that handle events in some manner, e.g., passing them on to other consumers, notifying other applications or triggering some processing in the application
  3. Event Distributors: Distributors that maintain a list of interested consumers and pass the events to the consumers as they are produced
By chaining event consumers together, an application manages the consumption of complex event hierarchies. Figure 1 shows the major components of the event distribution system. The three listed above are there, as well as the Event object itself.

Figure 1
Figure 1

Class Diagram
Figure 2 shows the class diagram for the event distribution system implemented in EJB. Notice that EventDistributor and EventConsumer are interfaces and the EventProducer doesn't even exist! Anyone can produce an event just by creating a new instance of an Event object and calling its publish method. The EventFilter class is an abstract class that implements both EventConsumer and EventDistributor.

Figure 2
Figure 2

The normal mechanism for creating an event consumer is to extend the EventFilter class, providing a consume method that performs some filtration on the events that are passed on to the consumers that register with it.

The Event, EventContext, EventConsumer, EventDistributor and EventFilter classes are the basic building blocks for local event distribution. In this article I'll implement these classes and their interfaces. The second article will explain the implementation of the rest of the classes diagrammed in Figure 2. However, for completeness, I'll discuss all of the classes here.

Enterprise JavaBeans
The distribution mechanism will be built on top of the classes in EJB. The EJB implementation of the event distributor is modeled as the EventController interface, which extends the EventDistributor interface and also provides the publish service so clients from any VM can inform the event distribution service of new events.

The EventController is actually an interface as well as a class on the client machine (generated by the EJB compiler). The EventController interface is implemented on the server by the EventControllerBean (as well as the EventControllerHome). With these three classes and one interface, EJB can provide the global interface to all clients who wish to publish events or consume events that meet certain criteria.

As I mentioned before, EJB has a severe limitation in the area of callbacks, so I've designed the class RemoteEventConsumer, which encapsulates the logic needed to provide a callback for the server when it needs to notify a consumer in another VM of the event. The last class shown in Figure 2 is the EventHelper class, which is used as a holder of two static methods that make the application programmer's job much easier.

I'm not showing the relationship of Event to the other classes in the diagram as I don't believe it adds anything and it certainly clutters up the rest of the details.

Event Contexts
To delineate between different event types, one can create many subclasses of Event, or a type attribute can be added to the Event object. Implementing a large number of classes just to identify types of events is a design that causes application bloat. However, if different events have a vastly different state as well as behavior, they are best implemented as a class hierarchy.

The design doesn't require either method to be implemented, and you can determine which method is best for your application. The sample implementation that I'll go over later will use an EventContext object to identify the event type. It will also use a dynamic set of attributes (implemented as a hashtable) to contain whatever data is required for this type of event. While this implementation meets my application's needs, yours may vary.

Generally, events will exist within a context. For example, an invalid logon attempt is a specific type of security violation. This design requires that when a consumer is added to the event distribution system, its context has to be passed as an argument to the addEventConsumer method. For the example code I'll model the context in a string representation based loosely on URLs. Your application's needs may vary and you'll want to change how the context is represented. The URL approach works well for my needs as it's universally recognized and easy to parse.

The event context hierarchy will match the set of event types produced in your application. For my application a subset of the tree looks like this:

Security
Security.Breach
Security.Breach.InvalidLogin
Security.IllegalAccessAttempt

Resource
Resource.Database
Resource.Database.Corrupt
Resource.Database.InvalidAttempt
Resource.Database.NotFound

Chart
Chart.Update

Inbasket
Inbasket.NewMessage
Inbasket.DeleteMessage

Notice that the first two categories of events (Security and Resource) are generally considered errors and would for the most part be sent from the producer to some sort of administration tool (logging device, paging device, etc.). However, the last two types of events (Chart and Inbasket) are notification from the server to the client that some interesting data has been updated in some manner.

The EventContext class is constructed by passing in a string containing one of these URLs; it provides all the functionality needed for the rest of the application to obtain all or some part of the context. This allows the rest of the application to be abstracted from the need to understand and parse event contexts.

Why This Design?
As with any design, there are advantages and disadvantages. Among the advantages:

  • It's simple. Simple solutions make the design easier to understand, implement and maintain.
  • It takes full advantage of EJB server strengths. It allows the EJB server to replicate the object and provide the fault tolerance, load balancing and other high-end server functionality.
  • It decouples the consumer from the producer. When two components are tightly coupled, the system is considered frail and brittle. Frail systems are hard to extend, and make maintenance difficult at best - impossible at worst.
The most obvious disadvantage of this design is the central distributor component. Any design that has a single point of failure (as this design appears to have) is suspect. However, with the capabilities of EJB servers, this single point of failure is easily mitigated. The EJB server itself can replicate the distributor, thus reducing the risk of a single point of failure. Two weaknesses in standard EJB are exposed in this design:
  1. It doesn't handle nontransaction-based applications well (which is to be expected of a transaction-based standard).
  2. It doesn't provide callback functionality by default.
In EJB parlance, one would remedy these weaknesses by building a custom EJB container that fits into the EJB server of choice. This article chooses another direction, that of working within the existing containers of the EJB Server and providing the extra functionality in the bean object itself.

Proposed Solution
Enough talk. Let's write the code and see how it works. For the rest of this article I'm going to describe how the base classes needed for event distribution in this model work. (I won't discuss the networking or EJB aspects of these classes until the next article.)

Interfaces
Two interfaces, EventConsumer and EventDistributor, need to be implemented first. Listing 1 shows how the EventConsumer interface requires the consume method. An important note is that an EventConsumer extends the EventListener interface of Java.

The EventListener interface, a little-used interface provided in the java.util package, doesn't implement any methods. Rather, it tags the interface or class as one that can be used by an EventListenerList derivative. The EventListenerList class, provided in the com.sun.java.swing.event package, contains all of the mechanisms needed to maintain and process callback lists.

Listing 2 has the code for the EventDistributor interface. This interface, also very simple, has two methods - one to add a new consumer and another to remove a consumer from the list.

Event Filter Abstract Class
The code for the EventFilter class is shown in Listing 3. The class uses the EventListenerList class from swing (discussed above), but the rest of it is very straightforward. The constructor simply creates a list object to handle the consumers that will register with this object. The method consume is the abstract method that children will have to implement. The addEventConsumer method and the removeEventConsumer methods manage the contents of the EventListenerList object. Finally, a protected method called fireEventConsumers handles the actual sending of the event to all consumers currently registered in the list object. Derived classes will want to call this method if the event is deemed to be of interest to the consumer's children.

Event and EventContext Classes
Listing 4 contains the code for the Event class. The constructor builds the EventContext object and creates a hashtable object to handle the attributes to be added to this Event.

The methods getAttributeByName and setAttributeByName are used to assign attributes a value and obtain the value from them. They both operate on a string for the attribute name and an object for the attribute value.

The method isA takes a string that contains a context portion and returns a Boolean indicating whether this event is of that type.

The publish method of the Event class is left empty at this time. It will be implemented when we get to the distributed version of the application. For this implementation the test drivers simply call the publish method on the distributor.

The EventContext class is shown in Listing 5. Its constructor verifies that the URL passed in is of the proper type, and initializes its internal variables. The other two methods of importance are getComponent and nextComponent. The former returns a string with the current component of the URL. A component is defined as ending with either a forward slash (/) or a question mark (?) character. The forward slash is to separate static context information (e.g., Chart Update, FailedLogin). The question mark is used to separate dynamic information (much as it is in many CGI-based URLs). An example of this would be the identifier of the chart that has been updated.

Distributor
Listing 6 contains the BasicDistributor class (the real distributor will be provided as an EJB object in the next article but this one will work for local distribution). There are only two points to notice about this distributor. The first is that it is a subclass of EventFilter. The second is that its publish method simply calls fireEventConsumer.

Consumers
Listings 7 through 9 show three consumers. Two handle events of type "Security" (SecurityConsumer and FailedLoginConsumer); the third handles events of type "Chart Update." All three consumers print a message when they receive an Event whose context matches the one in which the consumer is interested. The FailedLoginConsumer performs a second check (the attack method) to see whether an oversimplified attack to break into the system is taking place. If so, it prints a message that an attack was detected.

Test Driver
Listing 10 contains a test driver to test these classes. It first creates an instance of the BasicDistributor class. Then all three of the consumers are registered, and finally some Events are created and published. The standard output generated by the test driver is shown in Listing 11. It should be run with and without the sleep call commented out to ascertain that the FailedLogin consumer works as described.

Conclusion
This code shows how simple it is to implement the basic functionality of this design. In the next segment I'll show how to extend this design on top of EJB so clients running in VMs distributed across the network can be both event consumers and event producers.

References
Matena, Vlada et al. (1998). Enterprise JavaBeans 1.0 Specification, Sun Microsystems, Inc., Palo Alto, CA, HYPERLINK ; http://java.sun.com/products/ejb/docs.html.

Gamma, Helm, Johnson and Vlissides (1995). Design Patterns. Addison Wesley. ISBN 0-201-6336-2.

About the Author
Brian Zimbelman is a snowboard instructor who moonlights as a senior architect at CyberPlus Corporation. He has been working on distributed systems since 1984 in C, C++ and now Java. Brian can be reached at [email protected]

	

Listing 1:
  
package event; 

import java.util.EventListener; 
import java.io.Serializable; 

/** 
 *  Interface for anything that will consume events. 
 */ 

public interface EventConsumer 
    extends EventListener 
  { 

  /** 
   * Consume method processes events of interest. 
   * @param Event the event. 
   */ 
  public void consume(Event event); 
} 
  

Listing 2:
  
package event; 

/** 
 * The interface used by any component that 
 * distributes events to other consumers. 
 */ 
public interface EventDistributor { 
  /** 
   * Add an Event Consumer to the existing set of 
   * event consumers that will be called when an 
   * event of the proper type is recieved. 
   * @param EventConsumer the consumer to be 
   * added. 
   * @param String the context in which the event 
   * consumer wants to be called. 
   */ 
  public void addEventConsumer(EventConsumer cons, 
    String context); 

  /** 
   * Remove an Event Consumer from the existing 
   * set of event consumers that will be called 
   * when an event is processed. 
   * @param EventConsumer the consumer to be 
   * removed. 
   */ 
  public void removeEventConsumer(EventConsumer c); 
} 
  

Listing 3:
  
package event; 

import com.sun.java.swing.event.EventListenerList; 
import java.io.Serializable; 

/** 
 * Abstract class that both consumes events and 
 * forwards event to other consumers. 
 * Generally used to filter the events by their 
 * context and possibly some other data, so that 
 * the other consumers get only the events they 
 * are interested in. 
 * The consume method needs to be implemented. 
 */ 

public abstract class EventFilter implements 
    EventConsumer, EventDistributor, Serializable 
{ 
  private EventListenerList myEventConsumer = null; 

  /** 
   * Constructor. 
   */ 
  public EventFilter () { 
    myEventConsumer = new EventListenerList(); 
  } 

  /** 
   * Abstract method that processes Events. 
   * This method needs to be implmented to 
   * process each event when they are received. 
   * @param Event the event to be processed. 
   * @see EventConsumer. 
   */ 
  public abstract void consume(Event event); 

  /** 
   * Add an event consumer. 
   * @see EventDistributor. 
   */ 
  public void addEventConsumer( 
      EventConsumer consumer, String context) 
  { 
    myEventConsumer.add(EventConsumer.class, 
      consumer); 
  } 

  /** 
   * Remove an event consumer. 
   * @see EventDistributor. 
   */ 
  public void removeEventConsumer( 
      EventConsumer consumer) 
  { 
    myEventConsumer.remove(EventConsumer.class, 
      consumer); 
  } 

  /** 
   * Call all consumers that have registered. 
   * Helper method to walk the list of consumers 
   * calling all of their consume methods. 
   * @param Event the event to be processed. 
   * @see EventListenerList 
   */ 
  protected void fireEventConsumer(Event newEvent) 
  { 
    Object[] listeners = 
      myEventConsumer.getListenerList(); 

    for (int i = listeners.length-2; 
        i >= 0; 
        i -= 2) 
    { 
      if (listeners[i] == EventConsumer.class) { 
        ((EventConsumer) 
          listeners[i+1]).consume(newEvent); 
      } 
    } 
  } 
} 

Listing 4:
  
package event; 

import java.util.Hashtable; 
import java.io.Serializable; 

/** 
 * The Basic event object that is passed around by 
 * the * Event Distribution system. This object can 
 * contain any and all information that is needed 
 * for any event. 
 */ 

public class Event implements Serializable { 
  private EventContext context = null; 
  private Hashtable data = null; 

  /** 
   * Event constructor. Builds an event with the 
   * context set to the value of it's argument. 
   * @param String type The context in a URL 
   * notation. 
   */ 
  public Event(String type) { 
    context = new EventContext(type); 
    data = new Hashtable(); 
  } 

  /** 
   * Get an attribute from the attributes in the 
   * event. 
   * @param String name of the attribute that will 
   * be returned. 
   * @return Object the value of the attribute 
   * that was asked for. Null if attribute is 
   * not found. 
   */ 
  public Object getAttributeByName(String name) { 
    return data.get(name); 
  } 

  /** 
   * Add an attribute by name. Override existing 
   * one if their is a name collision. 
   * @param String name of the new attribute. 
   * @param Object value of the new attribute. 
   */ 
  public void setAttributeByName 
    (String name, Object value) { 
      data.put(name, value); 
  } 

  /** 
   * Checks to determine if this event's context 
   * matches the context of the argument. 
   * Advances the context to the next component 
   * if the context does match. 
   * @param String context we are checking 
   * against. 
   * @return boolean true if this object has the 
   * same context as the one passed in. 
   * @see EventContext 
   */ 
  public boolean isA(String type) { 
    String c = context.getComponent(); 
    if (c != null && (c.equals(type))) { 
      context.nextComponent(); 
      return true; 
    } 
    return false; 
  } 

  /** 
   * Publishes this event to the Event 
   * Distribution system. 
   */ 
  public void publish() { 
    // TODO: Call the helper... 
  } 

  /** 
   * Returns a string of this Event's context. 
   * @return String the context of this event. 
   */ 
  public String toString() { 
    return context.toString(); 
  } 
} 

Listing 5:
  
package event; 

import java.io.Serializable; 
import java.util.Vector; 

/** 
 * EventContext handles URL encoding of the type 
 * of event or event consumer that is required. 
 */ 
public class EventContext 
    implements Serializable 
{ 
  String originalContext = null; 
  String context = null; 

  /** 
   * EventContext constructor. Constructs an 
   * event context from the argument. 
   * @param String the context. 
   */ 
  public EventContext(String context) { 
    if (!context.startsWith("events://")) { 
      System.err.println( 
        "Invalid context string: " + context); 
    } 
    originalContext = context; 
    this.context = context.substring(9); 
  } 

  /** 
   * Get the current component of the event 
   * context. If the event context was 
   * "events://Security/Breach/FailedLogin 
   * the first call would return "Security", 
   * the second call would return "Breach", 
   * etc. Calls to getComponent do NOT move 
   * the context forward 
   * @return String the current component of 
   * the events context. 
   * @see nextComponent. 
   */ 
  public String getComponent() { 
    if (context == null) { 
      return null; 
    } 

    String component = null; 
    int end; 
    end = context.indexOf('/'); 

    if (end != -1) { 
      component = context.substring(0, end); 
    } else { 
      end = context.indexOf('?'); 
      if (end != -1) { 
        component = context.substring(0, end); 
      } else { 
        component = context; 
      } 
    } 
    return component; 
  } 

  /** 
   * Move from the current component to the next 
   * component. If the event context was 
   * "events://Security/Breach/FailedLogin 
   * before calling nextComponent() getComponent 
   * would return "Security", after calling it 
   * once nextComponent() would return "Breach", 
   * etc. 
   * @see nextComponent. 
   */ 
  public void nextComponent() { 
    if (context == null) { 
      return; 
    } 

    int end; 
    end = context.indexOf('/'); 

    if (end != -1) { 
      context = context.substring(end+1); 
    } else { 
      end = context.indexOf('?'); 
      if (end != -1) { 
        context = context.substring(end+1); 
      } else { 
        context = null; 
      } 
    } 
  } 

  /** 
   * Get the string representation of the event 
   * context. 
   * @return String the string representation of 
   * the context. 
   */ 
  public String toString() { 
    return originalContext; 
  } 
} 

Listing 6:
  
package event; 

/** 
 * A sample distributor for the Events distribution 
 * example code.  Used strictly for testing and 
 * demoing of the Event Distributor package. 
 * @see EventFilter 
 */ 

public class BasicDistributor extends EventFilter { 
  public BasicDistributor() { 
    super(); 
    System.out.println("BasicDistributor constructor"); 
  } 

  public void publish(Event event) { 
    System.out.println("BasicDistributor.publish " + 
      event.toString()); 
    fireEventConsumer(event); 
  } 

  public void consume(Event event) { 
    System.out.println("BasicDistributor.consume " + 
      event.toString()); 
  } 
} 

Listing 7:
  
package event; 

/** 
 * Sample Event consumer for demoing of the Event 
 * distribution package. 
 * @see EventFilter 
 */ 

public class ChartUpdateConsumer extends EventFilter { 
  public ChartUpdateConsumer() { 
    super(); 
    System.out.println("ChartUpdateConsumer constructor"); 
  } 

  public void consume(Event event) { 
    if (event == null) { 
      System.out.println( 
        "ChartUpdateConsumer.consume event is null"); 
      return; 
    } 

    if (event.isA("Chart Update")) { 
      System.out.println("ChartUpdateConsumer.consume: " + 
        event.toString()); 
      fireEventConsumer(event); 
    } 
  } 
} 

Listing 8:
  
package event; 

/** 
 * Another test consumer. 
 * @see FailedLoginConsumer. 
 */ 
public class SecurityConsumer 
    extends EventFilter 
{ 

  public SecurityConsumer() { 
    super(); 
    System.out.println( 
      "SecurityConsumer constructor"); 
  } 

  public void consume(Event event) { 
    if (event == null) { 
      System.out.println( 
        "SecurityConsumer.consume event is null"); 
      return; 
    } 

    if (event.isA("Security")) { 
      System.out.println( 
        "SecurityConsumer.consume: " + 
        event.toString()); 
      fireEventConsumer(event); 
    } 
  } 
} 

Listing 9:
  
package event; 

/** 
 * Test consumer to verify the event distribution system 
 * works correctly. 
 * @see EventFilter 
 */ 
public class FailedLoginConsumer 
    extends EventFilter 
{ 

  static final long OFFSET = 30000; 
  long firstTime = 0; 
  long secondTime = 0; 
  long thirdTime = 0; 

  /** 
   * Constructor. 
   */ 
  public FailedLoginConsumer() { 
    super(); 
    System.out.println( 
      "FailedLoginConsumer constructor"); 
  } 

  /** 
   * consume each event. 
   * @param Event the event. 
   * @see EventFilter. 
   */ 
  public void consume(Event event) { 
    if (event.isA("FailedLogin")) { 
      System.out.println( 
        "FailedLoginConsumer.consume: " + 
        event.toString()); 
      if (attack()) { 
        System.out.println( 
          "FailedLoginConsumer.AttackDetected: " + 
          event.toString()); 
        fireEventConsumer(event); 
      } 
    } 
  } 

  /** 
   * Some business logic to determine if a 
   * login attack is occuring. Demonstrates 
   * how to process events. 
   */ 
  private boolean attack() { 
    firstTime = secondTime; 
    secondTime = thirdTime; 
    thirdTime = System.currentTimeMillis(); 

    if((thirdTime - firstTime) < OFFSET) { 
      return true; 
    } else { 
      return false; 
    } 
  } 
} 

Listing 10:
  
package event; 

/** 
 * A test driver for the Event distribution 
 * system. Builds a distributor, adds a few 
 * consumers in a chain, and fires some events 
 * off to the distributor.  Should cause the 
 * consumers to process the events as desired. 
 */ 
public class main { 

  private BasicDistributor distro = null; 

  /** 
   * Main method. Just creates an object so 
   * the object can test the rest of the system. 
   */ 
  public static void main (String[] args) { 
    System.out.println("Testing Events."); 
    main m = new main(); 
  } 

  /** 
   * Constructor. Actually performs the test. 
   */ 
  public main () { 
    System.out.println("Starting Distributor."); 
    distro = new BasicDistributor(); 

    System.out.println( 
      "Registering consumer callback."); 
    SecurityConsumer s = new SecurityConsumer(); 
    distro.addEventConsumer( 
      s, "events://Security"); 

    ChartUpdateConsumer c = 
      new ChartUpdateConsumer(); 
    distro.addEventConsumer(c, 
      "events://Chart Update"); 

    FailedLoginConsumer f = 
      new FailedLoginConsumer(); 
    s.addEventConsumer(f, 
      "events://Security/FailedLogin"); 

    System.out.println("Publishing events."); 
    Event e = new Event( 
      "events://Security/FailedLogin"); 
    distro.publish(e); 

    e = new Event( 
      "events://Security/FailedLogin"); 
    distro.publish(e); 

    // Use this sleep to verify the login in 
    // the FailedLoginConsumer acts correctly. 

    /* 
    try { 
      Thread.currentThread().sleep(30000); 
    } catch (InterruptedException ex) { 
    } 
    */ 

    e = new Event( 
      "events://Security/FailedLogin"); 
    distro.publish(e); 

    e = new Event( 
      "events://Chart Update?Joe Smith"); 
    distro.publish(e); 

    e = new Event( 
      "events://Security/Breach/Invalid Cridentials"); 
    distro.publish(e); 

    System.out.println("Done."); 
  } 
} 

Listing 11:
  
Testing Events. 

Starting Distributor. 

BasicDistributor constructor 

Registering consumer callback. 

SecurityConsumer constructor 

ChartUpdateConsumer constructor 

FailedLoginConsumer constructor 

Publishing events. 

BasicDistributor.publish events://Security.FailedLogin 

SecurityConsumer.consume: events://Security.FailedLogin 

FailedLoginConsumer.consume: events://Security.FailedLogin 

BasicDistributor.publish events://Security.FailedLogin 

SecurityConsumer.consume: events://Security.FailedLogin 

FailedLoginConsumer.consume: events://Security.FailedLogin 

BasicDistributor.publish events://Security.FailedLogin 

SecurityConsumer.consume: events://Security.FailedLogin 

FailedLoginConsumer.consume: events://Security.FailedLogin 

FailedLoginConsumer.AttackDetected: events://Security.FailedLogin 

BasicDistributor.publish events://Chart Update/Joe Smith 

ChartUpdateConsumer.consume: events://Chart Update/Joe Smith 

BasicDistributor.publish events://Security.Breach.Invalid Cridentials 

SecurityConsumer.consume: events://Security.Breach.Invalid Cridentials 

Done. 

  
      
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.