I'm learning IBM MQ. I need to put and get messages to IBM MQ from java code using the best practices.
I did this question but I don't know if it's the best way: How to put and get IBM MQ messages without application server
Could you give me some tip about that, please?
IBM provides sample code with the MQ install, you should look into those.
There are samples for using MQ classes for Java and JMS as well.
The source for these samples are located under "MQ install dir"\Tools on Windows.
public static void main(String[] args) {
Connection connection = null;
Session session = null;
Destination destination = null;
Destination tempDestination = null;
MessageProducer producer = null;
MessageConsumer consumer = null;
try {
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, "localhost");
cf.setIntProperty(WMQConstants.WMQ_PORT, 1414);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, "SYSTEM.DEF.SVRCONN");
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "QM1");
connection = cf.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue("queue:///Q1");
producer = session.createProducer(destination);
long uniqueNumber = System.currentTimeMillis() % 1000;
TextMessage message = session.createTextMessage("SimpleRequestor: Your lucky number yesterday was " + uniqueNumber);
connection.start();
producer.send(message);
}catch (JMSException jmsex) {
jmsex.printStackTrace();
}
}
To me this seems like a duplicate of the question you asked half an hour previously. You seem quite desperate to get an answer so lets see if this helps move you forward:
The basic principles of message queue handling are the same regardless of the implementation. Given that is the case, and given that you are learning, I think you would benefit from looking at this RabbitMQ tutorial:
RabbitMQ tutorial
RabbitMQ is free and easy to install on your local machine, so you can have a play with it, and understand it more easily. The tutorial is suitable for a newbie, with a good explanation throughout.
This should give you a good idea of the approaches used, and the best practice.
My understanding is that RabbitMQ is also much more widely used than IBM MQ, so you would be able to get much more support when getting to grips with it.
Once you have learnt the techniques for RabbitMQ, I would hope you can apply them to IBM MQ, which should enable you to answer your own original question.
Good Luck !
Related
I am using Java SE. I create a topic when the app first starts like so:
connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
conn = connectionFactory.createTopicConnection();
session = conn.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
conn.start();
session.createTopic(name);
I am confused on how to retrieve this Topic in my classes.
Say for example I have a class, and it connects to the JMS Service just like above using:
connectionFactory = new ActiveMQConnectionFactory("vm://localhost");
conn = connectionFactory.createTopicConnection();
session = conn.createTopicSession(false,
TopicSession.AUTO_ACKNOWLEDGE);
conn.start();
How can I then get a reference to the Topic I created a app startup to send messages?
I would imagine something along the lines of:
session.getTopic(name);
Would exist, but I cant find anything on it.
I have read how to do this using Java EE using JNDI lookup, but this service is not available to me since I am not running in a container.
You don't 'retrieve' a topic. A Topic instance is just a piece of information. You construct an instance of it in your client if you want to subscribe to a topic (or queue), like is demonstrated in the ActiveMQ hello world example:
http://activemq.apache.org/hello-world.html
ex:
// the name should of course be the same as it exists on the producer side
Destination destination = session.createTopic("TEST.FOO");
// Create a MessageConsumer from the Session to the Topic or Queue
MessageConsumer consumer = session.createConsumer(destination);
This is all governed by the standardized and very mature JMS API, so you should refer to the JEE documentation. Any book on JMS will work for you as well.
Further reading: https://docs.oracle.com/javaee/6/tutorial/doc/bncdr.html
API docs: http://docs.oracle.com/javaee/6/api/javax/jms/package-summary.html
I am trying to develop a JMS standalone application to read and write to a Queue on MQSeries.
My boss asked me to use pure java JMS (not ibm.mq lib) to do that.
Here is the information that need to make the jms connection:
mq.hostname=10.10.10.10
mq.channel=API.CLIENTCHL
mq.queueManager=MQPETAPI
mq.port=1422
Do you know how to do that Or do you have any link that teach me to do that.
The issue here is the requirement that "My boss asked me to use pure java JMS (not ibm.mq lib) to do that." JMS is a specification and each implementation must comply with the API and the semantics, but is free to do whatever they want at a low level. It is always necessary to use the implementation classes provided by the transport vendor. Therefore if you use WebSphere MQ as the transport, you will need to use the IBM MQ JMS classes to write a JMS application.
That said, if you stick with pure JMS API calls you would be able to plug in any transport vendor's classes. This is what is usually intended when you are given requirements such as that mentioned in the original post.
There's an article describing exactly what you are looking to do called Running a standalone Java application on WebSphere MQ V6.0 It uses only the JMS API and it uses JNDI in a local file system (a .bindings file). By swapping out the IBM JMS classes for another vendor and using their JNDI tools you would be able to plug in any JMS transport without changing your code using this approach.
If you want to do the same thing without JNDI, look at the sample programs provided with the MQ client install where you obtained your Java classes. In a UNIX/Linux system these are in /opt/mqm/samp and on Windows they are in install_dir/tools/jms/samples. The SimpleRequestor.java sample has the following code for initializing your connection factory without JNDI:
try {
// Create a connection factory
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();
// Set the properties
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, "localhost");
cf.setIntProperty(WMQConstants.WMQ_PORT, 1414);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, "SYSTEM.DEF.SVRCONN");
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "QM1");
Because this approach does not use JNDI, you are required to write code that is not transportable across transport vendors. It is IBM WebSphere MQ specific.
If you grabbed the MQ jars from somewhere and do not have the full install (and thus do not have the samples) you can download it as SupportPac MQC7. The download is free. In general you should use the latest client, even with a back-level queue manager. Obviously you do not get V7 functionality from a V6 QMgr but the JMS implementation in the V7 client is much improved, even for V6 functionality. If for some reason you really must use the V6 client, you can download it as SupportPacMQC6. Whichever client version you use, make sure to use the corresponding Infocenter.
V6 Infocenter
V7 Infocenter
Finally, the landing page with an index for all the SupportPacs is here.
A complete (synchronous) standalone JMS application with TextMessage.
It is IBM WebSphere MQ specific.
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueReceiver;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import com.ibm.mq.jms.JMSC;
import com.ibm.mq.jms.MQQueueConnectionFactory;
public class JMSApplicationStandAlone {
public static void main(String[] args) {
try {
/*MQ Configuration*/
MQQueueConnectionFactory mqQueueConnectionFactory = new MQQueueConnectionFactory();
mqQueueConnectionFactory.setHostName("localhost");
mqQueueConnectionFactory.setChannel("MQ.CHANNEL");//communications link
mqQueueConnectionFactory.setPort(1416);
mqQueueConnectionFactory.setQueueManager("QUEUE.MGR");//service provider
mqQueueConnectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
/*Create Connection */
QueueConnection queueConnection = mqQueueConnectionFactory.createQueueConnection();
queueConnection.start();
/*Create session */
QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
/*Create response queue */
Queue queue = queueSession.createQueue("QUEUE.RESPONSE");
/*Create text message */
TextMessage textMessage = queueSession.createTextMessage("put some message here");
textMessage.setJMSReplyTo(queue);
textMessage.setJMSType("mcd://xmlns");//message type
textMessage.setJMSExpiration(2*1000);//message expiration
textMessage.setJMSDeliveryMode(DeliveryMode.PERSISTENT); //message delivery mode either persistent or non-persistemnt
/*Create sender queue */
QueueSender queueSender = queueSession.createSender(queueSession.createQueue("QUEUE.REQEST"));
queueSender.setTimeToLive(2*1000);
queueSender.send(textMessage);
/*After sending a message we get message id */
System.out.println("after sending a message we get message id "+ textMessage.getJMSMessageID());
String jmsCorrelationID = " JMSCorrelationID = '" + textMessage.getJMSMessageID() + "'";
/*Within the session we have to create queue reciver */
QueueReceiver queueReceiver = queueSession.createReceiver(queue,jmsCorrelationID);
/*Receive the message from*/
Message message = queueReceiver.receive(60*1000);
String responseMsg = ((TextMessage) message).getText();
queueSender.close();
queueReceiver.close();
queueSession.close();
queueConnection.close();
} catch (JMSException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Note: Replace the configuration values
If you don't mind writing WMQ-specific code then you can do
MQConnectionFactory cf = new MQConnectionFactory();
cf.setHostName(HOSTNAME);
cf.setPort(PORT);
cf.setChannel(CHANNEL);
cf.setQueueManager(QMNAME);
cf.setTransportType(WMQConstants.WMQ_CM_CLIENT);
then usual JMS resources
Connection c = cf.createConnection();
Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue q = s.createQueue("myQueue"); // replace with real queue name
MessageProducer p = s.createProducer(q);
and finally create and send a message
Message m = s.createTextMessage("Hello, World!);
p.send(m);
(i've typed that in off the top of my head so can't rule out a typo, but it's fundamentally correct). If you're really supposed to be using 'pure JMS' - i.e. with no provider-specific objects - then you need to bind a MQConnectionFactory object in JNDI (take a look at the JMSAdmin tool, it's in the docs) then look it up from your application, i.e.
InitialContext ic = new InitialContext(); // or as appropraite
ConnectionFactory cf = (ConnectionFactory)ic.lookup("myMQfactory"); // replace with JNDI name
Typically with JMS you would define the QueueConnectionFactory in your container via whatever configuration mechanism it makes available then add it to the container's JNDI registry. Each container would have it's own methods for doing that (i.e. Tomcat versus WebSphere).
If you want to forgo JNDI, you could create an instance of com.ibm.mq.jms.MQQueueConnectionFactory directly and set the hostname, port, queueManager, and channel properties on it. You can then use that object as you would an instance of javax.jms.QueueConnectionFactory since it implements it.
I can't teach you JMS in a single post, but I can point you to some of the resources that I used to learn it myself:
The O'Reilly Java Message Service book
IBM Developerworks JMS Tutorial (more MQSeries/Websphere MQ specific)
The Spring framework can help you use JMS more effectively, especially if you're developing a standalone app outside of a J2EE app server: Spring Java Message Service documentation
This is fairly common. Here are some examples.
I'm having a bit of trouble with understanding JMS from a performance perspective. We have this very straightforward code in our application:
QueueConnection connection = null;
QueueSession session = null;
QueueSender sender = null;
TextMessage msg = null;
try {
// The JNDIHelper uses InitialContext to look up things
QueueConnectionFactory qcf = JNDIHelper.lookupFactory();
Queue destQueue = JNDIHelper.lookupQueue();
// These objects are created for every message, which is quite slow
connection = qcf.createQueueConnection();
session = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
sender = session.createSender(destQueue);
// This is the actual message
msg = session.createTextMessage(xmlMsg);
sender.setTimeToLive(0);
sender.send(msg);
}
finally {
// Close all objects again
JMSUtilities.safeClose(sender);
JMSUtilities.safeClose(session);
JMSUtilities.safeClose(connection);
}
The code is correct, but probably some of the above artefacts could be reused for several messages. These are our configurations:
We use Oracle Weblogic 10.3.3
Weblogic connects to IBM MQ 7.0 (Problem also appears with 6.0) for JMS
The above logic is executed by a single thread on a backend server. It would be simple to keep some objects (QueueConnection, QueueSession, QueueSender) in memory as there is no concurrency involved.
My questions
Which types of objects can be shared among several messages? (of course we'd include error recovery, restoring those shared objects)
What are best practices to improve performance?
Here are some relevant parts of the jms spec:
section 2.8 Multithreading
JMS Object Supports Concurrent Use
Destination YES
ConnectionFactory YES
Connection YES
Session NO
MessageProducer NO
MessageConsumer NO
section 4.4.14 Serial Execution of Client Code
JMS does not cause concurrent execution of client code unless a client explicitly requests it. One way this is done is to define that a session serializes all asynchronous delivery of messages
So as already mentioned reuse as much as possible. Reuse the ConnectionFactory, Connection and Destinations for all Threads. For each Thread reuse consumers and producers.
If you are reusing a JMS connection beware, that the JMS Provider will multiplex different sessions on that connections. So even if it is safe to reuse connections it might be faster to create a connection for every session you need.
The only thing you need to create again and again is the msg itself - if you are sending to the same queue.
So yes, you can remember the Connection, Session and Sender.
Define "to share".
If you mean to share among different threads this is very dangerous. You can safely share QueueConnectionFactory object as well as the JMS Connection object. You must not share Session, Sender/Consumer or Message objects. Thats the way how TIBCO EMS works I am not sure about IBM platform but I guess this is very same.
If you can be sure your "send" method is not called by different threads you can encapulate this into a MySender class with Connection, Session and Sender member variables. But watch out! Do properly close the resources on exit. Thats what Heiko Rupp recommends. Somthing like this:
class MySender {
private QueueConnection connection = null;
private QueueSession session = null;
private QueueSender sender = null;
public MySender(...) { /* initialize conn/sess/sender */ }
public send(String xmlMsg) { /* sender.send(session.createTextMessage(xmlMsg)) */ }
public close() { /* close all resources */ }
}
Regarding performance. There is no much room for improvement in JMS standard. Keep messages small and optimize server setting. Use durable destinations only when you need it etc. Read documentation for your platform. But on the client side there is not much room. Some platforms offers additional features to JMS that allows some extra performance gain (batch sends etc) but it depends on the platform. I dont know IBM.
I've set up ActiveMQ and I can create/connect to a topic using "dynamicTopics/MyTopic". This works great:
Properties env = new Properties( );
env.setProperty(Context.INITIAL_CONTEXT_FACTORY,"org.apache.activemq.jndi.ActiveMQInitialContextFactory");
env.setProperty(Context.PROVIDER_URL,"tcp://myhostname:xxxxx");
javax.naming.Context ctx = new InitialContext(env);
InitialContext jndi = new InitialContext(env);
// Look up a JMS connection factory
TopicConnectionFactory conFactory = (TopicConnectionFactory)jndi.lookup("TopicConnectionFactory");
// Create a JMS connection
TopicConnection connection = conFactory.createTopicConnection(username,password);
Topic chatTopic = (Topic)jndi.lookup("dynamicTopics/MyTopic");
I would like to connect to a topic already exists without dynamically creating the topic if it doesn't exist, how can I do so?
Cheers,
Pete
While you don't need to create destinations manually with AMQ you always have that option. Basically you can alter the security settings to allow only an admin to create destinations. See the AMQ FAQ for this:
http://activemq.apache.org/how-do-i-create-new-destinations.html
Regards
Tim
www.fusesource.com
What you are actually doing is just connecting to topic. ActiveMQ is so nice that it creates topic for you if it did not exist before.
So, to connect to already existing topic just do the same that you are already doing.
I am using Glassfish JMS.
I am able to add messages to a queue.
I can see the messages using the QueueBrowser object.
However the MessageConsumer (nor the QueueReceiver) cannot receice any message and return null.
Message expiration is set to 0 and I remember to open the connection.
Any ideas?
Here is the code:
Session session = null;
Connection conn = null;
try
{
InitialContext jndi = new InitialContext();
ConnectionFactory qFactory = (ConnectionFactory)jndi.
lookup("myConnectionFactory");
conn = qFactory.createConnection();
conn.start();
Queue queue = (Queue)jndi.lookup("myQueueName");
session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
QueueReceiver mc = ((QueueSession)session).createReceiver(queue);
Object m = mc.receive(2000);
//m is NULL!
QueueBrowser browser = session.createBrowser(queue);
for(Enumeration e = browser.getEnumeration(); e.hasMoreElements(); )
{
//there are many messages here...
}
That would be good to have the client code.
Similar thing happened to me when not properly committing/closing the connection on the sender side. The message would be visible when using the admin console, however, not available yet to the MDB.
Hope it helps.
Does this code run in the appserver? If it does, I'd obtain the required objects via annotations, and for a message receiver I'd use a MDB.
If this is a piece of standalone code, I had a hell of a time getting a JNDI based client working, I reverted to using the "raw" Java API.
I witnessed the same behavior happening after the first session commit, meaning that before the messages where received correctly. In my case the issue was that I was re-creating the receiver while keeping the same session.
As pointed out in this article:
Creating temporary destinations, consumers, producers and connections
are all synchronous request-response operations with the broker and so
should be avoided for processing each request as it results in lots of
chat with the JMS broker.
The solution was as simple as reusing the same receiver.