I am currently successfully using an MQConnectionFactory to connect and post to a Websphere MQ queue using JMS.
However I'm getting a requirement from a client that I must use mqclient.ini instead.
So my question is, for a 'standard' JMS setup, should I be using:
Straight up MQConnectionFactory instance
A JMS configuration file
An mqclient.ini file
? What would one use one over the other? Does one take precedence over another?
The mqclient.ini and JMSconfig files are used setting attributes like what client side exits to use, TCP level overrides etc. They are basically used for configuring the client libraries/jars. They are not meant for application configuration for example what queue manager or queue to use. This sort of info, connection factory or destination info, is typically pulled from a JNDI so that the configuration can be modified without affecting application.
Related
Tomcat has a built-in JDBC connection pooling, but unfortunately no built-in JMS connection pooling.
We are migrating a legacy Tomcat web application from WebSphere MQ version 6 to 7.
Unfortunately, connection pooling has been removed in WebSphere MQ 7 as described here: http://www-01.ibm.com/support/docview.wss?uid=swg21665128
Now we are afraid that we will run into troubles if we just use the following code for configuring MQ in Tomcat:
<Resource name="jms/XXXQCF" auth="Container"
type="com.ibm.mq.jms.MQQueueConnectionFactory" factory="com.ibm.mq.jms.MQQueueConnectionFactoryFactory"
description="JMS Queue Connection Factory"
HOST="xxx.com" PORT="1429" CHAN="XXX" TRAN="1"
QMGR="XXX" />
The reason for our concerns is that this will not use a pooled JMS provider when using MQ 7. For details, see also http://activemq.apache.org/jmstemplate-gotchas.html
Alternative solutions we see are:
1) Use of Atomikos
Atomikos has a com.atomikos.jms.AtomikosConnectionFactoryBean that can be used instead of MQQueueConnectionFactory
But using an XA transaction manager is a huge overhead when we don't need XA
2) Use Spring's CachingConnectionFactory
looks like a good solution, but unfortunately our legacy application does not use Spring.
So we assume that using CachingConnectionFactory would mean quite some effort.
3) Use Apache Commons Pool
looks promising too, but implementing it correctly for JMS will require some good JMS knowledge
Our questions:
is there a JMS provider that can be used to wrap MQQueueConnectionFactory and that will pool connections, sessions, producers and consumers?
did anyone succeed in implementing one of the alternative solutions we outlined above?
As proposed by Umapathy in option 3, we now opted for the approach using Spring's CachingConnectionFactory, and it works well even for a non-Spring application. All you need to do is add the Spring Jars to the classpath, and wrap the MQQueueConnectionFactory with a CachingConnectionFactory.
We chose to create our own Tomcat QueueConnectionFactoryFactory that enables us to leave the original application code completely untouched, you just need to replace the original MQ connection factory from the Tomcat configuration file (shown above in the question) with the following XML definition:
<Resource name="jms/XXXQCF" auth="Container"
type="org.springframework.jms.connection.CachingConnectionFactory"
factory="at.rsf4j.core.utilities.RSFCachingMQQueueConnectionFactoryFactory"
description="JMS Queue Connection Factory"
HOST="xxx.com" PORT="1429" CHAN="XXX" TRAN="1"
QMGR="XXX" />
Here is the (simplified) code (without error checking) for the RSFCachingMQQueueConnectionFactoryFactory:
public class RSFCachingMQQueueConnectionFactoryFactory implements ObjectFactory{
public Object getObjectInstance (Object obj, Name name, Context nameCtx, Hashtable<?,?> environment)
throws NamingException {
Reference ref = (Reference) obj;
String beanClassName = ref.getClassName();
Class<?> beanClass = Class.forName(beanClassName);
if (CachingConnectionFactory.class.isAssignableFrom(beanClass)){
MQQueueConnectionFactoryFactory cff = new MQQueueConnectionFactoryFactory();
Reference mqReference = new Reference(
MQQueueConnectionFactory.class.getName());
Enumeration<RefAddr> allAddrs = ref.getAll();
while (allAddrs.hasMoreElements()){
mqReference.add(allAddrs.nextElement());
}
MQQueueConnectionFactory cf = (MQQueueConnectionFactory)cff.getObjectInstance(mqReference, name, nameCtx, environment);
CachingConnectionFactory ccf = (CachingConnectionFactory)beanClass.newInstance();
ccf.setTargetConnectionFactory(cf);
return ccf;
}
}
I think you already know the answer.
option 1: Go with a Java EE application server. This has inbuilt JMS pools.
option 2: If this is a simple application that does a single job (like putting on a queue with fire and forget type), you can connect to the JMS provider (qmgr) and create the producer or consumer in the servlet_init method. This means the legacy code needs an update. You also need to take care of the reconnect when something breaks as JMS reconnect properties doesn't kick off a reconnection (as far as my experience goes).
option 3: I have gone with Spring's CachingConnectionFactory when the application happens to be more than a little complex than one in option 2. I have applications that use JMSTemplate and some doesn't.
I managed to create simple Websocket application with Spring 4 and Stomp. See my last question here
Then I tried to use remote message broker(ActiveMQ). I just started the broker and changed
registry.enableSimpleBroker("/topic");
to
registry.enableStompBrokerRelay("/topic");
and it worked.
The question is how the broker is configured? I understand that in this case the application automagicaly finds the broker on localhost:defaultport, bu what if I need to point the app to some other broker on other machine?
The enableStompBrokerRelay method returns a convenient Registration instance that exposes a fluent API.
You can use this fluent API to configure your Broker relay:
registry.enableStompBrokerRelay("/topic").setRelayHost("host").setRelayPort("1234");
You can also configure various properties, like login/pass credentials for your broker, etc.
Same with XML Configuration:
<websocket:message-broker>
<websocket:stomp-endpoint path="/foo">
<websocket:handshake-handler ref="myHandler"/>
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:stomp-broker-relay prefix="/topic,/queue"
relay-host="relayhost" relay-port="1234"
client-login="clientlogin" client-passcode="clientpass"
system-login="syslogin" system-passcode="syspass"
heartbeat-send-interval="5000" heartbeat-receive-interval="5000"
virtual-host="example.org"/>
</websocket:message-broker>
See the StompBrokerRelayRegistration javadoc for more details on properties and default values.
Is it possible to provide and lookup connection factories that are transactional on a JBoss AS 7? The standard "RemoteConnectionFactory" doesn't seem to be transactional (XA) and I don't know how to make it XA or how to define some sort of XARemoteConnectionFactory that I can use from a remote client.
I tried adding the <xa>true</xa> element (which works in a hornetq configuration but is illegal in a jboss config file) and tried to create a <pooled-connection-factory> like the "java:/jmsXA" (but I wasn't able to look it up in the context even with a correct name for exporting).
I have traditional (com.ibm.mq.jar) MQ application in Java for testing purpose. Now I need to use that application to send some messages to JMS. When I try to set any JMS property on MQ message, for example:
message.setStringProperty("JMSDestination", "queue:///" + queueName);
I always get error: 2471 - MQRC_PROPERTY_NOT_AVAILABLE. It works if I just remove JMS from the property name.
Is it possible to set JMS properties directly on MQMessage? What is a correct way to do that on MQ level?
Btw. I have the same application in .NET where setting JMS properties this way is possible so I'm only trying to use the same code in Java.
It is not allowed to do this manually. Please use the JMS API to set JMS properties.
Restrictions to MQ properties are explained here.
One thing is interessting in that document page though,
The names of properties specified directly as MQRFH2 elements are not guaranteed to be validated by the MQPUT call.
You could perhaps work around this, on a short term basis. There seems to be no guarantee that setting the MQRFH2 elements directly will not be validated, though.
I have an enterprise application running on JBoss which contains a number of message driven beans. At the moment, each MDB listens for messages from a destination which is defined via annotations on the MDB class.
Is it possible to set the detination that MDBs subcribe to at runtime, rather than configuring this via annotations of a deployment descriptor?
Many thanks.
Generally you can't reconfigure existing MDB to listen another queue, but you can try this method to specify system properties instead of real queue names. System properties can be changed in runtime through JMX