My aim is to put n number of messages in a for loop to a WebSphere MQ queue using WebSphere MQ java programming.
My java program will run as a standalone program.
If any exception in between , I need to rollback all the messages.
If no exception then I should commit all the messages .
The outside world should not see my messages in the queue until I complete fully.
How do I achieve this?
Updated with sample code as per reply from T.Rob:
Please check if sample code is fine ?
Does setting MQGMO_SYNCPOINT is only related to my program's invocation ?
(because similar programs running parallely will also be putting messages on the same queue and those messages should not gett affected by my program's SYNCPOINT.)
public void sendMsg() {
MQQueue queue = null;
MQQueueManager queueManager = null;
MQMessage mqMessage = null;
MQPutMessageOptions pmo = null;
System.out.println("Entering..");
try {
MQEnvironment.hostname = "x.x.x.x";
MQEnvironment.channel = "xxx.SVRCONN";
MQEnvironment.port = 9999;
queueManager = new MQQueueManager("XXXQMANAGER");
int openOptions = MQConstants.MQOO_OUTPUT;
queue = queueManager.accessQueue("XXX_QUEUENAME", openOptions, null, null, null);
pmo = new MQPutMessageOptions();
pmo.options = CMQC.MQGMO_SYNCPOINT;
String input = "testing";
System.out.println("sending messages....");
for (int i = 0; i < 10; i++) {
input = input + ": " + i;
mqMessage = new MQMessage();
mqMessage.writeString(input);
System.out.println("Putting message: " + i);
queue.put(mqMessage, pmo);
}
queueManager.commit();
System.out.println("Exiting..");
} catch (Exception e) {
e.printStackTrace();
try {
System.out.println("rolling back messages");
if (queueManager != null)
queueManager.backout();
} catch (MQException e1) {
e1.printStackTrace();
}
} finally {
try {
if (queue != null)
queue.close();
if (queueManager != null)
queueManager.close();
} catch (MQException e) {
e.printStackTrace();
}
}
}
WMQ supports both local and global (XA) units of work. The local units of work are available simply by specifying the option. Global XA transactions require a transaction manager, as mentioned by keithkreissl in another answer.
For what you described, a POJO doing messaging under syncpoint, specify MQC.MQGMO_SYNCPOINT in your MQGetMessageOptions. When you are ready to commit, issue the MQQManager.commit() or MQQManager.backout() call.
Note that the response and doc provided by ggrandes refers to the JMS and not Java classes. The Java classes use Java equivalents of the WMQ procedural API, can support many threads (doc) and even provide connection pooling (doc). Please refer to the Java documentation rather than the JMS documentation for the correct behavior. Also, I've linked to the WMQ V7.5 documentation which goes with the latest WMQ Java V7.5 client. The later clients have a lot more local functionality (tracing, flexible install path, MQClient.ini, etc.) and work with back-level QMgrs. It is highly recommended to be using the latest client and the download is free.
you only need to create a session with transaction enabled.
Session session;
// ...
boolean transacted = true;
session = connection.createSession(transacted, Session.AUTO_ACKNOWLEDGE);
try {
// ...do things...
session.commit();
} catch (Exception e) {
session.rollback();
}
// ...
WARN-NOTE: Sessions are not thread-safe ;-)
Doc Websphere MQ/JMS
If you have access to a transaction manager and more importantly an XATransaction wired up to your MQ access, you can start a transaction at the beginning of your message processing put all the messages on the queue then commit the transaction. Using the XATransactions it will not put any messages until the transaction commits. If you don't have access to that, you can do a little more plumbing by placing your messages in a local data object, wrap your code in a try/catch if no exceptions iterate through the local data object sending the messages. The issue with the later approach is that it will commit all your other processing but if a problem occurs in the sending of messages your other processing will not be rolled back.
Related
I intend to use Smack to send messages through Firebase CCS. I modified a simple CCS client for my Web App but when I try to make connection, it results in exception.
I am using Smack 4.2.0
Here is the process of connection.
1) The connection method which is in my client:
public void connect() throws XMPPException{
try{
config = XMPPTCPConnectionConfiguration.builder()
.setPort(Config.FCM_PORT)
.setHost("fcm-xmpp.googleapis.com")
.setXmppDomain("googleapis.com")
.setSecurityMode(/*Default; Explicit setting for emphasis*/SecurityMode.ifpossible)
.setSendPresence(true)
.setUsernameAndPassword(fcmServerUsername, mApiKey)
.setSocketFactory(SSLSocketFactory.getDefault())
.setDebuggerEnabled(mDebuggable)/* Launch a window with info about packets sent and received */
.build();
}
catch(XmppStringprepException e){
e.printStackTrace();
}
connection = new XMPPTCPConnection(config);
// Configuring Automatic reconnection
ReconnectionManager manager = ReconnectionManager.getInstanceFor(connection);
manager.setReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.RANDOM_INCREASING_DELAY);
manager.enableAutomaticReconnection();
// Connect now then login
try{
connection.connect();
connection.login();
}
// TODO: Handle the exceptions if possible appropriately
catch(SmackException sme){
logger.severe(sme.getMessage());
sme.printStackTrace();
}
catch(IOException ioe){
logger.severe(ioe.getMessage());
ioe.printStackTrace();
}
catch(InterruptedException ie){
logger.severe("Connection got interrupted!!");
ie.printStackTrace();
}
}
2) I traced the exception and I got it here: (Smack's source)
At the line - HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode());
// AbstractXMPPConnection.java
protected List<HostAddress> populateHostAddresses() {
List<HostAddress> failedAddresses = new LinkedList<>();
if (config.hostAddress != null) {
hostAddresses = new ArrayList<>(1);
HostAddress hostAddress = new HostAddress(config.port, config.hostAddress);
hostAddresses.add(hostAddress);
}
else if (config.host != null) {
hostAddresses = new ArrayList<HostAddress>(1);
HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode());
if (hostAddress != null) {
hostAddresses.add(hostAddress);
}
} else {
// N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName
hostAddresses = DNSUtil.resolveXMPPServiceDomain(config.getXMPPServiceDomain().toString(), failedAddresses, config.getDnssecMode());
}
// Either the populated host addresses are not empty *or* there must be at least one failed address.
assert(!hostAddresses.isEmpty() || !failedAddresses.isEmpty());
return failedAddresses;
}
The exception is NullPointerException and I found that getDNSResolver() returns null. Of all the sources I have referenced, there wasn't anything related to DNS resolver as it is supposed to be internally handled by Smack. So my question is, have I missed out some crucial configuration or step in making the connection?
EDIT: I asked here because Smack is vast lib and there might some config someone knows that I might have missed. I am unable to set DNSResolver directly
EDIT : ANSWER UPDATE
This is NOT a bug in Smack's source as their Upgrade Guide for 4.2.0 explicitly mentions:
**
API Changes
**
Warning: This list may not be complete
Introduced ConnectionConfiguration.setHostAddress(InetAddress)
In previous versions of Smack,
ConnectionConfiguration.setHost(String) could be used to set the
XMPP service's host IP address. This is no longer possible due to the
added DNSSEC support. You have to use the new connection configuration
ConnectionConfiguration.setHostAddress(InetAddress) instead.
This seems to be a bug because I solved it by providing the Host Address (which was supposed to be inferred from {Host, Domain}). So, how did I know to provide the host address?
The trick lies here: (Smack' source)
// AbstractXMPPConnection.java
if (config.hostAddress != null) {
hostAddresses = new ArrayList<>(1);
HostAddress hostAddress = new HostAddress(config.port, config.hostAddress);
hostAddresses.add(hostAddress);
}
else if (config.host != null) {
hostAddresses = new ArrayList<HostAddress>(1);
HostAddress hostAddress = DNSUtil.getDNSResolver().lookupHostAddress(config.host, config.port, failedAddresses, config.getDnssecMode());
if (hostAddress != null) {
hostAddresses.add(hostAddress);
}
} else {
// N.B.: Important to use config.serviceName and not AbstractXMPPConnection.serviceName
hostAddresses = DNSUtil.resolveXMPPServiceDomain(config.getXMPPServiceDomain().toString(), failedAddresses, config.getDnssecMode());
}
You can see the if, else-if blocks here and since the exception arises in the else if (config.host != null) block, I provided hostAddress so that it never enters that block and it worked.
I know this is sort of a hack around the actual problem but this seems to be a bug in Smack 4.2.0 unless someone disproves me otherwise.
Bonus info: If after rectifying this problem, you get another exception in Base 64 encoding during login, refer to this - XMPP client using Smack 4.1 giving NullPointerException during login
Not sure in 4.2.0 but in 4.2.2 (and newer), you will need smack-resolver-dnsjava-4.2.2.jar to be in your classpath, smack calls DNSUtil which is included in the package, if the class doesn't exist it returns NullPointerException.
Hope this help!
David
I try to implement a queue browser for ActiveMQ.
The code shown below should display the text messages in the queue named 'Q1'. There are two messages in there. In general it works but the last e.hasMoreElements() call needs up to 20 seconds. I wanted to update the list every 500 millis. Why is that so slow?When i press 'update' in the browser view for http://localhost:8161/admin/browse.jsp?JMSDestination=Q1 e.hasMoreElements() returns immediately. What's going on here? How to achieve a 'realtime' view?
//init:
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE);
Queue queue = session.createQueue("Q1");
boolean run = true;
while (run) {
LOG.info("--------------------------------------------------------------------------------");
QueueBrowser browser = session.createBrowser(queue);
Enumeration e = browser.getEnumeration();
while (e.hasMoreElements()) { //<- very slow before returning false after last message. why?
Object o = e.nextElement();
if (o instanceof ActiveMQTextMessage) {
LOG.info(((ActiveMQTextMessage) o).getText());
} else {
LOG.info(o.toString());
}
}
Thread.sleep(500);
browser.close();
}
session.close();
connection.close();
After my previous comment, I've discovered that calling setTransactedIndividualAck(true) on the connection factory solves the problem
ActiveMQConnectionFactory cf2 = new ActiveMQConnectionFactory(...);
cf2.setTransactedIndividualAck(true);
I'm not sure that this is the right thing to do for the problem but at least it works now. See the message on the ActiveMQ user forum here:
http://activemq.2283324.n4.nabble.com/JMS-browser-getEnumeration-hasMoreElements-takes-15s-on-last-element-td4709969.html
I had the same issue. But changing the acknowledgment on the session eliminated the delay.
Try this:
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
I found that calling Session.commit() within my hasMoreElements() loop stopped the hanging using activemq-broker version 5.14.5:
while(enumeration.hasMoreElements()) {
final Message message = (Message)enumeration.nextElement();
final TextMessage textMessage = (TextMessage)message;
session.commit();
}
I did more research to see if this was a bug with ActiveMQ or not and found that activemq-broker version 5.15.1 did not hang, even without calling commit() after each iteration. All prior versions of the broker hanged on the final call to hasMoreElements(). It doesn't seem like the contributors deliberately fixed this particular issue, since the bug report on JIRA that the change referenced was for something different. The change that fixed this issue changed part of the iterate() method of the org.apache.activemq.broker.region.Queue class from:
// are we done browsing? no new messages paged
if (!added || browser.atMax()) {
browser.decrementQueueRef();
browserDispatches.remove(browserDispatch);
}
to
// are we done browsing? no new messages paged
if (!added || browser.atMax()) {
browser.decrementQueueRef();
browserDispatches.remove(browserDispatch);
} else {
wakeup();
}
To confirm this was the change that fixed the issue, I went to the previous version, 5.15.0 and forced wakeup() to be called using a debugger and the call to hasMoreElements() did not hang.
After some research and try i switched to the more up-to-date JMX technology. There are no performance issues while walking through the queues.
Some code:
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
JMXConnector connector = JMXConnectorFactory.connect(url, null);
connector.connect();
connection = connector.getMBeanServerConnection();
ObjectName name = new ObjectName(getObjectNameByBrokerName(brokerName));
brokerMBean = (BrokerViewMBean) MBeanServerInvocationHandler.newProxyInstance(connection, name, BrokerViewMBean.class, true);
ObjectName[] objNames = brokerMBean.getQueues();
for (ObjectName objName : objNames) {
QueueViewMBean queueMBean = (QueueViewMBean) MBeanServerInvocationHandler.newProxyInstance(connection, objName, QueueViewMBean.class, true);
System.out.println(queueMBean.getName());
}
You have to activate jmx in the configuration. It's de-activated by default.
My client-server app works with Apache MINA at both, client and server sides. Sending data via UDP works OK, but after a minute server closes the connection (or MINA's way - "session") and stops answering.
The strange part is that the connection is active the whole time. Client is sending data every 1000ms and server answers to it with the same data. I've found a MINA's mechanism to destroying inactive sessions ExpiringMap, it's got a default value for session's time-to-live public static final int DEFAULT_TIME_TO_LIVE = 60; but I haven't found a way how to change it or better, update time-to-live for sessions.
Imho the time-to-live should update automatically with every incoming packet but I couldn't find a thing why isn't it my server doing. Should I say explicitly that I don't want it to destroy the session yet or what?
My code is quite similar to MINA's tutorials:
SERVER
IoAcceptor acceptor = new NioDatagramAcceptor();
try {
acceptor.setHandler( new UDPHandler() );
acceptor.bind( new InetSocketAddress(RelayConfig.getInstance().getUdpPort()) );
acceptor.getSessionConfig().setReadBufferSize( 2048 );
acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, IDLE_PERIOD );
System.out.println("RELAY ["+RelayConfig.getInstance().getId()+"]: initialized!");
} catch (IOException e) {
System.out.println("RELAY ["+RelayConfig.getInstance().getId()+"]: failed: "+e.getLocalizedMessage());
//e.printStackTrace();
}
CLIENT
NioDatagramConnector connector = new NioDatagramConnector();
connector.getSessionConfig().setUseReadOperation(true);
handler = new UDPHandler();
connector.setHandler(handler);
connector.getSessionConfig().setReadBufferSize(2048);
// try to connect to server!
try {
System.out.println("Connecting to " + relayIP + ":" + port);
ConnectFuture future = connector.connect(new InetSocketAddress(relayIP, port));
future.addListener(new IoFutureListener<IoFuture>() {
public void operationComplete(IoFuture future) {
ConnectFuture connFuture = (ConnectFuture)future;
if( connFuture.isConnected() ){
UDPClient.setSession(future.getSession());
Timer timer = new Timer("MyTimerTask", true);
timer.scheduleAtFixedRate(new MyTimerTask(), 1000, 1000); // My message is written here every 1000ms
} else {
log.error("Not connected...exiting");
}
}
});
future.awaitUninterruptibly();
} catch (RuntimeIoException e) {
System.err.println("Failed to connect.");
e.printStackTrace();
System.exit(1);
} catch (IllegalArgumentException e) {
System.err.println("Failed to connect. Illegal Argument! Terminating program!");
e.printStackTrace();
System.exit(1);
}
For any additional info please write in comments.
EDIT: Unfortunately I don't have access to that server any more, but problem was not solved back then. If there's anybody else who has the same problem and solved it, let us know.
I did some research and found the link below. You may need to explicitly set the disconnect option to false, but there is also another option to reset the timeout option. A timeout of 30000 is 30 seconds, 60000 is 60 seconds, etc... These solutions are from MINA2. It was not clear if you were using that or an older version. From this you should be able to add the call that implements a specific set of options when you open the UDP port.
MINA2 Documentation
I am constructing a messaging system using MQSeries. For some reason, when I perform q.get(...), I am getting an exception thrown (I don't know the specific MQException). Below is the code causing the error:
private static MQGetMessageOptions GMO = new MQGetMessageOptions();
private static int GMO_OPTIONS = MQC.MQGMO_SYNCPOINT | MQC.MQGMO_WAIT;
GMO.options = GMO.options | GMO_OPTIONS;
GMO.waitInterval = MQC.MQWI_UNLIMITED;
MQEnvironment.hostname = args[0];
MQEnvironment.channel = args[2];
MQEnvironment.port = Integer.parseInt(args[1]);
MQQueueManager queueManager = new MQQueueManager(args[3])
MQMessage msg = new MQMessage();
MQQueue q = queueManager.accessQueue("qName1",MQC.MQOO_OUTPUT);
q.get(msg, GMO);
My plan is, when this error occurs, skip the message and delete it. To perform the delete I will call the following function:
private void deleteMsg(MQQueueManager queueManager, String queueName) throws MQException {
MQGetMessageOptions tempGmo = new MQGetMessageOptions();
tempGmo.options |= MQC.MQGMO_WAIT;
tempGmo.waitInterval = 1000;
MQQueue remover = queueManager.accessQueue(queueName, MQC.MQOO_INPUT_AS_Q_DEF);
remover.get(new MQMessage(), tempGmo);
queueManager.commit();
}
Would the remover.get() in my deleteMsg function also, in this specific scenario, fail for the same reason? Or does the option used to construct the MQQueue(MQC.MQOO_INPUT_AS_Q_DEF vs MQC.MQOO_OUTPUT) prevent it from also failing? If I am having trouble accessing my queue's message, how do I discard the top message and move to the next?
To shorten my question:
If I am unable to perform a get() on a given queue to retrieve a message, how can we delete that corrupt message on the same queue?
Thank you!
OMG!
MQQueue q = queueManager.accessQueue("qName1",MQC.MQOO_OUTPUT);
q.get(msg, GMO);
Your are opening a queue for output (writing) but you are trying to get a message. You have your shoes on the wrong feet!! Secondly, why aren't you catching the MQException that MQ would be throwing?? The exception would have included the reason code which would have given you the exact explanation to your issue.
Here's how you should be opening the queue for reading:
try
{
int oo = MQC.MQOO_INPUT_SHARED + MQC.MQOO_FAIL_IF_QUIESCING;
MQQueue q = queueManager.accessQueue("qName1",oo);
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.options = MQC.MQGMO_NO_WAIT + MQC.MQGMO_FAIL_IF_QUIESCING;
q.get(msg, gmo);
}
catch (MQException e)
{
System.err.println(e.getLocalizedMessage() );
System.err.println("CC = " + e.completionCode + " - RC = " + e.reasonCode);
}
Also, make sure you use the appropriate "Fail if quiescing" option for the particular MQ API call.
Finally, look up "backout queue". If your application is having an issue with a message then the message should be moved to a backout queue and not simply deleted.
I do not why what you are doing does not work for you but I wonder why are you using proprietary API of MQ Series instead of using JMS API. In JMS terms remove top message just means receive the message, so call of session.receieve() does the work.
Using common JMS API has a lot of advantages. The main of them is that you can easily move from MQ Series to any other messaging solution without changing even one line of your code.
I wonder if the program compiled because there is no option called GMO_OPTIONS. All MQ constants are prefixed MQC
Is there a way to remove a JMS message from an IBM MQ Queue using JMSMessageId ina Java application(not using tools)? Also are such operations vendor-specific?
Looked through the API for receive operations which are used to remove messages, but for removing specific messages, do we need to filter using MessageSelector and remove appropriately, or is there a more simple way? [checking for any available method which can be directly used]
Can you please provide tutorials/examples [can be links too] to show the API usage for such operations?
When you use JMSMessageID as the only message property in a selector, WMQ optimizes the lookup to be the same as a native WMQ API get by MQMD.MessageID which is an indexed field in the queue. Please see the JMS Message Selection topic for more details.
QueueReceiver rcvr = sess.createReceiver(inputQ, "JMSCorrelationID = '"+msgId+"'")
You can also do the same thing using native WMQ API calls using Java native code. You would do a normal GET operation but specify the message ID in the MQMD structure.
myMsg.messageId = someMsgID;
MQGetMessageOptions gmo = new MQGetMessageOptions();
myQueue.get(myMsg, gmo);
How to delete specific message form queue by using messageid?
I also have like your problem, I provide the resuable function. You just need to pass MessageId and Queue name. It is ok for me.
private void deleteMessage(String messageId, String queueName) {
try {
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url);
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
ObjectName name = new ObjectName("org.apache.activemq:type=Broker,brokerName=localhost");
BrokerViewMBean proxy = (BrokerViewMBean)MBeanServerInvocationHandler.newProxyInstance(conn, name, BrokerViewMBean.class, true);
for (ObjectName queue : proxy.getQueues()) {
QueueViewMBean queueBean = (QueueViewMBean) MBeanServerInvocationHandler.newProxyInstance(conn, queue, QueueViewMBean.class, true);
if(queueBean.getName().equals(queueName)) {
System.out.println("Deleted : " + messageId);
queueBean.removeMessage(messageId);
return;
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
I use activemq-all-5.8.0.jar.