There appears to be some inconsistency on how to use JMS resources, and setting up activationConfig
with proper @ActivationConfigProperty
on a @MessageDriven
annotation.
First, here is my resource config (glassfish-resources.xml, but translatable to other deployment descriptors). This is applied to Glassfish (asadmin add-resources glassfish-resources.xml
) along with the ActiveMQ Resource Adapter:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<resource-adapter-config name="activemq-rar"
thread-pool-ids="thread-pool-1"
resource-adapter-name="activemq-rar">
<property name="ServerUrl" value="tcp://localhost:61616"/>
<property name="UserName" value="admin"/>
<property name="Password" value="admin"/>
<property name="UseInboundSession" value="false"/>
</resource-adapter-config>
<admin-object-resource enabled="true"
jndi-name="jms/queue/myApp"
object-type="user"
res-adapter="activemq-rar"
res-type="javax.jms.Queue">
<description>MyApp JMS Queue</description>
<property name="Name" value="myAppAMQ"/>
<property name="PhysicalName" value="myAppAMQ"/>
</admin-object-resource>
<connector-resource enabled="true"
jndi-name="jms/factory/myApp"
object-type="user"
pool-name="jms/factoryPool/myApp">
<description>MyApp Connection Factory</description>
<property name="Name" value="myAppFactory"/>
</connector-resource>
<connector-connection-pool associate-with-thread="false"
connection-creation-retry-attempts="0"
connection-creation-retry-interval-in-seconds="10"
connection-definition-name="javax.jms.QueueConnectionFactory"
connection-leak-reclaim="false"
connection-leak-timeout-in-seconds="0"
fail-all-connections="false"
idle-timeout-in-seconds="300"
is-connection-validation-required="false"
lazy-connection-association="false"
lazy-connection-enlistment="false"
match-connections="true"
max-connection-usage-count="0"
max-pool-size="32"
max-wait-time-in-millis="60000"
name="jms/factoryPool/myApp"
ping="false"
pool-resize-quantity="2"
pooling="true"
resource-adapter-name="activemq-rar"
steady-pool-size="8"
validate-atmost-once-period-in-seconds="0"/>
</resources>
Here is my message provider bean. You'll notice that JNDI names are found and the ActiveMQ resources are used without error, the message sent to the proper queue:
@Stateless
@LocalBean
public class ServicesHandlerBean {
@Resource(mappedName = "jms/queue/myApp")
private Queue queue;
@Resource(mappedName = "jms/factory/myApp")
private ConnectionFactory factory;
public void sendJMSMessage(MessageConfig messageData) throws JMSException {
Connection connection = null;
Session session = null;
try {
connection = factory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer messageProducer = session.createProducer(queue);
messageProducer.send(createJMSMessage(session, messageData));
} finally {
if (session != null) {
try {
session.close();
} catch (JMSException e) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Cannot close session", e);
}
}
if (connection != null) {
connection.close();
}
}
}
}
The confusion begins when defining a @MessageDriven bean. The following which uses mappedName throws an exception:
@MessageDriven(mappedName = "jms/queue/myApp")
public class MessageBean implements MessageListener
Warning: RAR8000 : The method setName is not present in the class : org.apache.activemq.command.ActiveMQQueue Warning: RAR7097: No setter method present for the property Name in the class org.apache.activemq.command.ActiveMQQueue Info: visiting unvisited references Info: visiting unvisited references Warning: RAR8501: Exception during endpoint activation for ra [ activemq-rar ], activationSpecClass [ org.apache.activemq.ra.ActiveMQActivationSpec ] : javax.resource.ResourceException: Unknown destination type: null Severe: MDB00017: [InvoiceProductionMessageBean]: Exception in creating message-driven bean container: [java.lang.Exception] Severe: java.lang.Exception
I'm forced to define my MDB as such:
@MessageDriven(
activationConfig = {
@ActivationConfigProperty(propertyName = "connectionFactoryLookup", propertyValue = "jms/factory/myApp"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "myAppAMQ"),
@ActivationConfigProperty(propertyName = "messageSelector", propertyValue = " JMSType = 'TypeA' "),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue")
}
)
public class MessageBean implements MessageListener
AND I need to supply a glassfish-ejb-jar.xml telling the container to use the ActiveMQ resource, otherwise I get a java.lang.ClassCastException
:
Warning: RAR8501: Exception during endpoint activation for ra [ jmsra ], activationSpecClass [ com.sun.messaging.jms.ra.ActivationSpec ] : java.lang.ClassCastException: org.apache.activemq.ra.ActiveMQConnectionFactory cannot be cast to com.sun.messaging.jms.ra.DirectConnectionFactory Severe: MDB00017: [MessageBean]: Exception in creating message-driven bean container: [java.lang.Exception] Severe: java.lang.Exception
glassfish-ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-ejb-jar PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 EJB 3.1//EN" "http://glassfish.org/dtds/glassfish-ejb-jar_3_1-1.dtd">
<glassfish-ejb-jar>
<enterprise-beans>
<ejb>
<ejb-name>MessageBean</ejb-name>
<mdb-resource-adapter>
<resource-adapter-mid>activemq-rar</resource-adapter-mid>
</mdb-resource-adapter>
</ejb>
</enterprise-beans>
</glassfish-ejb-jar>
So, there seems to be some inconsistencies between how a producer can use a resource (JNDI), and how a consumer does (XML + @ActivationConfigProperty
). Also, the EE7 ActivationConfigProperty properties don't appear to work. For instance, using destinationLookup doesn't lookup the destination, and I'm forced to use ActiveMQ's destination
property.
ActiveMQ lists the following Activation Spec Properties:
acknowledgeMode (The JMS Acknowledgement mode to use. Valid values are: Auto-acknowledge or Dups-ok-acknowledge)
clientId (The JMS Client ID to use (only really required for durable topics))
destinationType (The type of destination; a queue or topic)
destination (The destination name (queue or topic name))
enableBatch (Used to enable transaction batching for increased performance)
maxMessagesPerBatch (The number of messages per transaction batch)
maxMessagesPerSessions (This is actually the prefetch size for the subscription. (Yes, badly named).)
maxSessions (The maximum number of concurrent sessions to use)
messageSelector (The JMS Message Selector to use on the subscription to perform content based routing filtering the messages)
noLocal (Only required for topic subscriptions; indicates if locally published messages should be included in the subscription or not)
password (The password for the JMS connection)
subscriptionDurability (Whether or not a durable (topic) subscription is required. Valid values are: Durable or NonDurable)
subscriptionName (The name of the durable subscriber. Only used for durable topics and combined with the clientID to uniquely identify the durable topic subscription)
userName (The user for the JMS connection)
useRAManagedTransaction (Typically, a resource adapter delivers messages to an endpoint which is managed by a container. Normally, this container likes to be the one that wants to control the transaction that the inbound message is being delivered on. But sometimes, you want to deliver to a simpler container system that will not be controlling the inbound transaction. In these cases, if you set useRAManagedTransaction to true, the resource adapter will commit the transaction if no exception was generated from the MessageListener and rollback if an exception is thrown.)
initialRedeliveryDelay (The delay before redeliveries start. Also configurable on the ResourceAdapter)
maximumRedeliveries (The maximum number of redeliveries or -1 for no maximum. Also configurable on the ResourceAdapter)
redeliveryBackOffMultiplier (The multiplier to use if exponential back off is enabled. Also configurable on the ResourceAdapter)
redeliveryUseExponentialBackOff (To enable exponential backoff. Also configurable on the ResourceAdapter useJndi no false when true, use destination as a jndi name)
Java EE7 spec lists the following Activation Spec Properties:
acknowledgeMode (This property is used to specify the JMS acknowledgement mode for the message delivery when bean-managed transaction demarcation is used. Its values are Auto_acknowledge or Dups_ok_acknowledge. If this property is not specified, JMS AUTO_ACKNOWLEDGE semantics are assumed.
messageSelector (This property is used to specify the JMS message selector to be used in determining which messages a JMS message driven bean is to receive)
destinationType (This property is used to specify whether the message driven bean is intended to be used with a queue or a topic. The value must be either javax.jms.Queue or javax.jms.Topic.)
destinationLookup (This property is used to specify the JMS queue or topic from which a JMS message-driven bean is to receive messages.)
connectionFactoryLookup (This property is used to specify the JMS connection factory that will be used to connect to the JMS provider from which a JMS message-driven bean is to receive messages.)
subscriptionDurability (If the message driven bean is intended to be used with a topic, this property may be used to indicate whether a durable or non-durable subscription should be used. The value of this property must be either Durable or NonDurable)
subscriptionName (This property is used to specify the name of the durable subscription if the message-driven bean is intended to be used with a Topic, and the bean provider has indicated that a durable subscription should be used.)
clientId (This property is used to specify the JMS client identifier that will be used when connecting to the JMS provider from which a JMS message-driven bean is to receive messages. If this property is not specified then the client identifier will be left unset.)
What is the proper way to use an ActiveMQ resource in both a producer and consumer with only @Inject points and jndi lookup? I'd like to avoid the glassfish-ejb-jar.xml and defining the queue name with an @ActivationConfigProperty
.