Version 10

    In order to try to illustrate the differences in how message interactions would occur in the new architecture versus how they currently happen, the team has had a few email discussions. Here is my take on things. Hopefully this will form the basis of discussions.

     

    First let's take a look at how messages are sent in the Beta release. The diagram below is courtesy of Esteban:

     

     

    In this situation the "service" is running and is essentially whatever is on the other end of the Action, which is triggered when a message is received. The client knows about the service via the configuration file.

     

    Here's an outline of how a service invocation would occur in the new architecture. I'm going to gloss over issues such as asynchronous invocations, reliable invocations, contracts, etc. because they will confuse things (and we're not expected to get it all done for the first GA anyway). However, it is important to remember that both clients and services do require different qualities of service with their interactions. Furthermore, they may not know about each other's capabilities a priori. As such, there needs to be a contract between the client and the service. In the current Beta release the closest thing we have to the contract is the XML configuration file(s) that glue together clients and services. That's too basic and not dynamic enough. Ultimately we need to use a contract definition language that will allow users to specify functional and non-functional aspects of their services (and the requirements of their clients). I'm not expecting that for the first GA since that's a fair amount of work in and of itself! But the notion of a contract which is probably obtained from a registry, needs to be in the architecture from the start.

     

    Note: any code snippets used in this discussion are purely pseudo-code and not meant to reflect how things should be done as we move forward. What I want to get across in this email is the sequence of events necessary to make a call between client and service and how components in the architecture are used to accomplish this.

     

    Who defines the contract? The service deployer. Let's take a simple situation and assume that all services are always running. The service deployer needs to define the contract for the service which will include information such as: what it does (bank, travel agency etc.), where it is located, how to get a message to it (e.g., JMS or HTTP), quality of service (is it transactional? does it support secure communication? etc.) There could be a lot more information in the contract, but let's ignore that for now. The only thing we need to do now is put the contract somewhere. That's where the registry comes in. How the registry is structured (e.g., so that a client can find all travel agency services that deal with flights to South America) is a separate discussion.

     

    What's the service look like when it's running? If you recall the diagram below (from the architecture document):

     

     

    The service is plugged into the ESB via a ServicePlugin, which allows it to receive messages in a similar way to the Action mechanism in the beta release. In reality, the real service will be a POJO and there'll be some code between it and the Receiver API which will do the job of traditional server stub code, but for the moment it'll have to be "raw".

     

     public interface ServicePlugin
     {
        public Body receive (EPR from);
     }
    

     

    All this interface does is allow the service to receive a message and indicates where it came from. Note, this is pseudo-code (lifted from the POC Alpha1 code), so there would be more information available in reality.

     

    However, it's the code below the plugin that is important: the dispatchers. Each dispatcher is concerned with a specific capability (e.g., transactionality or security) and can perform processing on the message as it is received and passed to the service. What (if anything) a dispatcher does, it entirely up to it. For example, if the service is transactional, then a transaction context may have been shipped from the client to the service as part of the invocation. A transaction-aware dispatcher at the service will be responsible for locating the relevant section of the incoming context and stripping it off the message. It will then do something with that context to ensure that the work the service does is scoped within the transaction the client sent. Another dispatcher may be responsible for decrypting the message. Another one may do logging. Hopefully you get the idea. However, whatever the dispatcher hierarchy at the service, the client needs to match it in order to ensure that the messages are compatible. That's why the how needs to be communicated to the client via the contract definition it obtains from the registry. The absence of a how could be interpreted as a default dispatcher hierarchy, or better still require an out-of-band handshake protocol between the client and the service, where the client probes the service for the hierarchy. But for now let's assume that the how is always communicated.

     

    Here's the code for the dispatcher:

     

     public interface Dispatcher
     {
        public void setContract (DispatcherContract contract);
        public Message[] dispatch (Message[] msg);
     }
    

     

    The interface doesn't impose a direction of travel for the message. What I mean by this is that it's not possible to tell from the interface whether a message passed to an instance of a dispatcher will go "down" or "up". It's not important.

     

    BTW, one thing of interest and a slight aside: dispatchers can be thought of as services in their own rights so when a dispatcher receives a message, it may actually be intended for it rather than for an application service.

     

    One of the dispatchers is responsible for communicating with the messaging implementation (JMS, email, FTP, database, whatever). It does this through:

     

     public interface ReceiverPlugin
     {
        public Message receive ();
     }
    

     

    It is this implementation which talks directly to the messaging implementation. So the equivalent in the beta release would be the Action I think.

     

    Before I describe the sequence of events for receiving a message, it's necessary to talk briefly about the message structure. Each message contains a header, context, body, fault and attachment elements (very similar to Web Services):

     

     public class Message
     {
        public Header getHeader ();
        public Context getContext ();
        public Body getBody ();
        public Fault getFault ();
        public Attachment getAttachment ();
     }
    

     

    The structure of the message and how that is represented on the wire are two separate things. All messages will have the structure outlined above. However, how that structure is represented on-the-wire can be arbitrary, as long as both endpoints involved in an exchange understand one another. When that's not the case, there may be need for message transformation or a normalized message structure.

     

    Probably the first thing to note will be that all messages exchanges may have to be normalized. This is in line with EBSs (though SOA doesn't really require it). Normalization isn't needed for SOA, though JBI does mandate it. From the outset we decided that we'd support normalization but not mandate it. So (and this is probably getting ahead of ourselves) I think we should normalize on-demand and not on all messages. But like I said, this is really a topic for later. We do need a normalized message structure and the initial implementation should be based on XML. However, I'd like an architecture approach that does not impose XML (or even just our schema) on messages: if someone else has their own preferred data format (e.g., ASN.1) then they should be able to say: "use this instead".

     

    Ultimately the service is only interested in the payload (a combination of the body and attachments). The rest of the message is typically only needed by components in the path from when the message is received to when it is delivered to the service object. Thus, typically the message gets smaller as it progresses up the stack from the messaging implementation to the actual service. Hopefully this is all made clearer by the diagram below (the "from JMS" is simply being used as an example messaging implementation; it could just as easily be "from email" or "from FTP"):

     

     

    At the service, the message first hits the corresponding messaging implementation (e.g., the JMS topic). There needs to be an appropriate ReceiverPlugin implementation that knows how to get a message from the queue and where to pass it (the lowest level dispatcher implementation). Once the newly received (and created) message is passed into the dispatcher hierarchy, it flows up towards the service and the ServicePlugin. At this point, the received message has been stripped back to the body, which the service implementation is expected to deal with.

     

    To compare with the original beta release sequence diagram, I think the listener/action class combination is a specific instance of the ReceiverPlugin and the EJB is the service implementation.

     

    Client

     

    Now let's turn to the client. As I said above, one of the important aspects of a contract will be the service's location: it's address. That's where the Endpoint Reference (EPR) comes into play. The EPR in pseudo-code looks something like:

     

     public class EPR
     {
        public void setAddr (URI uri);
        public URI getAddr () throws URISyntaxException;
        public MetaData setters/getters
     }
    

     

    Since we're using URIs, we can represent pretty much anything. There is already an EPR definition for Web Services, and we'll need to provide our own for the various messaging implementations we want to support. Clients (and the ESB) only work in terms of EPRs. They need not know about the URI component in most cases, because the EPR comes from the registry. As I said at the face-to-face, we will support logical names (e.g., Kurt's Travel Agency) and the registry will map that down to the real service and the physical address (EPR).

     

    The client plugs in to the ESB via the following interface:

     

     public interface ClientPlugin
     {
        public void send (EPR addr, Body msg) throws ProtocolNotSupportedException, SendFailedException;
        public void sendAsync (EPR addr, Body msg) throws ProtocolNotSupportedException, SendFailedException;
        public void sendAsync (EPR addr, Body msg, Callback cb) throws ProtocolNotSupportedException, SendFailedException;
        public void sendReliable (EPR addr, Body msg) throws ProtocolNotSupportedException, SendFailedException;
     }
    

     

    which is pretty simple, allowing it to send messages in a variety of ways. The ESB will try to honour the request, e.g., if the client says that a message must be sent reliably and there is no appropriate messaging implementation then the ESB will fault back to the client, who may try again with a different delivery requirement.

     

    Note, reliability may be provided by a suitable configuration of dispatchers and messaging implementation. So, for example, you could implement reliable HTTP using dispatchers.

     

    The client gets the EPR and how to communicate with the service from the registry, a file, or hard-coded. The how contains the DispatcherContract: the information necessary for the ESB at the client to configure itself to match the service-side ESB dispatcher hierarchy.

     

    Again, the real client is probably a set of POJOs and may communicate with the service through another API that layers on top of the ClientPlugin like traditional client stubs in RPC systems. However, under this plugin is the client's dispatcher hierarchy which corresponds to the service's. As with the service, at the lowest level of the client's dispatcher hierarchy is an interface to the messaging implementation:

     

     public interface SenderPlugin
     {
        public void send (Message msg) throws ProtocolNotSupportedException, SendFailedException;
        public void sendAsync (Message msg) throws ProtocolNotSupportedException;
        public void sendAsync (Message msg, Callback cb) throws ProtocolNotSupportedException;
        public void sendReliable (Message msg) throws ProtocolNotSupportedException;
     }
    

     

    At this level of the ESB, we are dealing with Messages not just Bodies.

     

    The message flow from the client is illustrated below (we'll assume that the dispatcher hierarchy has been previously created when the client contacted the registry):

     

     

    The client creates the message at that level (the body) and passes it to the bus that it is using to communicate with the service (an instance of the ClientPlugin). This then creates the full message (header, context and body) and passes it to the first dispatcher in the hierarchy (the only dispatcher in our case). This dispatcher logs the message (let's say to a file) and then calls the messaging implementation: JMS via an appropriate SenderPlugin implementation. That SenderPlugin instance is then responsible for putting the message out onto the bus (e.g., via JMS), using information in the message (e.g., the topic may be mentioned in the EPR for the service).

     

    Hopefully this is as clear to you as it is to me

     

    Mark.

     

    Referenced by: