When you create a new Petals component with Maven, classes are created in a sub-package (listener or listeners). In both cases, the first task to implement your component is the processing of messages, also called exchanges in Petals. |
Table of contents Contributors
No contributors found for: authors on selected page(s)
|
Exchanges
An exchange is the envelop of a Petals message. It is received by a component and addressed to one of its services.
The component has to use the associated service-unit resources to process this message.
An exchange holds information about the Message Exchange Pattern (MEP), the called operation (as described in a WSDL file), the input message (an XML document defining the parameters for the called operation), the output message (the result of the service execution), faults (exceptions which can occur during service execution), properties, attachments and the called service (service, interface, end-point names – these last ones allow to determine the service-unit configuration to use for the processing).
When an exchange / message is sent to a service into Petals, the component that holds this service receives this message.
It then uses the exchange information to decide which treatment perform and how.
Typically, check the role or that the called operation supports the MEP, and then start the processing.
The example below is taken for a service engine in the provider role.
@Override public boolean onJBIMessage( final Exchange exchange ) { try { // 'exec' should be called in ROBUST_IN_ONLY if( exchange.getOperationName().equals( "exec" ) && MEPConstants.ROBUST_IN_ONLY_PATTERN.equals( exchange.getExchangePattern())) { // TODO: execute some code } else if... } catch( MessagingException e1 ) { // TODO: create a fault or set an error depending on the MEP } }
Checking MEP and operations is the kind of operation that can rely on a provided WSDL.
But even with a WSDL, dispatching the work according to the called operation cannot be automatic.
You have to explicitly check the called operation and decide of the next steps.
Message Exchange Patterns
Message exchange patterns, or MEP, are a concept inherited from web services.
They define the way a service is called and the way it should respond.
In Petals, there are three used patterns.
In-Only
IN_ONLY is used for one-way exchanges.
When a component receives an IN_ONLY message, it should set the status of the exchange to DONE and send the response while processing the message (e.g. in a thread).
The result of the message processing does not matter for the consumer. The consumer only wants to know whether its message arrived or not.
This why the response should be sent as soon as possible. No fault and no error should be set on the returned exchange with this MEP.
(illustration taken from the version 1.0 of the JBI specifications)
As a consequence, when you process this MEP in Petals, the exchange status should be set before processing the message itself. Unless the processing itself does not last a very long time. Otherwise, for long-running processes, the chronology should be:
- On reception, put the message in a queue.
- Return the status.
- Process the queue (in a separate thread).
Robust-In-Only
ROBUST_IN_ONLY is used for reliable one-way exchanges.
When a component receives a ROBUST_IN_ONLY message, it can reply by setting the status or by setting a fault on the returned exchange.
The decision depends on the processing of the message. If a fault is returned, it should have been declared in the service (WSDL) interface.
When you process this MEP, the exchange should be processed before updating the exchange and in the same thread (unlike the IN_ONLY pattern).
(illustration taken from the version 1.0 of the JBI specifications)
If the provider responds with a status, the exchange is complete.
If it responds with a fault, the consumer responds with a status to complete the exchange.
In this last case, the CDK parameter ignore-status determines what is done of this returned status.
If it is set to DONE_AND_ERRORS_IGNORED, then your JBI listener does not have to process the returned status. The CDK does it for you.
In-Out
IN_OUT is used for two-way exchanges.
When a component receives an IN_OUT message, it can respond either with a message, or with a fault.
The decision depends on the processing of the message.
If a fault is returned, it should have been declared in the service (WSDL) interface.
(illustration taken from the version 1.0 of the JBI specifications)
No matter what the provider responds (message or fault), the consumer sends a status to complete the exchange.
As in the ROBUST_IN_ONLY pattern, the way this status is processed depends on the CDK ignore-status parameter.
When you process this MEP, the exchange should be processed before updating the exchange and in the same thread (unlike the IN_ONLY pattern).
There is a last MEP, called IN_OPTIONAL_OUT, but it is rather complicated and not used in Petals.
People interested by this pattern can read the JBI specification, version 1.0.
Status, faults, errors and messages
The previous section explained message exchange patterns.
But it did not explain concretely how to set a fault or a status on an exchange.
Status
Status are acknowledgments.
They can be set by using one of the following Exchange methods:
Exchange#setDoneStatus(); // Case OK Exchange#setErrorStatus(); // Case not OK Exchange#setStatus( ExchangeStatus status );
Faults
Faults express a business fault (with respect to the service logic).
A fault can be created with SourceUtil#createSoapFaultDocument( ... ).
They can be set on an exchange by using one of the following methods:
Exchange#setFault( Throwable e ); Exchange#setFault( Fault fault );
Errors
Errors express errors at the messaging level.
They can be set by using:
Exchange#setError( Exception exceptionError );
Messages
Messages express a normal result (with respect to the service logic).
They can be set in the returned exchange by using:
Exchange#setOutMessage( NomalizedMessage msg );
Exchange#setOutMessageContent( Document outContent );
Exchange#setOutMessageContent( Source outContent );
Exchange#setOutMessageContent( String outContent );
Building the out content has to be made in the component.
The out content structure should respect the WSDL out message schema of the called operation.
Processing exchanges in Petals components
Obviously, the code to execute in the component depends on its job.
In any case, the executed code is called by a message listener.
The rest of the processing can be written inside the method body, or delegated to another class.
This is a good solution if you have to manage resources or a pool of resources, like JDBC connections.
The best thing to do to get examples of processing or patterns, is to check the Petals forge, and take a look at existing components and how they are implemented. |
One thing maybe to highlight, is that message listeners have access to the component instance.
The component instance can be used to get a logger. It can also be extended and used to store objects (e.g. a class instance managing resources).
getComponent().getLogger().info( "Received a call for unknown operation '" + exchange.getOperationName() + "'." );
Processing exchanges as a provider
Components acting and configurable as providers expose services from configurations in provide mode.
That is to say the service-unit jbi.xml has a provides mark-up.
<?xml version="1.0" encoding="UTF-8"?> <jbi:jbi version="1.0" xmlns:jbi="http://java.sun.com/xml/ns/jbi" xmlns:petalsCDK="http://petals.ow2.org/components/extensions/version-5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <jbi:services binding-component="false"> <jbi:provides interface-name=...
As providers expose services, they should be implemented to respond to invocations.
It means they receive messages for a determined operation, check the operation, the MEP, the role, and then dispatches the work depending on the called operation.
In terms of message processing, a provider must:
- Process the received messages.
- Set a status, a fault or an out message on the returned exchange (depends on the MEP).
- Optionally, take acknowledgments (status) into account (depends on the MEP).
This last part can be configured in the component jbi.xml, so that the CDK handles this instead of your component.
The provider role must respect the message exchange pattern of the calling message.
This is illustrated in the section about MEP (the diagrams show the provider role).
Processing exchanges as a consumer
Components acting or configured as consumers invoke service operations.
The services to call are defined in configurations in consume mode.
That is to say the service-unit jbi.xml has a consumes mark-up.
<?xml version="1.0" encoding="UTF-8"?> <jbi:jbi version="1.0" xmlns:jbi="http://java.sun.com/xml/ns/jbi" xmlns:petalsCDK="http://petals.ow2.org/components/extensions/version-5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <jbi:services binding-component="false"> <jbi:consumes interface-name=...
As consumers call services, they should be implemented to send calls and also to process responses to these calls.
It means they send messages for a determined operation, with a given MEP and parameters for the operation.
The caller also decides whether the call is asynchronous or synchronous.
In any case, the send messages should respect the WSDL interface of the called service.
The messages sent by a consumer component will be received and processed by a provider component.
In terms of message processing, a consumer must:
- Build and send messages to services.
- Wait for responses and process them according to the MEP. If the response exchange was set with a status, the exchange is terminated. Otherwise, the consumer has to send back an acknowledgment (status) to the provider and thus close the exchange. All of this is specified in the section 3.2 about MEP (the diagrams show the consumer role).
Processing the responses can also permit to implement retry policies.
The consumer role must respect the WSDL interface of the services it calls. It includes respecting message exchange patterns, operation names and operation parameters. |
Message listeners in Service Engines
In service engines, the Petals Maven plug-in creates a listener package and two classes: ServiceEngine.java (which should be renamed) and JBIListener.java.
- The first class is mainly used to manage the component life cycle or to store data for JBI listeners. It is discussed in the life cycles section.
- The last class extends the abstract class org.ow2.petals.component.framework.listener.AbstractJBIListener .
It requires one method to be completed:
public abstract boolean onJBIMessage( Exchange exchange );
It is in this method that the provider role is implemented.
The result of the onJBIMessage method needs some clarifications with respect to its Java doc.
Indeed, if the processing when the message arrives is defined by the component, the returned message can be completed and sent by the component or by the CDK.
Let's take the example of a SE in provider role. And let's complete it in two different ways.
@Override public boolean onJBIMessage( final Exchange exchange ) { try { // 'exec' should be called in ROBUST_IN_ONLY if( exchange.getOperationName().equals( "exec" ) && MEPConstants.ROBUST_IN_ONLY_PATTERN.equals( exchange.getExchangePattern())) { // TODO: execute some code } } catch( MessagingException e1 ) { // TODO: create a fault or set an error depending on the MEP } return true; }
By returning true, we let the CDK send the response message.
@Override public boolean onJBIMessage( final Exchange exchange ) { try { // 'exec' should be called in ROBUST_IN_ONLY if( exchange.getOperationName().equals( "exec" ) && MEPConstants.ROBUST_IN_ONLY_PATTERN.equals( exchange.getExchangePattern())) { Runnable runnable = new Runnable() { public void run() { // TODO: execute some code // bla-bla // Update the exchange, depending on the MEP, e.g. exchange.setDoneStatus(); // Send the response send( exchange ); } }; Thread myThread = new Thread( runnable ); myThread.start(); } } catch( MessagingException e1 ) { // TODO: create a fault or set an error depending on the MEP } return false; }
By returning false, it explicitly indicates to the CDK that the component will send back the exchange to the client (the consumer).
This second solution is best suited for asynchronous calls.
As an example, you could imagine putting the runnable into a blocking queue and process it later.
In this case, we would use the component (or any other unique instance) to hold this queue.
However, since JBI listeners are already into a pool of threads and associated with a waiting queue, the use of another pool is not a best practice.
In a general manner, try to reuse the waiting queue and the pool provided by the CDK.
Message and event listeners in Binding Components
In binding components, the Petals Maven plug-in creates a listeners package and three classes: BindingComponent.java (which should be renamed), JBIListener.java and ExternalJBIListener.java.
- The first class is mainly used to manage the component life cycle or to store data for JBI listeners. It is discussed in the life cycles section.
- The JBIListener class is the same than in service engines. It is used to process messages received from Petals services, that is to say from services inside the bus.
- The ExternalListener class is used to process messages (and events) from outside the bus. Since binding components are connectors between services inside the bus and the external world, they must be able to listener calls from services inside the bus (JBIListener) and from outside the bus (ExternalListener).
The external listener extends the abstract class org.ow2.petals.component.framework.listener.AbstractExternalListener.
It requires two methods to be completed:
public abstract boolean start() throws PEtALSCDKException public abstract boolean stop() throws PEtALSCDKException
These two methods are called when the binding component is started and stopped.
They should start and stop a listening service to implement in this class.
As an example, the FileTransfer binding component starts polling a directory when the start() method is called, and stops polling when stop() is called.
For network related binding components, it can be creating a socket to listen calls.
Examples of binding components are available in the Petals source forge. They can be used as a complement of information.
Also, do not forget that an instance of external listener is created for every consumes section in a service-unit configuration.
The use of pools and shared instances is recommended, but must be designed carefully.