Cannot send ObjectMessage to IBM MQ - java

How can I send object message to IBM MQ?
Fragment of my Java code:
QueueConnectionFactory cf;
QueueConnection conn;
MQQueueSession sess;
QueueSender sender;
Queue putQueue = null;
Message RQMessage
//...
cf = new MQQueueConnectionFactory();
((MQQueueConnectionFactory)cf).setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
((MQQueueConnectionFactory)cf).setHostName(host_ip);
((MQQueueConnectionFactory)cf).setPort(1414);
((MQQueueConnectionFactory)cf).setQueueManager(MQMgrName);
((MQQueueConnectionFactory)cf).setChannel(MQChannel);
((MQQueueConnectionFactory)cf).setCCSID(1251);
conn = cf.createQueueConnection(" ", " ");
sess = (MQQeueSession) conn.create
putQueue = ((MQQueueSession)sess).createQueue("queue://"+MQMgrName+"/"+putQueueName);
sender = (MQQueueSender) sess.createSender(putQueue);
MyClass rq_obj = new MyClass();
//MyClass is serializable
rq_obj.setid("1111");
System.out.println(rq_obj.toString());
//got string with my object
RQMessage = sess.createObjectMessage(rq_obj);
RQMessage.setStringProperty("prop_name", "prop_value");
Sender.send(RQMessage);
sess.commit();
As a result of this code I get a message with property prop_name = prop_value, but message's body is null. I don't know why, but maybe I should change properties of my connection factory (((MQQueueConnectionFactory)cf).setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);) but even if it is so, I don't know what properties I need.

I prefer to send my data as either XML or JSON rather than as an Object. Also years ago, the JVMs (sender & receiver) had to be at the same version level otherwise the Object could not be understood/translated.
From the IBM MQ Knowledge Center, it says you should be doing it this way:
ObjectMessage objMessage = session.createObjectMessage();
objMessage.setObject(myObj);
publisher.send(objMessage);

Related

MQ - Get Multi Instance MQ Manager Connection List

In a Java client app we are connecting to a multi-instance MQ Manager as follows:
java.net.URL ccdt = new URL("file:./config/qmgrs/MQMGR/AMQCLCHL.TAB");
MQQueueManager mqQueueManager = new MQQueueManager("*MQMGR", ccdt);
We can then for example enquire about the current depth of a queue as follows:
int openOptions = CMQC.MQOO_INQUIRE;
MQQueue mqQueue = mqQueueManager.accessQueue("A.QUEUE.NAME", openOptions);
System.out.println("queue depth:" + mqQueue.getCurrentDepth());
Question is, using the same MQQueueManager object, how can we get the list of multi-instance MQ Managers' addresses and ports. Or any other info about the manager itself...
We can see there is the following sort of thing available:
String nameList = mqQueueManager.getAttributeString(MQConstants.MQCA_NAMELIST_NAME, MQConstants.MQ_NAMELIST_NAME_LENGTH);
But when we call the above command, we get:
com.ibm.mq.MQException: MQJE001: Completion Code '2', Reason '2067'.
We are not sure if this is because the client code is not configured correctly or, if it is because the connection that we are using does not have sufficient permissions to get information about the manager?
You will have to use MQ PCF classes to query queue manager attributes. There is sample PCF_WalkThroughQueueManagerAttributes.java shipped with MQ that displays all attributes of queue manager. Here is small sample that lists local queues of a queue manager.
private void runPCFTest() {
try {
PCFAgent agent = new PCFAgent(connect());
PCFParameter[] parameters = { new MQCFST (MQConstants.MQCA_Q_NAME, "*"),
new MQCFIN (MQConstants.MQIA_Q_TYPE, MQConstants.MQQT_LOCAL)};
MQMessage[] responses = agent.send(CMQCFC.MQCMD_INQUIRE_Q_NAMES, parameters);
MQCFH cfh = new MQCFH(responses[0]);
for (int i = 0; i < cfh.getParameterCount(); i++) {
System.out.println (PCFParameter.nextParameter (responses [0]));
}
}catch(Exception ex) {
System.out.println(ex);
}
}
#SuppressWarnings({ "unchecked", "rawtypes" })
private MQQueueManager connect() throws MQException {
Hashtable props = new Hashtable();
props.put(MQConstants.HOST_NAME_PROPERTY, "localhost");
props.put(MQConstants.PORT_PROPERTY, 1414);
props.put(MQConstants.CHANNEL_PROPERTY, "MFT_CHN");
props.put(MQConstants.USER_ID_PROPERTY, "user1");
props.put(MQConstants.PASSWORD_PROPERTY, "passw0rd");
props.put(MQConstants.USE_MQCSP_AUTHENTICATION_PROPERTY, true);
return new MQQueueManager("MQM", props);
}
But why do you want to query connection information, host, port etc?
If I understand your question correctly, you want to know all of the hostnames (or IP addresses) and Port numbers of the servers where the MI queue manager may reside. Correct?
This information is in your CCDT file. When you (or MQAdmin) created your CCDT entry for the CLNTCONN (client-side channel), you would have issued a like:
DEFINE CHANNEL(TEST.CHL) CHLTYPE(CLNTCONN) TRPTYPE(TCP) CONNAME('ipaddr1(1414), ipaddr2(1414)') QMNAME(QM1)
Hence, the CONNAME parameter has the information and that is what the MQ client library uses to connect to the remote queue manager. First it will try 'ipaddr1(1414)' and if it fails then it will try 'ipaddr2(1414)'.

