Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

A JMS provider can be used as the transport for one-way and request-response operations. A minimal one-way configuration is shown below:

Code Block
xml
languagexmljava
@Component
<component name="OneWayClient">
   <implementation.java class="..."/>public class Client ... {

    <reference@JMS(value name="service">
   @JMSConfiguration(destination = "ServiceQueue"))
   <binding.jms> protected Service service;

     <destination jndiName="serviceQueue"/>
public String process() {
		// ...
     </binding.jms>    </reference>
</component>

<component name="OneWayService">
   <implementation.java class="..."/>
   <service>
      <binding.jms>service.onReceive(message);
		// ...
    }
}
 
@Component
@JMS(value = @JMSConfiguration(destination = "ServiceQueue"))
public class ServiceImpl implements Service {

    public void onReceive(String message) <destination jndiName="serviceQueue"/>{
       <//binding ...jms>
   </service>
</component>

The above configuration uses the "serviceQueue" queue to propagate messages. Depending on the JMS provider, it may also be necessary to specify a connection factory name (see below).

Info
titleNon-Blocking Operations

When writing asynchronous Java components, it is important to remember the @OneWay annotation. If a method is not marked with @OneWay, it will be taken as a request-response operation even if the return value is void. This means the operation will block until a response message is received.  

Configuring request-response operations involves specifying a separate response queue in addition to the forward queue:

Code Block
xmlxml

<component name="RequestResponseClient }
}
 
public interface Service {
	@OneWay
	void onReceive(String message);
}

In XML, JMS bindings are configured as follows:

Code Block
xml
xml
<component name="Client">
   <implementation.java class="..."/>
   <reference name="service">
      <binding.jms>
         <destination jndiName="ServiceQueue"/>
      </binding.jms>
   </reference>
</component>

<component name="Service">
   <implementation.java class="..."/>
   <reference name="service"><service>
      <binding.jms>
         <destination namejndiName="serviceQueueServiceQueue"/>
      </binding.jms>
  <response> </service>
           <destination jndiName="responseQueue"/>
         </response>
      </binding.jms>
   </reference>
</component>

<component name="RequestResponseService">
   <implementation.java class="..."/>
   <service>
      <binding.jms>
         <destination name="serviceQueue"/>
         <response>
            <destination jndiName="responseQueue"/></component>

The above configuration uses the "ServiceQueue" queue to propagate messages. Depending on the JMS provider, it may also be necessary to specify a connection factory name (see below).

Info
titleNon-Blocking Operations

When writing asynchronous Java components, it is important to remember the @OneWay annotation. If a method is not marked with @OneWay, it will be taken as a request-response operation even if the return value is void. This means the operation will block until a response message is received.  

Configuring request-response operations involves specifying a separate response queue in addition to the forward queue:

Code Block
languagejava
@Component
public class Client ... {

    @JMS(value = @JMSConfiguration(destination = "ServiceQueue", responseDestination="ResponseQueue"))
    protected Service service;

    public void process() {
</response>		// ...
      </binding.jms>  String  </service>
</component>

Using Callbacks

...

response = service.onReceive(message);
		// ...
    }
}
 
@Component
@JMS(value = @JMSConfiguration(destination = "ServiceQueue", responseDestination="ResponseQueue"))
public class ServiceImpl implements Service {

    public String onReceive(String message) {
       // ...
	   return response;
    }
}
 
public interface Service {
	
	String onReceive(String message);
}
Code Block
xml
xml
<component name="Client">
   <implementation.java class="..."/>
   <reference name="service">
      <binding.jms>
         <destination name="ServiceQueue"/>
         <response>
            <destination jndiName="ResponseQueue"/>
         </response>
      </binding.jms>
   </reference>
</component>

<component name="Service">
   <implementation.java class="..."/>
   <service>
      <binding.jms>
         <destination name="ServiceQueue"/>
         <response>
            <destination jndiName="ResponseQueue"/>
         </response>
      </binding.jms>
   </service>
</component>

Using Callbacks

While JMS is an asynchronous model, it is important to note that the client component will block on request-response operations until a response is received. In some cases, this is the desired behavior. In other situations, such as long-running interactions, looser coupling is required where the client can continue processing without waiting for a response to be returned. Callbacks can be used to provide responses at some later point in time. Configuring callbacks involves specifying a callback queue:

 

Code Block
languagejava
@Component
public class CallbackClientImpl implements Client, ConsumerCallback {


    @JMS(value = @JMSConfiguration(destination = "ServiceQueue"), callback = @JMSConfiguration(destination = "CallbackQueue"))
    protected Service service;


    public void invoke(String message) {
        service.onReceive(message);
    }

    public void onResponse(String message) {
		// ...
    }
}


public interface ConsumerCallback {
    @OneWay
    void onResponse(String message);
}


@Component
@JMS(value = @JMSConfiguration(destination = "ServiceQueue"), callback = @JMSConfiguration(destination = "CallbackQueue"))
public class ServiceImpl implements Service {


