Enterprise JavaBeans and Events


Events are occurrences that are understood as objects of particular event classes. Events can be used to inform about the arrival in particular states or the execution of particular actions. The Java AWT (Abstract Windowing Toolkit), for example, generates objects of type MouseEvent when a user executes an action with the mouse (mouse click, mouse movement) on the interface. In the JavaBeans model a PropertyChangeEvent object is generated by a bean whenever an attribute of the bean is changed. This event object is transmitted to other objects that are interested in such events. This type of event handling is called the delegation event model. An event pool offers interested parties (listeners) the opportunity to register for particular events. If such an event occurs, then an event object is generated from the event pool and transmitted to all registered listeners. The event pool thus does not deal with the event itself, but delegates the handling of the event to the listener objects.

As mentioned already in this chapter, an event model for the dynamic coupling of Enterprise Bean components would be desirable. What is frequently missed in the development of EJB-based applications is the ability to relay events from server to client. For example, a bookkeeping application that is used in parallel on a number of client computers wishes to be informed about the establishment or deletion of accounts in order to notify the user or to make its views current. For such cases one needs a particular event mechanism, namely, a distributed one. Since Enterprise Beans act in another process as the objects of the client application, the event (the event object) must be transported across process boundaries.

Versions 2.0 and 2.1 of the EJB specification allow for neither a local event mechanism for coupling of Enterprise Beans nor a distributed event mechanism for communication with other processes. What is does allow, however, is the integration of the Java Message Service.

In this section we wish to introduce a conception of distributed events in which objects of a remote client register themselves as interested parties (listeners) and over which Enterprise Beans can send event objects whenever a particular event occurs. Since the Java Message Service is specialized to asynchronous communication between processes over a network, it offers the ideal basis for such an implementation. A comparable mechanism can also support message-driven beans as event listeners. Session and entity beans cannot be used as event listeners.

In our implementation we will make the following assumptions:

  • The solution should above all enable session and entity beans to transmit events across process boundaries. The beans will thus support the remote client view.

  • An event should be relayed in the form of a lightweight Java object.

  • An Enterprise Bean is not allowed to be capable of receiving events.

  • The quality of message transmittal in this event mechanism should be low. That is, the sender of an event obtains no guarantee and no confirmation of an event object having reached the receivers.

  • A sent message is relayed to all registered listeners, with the order of messages sent being random.

  • The state of the event service (the registered listeners) should not be stored in the Enterprise Bean itself if possible, since that can result in conflicts with the life cycle management of the EJB container.

Figure 9-12 represents the implementation schematically. For each client process there is an EJBEventManager object available. The event manager offers listeners the possibility of registering over a particular interface for Enterprise Bean events. The event manager is in turn registered as recipient of the topic EJBEvents from the Java Message Service. If an Enterprise Bean wishes to trigger an event to inform one or more clients about a particular occurrence, it packages it into an EJBEvent object in an object of type javax.jms.ObjectMessage and sends it to the topic EJBEvents. The Enterprise Bean acts here like a normal (sending) JMS client. The JMS provider ensures that the message is transmitted to all recipients registered for this topic. The event manager receives the message, takes the EJBEvent object from the message, and distributes it to all registered parties (see Figure 9-13).

click to expand
Figure 9-12: Distributed event service via Java Message Service.


Figure 9-13: Process-internal communication with the event manager.

Various participants and implementations for the distributed event service for informing clients can be derived from this situation, and we shall discuss this more fully in the sequel.

The Event

Listing 9-9 defines the EJB event class.

Listing 9-9: Class EJBevent.

start example
 package ejb.event; import javax.ejb.Handle; public class EJBEvent implements java.io.Serializable {     private int eventType;     private Handle eventSource;     public EJBEvent(int type) {         this(type, null);     }     public EJBEvent(int type, Handle source) {        eventSource = source;        eventType = type;     }     public Handle getEventSource() {         return eventSource;     }     public int getEventType() {         return eventType;     } } 
end example

The class EJBEvent represents events that have occurred. The event can be identified via the attribute eventType. The attribute eventSource makes it possible to identify the trigger of the event. This attribute is of type javax.ejb.Handle. Via the handle the client is able to address the Enterprise Bean that triggered the event. Only message-driven beans possess no handle, since they cannot be directly addressed by a client. If message-driven beans should also be able to trigger EJB events, then the clients must be prepared for the fact that the attribute eventSource can assume the value null. An enterprise bean could also use a class derived from EJBEvent to send an event to the client. With derived classes the information content of an event is increased. Thus, for example, an event class NewAccountEvent (derived from NewAccountEvent) could provide information about the opening of a new account. Additionally, the NewAccountEvent class could possess an attribute accountNumber that informs the client of the account number of the newly opened account.

