Chapter 30. Application Server Integration and Java EE

Since HornetQ also provides a JCA adapter, it is also possible to integrate HornetQ as a JMS provider in other Java EE compliant app servers. For instructions on how to integrate a remote JCA adaptor into another application sever, please consult the other application server's instructions.
A JCA Adapter basically controls the inflow of messages to Message-Driven Beans (MDBs) and the outflow of messages sent from other Java EE components, (for example, EJBs and Servlets).
This section explains the basics behind configuring the different Java EE components in the AS.

30.1. Configuring Message-Driven Beans

Message delivery to an MDB using HornetQ is configured on the JCA Adapter in the<JBOSS_HOME>/jboss-as/server/<PROFILE>/deploy/jms-ra.rar/META-INF/ra.xml file. By default this is configured to consume messages using an InVM connector from the instance of HornetQ running within the application server. If you need to adjust the configuration parameters, parameter details can be found in Section 30.4, “Configuring the JCA Adaptor”.
HornetQ provides standard configuration in the default installation so MDBs can reference the Resource Adapter destination and destination type.
The <JBOSS_HOME>/jboss-as/server/<PROFILE>/deploy/hornetq/jms-ds.xml data source file links destination and destination type configuration information in the ra.xml file using the <rar-name> directive.

30.1.1. Using Container-Managed Transactions

When an MDB is using Container-Managed Transactions (CMT), the delivery of the message is done within the scope of a JTA transaction. The commit or rollback of this transaction is controlled by the container itself. If the transaction is rolled back then the message delivery semantics will kick in (by default, it will try to redeliver the message up to 10 times before sending to a DLQ). Using annotations this would be configured as follows:
@MessageDriven(name = "MDB_CMP_TxRequiredExample",
  activationConfig =
  {
    @ActivationConfigProperty
      (propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty
      (propertyName = "destination", propertyValue = "queue/testQueue")
  })
@TransactionManagement(value= TransactionManagementType.CONTAINER)
@TransactionAttribute(value= TransactionAttributeType.REQUIRED)
public class MDB_CMP_TxRequiredExample implements MessageListener
{
   public void onMessage(Message message)...
}
The TransactionManagement annotation tells the container to manage the transaction. The TransactionAttribute annotation tells the container that a JTA transaction is required for this MDB. Note that the only other valid value for this is TransactionAttributeType.NOT_SUPPORTED which tells the container that this MDB does not support JTA transactions and one should not be created.
It is also possible to inform the container that it must rollback the transaction by calling setRollbackOnly on the MessageDrivenContext. The code for this would look something like:
@Resource
   MessageDrivenContextContext ctx;

   public void onMessage(Message message)
   {
      try
      {
         //something here fails
      }
      catch (Exception e)
      {
         ctx.setRollbackOnly();
      }
   }
If you do not want the overhead of an XA transaction being created every time but you would still like the message delivered within a transaction (for example, you are only using a JMS resource) then you can configure the MDB to use a local transaction. This would be configured as such:
@MessageDriven(name = "MDB_CMP_TxLocalExample",
  activationConfig =
  {
    @ActivationConfigProperty
      (propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty
      (propertyName = "destination", propertyValue = "queue/testQueue"),
    @ActivationConfigProperty
      (propertyName = "useLocalTx", propertyValue = "true")
  })
@TransactionManagement(value = TransactionManagementType.CONTAINER)
@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)
public class MDB_CMP_TxLocalExample implements MessageListener
{
   public void onMessage(Message message)...
}

30.1.2. Using Bean-Managed Transactions

Message-driven beans can also be configured to use Bean-Managed Transactions (BMT). In this case a User Transaction is created. This would be configured as follows:
@MessageDriven(name = "MDB_BMPExample",
  activationConfig =
    {
      @ActivationConfigProperty
        (propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
      @ActivationConfigProperty
        (propertyName = "destination", propertyValue = "queue/testQueue"),
      @ActivationConfigProperty
        (propertyName = "acknowledgeMode", propertyValue = "Dups-ok-acknowledge")
    })
@TransactionManagement(value= TransactionManagementType.BEAN)
public class MDB_BMPExample implements MessageListener
{
   public void onMessage(Message message)
}
When using Bean-Managed Transactions the message delivery to the MDB will occur outside the scope of the user transaction and use the acknowledge mode specified by the user with the acknowledgeMode property. There are only 2 acceptable values for this Auto-acknowledge and Dups-ok-acknowledge. Please note that because the message delivery is outside the scope of the transaction a failure within the MDB will not cause the message to be redelivered.
A user would control the life cycle of the transaction something like the following:
@Resource
  MessageDrivenContext ctx;

  public void onMessage(Message message)
  {
    UserTransaction tx;
    try
    {
      TextMessage textMessage = (TextMessage)message;
      String text = textMessage.getText();
      UserTransaction tx = ctx.getUserTransaction();
      tx.begin();
      //do some stuff within the transaction
      tx.commit();
    }
    catch (Exception e)
    {
       tx.rollback();
    }
  }

30.1.3. Using Message Selectors with Message-Driven Beans

It is also possible to use MDBs with message selectors. To do this simple define your message selector as follows:
@MessageDriven(name = "MDBMessageSelectorExample",
  activationConfig =
    {
      @ActivationConfigProperty
        (propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
      @ActivationConfigProperty
        (propertyName = "destination", propertyValue = "queue/testQueue"),
      @ActivationConfigProperty
        (propertyName = "messageSelector", propertyValue = "color = 'RED'")
    })
@TransactionManagement(value= TransactionManagementType.CONTAINER)
@TransactionAttribute(value= TransactionAttributeType.REQUIRED)
public class MDBMessageSelectorExample implements MessageListener
{
   public void onMessage(Message message)....
}

30.1.4. High Availability in Message-driven Beans

For message-driven beans to be compatible with High Availability (HA) environments, you must set an @ActivationConfigProperty relating to HA in the bean.

Important

Not all server profiles are enabled for clustering by default. Ensure the <clustered>true</clustered> directive is set in <JBOSS_HOME>/jboss-as/server/<PROFILE>/deploy/hornetq/hornetq-configuration.xml. The Activation Property will cause a HornetQException if the <clustered> directive is not correctly specified. For more information about Clustering, refer to Chapter 36, Clusters.
Add an activation property to the bean's activationConfig block to make the MDB compatible with HA environments:
activationConfig =
    {
     @ActivationConfigProperty
        (propertyName = "hA", propertyValue = "true"),
    }
For more information about High Availability, refer to Chapter 37, High Availability and Fail-over