Basic JMS Query - java

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).

Related

Remotely access an EJB across a network or networks from client

Assuming a CLI or Swing interface client, how is a remote Bean accessed through Glassfish? By obtaining a reference to the BeanManager?
sample code:
Hashtable contextArgs = new Hashtable();
// First you must specify the context factory.
// This is how you choose between jboss implementation
// vs. an implementation from Sun or other vendors.
contextArgs.put( Context.INITIAL_CONTEXT_FACTORY, "com.jndiprovider.TheirContextFactory" );
// The next argument is the URL specifying where the data store is:
contextArgs.put( Context.PROVIDER_URL, "jndiprovider-database" );
// (You may also have to provide security credentials)
// Next you create the initial context
Context myCurrentContext = new InitialContext(contextArgs);
http://en.wikipedia.org/wiki/Java_Naming_and_Directory_Interface#Basic_lookup
So, for a CLI app to access a running glassfish app server, it would use something along the lines of:
Context context = null;
try {
context = new InitialContext();
hello = (Hello) context.lookup("java:global/SalutationApp/SalutationApp-ejb/Hello");
hello.myRemoteMethod();
} catch (Exception e) {
e.printStackTrace();
}
only with a specified URL to connect to a specific glassfish instance? How is a connection established?
If you are doing a real remote lookup, you cannot do it through the CDI's BeanManager, since the Swing GUI is not local.
First you need to configure the application server to allow remote calls - this most likely includes some authentication settings, but I have never done this on Glassfish.
Here is how we lookup the remote interface of our EJB bean in one of our own Swing applications that accesses a EJB deployed on JBoss 4.
First, we added a jndi.properties into the ClassPath of the UI, which contains the information which server is responsible for the naming lookup (note that the port number is application server specific). This is a jndi.properties file used in one of our own Swing GUIs:
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=server.local\:1199
Next, given that your naming context now knows how to lookup remote JNDI names, you can use something like this (again, this is used in our Swing UI):
InitialContext ctx = new InitialContext();
MyRemote mr = (MyRemote) ctx.lookup("global/jndi/name/of/remote/interface");
Instead of using the properties file, you can also configure the InitialContext by passing config values in code, something like this:
Properties env = new Properties();
env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.security.jndi.JndiLoginInitialContextFactory"); // depends on your server
env.setProperty(Context.PROVIDER_URL, "jnp://server.local:1099/");
env.setProperty(Context.SECURITY_PRINCIPAL, "user");
env.setProperty(Context.SECURITY_CREDENTIALS, "password");
InitialContext ctx = new InitialContext(env);
This answer is not complete, but I hope this helps to get you started.

How does the JNDI lookup work in this JMS example?

I am having a hard time to understand the JNDI part of the following JMS example.
public static void main(String[] args) {
try {
// Gets the JNDI context
Context jndiContext = new InitialContext();
// Looks up the administered objects
ConnectionFactory connectionFactory = (ConnectionFactory)
jndiContext.lookup("jms/javaee7/ConnectionFactory");
Destination queue = (Destination) jndiContext.lookup("jms/javaee7/Queue");
// Sends a text message to the queue
try (JMSContext context = connectionFactory.createContext()) {
context.createProducer().send(queue, "Text message sent at " + new Date());
}
} catch (NamingException e) {
e.printStackTrace();
}
}
The book where I got this example didn't mention the setup to make this JNDI lookup possible. For example, in
ConnectionFactory connectionFactory = (ConnectionFactory)
jndiContext.lookup("jms/javaee7/ConnectionFactory");
should there be some kind of server running so that the jndiContext can get a hold of a ConnectionFactory object? In general, what sort of setup is required for the JNDI lookup above to work?
Thank you very much.
In general, JNDI is a service that provides a set of objects to be used by application. This service is usually provided by application server or web server or a dedicated LDAP server.
If the tutorial you are trying to follow explains the JMS tutorial in the context of web application, then most likely there are some setups to be done in the application server (e.g. Glassfish, JBoss) or web server (e.g. Tomcat).
The way to access JNDI also depends on the provider. Usually, this involves a configuration file (either properties file, or XML file).
Another alternative to use JMS is to utilize a dedicated JMS provider such as ActiveMQ. This way, you don't need any application server. Your application can just be a standalone java application (i.e. not necessarily a web application). Accessing objects provided by ActiveMQ via JNDI is explained here: https://activemq.apache.org/jndi-support.html.
General JNDI tutorial: http://docs.oracle.com/javase/tutorial/jndi/

Websphere MQ add Exception Listener

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());

Can I send messages to a JMS queue from outside the app server?

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).

Post a message to a remote JMS queue using JBoss

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).

Categories

Resources