The Event Listener

Listing 9-10 defines the interface EJBEventListener.

Listing 9-10: The interface EJBEventListener.

start example
 package ejb.event; public interface EJBEventListener {     public void notify(EJBEvent event); } 
end example

The class of an object that is interested in Enterprise Bean events must have implemented this interface to be able to register with the event manager as a listener. If an event occurs, the method notify() is called on a registered object, and the event object is passed as parameter.

The Trigger of Events

Events are triggered by Enterprise Beans. They trigger an event by generating an object of type EJBEvent or of a derived type. The Enterprise Bean passes its handle (or null if it is a message-driven bean) to the event object. Depending on the event class, the Enterprise Bean relays additional information to the event object. The event classes as well as the relevant values of an event object can be set arbitrarily by an application. If the application uses only the base class EJBEvent, then a central class or a central interface should be used in which values for the various event types are defined in the form of public constants.

To facilitate the triggering of EJBEvents for the Enterprise Bean, one could provide an auxiliary class, as shown in Listing 9-11.

Listing 9-11: The class EJBEventHelper.

start example
 package ejb.event; import javax.jms.JMSException; import javax.jms.ObjectMessage; import javax.jms.Session; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicConnectionFactory; import javax.jms.TopicPublisher; import javax.jms.TopicSession; import javax.naming.NamingException; import ejb.util.Lookup; public class EJBEventHelper {     public static final String FACTORY =         "java:/env/jms/DefaultConnectionFactory";     public static final String TOPIC_NAME =         "java:/env/jms/EJBEvents";     private static TopicConnectionFactory topicCF = null;     private static Topic ejbEvent = null;     public EJBEventHelper()         throws NamingException     {         topicCF = (TopicConnectionFactory)Lookup.get(FACTORY);         ejbEvent = (Topic)Lookup.get(TOPIC_NAME);     }     public void fireEvent(EJBEvent event)         throws JMSException     {         if(event == null) {             throw new               IllegalArgumentException("event must not be null!");         }         TopicConnection tc = null;         TopicSession ts = null;         TopicPublisher tpub = null;         try {             tc = topicCF.createTopicConnection();             ts = tc.createTopicSession(false,                                        Session.AUTO_ACKNOWLEDGE);             tpub = ts.createPublisher(ejbEvent);             ObjectMessage om = ts.createObjectMessage();             om.setObject(event);             tpub.publish(om);         } finally {             try { tpub.close(); } catch(Exception ex) {}             try { ts.close(); } catch(Exception ex) {}             try { tc.close(); } catch(Exception ex) {}         }     } } 
end example

An Enterprise Bean BankAccount could inform the clients with the following code fragment dealing with the creation of a new bank account:

 Handle h = theContext.getEJBObject().getHandle(); EJBEvent event = new EJBEvent(EventTypes.NEW_ACCOUNT, h); EJBEventHelper helper = new EJBEventHelper(); helper.fireEvent(event); 

Since the Java Message Service operates asynchronously, the Enterprise Bean will not be blocked for an unnecessarily long time by the triggering of an event. While the Enterprise Bean continues its processing, the JMS provider concerns itself with delivering the message, and the event manager is concerned with the distribution of the event to the listeners.

The Event Manager

Listing 9-12 defines the class EJBEventManager.

Listing 9-12: The class EJBEventManager.

