Wednesday 25 January 2012

Looking at Dependecy Inversion Principle (DIP) and JAIN SLEE typed events...

JAIN SLEE follows Event-Driven Programming Model, this means the glue between coorperating components (SBBs) is the Events, with an exception of parent-child relationships in the case of composite services (those services composed of a number of SBBs - with one as a root). This also means that the dependencies between components or the relationship between them is that one produces event while the other(s) consume(s) these events. It is therefore a good place to discuss the Dependency Inversion Principle (DIP), the Open Close Principle (OCP) as well as the Interface-Segregation-Principle (ISP).

The definition of an Event in JAIN SLEE is located in the event-jar.xml descriptor file and is as follows:

   <event-definition>
        <event-type-name>co.za.ishmael.lab.messaging.events.DELIVERY_REPORT</event-type-name>
        <event-type-vendor>co.za.ishmael.lab</event-type-vendor>
        <event-type-version>1</event-type-version>
        <event-class-name>
                    co.za.ishmael.lab.messaging.api.MessageDeliveryReportEvent
        </event-class-name>
    </event-definition>


To "subscribe" to such an event, the SBB that wants to receive the event would add the following in its sbb-jar.xml descriptor file:

<event event-direction="Receive" initial-event="False">
            <event-name>MessageDeliveryReportEvent</event-name>
            <event-type-ref>
                <event-type-name>
                     co.za.ishmael.lab.messaging.events.DELIVERY_REPORT
                </event-type-name>
                <event-type-vendor>co.za.ishmael.lab</event-type-vendor>
                <event-type-version>1</event-type-version>
            </event-type-ref>

            <initial-event-select variable="ActivityContext"/>
</event>


This discussion focuses on the <event-class-name> because this points to the actual class (abstract or concrete) or an interface. I am using the following use-case to guide the DIP discussion on this post.

An imaginary Messaging-Service SBB received request events to send messages out, and upon successful submission of the messages, it sends back to the requestor the delivery report. So we have two events here, the "Request-to-Send-Message" event (we call it SendMessageRequestEvent) which carries with it the message to be sent, the message indicates who the sender is (from) and the intended recipient (to) as well as the string representing the message body. The "Message-Delivery-Report" event (let's call it MessageDeliveryReportEvent) on the other hand carries the original request ("Request-to-Send-Message") to which this is the response, and the status of the delivery (say, OK, ERROR, etc).

Comforming to the DIP:
Dependecy should only be to Interfaces or Abstract Classes, not concrete objects. In terms of JAIN SLEE  events this would require that the <event-class-name> should point to Abstract classes or Interface, and these Interfaces (if applicable) should in turn depend yet on other Abstract Classes or Interfaces.

Coming back to the example: This means the "Request-to-Send-Message" and "Message-Delivery-Report" events should be interfaces. The "Message" should also be an Interface. Of course, someone somewhere will have to create concrete implementations of these interfaces ( this is where the AbstractFactory design pattern comes into play...let's not discuss this here though).

The SBB that receives the request event to send messages now depends on the Interface Message, and the Interface SendMessageRequestEvent.

public Interface SendMessageRequestEvent{
     public Message getMessage(); 
     ...
}

    /**
     * This event-handler method is invoked when receiving a SendMessageRequestEvent event
     */
    public void onSendMessageRequestEvent(SendMessageRequestEvent event, ActivityContextInterface aci, EventContext context) {       
       //get the message (Message is some Interface representing a message)
       Message msg = event.getMessage();
       someSbbRAInterface.sendMessage(msg.getFrom(),msg.getTo(),msg.getMessageBody());
    }

Now, because the Message is an Interface, we can extend this by having specific types of messages, say Email, SMS and IM, it should still be possible to fire the same SendMessageRequestEvent  event, bearing the Message which may be Email/SMS/IM . This is the Open Close Principle (OCP) - without modifying the Message Inteface source code, but only extending it into more specific implementations of a Message. The only difference in the Message types/implementations might be in terms of what constitutes a valid recipient/sender addresses, for example, but they all have the From, To and the message-content/body data elements.

Furthermore, if the implementation fo SMS-Message changes, this does not affect how the event handler for SendMessageRequestEvent functions, it can still treat it as a "Message". So by depending on Message Interface and not Email/SMS/IM, the SBB in question conforms to the DIP.

Also, if a new "Request-to-Send-Message"is defined, which has some additional properties such as TimeToLive or validity-period, then a new event can be defined, pointing to this extended "Request-to-Send-Message"interface. Those SBBs that are interested in receiving such an event will add it in their sbb-jar.xml descriptor files. This leans towards the Interface-Segregation-Principle (ISP).

Example:
public Interface ExtendedSendMessageRequestEvent extends SendMessageRequestEvent{
     public long getValidityPeriodInMillis(); 
     ...
}

In some interested SBB:

public void onExtSendMessageRequestEvent(ExtendedSendMessageRequestEvent event, ActivityContextInterface aci, EventContext context) {       
       //get the message (Message is some Interface representing a message)
       Message msg = event.getMessage();
       long ttl = event.getValidityPeriodInMillis();
       //check if the request is still valid and send the message if true..
       if( (ttl - System.getTimeMillis()) >0){
          someSbbRAInterface.sendMessage(msg.getFrom(),msg.getTo(),msg.getMessageBody());
        }
       else{
          // you might want to fire some "request-expired-event" here
        }
    } 

These seemingly simple techniques come in very handy when developing very complex systems typical of converged communications systems with a number of collaborative components. In my Masters Degree research project I am looking at Access-Agnostic Delivery Pattern - again, Abstraction is key!

1 comment:

  1. I have begun to develop this post into a research paper/report.

    ReplyDelete