How does the JNDI lookup work in this JMS example? - java

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/

Related

ActiveMQ generating queues which don't exists

I have a problem with using ActiveMQ in Spring application.
I have a few environments on separate machines. On each machine I had one ActiveMQ instance installed. Now, I realized that I can have only one ActiveMQ instance installed on one server, and few applications can use that ActiveMQ for sending messages. So, I must change queue names in order to have different queues for different environments ("queue.search.sandbox", "queue.search.production", ...).
After that change, now ActiveMQ is generating new queues, but also the old ones, although there is no such configuration for doing that.
I am using Java Spring application with Java configuration, not XML.
First, I create queueTemplate as a Spring bean:
#Bean
public JmsTemplate jmsAuditQueueTemplate() {
log.debug("ActiveMQConfiguration jmsAuditQueueTemplate");
JmsTemplate jmsTemplate = new JmsTemplate();
String queueName = "queue.audit.".concat(env.getProperty("activeMqBroker.queueName.suffix"));
jmsTemplate.setDefaultDestination(new ActiveMQQueue(queueName));
jmsTemplate.setConnectionFactory(connectionFactory());
return jmsTemplate;
}
Second, I create ActiveMQ Listener configuration:
#Bean
public DefaultMessageListenerContainer jmsAuditQueueListenerContainer() {
log.debug("ActiveMQConfiguration jmsAuditQueueListenerContainer");
DefaultMessageListenerContainer dmlc = new DefaultMessageListenerContainer();
dmlc.setConnectionFactory(connectionFactory);
String queueName = "queue.audit.".concat(env.getProperty("activeMqBroker.queueName.suffix"));
ActiveMQQueue activeMQ = new ActiveMQQueue(queueName);
dmlc.setDestination(activeMQ);
dmlc.setRecoveryInterval(30000);
dmlc.setSessionTransacted(true);
// To perform actual message processing
dmlc.setMessageListener(auditQueueListenerService);
dmlc.setConcurrentConsumers(10);
// ... more parameters that you might want to inject ...
return dmlc;
}
After building my application, as the result I have properly created queue with suffix ("queue.audit.sandbox"), but after some time ActiveMQ generates and the old version ("queue.audit").
Does someone knows how ActiveMQ is doing this? Thanks in advance.
There is probably still an entry in the index for the queue, so when ActiveMQ restarts it is displaying the queue. If you want to be certain about destinations, use startup destinations and disable auto-creation by denying the "admin" permission to the connecting user account in the authorization entry
After some time ActiveMQ just stopped creating queues that don't exist.
Now, we have expected behavior, without unnecessary queues.
Still I didn't found out what solved this problem, to be sincere...

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.

Access an service in jboss through JNDI (RMI?)

I need to make an service in jboss and access it through JNDI on the client side.
I have been playing around a bit with JNDI and made something like this on the client side:
import javax.naming.*;
public class App {
public static void main(String[] args) throws NamingException {
App app = new App();
app.setSysProp();
app.setObject();
app.getObject();
}
public void setSysProp() {
System.setProperty(Context.PROVIDER_URL, "127.0.0.1:1099");
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
}
public void setObject() throws NamingException {
Context context = new InitialContext();
MyObject obj = new MyObject();
obj.setName("NameOfMyObject");
context.bind("obj", obj);
}
public void getObject() throws NamingException {
Context context = new InitialContext();
MyObject obj = (MyObject) context.lookup("obj");
System.out.println(obj.getName());
}
}
This only binds and object to jndi on the client side and later retrieves it.
Now what i want is to bind a similar object on the server side (Jboss 4.2.3) and through it make some operations on the server. How can this be done? Ive read that something named RMI should be used in this case but what exactly is that and how to use it?
Ive read that something named RMI should be used in this case but what exactly is that and how to use it?
RMI is the Java standar API for Remote Method Invocation. It allows you execute a method on a object that reside in other Java Virtual Machine. Take in mind that you don't need a application server like JBoss to communicate with a remote java object. This link provides a simple tutorial.(notice that JBoss or any other app server is not mentioned)
I need to make an service in jboss and access it through JNDI on the client side.
This is a different thing. Although, JBoss (as a Java EE specification compliant server) use RMI extensively, you don't need understand how this API works. What you need is to create a server side component called EJB which allows you to have a service running on a server.
How can this be done?
There are hundred of tutorials about how to implement a basic EJB. Choose one compatible with your JBoss version due to some implementation details often change from one version to another.
You will also find that EJB specification has been evolving. With JBoss 4.2.3 and Java 5 you can start with EJB 3.0 which is easier than the previous one (2.1)

Basic JMS Query

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

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

Categories

Resources