I am trying to set the Accounting Token on the a message which my system received from inbound queue. I am setting this token using the following.
msg.setObjectProperty(JmsConstants.JMS_IBM_MQMD_ACCOUNTINGTOKEN,value)
Also I have the following properties in my JmsSUpport class
((JmsDestination) dest).setBooleanProperty(WMQConstants.WMQ_MQMD_READ_ENABLED, true);
((JmsDestination) dest).setBooleanProperty(WMQConstants.WMQ_MQMD_WRITE_ENABLED, true);
((MQDestination) dest).setMQMDWriteEnabled(true);
((MQDestination) dest).setMQMDReadEnabled(true);
((MQDestination) dest).setTargetClient(JMSC.MQJMS_CLIENT_NONJMS_MQ);
Since the outbound queue is MQDestination I have to set the above properties. Now I am observing that I am correctly setting the value on the outbound message. But when the other application reads the message from the MQ it has default value for Accounting Token. Could this be because the MQ is configured in such way to override the value? Or is it because the other application is not reading MQ message correctly? Or should I be using any other property to enable accounting token?
Is it because I am setting ((MQDestination) dest).setTargetClient(JMSC.MQJMS_CLIENT_NONJMS_MQ); the msg gets stripped of MQMD Headers?
No, MQMD is never stripped off. You have to set identity context on the destination before sending a message. Otherwise queue manager will ignore the Accounting token. Please see the sample code:
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import com.ibm.msg.client.jms.JmsConnectionFactory;
import com.ibm.msg.client.jms.JmsFactoryFactory;
import com.ibm.msg.client.jms.JmsDestination;
import com.ibm.msg.client.wmq.WMQConstants;
import com.ibm.msg.client.jms.JmsConstants;
import com.ibm.mq.jms.MQDestination;
public class AccountingTokenDemo {
public static void main(String[]args) {
// TODO Auto-generated method stub
AccountingTokenDemo demo = new AccountingTokenDemo();
demo.putMessageWithAccountingToken();
}
public void putMessageWithAccountingToken() {
JmsConnectionFactory cf = null;
Connection connection = null;
Session session = null;
Destination reqQ = null;
MessageProducer producer = null;
try {
// Create a connection factory
JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
cf = ff.createConnectionFactory();
// Set the properties
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_BINDINGS);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, "QM2");
// Create JMS objects
connection = cf.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create a 32 byte accounting toke
byte [] accountingToken = new byte[32];
byte b = 'a';
for(int i=0; i < 32;i++)
accountingToken[i] = b++;
// Create destination to send requests
reqQ = session.createQueue("queue:///REQUESTQ");
((MQDestination) reqQ).setTargetClient(WMQConstants.WMQ_CLIENT_NONJMS_MQ);
((JmsDestination) reqQ).setBooleanProperty(WMQConstants.WMQ_MQMD_READ_ENABLED, true);
((JmsDestination) reqQ).setBooleanProperty(WMQConstants.WMQ_MQMD_WRITE_ENABLED, true);
((MQDestination) reqQ).setMQMDMessageContext(WMQConstants.WMQ_MDCTX_SET_IDENTITY_CONTEXT);
// Create producer
producer = session.createProducer(reqQ);
// Create a request message
Message requestMessage = session.createTextMessage("Setting Accounting token on message");
requestMessage.setObjectProperty(JmsConstants.JMS_IBM_MQMD_ACCOUNTINGTOKEN, accountingToken);
// Send it off
producer.send(requestMessage);
}catch(Exception ex){
System.out.println(ex);
}
}
}
Related
I'm using spring boot with mq-jms-spring-boot-starter to create a JMS Listener application which reads a message from a queue, process it and forward the message in to another queue.
In case of a poison message scenario, I'm trying to generate an alert. However, in order to not generate multiple alerts to the same message, I'm thinking of comparing the JMSXDeliveryCount with BOTHRESH value and generate the alert in the last redelivery before sending to the BOQ.
BOTHRESH and BOQNAME are configured for the source queue.
#JmsListener(destination = "${sourceQueue}")
public void processMessages(Message message) {
TextMessage msg = (TextMessage) message;
int boThresh;
int redeliveryCount;
try {
boThresh = message.getIntProperty("<WHAT COMES HERE>");
redeliveryCount = message.getIntProperty("JMSXDeliveryCount");
String processedMessage = this.processMessage(message);
this.forwardMessage("destinationQueue", processedMessage);
} catch (Exception e) {
if (redeliveryCount >= boThresh) {
//generate alert here
}
}
}
How should I get the value of BOTHRESH here? Is it possible at all? I tried to get all the available properties using getPropertyNames() method and following are all the properties I see.
JMS_IBM_Format
JMS_IBM_PutDate
JMS_IBM_Character_Set
JMSXDeliveryCount
JMS_IBM_MsgType
JMSXUserID
JMS_IBM_Encoding
JMS_IBM_PutTime
JMSXAppID
JMS_IBM_PutApplType
This will do it, but the code does need admin access to an admin channel, which may not be optimal for a client application.
The Configuration
import com.ibm.mq.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ibm.mq.constants.CMQC;
import java.util.Hashtable;
#Configuration
public class MQConfiguration {
protected final Log logger = LogFactory.getLog(getClass());
#Value("${ibm.mq.queueManager:QM1}")
public String qMgrName;
#Value("${app.mq.admin.channel:DEV.ADMIN.SVRCONN}")
private String adminChannel;
#Value("${app.mq.host:localhost}")
private String host;
#Value("${app.mq.host.port:1414}")
private int port;
#Value("${app.mq.adminuser:admin}")
private String adminUser;
#Value("${app.mq.adminpassword:passw0rd}")
private String password;
#Bean
public MQQueueManager mqQueueManager() {
try {
Hashtable<String,Object> connectionProperties = new Hashtable<String,Object>();
connectionProperties.put(CMQC.CHANNEL_PROPERTY, adminChannel);
connectionProperties.put(CMQC.HOST_NAME_PROPERTY, host);
connectionProperties.put(CMQC.PORT_PROPERTY, port);
connectionProperties.put(CMQC.USER_ID_PROPERTY, adminUser);
connectionProperties.put(CMQC.PASSWORD_PROPERTY, password);
return new MQQueueManager(qMgrName, connectionProperties);
} catch (MQException e) {
logger.warn("MQException obtaining MQQueueManager");
logger.warn(e.getMessage());
}
return null;
}
}
Obtain the Queue's backout threshold
import com.ibm.mq.*;
import com.ibm.mq.constants.CMQC;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
#Component
public class Runner {
protected final Log logger = LogFactory.getLog(getClass());
#Value("${app.mq.queue:DEV.QUEUE.1}")
private String queueName = "";
private final MQQueueManager mqQueueManager;
Runner(MQQueueManager mqQueueManager) {
this.mqQueueManager = mqQueueManager;
}
#Bean
CommandLineRunner init() {
return (args) -> {
logger.info("Determining Backout threshold");
try {
int[] selectors = {
CMQC.MQIA_BACKOUT_THRESHOLD,
CMQC.MQCA_BACKOUT_REQ_Q_NAME };
int[] intAttrs = new int[1];
byte[] charAttrs = new byte[MQC.MQ_Q_NAME_LENGTH];
int openOptions = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_INQUIRE | MQC.MQOO_SAVE_ALL_CONTEXT;
MQQueue myQueue = mqQueueManager.accessQueue(queueName, openOptions, null, null, null);
logger.info("Queue Obtained");
MQManagedObject moMyQueue = (MQManagedObject) myQueue;
moMyQueue.inquire(selectors, intAttrs, charAttrs);
int boThresh = intAttrs[0];
String backoutQname = new String(charAttrs);
logger.info("Backout Threshold: " + boThresh);
logger.info("Backout Queue: " + backoutQname);
} catch (MQException e) {
logger.warn("MQException Error obtaining threshold");
logger.warn(e.getMessage());
}
};
}
}
This sounds like you are mixing retriable and non-retriable error handling.
If you are tracking redelivers and need to send an alert, then you probably do not want to set a BOTHRESH value, and instead manage it all in your client-side code.
Recommended consumer error handling pattern:
If the message is invalid (ie.. bad JSON or XML) move to DLQ immediately. The message will never improve in quality and there is no reason to do repeated retries.
If the 'next step' in processing is down (ie. the database) reject delivery and allow redelivery delays and backout retries to kick in. This also has the benefit of allowing other consumers on the queue to attempt processing the message and eliminates the problem where one consumer has a dead path from holding up a messages.
Also, consider that using client-side consumer code to do monitoring and alerting can be problematic, since it combines different functions. If your goal is to track invalid messages, monitoring the DLQ is generally a better design pattern and it removes 'monitoring' code from your consumer code.
How do I retrieve a message that is in the JMS Queue? I was looking at this solution: Jboss Messaging JMS, but that just sends and receives right away. If the server restarted, how can I retrieve the message with Java code?
I haven't found anything online; unless, of course, I totally misunderstood how JMS Queues work.
The current version of JBoss uses HornetQ for JMS messaging.
If you are using Java JMS client code your are and attaching to a clustered messaging broker instance, your client should failover to another node.
In a single instance broker configuration, you will get a JMS exception in the client code. This means that you will need to get a new connection and start a new session.
For browsing a queue:
/**
* QueueBrowserGss.java
*
* Created on Sep 24, 2012, 3:52:28 PM
*
* To the extent possible under law, Red Hat, Inc. has dedicated all copyright to this
* software to the public domain worldwide, pursuant to the CC0 Public Domain Dedication. This
* software is distributed without any warranty.
*
* See <http://creativecommons.org/publicdomain/zero/1.0/>.
*
*/
package com.redhat.gss.client;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import javax.jms.JMSException;
import javax.jms.Queue;
import javax.jms.QueueBrowser;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* #author grovedc
*
*/
public class QueueBrowserGss {
private static final Log logger = LogFactory.getLog(QueueBrowserGss.class);
#SuppressWarnings("unchecked")
public static void main(String[] args) {
QueueConnection queueConnection = null;
Queue queue = null;
QueueConnectionFactory queueConnFactory = null;
QueueBrowser queueBrowser = null;
QueueSession queueSession = null;
List<Object> messages = new ArrayList<Object>(7000);
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
properties.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
// properties.put(Context.PROVIDER_URL, "jnp://" + server + ":" + port);
properties.put("Context.PROVIDER_URL", "jnp://10.0.0.150:1100");
try {
InitialContext ctx = new InitialContext(properties);
queue = (Queue) ctx.lookup("queue/D");
queueConnFactory = (QueueConnectionFactory) ctx.lookup("ConnectionFactory");
queueConnection = queueConnFactory.createQueueConnection();
queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queueBrowser = queueSession.createBrowser(queue);
queueConnection.start();
for (Enumeration<Object> e = queueBrowser.getEnumeration(); e.hasMoreElements();) {
messages.add(e.nextElement());
}
logger.info("Messages are retrieved from queue. Process completed on: " + new Date());
logger.info("Number of Messages present: " + messages.size());
} catch (Exception ex) {
logger.error(String.format("Exception Occured : %s", ex.getMessage()), ex);
} finally {
try {
if (queueConnection != null)
queueConnection.stop();
} catch (JMSException e) {
logger.error(e);
}
}
}
}
The jnp protocol and ports are for JBoss Messaging.
Depending on your use-case, you can use a tool like JMSToolBox to browse/post/delete etc.. the content of JBoss/HornetQ destinations
You have to make a QueueBrowserand enumerate through them. Example code:
// Create the connection
InitialContext context = new InitialContext(properties);
QueueConnectionFactory queueConnFactory = (QueueConnectionFactory) context.lookup("ConnectionFactory");
QueueConnection conn = queueConnFactory.createQueueConnection();
Queue queue = (Queue) context.lookup("/queue/Test");
QueueSession session = conn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
// Start the connection
conn.start()
// Create a QueueBrowser using the session
QueueBrowser queueBrowser = session.createBrowser(queue);
Enumeration e = queueBrowser.getEnumeration();
// Iterate through the queue
while(e.hasMoreElements()) {
Message msg = (Message) e.nextElement();
TextMessage txtMsg = (TextMessage) msg;
System.out.println(txtMsg.getText());
}
As this is just an example, you can change the TextMessage part to suit your needs.
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MessageSender {
public static ConnectionFactory factory = null;
public static Connection connection = null;
public static Session session = null;
public static Destination destination = null;
public static MessageProducer producer = null;
public static void sendMessage(String queueName,String messageContent) {
try {
factory = new ActiveMQConnectionFactory(
ActiveMQConnection.DEFAULT_BROKER_URL);
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination = session.createQueue(queueName);
producer = session.createProducer(destination);
TextMessage message = session.createTextMessage();
message.setText(messageContent);
producer.send(message);
System.out.println("Sent: " + message.getText() );
} catch (JMSException e) {
e.printStackTrace();
}
}
}
Above is my java JMS code to push messages to the queue. Every time I want push message to queue, I used to invoke like below
MessageSender.sendMessage("orderdetailsQueue","OrderReceivedIdB6789");
so for each message it invoke new connection.start(). How can I generalize this?
Creating connection factory and then a connection in every sendMessage is probably not a good idea.
You can move this connection factory initialization part to a singleton util class which creates the connection factory only once when that util class is instantiated. Also use the PooledConnectionFactory instead of normal connection factory which creates a pool of connections, sessions and producers. The sendMessage should get the connection by calling connectionUtil.getConnection(). The rest of the code in sendMessage looks fine except you need to release the session, producer and connection in finally by calling close() on these objects.
Pooled Connection Factory
I am using jboss 5.1.0 GA. I am trying to create a helloworldjMS.java. The code is as follows,
import java.util.Hashtable;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
public class HelloWorldJMS {
public static void main(String[] args) {
try {
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
env.put(Context.PROVIDER_URL, "jnp://localhost:1099");
env.put(Context.URL_PKG_PREFIXES,
"org.jboss.naming:org.jnp.interfaces");
// Create the initial context
Context ctx = new InitialContext(env);
// Lookup the JMS connection factory from the JBoss 5.1 object store
ConnectionFactory connectionFactory = (ConnectionFactory) ctx
.lookup("ConnectionFactory");
// Lookup a queue from the JBoss 5.1 object store
Queue queue = (javax.jms.Queue) ctx.lookup("/queue/DLQ");
// Create a connection to the JBoss 5.1 Message Service
Connection connection = connectionFactory.createConnection();
// Create a session within the connection
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
// Create a message producer to put messages on the queue
MessageProducer messageProducer = session.createProducer(queue);
// Create and send a message to the queue
TextMessage textMessage = session.createTextMessage();
textMessage.setText("Hello World");
System.out.println("Sending Message: " + textMessage.getText());
messageProducer.send(textMessage);
// Create a message consumer
MessageConsumer messageConsumer = session.createConsumer(queue);
// Start the Connection created
connection.start();
// Receive a message from the queue.
Message msg = messageConsumer.receive();
// Retrieve the contents of the message.
if (msg instanceof TextMessage) {
TextMessage txtMsg = (TextMessage) msg;
System.out.println("Read Message: " + txtMsg.getText());
}
// Close the session and connection resources.
session.close();
connection.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
I am getting the following error,
org.jboss.jms.exception.MessagingNetworkFailureException
at org.jboss.jms.client.delegate.DelegateSupport.handleThrowable(DelegateSupport.java:245)
at org.jboss.jms.client.delegate.ClientConnectionFactoryDelegate.org$jboss$jms$client$delegate$ClientConnectionFactoryDelegate$createConnectionDelegate$aop(ClientConnectionFactoryDelegate.java:187)
at org.jboss.jms.client.delegate.ClientConnectionFactoryDelegate$createConnectionDelegate_N3019492359065420858.invokeNext(ClientConnectionFactoryDelegate$createConnectionDelegate_N3019492359065420858.java)
at org.jboss.jms.client.container.StateCreationAspect.handleCreateConnectionDelegate(StateCreationAspect.java:83)
at org.jboss.aop.advice.org.jboss.jms.client.container.StateCreationAspect_z_handleCreateConnectionDelegate_328011903.invoke(StateCreationAspect_z_handleCreateConnectionDelegate_328011903.java)
at org.jboss.jms.client.delegate.ClientConnectionFactoryDelegate$createConnectionDelegate_N3019492359065420858.invokeNext(ClientConnectionFactoryDelegate$createConnectionDelegate_N3019492359065420858.java)
at org.jboss.jms.client.delegate.ClientConnectionFactoryDelegate.createConnectionDelegate(ClientConnectionFactoryDelegate.java)
at org.jboss.jms.client.JBossConnectionFactory.createConnectionInternal(JBossConnectionFactory.java:205)
at org.jboss.jms.client.JBossConnectionFactory.createConnection(JBossConnectionFactory.java:87)
at org.jboss.jms.client.JBossConnectionFactory.createConnection(JBossConnectionFactory.java:82)
at HelloWorldJMS.main(HelloWorldJMS.java:67)
Caused by: org.jboss.remoting.ConnectionFailedException: Timed out trying to create control socket
at org.jboss.remoting.transport.bisocket.BisocketClientInvoker.handleConnect(BisocketClientInvoker.java:276)
at org.jboss.remoting.MicroRemoteClientInvoker.connect(MicroRemoteClientInvoker.java:309)
at org.jboss.remoting.Client.connect(Client.java:1612)
at org.jboss.remoting.Client.connect(Client.java:515)
at org.jboss.remoting.callback.ServerInvokerCallbackHandler.connect(ServerInvokerCallbackHandler.java:168)
at org.jboss.remoting.ServerInvoker.getCallbackHandler(ServerInvoker.java:2064)
at org.jboss.remoting.ServerInvoker.handleInternalInvocation(ServerInvoker.java:1646)
at org.jboss.remoting.transport.bisocket.BisocketServerInvoker.handleInternalInvocation(BisocketServerInvoker.java:863)
at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:878)
at org.jboss.remoting.transport.socket.ServerThread.completeInvocation(ServerThread.java:744)
at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:697)
at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:551)
at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:232)
at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:163)
at org.jboss.remoting.Client.invoke(Client.java:1550)
at org.jboss.remoting.Client.addCallbackListener(Client.java:1619)
at org.jboss.remoting.Client.addListener(Client.java:913)
at org.jboss.jms.client.remoting.JMSRemotingConnection.addInvokerCallbackHandler(JMSRemotingConnection.java:230)
at org.jboss.jms.client.remoting.JMSRemotingConnection.start(JMSRemotingConnection.java:340)
at org.jboss.jms.client.delegate.ClientConnectionFactoryDelegate.org$jboss$jms$client$delegate$ClientConnectionFactoryDelegate$createConnectionDelegate$aop(ClientConnectionFactoryDelegate.java:154)
... 9 more
I have the following jars,
concurrent.jar,javassist-3.10.0.GA.jar,jboss-aop-client.jar,jboss-common-core-2.2.10.GA.jar,jboss-common.jar, jboss-j2ee.jar , jboss-mdr-2.0.1.GA.jar, jboss-messaging-client.jar, jboss-remoting.jar, jbossmq.jar, jetty-session-redis-2.3.ga-serialjboss.jar, jnpserver.jar.
I know this is very old jboss questions. But I tried various suggestions, could not find any way. So, i am posting the question. So kindly provide some suggestions.
Thanks
Remove jboss-remoting.jar and add jboss-remoting-4.2.2.GA.jar
What are the options available to develop Java applications using Service Bus for Windows?
Java Message Broker API - This need ACS to work with, which SB for Win doesnt support.
AMQP - This doesnt seem to work on SB for Windows, I keep getting error
org.apache.qpid.amqp_1_0.client.Sender$SenderCreationException: Peer did not create remote endpoint for link, target:
While the same code works with Azure SB. So AMQP on SB for Windows seems to be not fully working?
Correct me if I have missed something?
Update
To test AMQP on local machine, this is what I did
Installed Service bus 1.1 on my local machine
Took the sample mentioned here http://www.windowsazure.com/en-us/develop/java/how-to-guides/service-bus-amqp/
Created a new namespace on my local machine
Specified the following connection string in servicebus.properties (which is correctly referred in the code
connectionfactory.SBCF = amqps://<username>:<password>#<MachineName>:5671/StringAnalyzerNS/
queue.QUEUE = queue1
Code is updated with certificates.
At runtime I get this error
javax.jms.JMSException: Peer did not create remote endpoint for link, target: queue1
at org.apache.qpid.amqp_1_0.jms.impl.MessageProducerImpl.<init>(MessageProducerImpl.java:77)
at org.apache.qpid.amqp_1_0.jms.impl.SessionImpl.createProducer(SessionImpl.java:348)
at org.apache.qpid.amqp_1_0.jms.impl.SessionImpl.createProducer(SessionImpl.java:63)
at com.stringcompany.Analyzer.SimpleSenderReceiver.<init>(SimpleSenderReceiver.java:70)
at com.stringcompany.Analyzer.SimpleSenderReceiver.main(SimpleSenderReceiver.java:95)
Caused by: org.apache.qpid.amqp_1_0.client.Sender$SenderCreationException: Peer did not create remote endpoint for link, target: queue1
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:171)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:104)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:97)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:83)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:69)
at org.apache.qpid.amqp_1_0.client.Sender.<init>(Sender.java:63)
at org.apache.qpid.amqp_1_0.client.Session.createSender(Session.java:74)
at org.apache.qpid.amqp_1_0.client.Session.createSender(Session.java:66)
at org.apache.qpid.amqp_1_0.jms.impl.MessageProducerImpl.<init>(MessageProducerImpl.java:72)
... 4 more
javax.jms.JMSException: Session remotely closed
With the same code If I point to Azure service bus by setting the SB namespace and queue like below
connectionfactory.SBCF = amqps://<Policy name>:<Sec. Key>#<ns>.servicebus.windows.net
queue.QUEUE = testq
This works, messages are exchanged.
Here is the code if someone wants to try it
package com.stringcompany.Analyzer;
//SimpleSenderReceiver.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Hashtable;
import java.util.Random;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
public class SimpleSenderReceiver implements MessageListener {
private static boolean runReceiver = true;
private Connection connection;
private Session sendSession;
private Session receiveSession;
private MessageProducer sender;
private MessageConsumer receiver;
private static Random randomGenerator = new Random();
public SimpleSenderReceiver() throws Exception {
// Configure JNDI environment
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.qpid.amqp_1_0.jms.jndi.PropertiesFileInitialContextFactory");
env.put(Context.PROVIDER_URL, "D:\\Java\\Azure\\workspace\\Analyzer\\src\\main\\resources\\servicebus.properties");
Context context = new InitialContext(env);
// Lookup ConnectionFactory and Queue
ConnectionFactory cf = (ConnectionFactory) context.lookup("SBCF");
System.out.println("cf:"+cf);
// Create Connection
connection = cf.createConnection();
System.out.println("connection :"+connection);
connection.setExceptionListener(new ExceptionListener() {
public void onException(JMSException arg0) {
System.err.println(arg0);
}
});
connection.start();
// Create sender-side Session and MessageProducer
sendSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
System.out.println("Session open");
Destination queue = (Destination) context.lookup("QUEUE");
System.out.println("queue:"+queue);
sender = sendSession.createProducer(queue);
Queue q=(Queue) queue;
System.out.println(sender.getDestination());
System.out.println("sender:"+sender);
if (runReceiver) {
System.out.println("Waitng for new message");
// Create receiver-side Session, MessageConsumer,and MessageListener
receiveSession = connection.createSession(false,
Session.CLIENT_ACKNOWLEDGE);
receiver = receiveSession.createConsumer(queue);
receiver.setMessageListener(this);
connection.start();
}
}
public static void main(String[] args) {
try {
if ((args.length > 0) && args[0].equalsIgnoreCase("sendonly")) {
runReceiver = false;
}
//System.setProperty("javax.net.debug","ssl");
System.setProperty("javax.net.ssl.trustStore","D:\\Java\\Azure\\workspace\\Analyzer\\src\\main\\resources\\SBKeystore.keystore");
System.setProperty("log4j.configuration","D:\\Java\\Azure\\workspace\\Analyzer\\src\\main\\resources\\log4j.properties");
SimpleSenderReceiver simpleSenderReceiver = new SimpleSenderReceiver();
System.out
.println("Press [enter] to send a message. Type 'exit' + [enter] to quit.");
BufferedReader commandLine = new java.io.BufferedReader(
new InputStreamReader(System.in));
while (true) {
String s = "Message";//commandLine.readLine();
if (s.equalsIgnoreCase("exit")) {
simpleSenderReceiver.close();
System.exit(0);
} else {
simpleSenderReceiver.sendMessage();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void sendMessage() throws JMSException {
TextMessage message = sendSession.createTextMessage();
message.setText("Test AMQP message from JMS");
long randomMessageID = randomGenerator.nextLong() >>> 1;
message.setJMSMessageID("ID:" + randomMessageID);
sender.send(message);
System.out.println("Sent message with JMSMessageID = "
+ message.getJMSMessageID());
}
public void close() throws JMSException {
connection.close();
}
public void onMessage(Message message) {
try {
System.out.println("Received message with JMSMessageID = "
+ message.getJMSMessageID());
message.acknowledge();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Hi we had the same problems and thankfully MS updated their documentation to show how to do this correctly. :
http://msdn.microsoft.com/en-us/library/dn574799.aspx
The simplest answer to the question is as you should URL Encode the SASPolicyKey.
connectionfactory.SBCF = amqps://[SASPolicyName]:[SASPolicyKey]#[namespace].servicebus.windows.net
Where SASPolicyKey should be URL-Encoded.
AMQP 1.0 is supported with Service Bus 1.1 for windows server. Basically there are two differences between the cloud and on-prem usage of AMQP in ServiceBus:
1. Addressing: You will need to build an AMQP connection strings (and will need DNS in case you're looking for HA)
2. Authentication: You will need to use domain joined accounts as ACS is not there on-prem. You will also need to distribute your SB certificate to your clients.
Ok, I have sorted the first issue (Java Message Broker API not supporting SAS endpoint), by writing a wrapper which will seamlessly work with existing API. You can get the library from this GitHub repository. With this, I can develop/test my Java application on local service bus environment and host it on Azure / On-Premise Service Bus farm.
https://github.com/Dhana-Krishnasamy/ServiceBusForWindows-SASWrapper
The sender and receiver Queues you will have to configure differently. Here is an example of my working configuration (servicebus.properties):
connectionfactory.SBCF = amqps://$PolicyName:$UrlEncodedKey#$Your-EventHub-NamespaceName.servicebus.windows.net
queue.EventHubSender=$YourEventHubName
queue.EventHubReceiver=$YourEventHubName/ConsumerGroups/$YourConsumerGroupName/Partitions/1
Replace appropriately your own '$' items in there.
The Shared Policy Key has to be URL encoded.
Make sure that your sender will reference the 'EventHubSender' defined in this config and the receiver will reference the 'EventHubReciever'.
Grab the Azure Java SDK from http://www.windowsazure.com/en-us/develop/java/ and then follow this guide: http://www.windowsazure.com/en-us/develop/java/how-to-guides/service-bus-queues/