Unable to receive message sent to acivemq by C# in Java

What I want to do is to send messages via Apache Activemq between C# app and Java app.
C#:
using (IConnection connection = factory.CreateConnection())
using (ISession session = connection.CreateSession())
{
IDestination destination = SessionUtil.GetDestination(session, "queue://ISI");
// Create a consumer and producer
using (IMessageProducer producer = session.CreateProducer(destination))
{
// Start the connection so that messages will be processed.
connection.Start();
ITextMessage request = session.CreateTextMessage(JsonConvert.SerializeObject(obj));
/*request.NMSCorrelationID = "abc";
request.Properties["NMSXGroupID"] = "cheese";
request.Properties["myHeader"] = "Cheddar";*/
producer.Send(request);
return request;
}
}
Java:
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(isiProperties.getMqUrl());
connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("ISI");
MessageConsumer consumer = session.createConsumer(destination);
Message message = consumer.receive();
if(message instanceof TextMessage) {
try {
String text = ((TextMessage) message).getText();
ObjectMapper mapper = new ObjectMapper();
StatusChangeMessage obj = mapper.readValue(text, StatusChangeMessage.class);
if (obj instanceof StatusChangeMessage) {
StatusChangeMessage received = (StatusChangeMessage) obj;
Order order = orderRepository.findOne(received.getOrderId());
order.setStatus(received.getStatus());
orderRepository.saveAndFlush(order);
}
} catch(JMSException e) {
} catch(IOException e) {
}
}
The C# app correctly sends messages (it is visible in activemq admin interface) but there are no active subscribers (Java app should do that). Do you see anything wrong here?
Basically, breakpoint on if(message instanceof TextMessage) { does not get executed.
I have finally found a solutioin. It was two-steps problem. Firstly, Windows firewall influenced activemq. Secondly, client library probably didn't fully match to the server. Problem finally gone after downgrading server to 5.8.0.

How to initialize initial context in JMS

I would like to create a Message queue in a standalone application using JMS Queue. I am not using any kind of container like tomcat and JBoss. What should be the arguments passed to the initial context object.? It s completely a standalone application..
Note: If anybody wishes to give down vote for this question, please give the reason in the comment and give down vote. Thanks!
InitialContext ctx = new InitialContext(?????);
Queue queue = (Queue) ctx.lookup("queue/queue1");
QueueConnectionFactory connFactory = (QueueConnectionFactory) ctx.lookup("queue/connectionFactory");
QueueConnection queueConn = connFactory.createQueueConnection();
QueueSession queueSession = queueConn.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);
QueueSender queueSender = queueSession.createSender(queue);
queueSender.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
TextMessage message = queueSession.createTextMessage("Hello");
queueSender.send(message);
System.out.println("sent: " + message.getText());
queueConn.close();
You can't resolve the connectionFactory via jndi as there are no container to provide it.
You will have to instantiate the connectionFactory yourself providing the necessary (transport) parameters.
As you don't retrieve it from a Java EE container this behavior is not covered by the related JSR and is so provider specific.
below an example using HornetQ :
// Transport parameters
final Map< String, Object > connectionParams = new HashMap< String, Object >();
connectionParams.put(TransportConstants.PORT_PROP_NAME, port);
connectionParams.put(TransportConstants.HOST_PROP_NAME, host);
final TransportConfiguration transportConfiguration = new TransportConfiguration(
NettyConnectorFactory.class.getName(), connectionParams);
// this should be created only once and reused for the whole app lifecycle
connectionFactory = (ConnectionFactory) org.hornetq.api.jms.HornetQJMSClient
.createConnectionFactoryWithoutHA(JMSFactoryType.QUEUE_CF, transportConfiguration);
final jmsQueue = HornetQJMSClient.createQueue(queueName)
try {
// connection is thread safe
Connection connection = null;
// session is not
Session session = null;
connection = connectionFactory.createConnection(user, password);
connection.start();
/* following objects must be propper to a thread (but should be reused if possible) */
// Create a non transacted Session (no XA support outside of Java EE container)
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
final MessageProducer producer = session.createProducer(jmsQueue);
final ObjectMessage objectMessage = session.createObjectMessage();
objectMessage.setObject(myMessageSerializableObject);
producer.send(objectMessage);
}
finally {
// Release resources
try {
if (session != null) {
session.close();
}
if (connection != null) {
connection.close();
}
}
catch (final JMSException e) {
LOG.warn("An error occurs while releasing JMS resources", e);
}
}
Note that connections, sessions and producers should be reused (not created and released for each use but not shared between threads) and ideally pooled.
See https://developer.jboss.org/wiki/ShouldICacheJMSConnectionsAndJMSSessions

Transacted session with message listener, messages not cosumed