    @Callback
    protected ConsumerCallback callback;

    public void onReceive(String message) {
        callback.onResponse(message);
    }
}
 
public interface Service {
	@OneWay
	void onReceive(String message);
}

And in XML:

Code Block
xml
xml

<component name="CallbackClient">
   <implementation.java class="..."/>
   <reference name="service">
      <binding.jms>
         <destination name="serviceQueueServiceQueue"/>
      </binding.jms>
      <callback>
         <binding.jms>
            <destination name="callbackQueueCallbackQueue"/>
         </binding.jms>
      </callback>
   </reference>
</component>

<component name="CallbackService">
   <implementation.java class="..."/>
   <service>
      <binding.jms>
         <destination name="serviceQueueServiceQueue"/>
      </binding.jms>
      <callback>
         <binding.jms>
            <destination name="callbackQueueCallbackQueue"/>
         </binding.jms>
      </callback>
   </service>
</component>

...

The previous examples assumed the JMS provider did not require the connection factory to be specified for simplicity. However, most JMS providers will require the connection factory to be specified using the conectionFactory element:

Code Block
xml
xml

<component name="CallbackClient">
   <implementation.java class="..."/>
   <reference name="service">
      <binding.jms>
         <connectionFactory jndiName="TheConnectionFactory"/>
         <destination name="serviceQueue"/>
      </binding.jms>
   </reference>
</component>

...

Fabric3 supports XA transacted messaging. This is useful when a message must be reliably sent in conjunction with a database update. To enable transacted messaging, use the transactedOneWay intent on the JMS binding:

Code Block
xml
xml

<component name="TheComponent">
   <implementation.java class="..."/>
   <reference name="service">
      <binding.jms requires="transactedOneWay">
         <destination jndiName="TheQueue"/>
      </binding.jms>
   </reference>
</component>

The above will enqueue a message transactionally with the message provider. The component implementation is as follows:

Code Block
java
java

public class TheComponent implements ... {

   @Reference
   protected Service service;

   public void operation() {
      Message message = ...
      service.invoke(message);
   }

}

If the above implementation also needed to update a database using Hibernate in the same transaction as the message enque, it could be modified as follows to use the @ManagedTransaction annotation:

Code Block
java
java

@ManagedTransaction
public class TheComponent implements ... {

   @PersisitenceContext(unitName = "employee")
   Session session;

   @Reference
   protected Service service;

   public void operation() {
      Message message = ...
      long id = message.getId();

      Entity entity = session.find(Entity.class, id);
      entity.update(message.getUpdate());
      service.invoke(message);
   }
}

Transacted Messaging and Request-Response
In a word: don't try it. Transacted messaging will not work with request reply. To see why, consider the following:

Code Block
java
java

@ManagedTransaction
public class TheComponent implements ... {

   @Reference
   protected Service service;

   public void operation() {
      Message message = ...
      Response response = service.invoke(message);
   }
}

...

If you need to receive a response and require transacted messaging, use a callback. The following shows how this is configured: 

Code Block
xml
xml

<component name="TheComponent">
   <implementation.java class="..."/>
   <reference name="service">
      <binding.jms requires="transactedOneWay">
         <destination jndiName="TheQueue"/>
      </binding.jms>
      <callback>
         <binding.jms requires="transactedOneWay">
            <destination jndiName="TheCallbackQueue"/>
         </binding.jms>
      </callback>
  </reference>
</component>

...

Starting with Fabric3 1.9.5, JMS listeners can be configured to pause (i.e. not receive messages) when the runtime is booted. The listeners can then be started using an HTTP POST operation via the REST Management API. To configure listeners to pause on startup, use the jms/@pause.on.start attribute in systemConfig.xml:

Code Block
xml
xml

<config>  
    <jms pause.on.start="true"/>
    <!-- ... -->
</config>

The Fabric3 Admin CLI can then be used to start the listeners. Alternatively, a script can issue an HTTP POST operation. Note that if the POST is made against the zone cluster address, Fabric3 will replicate the management change to all runtimes in the zone (cluster). The following is an example CLI session that starts all listeners:

...

:

Code Block
 //Goes ("follows" the link) to zone2 where the listeners are deployed. Substitute the appropriate zone name.
p zone/runtime/transports/resume jms
f zone2                    

// Puts the value "jms" to the zone/runtime/transports/resume resource. 
// Since this is a zone address, the changes will be replicated throughout the cluster.

p zone/runtime/transports/resume jms   

Alternatively, a script could POST the "jms" value serialized in the JSON format to http://<zone leader>/management/zone/runtime/transports/resume. Listeners on individual runtime instances can be started by using the direct runtime address as opposed to the zone address, e.g.:

Code Block

p runtime/transports/resume jms