I am using IBM Websphere MQ 7.0.1 and I am trying to apply it a JMS exception listener.
On normal JMS I would do something like this:
connection = connectionFactory.createConnection();
connection.setExceptionListener(new ExceptionListenerImpl());
I can't find out how to do it.
Can anyone provide an example (I am using JAVA)?
In order to get exception listener callbacks, you need to create a JMS session against the connection which you're registering your exception on. This should work:
Connection conn = connectionFactory.createConnection();
Session session = createSession(false, Session.AUTO_ACKNOWLEDGE);
conn.setExceptionListener(new ExceptionListenerImpl());
Related
I have a question about best pattern for JMS message send and recv synchronization...
I have an C++ client talking to J2EE server using JAX-RS (REST) over HTTP. On the server side I have two EJBs - one for resource manipulation and other for session state tracking (#Singleton). And I need to notify client when something is changed, created or deleted on server. So I made this approach:
1 - When client connects and logs in, session bean creates temporary jms queue (non transactional) with code like:
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
connection.start();
producer = session.createProducer(queue);
consumer = session.createConsumer(queue);
2 - Client call "listener" REST resource which calls:
public void listen() {
...
consumer.recv(timeout);
...
}
and blocks until it got an message or timeout expires.
3 - When I make some changes in resource bean it calls
public void update(...) {
em.update(...);
producer.send(session.createMessage(CHANGE_NOTIFY_MESSAGE));
}
4 - "listener" resource get the message from JMS and returns it to client with changed (created) object information and than client calls "get" method to get created or changed object.
My problem is that "listener" gets message before changes are written to database (I'm using JPA), so when client asks for created or changed object, this one doesn't exist yet or has old information.
How I can modify my alghoritm to be notified only after changes are saved to database?
Thank you for ideas in advance)))
As Steve C says, I think you need to make the session transactional. An example:
// do the operation
em.getTransaction().begin();
em.update(p);
em.getTransaction().commit();
// now you can send
producer.send(session.createMessage(CHANGE_NOTIFY_MESSAGE));
I'm working with Java EE and ActiveMQ. I want to realize a JMS Queue where I can send messages to my QUEUE and a Consumer + MessageListener should read this messages.
The Code for my Consumer ist the following:
private void initializeActiveMq() throws JMSException {
// Create a ConnectionFactory
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
// Create a Connection
connection = connectionFactory.createConnection();
connection.start();
// Create a Session
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// Create the destination (Queue)
Queue queue = session.createQueue(queueName);
// Create a MessageConsumer from the Session to the Queue
consumer = session.createConsumer(queue);
consumer.setMessageListener(this);
}
But the problem is, every time i will run this code it ads a new consumer to my queue, and then i have some strange behavior the consumers did then not deliver the messages correctly. If i have only one consumer it works perfect.
So how can I make sure that I have only one consumer in my queue?
I've experienced the exact same issue. You need to make sure that your war has a concept of a graceful shutdown procedure when undeployed.
You can achieve this by having a HTTP Servlet that implements init (do all your initialisation here) and destroy (do all you clearing down here). Destory() will get get called by the JVM when the war is undeployed.
I use Spring to define all my beans, ActiveMQ connections, message consumers and producers. My init() loads the Spring application context from a master xml file, caches a reference to it locally, then the destory() calls close() on the application context.
I have a set of parameters that I need to use to access a JMS queue.
Can anyone supply me with a basic example of how I can send a block of XML to an awaiting server using these parameters. For this initial version I don't mind hardcoding these parameters.
I am currently trying this:
Context ctx = new InitialContext();
QueueConnectionFactory queueConnectionFactory = (QueueConnectionFactory) ctx.lookup("QueueConnectionFactory");
Queue queue = (Queue) ctx.lookup("OCP.GET.PRODUCTS.COMSRV");
QueueConnection queueConnection = queueConnectionFactory.createQueueConnection();
QueueSession queueSession = queueConnection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueSender queueSender = queueSession.createSender(queue);
TextMessage message = queueSession.createTextMessage();
message.setText(xmlString);
But I have no idea how to set the parameters for Host, Port, QueueManager or Channel
The parameters supplied to me are
Manager: OCP.QMGR
Channel: OCP.SVRCONN
Port: 14234
Host: host.server.com
sentToQueue: OCP.GET.PRODUCTS.COMSRV
replyToQueue:COMSRV.GET.PRODUCTS.OCP
I am very new to Java and JMS and am starting to drown with this.
My understanding is that you are trying to connect to MQSeries (QueueManager and Channel are MQ concepts and are not part of the JMS API AFAIK) so I think that you'll have to use MQ specific client API. I'm really not a MQ expert but it seems that the code below (see Implementing vendor-independent JMS solutions) is close to what you are looking for:
MQQueueConnectionFactory qconFactory = new MQQueueConnectionFactory();
qconFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP); //Used when the MQSeries server is on a different host from the client
qconFactory.setQueueManager(queueManager);
qconFactory.setHostName(hostName);
qconFactory.setPort(port);
qconFactory.setChannel(channel);
connection = qconFactory.createQueueConnection();
session1 = connection.createQueueSession(true, Session.CLIENT_ACKNOWLEDGE);.....
As I said, I'm not a MQ specialist but the MQQueueConnectionFactory seems to be aware of most of the things you are talking about.
Side Note:
When using JNDI, you need to set up JNDI environment properties like the initial context factory and the provider url. Basically, these properties are used to declare what class to use to create the initial context and where to find the JNDI server. Obviously, the values of these properties are dependent on the JNDI service you're connecting to.
You can specify environment properties by using the non empty InitialContext constructor and passing the environment parameter to it. For example to connect to BEA WebLogic JNDI service:
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
p.put(Context.PROVIDER_URL,"t3://myhost:7001");
ctx = new InitialContext(p);
Or you can provide a jndi.properties file and use the non-arg InitialContext constructor. For example, to connect to IBM WebSphere JNDI service, you would put a jndi.properties file with the following content on the classpath:
java.naming.provider.url=iiop://myhost:9001
java.naming.factory.initial=com.ibm.websphere.naming.WsnInitialContextFactory
This second approach is of course more portable as you don't hard code the values of the parameters in the Java code (this might not be an issue though).
Now, in your case, I can't tell if you need this (and even less what values to use) as you didn't provide any detail on your context (like the application server or the JMS provider or the Messaging solution you are trying to connect to).
As I understand it, a J2EE container is required to include a JMS provider. Is it possible for a standalone Java application to send messages to a JMS queue provided by the container? If so, how do I access the JNDI lookups from outside the container?
(I am trying this with Geronimo if it makes any difference, but I am hoping there is a standard way of doing this.)
You should be able to create an InitialContext that uses the JNDI server in Geronimo. You can then use this to lookup your JMS Connection Factory and Queue.
The following example was adapted from http://forums.sun.com/thread.jspa?threadID=5283256 to use the Geronimo JNDI Factory.
Context jndiContext = null;
ConnectionFactory connectionFactory = null;
Connection connection = null;
Session session = null;
Queue queue = null;
MessageProducer messageProducer = null;
try
{
//[1] Create a JNDI API InitialContext object.
Hashtable properties = new Hashtable(2);
// CHANGE these to match Geronimos JNDI service
properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.RemoteInitialContextFactory");
properties.put(Context.PROVIDER_URL, "ejbd://127.0.0.1:4201");
jndiContext = new InitialContext(properties);
//[2] Look up connection factory and queue.
connectionFactory = (ConnectionFactory)jndiContext.lookup("jms/ConnectionFactory");
queue = (Queue)jndiContext.lookup("jms/Queue");
//[3]
// - Create connection
// - Create session from connection; false means session is not transacted.
// - Create sender and text message.
// - Send messages, varying text slightly.
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
messageProducer = session.createProducer(queue);
//send a message
TextMessage message = session.createTextMessage(this.jTextSend.getText());
messageProducer.send(message);
//example for send some object
//ObjectMessage message = session.createObjectMessage();
//MyObj myObj = new MyObj ("Name"); //this class must be serializable
//message.setObject(myObj );
//messageProducer.send(message);
}
catch(Exception ex)
{
LOG.error(ex);
}
finally
{
if(connection !=null)
{
try
{
connection.close();
}
catch(JMSException e)
{
LOG.error(e);
}
}
}
You can place messages in a JMS queue without an application server.
However, you'll need to know how to get to the JMS provider directly -- without using JNDI, since that is provided by the JavaEE application server.
You can do it, and there may be multiple ways depending on the thin client that is accessing the queue. The example given by #pjp will work providing you have the correct jar files for accessing the server in question, including a jar which will provide your application with a JNDI instance. These jars should be provided by the vendor, and may include instructions on how to connect without using JNDI as well. Although I think the JNDI method is the simplest and keeps coding consistent both on and off the server.
Each vendor will have different jars to provide client access, in IBM's case, they are different for the internal JMS provider vs. WebSphere MQ (since they are two different implementations).
This looks simple but I can't find a simple answer.
I want to open a connection to a remote JMS broker (IP and port are known), open a session to the a specific queue (name known) and post a message to this queue.
Is there any simple Java API (standard if possible) to do that ?
EDIT
Ok I understand now that JMS is a driver spec just like JDBC and not a communication protocol as I thought.
Given I am running in JBoss, I still don't understand how to create a JBossConnectionFactory.
EDIT
I actually gave the problem some thoughts (hmmm) and if JMS needs to be treated the same as JDBC, then I need to use a client provided by my MQ implementation. Since we are using SonicMQ for our broker, I decided to embed the sonic_Client.jar library provided with SonicMQ.
This is working in a standalone Java application and in our JBoss service.
Thanks for the help
You'll need to use JMS, create a QueueConnectionFactory and go from there. Exactly how you create the QueueConnectionFactory will be vendor specific (JMS is basically a driver spec for message queues just as JDBC is for databases) but on IBM MQ it something like this:
MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setHostName(<hostname>);
connectionFactory.setPort(<port>);
connectionFactory.setTransportType(JMSC.MQJMS_TP_CLIENT_MQ_TCPIP);
connectionFactory.setQueueManager(<queue manager>);
connectionFactory.setChannel("SYSTEM.DEF.SVRCONN");
QueueConnection queueConnection = connectionFactory.createQueueConnection();
QueueSession queueSession = connection.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = queueSession.createQueue(<queue name>);
QueueSender queueSender = session.createSender(queue);
QueueReceiver queueReceiver = session.createReceiver(queue);
EDIT (following question edit)
The best way to access a remote queue, or any queue for that matter, is to add a Queue instance to the JNDI registry. For remote queues this is achieved using MBeans that add the Queue instance when the server starts.
Take a look at http://www.jboss.org/community/wiki/UsingWebSphereMQSeriesWithJBossASPart4, which while it's an example with IBM MQ, is essentially what you have to do to connect to any remote queue.
If you look at jbossmq-destinations-service.xml and org.jboss.mq.server.jmx you'll see the MBeans you need to create in relation to a JBoss queue.
Here is the code we used to connect to the SonicMQ broker using the sonic_Client.jar library:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
public class JmsClient
{
public static void main(String[] args) throws JMSException
{
ConnectionFactory factory = new progress.message.jclient.ConnectionFactory("tcp://<host>:<port>", "<user>", "<password>");
Connection connection = factory.createConnection();
try
{
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
try
{
MessageProducer producer = session.createProducer(session.createQueue("<queue>"));
try
{
producer.send(session.createTextMessage("<message body>"));
}
finally
{
producer.close();
}
}
finally
{
session.close();
}
}
finally
{
connection.close();
}
}
}
Actually I'm using JBoss 4 and JNDI is not difficult to use.
First of all you have to know where your JNDI is running.
In my JBoss (conf\jboss-service.xml) I have:
<mbean code="org.jboss.naming.NamingService" name="jboss:service=Naming" xmbean-dd="resource:xmdesc/NamingService-xmbean.xml">
...
<attribute name="Port">7099</attribute>
...
</mbean>
This is important, this is port you want to connect to.
Now you can easily connect to JNDI using this code:
Hashtable<String, String> contextProperties = new Hashtable<String, String>();
contextProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
contextProperties.put(Context.PROVIDER_URL, "jnp://localhost:7099");
InitialContext initContext = new InitialContext(contextProperties);
Now when you have context, it's very similar to #Nick Holt's answer, except connection factory creation, you have to use:
QueueConnectionFactory connFactory = (QueueConnectionFactory) initContext.lookup("ConnectionFactory");
Also you do not need to create queue if there is deployed some
Queue queue = (Queue) initContext.lookup("queueName");
All the code above was tested with JBoss 4.2.2 GA and JBossMQ (JBossMQ was, if I'm correct, replaced in 4.2.3 with JBoss messaging).