start example
 package ejb.event; import java.util.List; import javax.ejb.EnterpriseBean; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageListener; import javax.jms.ObjectMessage; import javax.jms.Session; import javax.jms.Topic; import javax.jms.TopicConnection; import javax.jms.TopicConnectionFactory; import javax.jms.TopicSubscriber; import javax.jms.TopicSession; import javax.naming.NamingException; import ejb.util.Lookup; public class EJBEventManager implements MessageListener {     public static final String FACTORY =         "java:/env/jms/DefaultConnectionFactory";     public static final String TOPIC_NAME =         "java:/env/jms/EJBEvents";     private static EJBEventManager theInstance = null;     private static TopicConnectionFactory tcFactory = null;     private static Topic ejbEvents = null;     private TopicConnection tConnection = null;     private TopicSession tSession = null;     private TopicSubscriber tSub = null;     private List tListeners = null;     static {         try {             tcFactory = (TopicConnectionFactory) Lookup.get(FACTORY);             ejbEvents = (Topic)Lookup.get(TOPIC_NAME);             theInstance = new EJBEventManager();         } catch(NamingException nex) {             nex.printStackTrace();             throw new IllegalStateException(nex.getMessage());         }     }     private EJBEventManager() {         tListeners = new java.util.ArrayList();     }     public synchronized void            addEJBEventListener(EJBEventListener listener)     {         if(listener instanceof EnterpriseBean) {             throw new                 IllegalArgumentException("beans are not allowed!");         }         if(tListeners.isEmpty()) {             connect();         }         if(!tListeners.contains(listener)) {             tListeners.add(listener);         }     }     public synchronized void               removeEJBEventListener(EJBEventListener listener)     {         tListeners.remove(listener);         if(tListeners.isEmpty()) {             disconnect();         }     }     public static EJBEventManager getInstance() {         return theInstance;     }     private void connect() {         try {             tConnection = tcFactory.createTopicConnection();             tSession =                 tConnection.createTopicSession(                     false,                     Session.AUTO_ACKNOWLEDGE);             tSub = tSession.createSubscriber(ejbEvents);             tSub.setMessageListener(this);             tConnection.start();         } catch(JMSException jmsex) {             jmsex.printStackTrace();             throw new IllegalStateException(jmsex.getMessage());         }     }     private void disconnect() {         try {             tConnection.stop();             tSub.close();             tSession.close();             tConnection.close();         } catch(JMSException jmsex) {             jmsex.printStackTrace();             throw new IllegalStateException(jmsex.getMessage());         }     }     public void onMessage(Message msg) {         EJBEvent event = null;         try {             event = (EJBEvent)((ObjectMessage)msg).getObject();         } catch(ClassCastException ccex) {             ccex.printStackTrace();             System.err.println("expected ObjectMessage!");             return;         } catch(JMSException jmsex) {             jmsex.printStackTrace();             return;         }         EJBEventListener l = null;         if(event == null) {             return;         }         for(inti=0;i< tListeners.size(); i++) {             l = (EJBEventListener)tListeners.get(i);             l.notify(event);         }     } } 
end example

The class EJBEventManager is implemented according to the singleton pattern (see [5]). The singleton pattern ensures that there can exist only one instance of this class in a Java process. In the case of the event manager this ensures that JMS resources are used sparingly. Since the constructor is given the attribute private, only the class itself can generate instances. The instance of the event manager is generated in the class's static initializer. This is executed once, after the class is loaded. There the event manager obtains a reference to the topic connection factory as well as to the topic over which events are to be distributed.

Clients obtain access to the instance of the event manager over the static method getInstance. They are able to register for events as listeners via the method addEJBEventListener. This method also ensures that no Enterprise Beans can register as listeners for events. If an Enterprise Bean were able to register as a listener, upon receipt of a message the event manager would call a method on the Enterprise Bean instance without the EJB container being able to monitor this. This situation would represent a significant strike against the specification. Event listeners can unregister via the method removeEJBEventListener. The event manager registers as recipient of a topic only when the first listener has registered. When the last listener has unregistered, the event manager automatically closes the connection to the topic. Thus the JMS provider does not deliver messages unnecessarily. As already mentioned in Chapter 6, messages distributed over a topic are delivered only to those recipients who at the time are registered as recipients.

If an Enterprise Bean triggers an event, the event manager instance is sent a message by the JMS provider by a call to the method onMessage. The event manager removes the event object from the message and distributes it to the listeners registered with it by calling the method notify on the interface EJBEventListener.

The Client

Listing 9-13 shows how a client would use the event manager to receive events triggered by Enterprise Beans.

Listing 9-13: Class EJBEventManager client.

start example
 package ejb.event; public class Client implements EJBEventListener {     ...     public Client() {         ...     }     public void init() {         ...         EJBEventManager em = EJBEventManager.getInstance();         em.addEJBEventListener(this);         ...     }     public void shutdown() {         ...         EJBEventManager em = EJBEventManager.getInstance();         em.removeEJBEventListener(this);         ...     }     public void notify(EJBEvent event) {         switch(event.getEventType()) {             case EventTypes.NEW_ACCOUNT: ...             case EventTypes.REMOVE_ACCOUNT: ...         }     }     ... } 
end example

The client registers at initialization time (in our example, in the method init) as a listener for events. If an Enterprise Bean triggers an event, it is sent via the JMS provider to the event manager, which relays it to registered listeners by a call to the method notify. In the method notify the client evaluates the event object and triggers the appropriate actions. At termination of the client (in the example, in the method shutdown) it unregisters as an event listener.

To increase the information content of an event one could, as mentioned previously, create subclasses of the class EJBEvent. Furthermore, registration could be divided into categories. That is, listeners could register with the event manager for particular event types only. For this, the methods addEJBEventListener and removeEJBEventListener would have to be extended to include the attribute event class. Then a listener would no longer have to receive all events, but only those events in whose type it was truly interested.




Enterprise JavaBeans 2.1
Enterprise JavaBeans 2.1
ISBN: 1590590880
EAN: 2147483647
Year: 2006
Pages: 103

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net