I am using Websphere java classes to implement jms in my application.
Sender code :
MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setHostName(environment.getProperty(MQ_CONNECTION_HOSTNAME));
connectionFactory.setPort(Integer.parseInt(environment.getProperty(MQ_CONNECTION_PORT)));
connectionFactory.setQueueManager(environment.getProperty(MQ_CONNECTION_QMANAGER));
connectionFactory.setChannel(environment.getProperty(MQ_CONNECTION_CHANNEL));
connectionFactory.setTransportType(1);
final String username = environment.getProperty(MQ_CONNECTION_USERNAME);
final String password = environment.getProperty(MQ_CONNECTION_PASSWORD);
MQQueueConnection connection = null;
if(username != null && username.trim().length() > 0 && password != null && password.trim().length() > 0) {
connection = (MQQueueConnection) connectionFactory.createQueueConnection(username, password);
}
else {
connection = (MQQueueConnection) connectionFactory.createQueueConnection();
}
senderSession = (MQQueueSession) connection.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
MQQueue queue = (MQQueue) senderSession.createQueue("queue:///" + environment.getProperty(MQ_CONNECTION_QUEUE));
MQQueueSender sender = (MQQueueSender) senderSession.createSender(queue);
JMSMessage message = (JMSMessage)senderSession.createTextMessage(messageContent);
connection.start();
sender.send(message);
message.acknowledge()
Receiver Code :
final MQQueueConnection connection = (MQQueueConnection) (useAuth ? connectionFactory.createQueueConnection(username, password) : connectionFactory.createQueueConnection());
connection.start();
final MQQueueSession receiverSession = (MQQueueSession) connection.createQueueSession(true, Session.CLIENT_ACKNOWLEDGE);
final MQQueue queue = MQQueue)receiverSession.createQueue(queueName);
MQQueueReceiver receiver = createReceiver(session, queue);
receiver.setMessageListener(listener);
When I send the message using these settings message listener never receives any message.
But when i open both the sessions with transacted as false, everything seems to work fine.
I can understand the reason.I want to have transactional session.
Let me know in case any other details are required
If you want to use a local transaction (syncpoint) then after you send the message you need to commit it.
i.e.
senderSession.commit();
message.Acknowledge() from sender side is not required. It should only be used on the receiver side and in a session with acknowledge type ClientAcknowledge. This call will notify the messaging provider (MQ in this case) to remove the current and all messages received earlier from a queue.

How to kill consumers in activemq

I am trying to get rid of all of the "Number of Consumers" in a certain queue. Whenever I purge/delete the queue, the number of consumers still remain if I ever create that queue with the same name again. Even with 0 pending messages, there are still 6 consumers.
My problem may have stemmed in my java code while not closing the session or connection.
I have tried both restarting and reinstalling the server.
Here is my producer code:
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
public static String addElementToQueue(String queueName,String param1, String param2) throws JMSException, NamingException {
// Getting JMS connection from the server and starting it
ConnectionFactory connectionFactory =
new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
// JMS messages are sent and received using a Session. We will
// create here a non-transactional session object. If you want
// to use transactions you should set the first parameter to 'true'
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
// Destination represents here our queue on the
// JMS server. You don't have to do anything special on the
// server to create it, it will be created automatically.
Destination destination = session.createQueue(queueName);
// MessageProducer is used for sending messages (as opposed
// to MessageConsumer which is used for receiving them)
MessageProducer producer = session.createProducer(destination);
String queueMessage = param1+ "-" + param2;
TextMessage message = session.createTextMessage(queueMessage);
// Here we are sending the message!
producer.send(message);
connection.close();
session.close(); // added after problem came up
producer.close(); // added after problem came up
return commandID;
}
Here is my consumer code:
// URL of the JMS server
private static String url = ActiveMQConnection.DEFAULT_BROKER_URL;
public static Pair consumeNextElement(String queueName) throws JMSException {
// Getting JMS connection from the server
ConnectionFactory connectionFactory
= new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
// Creating session for seding messages
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
// Getting the queue
Destination destination = session.createQueue(queueName);
// MessageConsumer is used for receiving (consuming) messages
MessageConsumer consumer = session.createConsumer(destination);
// Here we receive the message.
// By default this call is blocking, which means it will wait
// for a message to arrive on the queue.
Message message = consumer.receive();
// There are many types of Message and TextMessage
// is just one of them. Producer sent us a TextMessage
// so we must cast to it to get access to its .getText()
// method.
String[] parts = ((TextMessage)message).getText().split("-");
Pair retVal = new Pair(parts[0], parts[1]);
connection.close();
session.close(); // added after problem came up
consumer.close(); // added after problem came up
return retVal;
}
Any thoughts?
Thanks.
The number of consumers is the number of listeners on the queue. Purging the queue should only remove the enqueued messages - those consumers listening will be unaffected.
The ability of the consumer to maintain/re-establish a connection may depend on the transport used to connect, and settings for the transport may allow for some tweaking of connection properties.
I frankly don't have much experience with these, but you might investigate Advisory Messages as a means to help debug your connections. The JMX interface or web console don't appear to be helpful beyond reporting consumer counts.

Categories