I would like to know how JMS and RMI uses JNDI ,i would also appreciate any snippets of code explaining it .
Thanks
The Java Naming and Directory Interface (JNDI) provides a standard way of accessing naming and directory services
Example Obtaining the initial JNDI context:
import javax.naming.*;
private static InitialContext ctx = null;
...
public static InitialContext getInitialContext( ) throws NamingException {
if (ctx == null) {
Hashtable env = new Hashtable( );
env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL,
"t3://myserver:8001");
ctx = new InitialContext(env);
}
return ctx;
}
See Chapter 4. Using JNDI and RMI of WebLogic: The Definitive Guide for more details.
Related
I am new to using JNDI and I am trying to connect to Active Directory using JNDI and I am facing either Authentication Error or Connection Time out. I am unable to understand what is the potential reason.This how my Active Directory looks like
I have tried the following code
public class ConnectAD {
static DirContext ldapContext;
public static void main(String[] args) throws NamingException {
try {
System.out.println("Début du test Active Directory");
Hashtable<String, String> ldapEnv = new Hashtable<String, String>();
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, "ldap://172.16.1.179:389");
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, "ou=Users,ou=Test1,dc=gigabyte,dc=local");
ldapEnv.put(Context.SECURITY_CREDENTIALS, "5uperCharger");
ldapContext = new InitialDirContext(ldapEnv);
//LdapContext ctx = new InitialLdapContext(ldapEnv,null);
I get the error at while creating the InitialDirContext.
I have an administrator user but I tried giving the cn=administrator but could not connect. I was getting an Authentication Error when I do so.
I also have a name to my ADServer which is GIGA(just trying to provide as much as i can)
Can you please let me know what can be the issue.
I'm working with weblogic Server. I was wondering if after binding an object in the JNDI context it would be possible to make remote call on this object (executing it inside the remote JVM).
In my local JVM:
Context ctx = null;
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL,"t3://remoteServer:7001/);
env.put(Context.SECURITY_PRINCIPAL,"");
env.put(Context.SECURITY_CREDENTIALS,"");
try {
ctx = new InitialContext(env);
MyObjectImpl obj1 = new MyObjectImpl();
ctx.bind("jndi_name", obj1);
//Now my object can be retrieve from the JNDI context under "jndi_name"
MyObjectImpl obj2 = (MyObjectImpl)ctx.lookup("jndi_name"); //lookup of object
System.out.println(obj2.method(1,2)); //call
}catch (Exception e) {
// a failure occurred
}
But the call is done locally in the client JVM and not the remote JVM.
Is there any way to counter this problem?
Regards,
Make sure you have a remote interface(using annotation #Remote) for jndi lookup.
You can see this example: http://middlewaremagic.com/weblogic/?p=5532 (search for the class “CalculatorBean.java”).
For testing purposes, I'm looking for a simple way to start a standalone JNDI server, and bind my javax.sql.DataSource to "java:/comp/env/jdbc/mydatasource" programmatically.
The server should bind itself to some URL, for example: "java.naming.provider.url=jnp://localhost:1099" (doesn't have to be JNP), so that I can look up my datasource from another process. I don't care about which JNDI server implementation I'll have to use (but I don't want to start a full-blown JavaEE server).
This should be so easy, but to my surprise, I couldn't find any (working) tutorial.
The JDK contains a JNDI provider for the RMI registry. That means you can use the RMI registry as a JNDI server. So, just start rmiregistry, set java.naming.factory.initial to com.sun.jndi.rmi.registry.RegistryContextFactory, and you're away.
The RMI registry has a flat namespace, so you won't be able to bind to java:/comp/env/jdbc/mydatasource, but you will be able to bind to something so it will accept java:/comp/env/jdbc/mydatasource, but will treat it as a single-component name (thanks, #EJP).
I've written a small application to demonstrate how to do this: https://bitbucket.org/twic/jndiserver/src
I still have no idea how the JNP server is supposed to work.
I worked on the John´s code and now is working good.
In this version I'm using libs of JBoss5.1.0.GA, see jar list below:
jboss-5.1.0.GA\client\jbossall-client.jar
jboss-5.1.0.GA\server\minimal\lib\jnpserver.jar
jboss-5.1.0.GA\server\minimal\lib\log4j.jar
jboss-remote-naming-1.0.1.Final.jar (downloaded from http://search.maven.com)
This is the new code:
import java.net.InetAddress;
import java.util.Hashtable;
import java.util.concurrent.Callable;
import javax.naming.Context;
import javax.naming.InitialContext;
import org.jnp.server.Main;
import org.jnp.server.NamingBeanImpl;
public class StandaloneJNDIServer implements Callable<Object> {
public Object call() throws Exception {
setup();
return null;
}
#SuppressWarnings("unchecked")
private void setup() throws Exception {
//configure the initial factory
//**in John´s code we did not have this**
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
//start the naming info bean
final NamingBeanImpl _naming = new NamingBeanImpl();
_naming.start();
//start the jnp serve
final Main _server = new Main();
_server.setNamingInfo(_naming);
_server.setPort(5400);
_server.setBindAddress(InetAddress.getLocalHost().getHostName());
_server.start();
//configure the environment for initial context
final Hashtable _properties = new Hashtable();
_properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
_properties.put(Context.PROVIDER_URL, "jnp://10.10.10.200:5400");
//bind a name
final Context _context = new InitialContext(_properties);
_context.bind("jdbc", "myJDBC");
}
public static void main(String...args){
try{
new StandaloneJNDIServer().call();
}catch(Exception _e){
_e.printStackTrace();
}
}
}
To have good logging, use this log4j properties:
log4j.rootLogger=TRACE, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
To consume the Standalone JNDI server, use this client class:
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
/**
*
* #author fabiojm - Fábio José de Moraes
*
*/
public class Lookup {
public Lookup(){
}
#SuppressWarnings("unchecked")
public static void main(String[] args) {
final Hashtable _properties = new Hashtable();
_properties.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
_properties.put("java.naming.provider.url", "jnp://10.10.10.200:5400");
try{
final Context _context = new InitialContext(_properties);
System.out.println(_context);
System.out.println(_context.lookup("java:comp"));
System.out.println(_context.lookup("java:jdbc"));
}catch(Exception _e){
_e.printStackTrace();
}
}
}
Here's a code snippet adapted from JBoss remoting samples. The code that is
in the samples (version 2.5.4.SP2 ) no longer works. While the fix
is simple it took me more hours than I want to think about to figure it out.
Sigh. Anyway, maybe someone can benefit.
package org.jboss.remoting.samples.detection.jndi.custom;
import java.net.InetAddress;
import java.util.concurrent.Callable;
import org.jnp.server.Main;
import org.jnp.server.NamingBeanImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StandaloneJNDIServer implements Callable<Object> {
private static Logger logger = LoggerFactory.getLogger( StandaloneJNDIServer.class );
// Default locator values - command line args can override transport and port
private static String transport = "socket";
private static String host = "localhost";
private static int port = 5400;
private int detectorPort = 5400;
public StandaloneJNDIServer() {}
#Override
public Object call() throws Exception {
StandaloneJNDIServer.println("Starting JNDI server... to stop this server, kill it manually via Control-C");
//StandaloneJNDIServer server = new StandaloneJNDIServer();
try {
this.setupJNDIServer();
// wait forever, let the user kill us at any point (at which point, the client will detect we went down)
while(true) {
Thread.sleep(1000);
}
}
catch(Exception e) {
e.printStackTrace();
}
StandaloneJNDIServer.println("Stopping JBoss/Remoting server");
return null;
}
private void setupJNDIServer() throws Exception
{
// start JNDI server
String detectorHost = InetAddress.getLocalHost().getHostName();
Main JNDIServer = new Main();
// Next two lines add a naming implemention into
// the server object that handles requests. Without this you get a nice NPE.
NamingBeanImpl namingInfo = new NamingBeanImpl();
namingInfo.start();
JNDIServer.setNamingInfo( namingInfo );
JNDIServer.setPort( detectorPort );
JNDIServer.setBindAddress(detectorHost);
JNDIServer.start();
System.out.println("Started JNDI server on " + detectorHost + ":" + detectorPort );
}
/**
* Outputs a message to stdout.
*
* #param msg the message to output
*/
public static void println(String msg)
{
System.out.println(new java.util.Date() + ": [SERVER]: " + msg);
}
}
I know I'm late to the party, but I ended up hacking this together like so
InitialContext ctx = new InitialContext();
// check if we have a JNDI binding for "jdbc". If we do not, we are
// running locally (i.e. through JUnit, etc)
boolean isJndiBound = true;
try {
ctx.lookup("jdbc");
} catch(NameNotFoundException ex) {
isJndiBound = false;
}
if(!isJndiBound) {
// Create the "jdbc" sub-context (i.e. the directory)
ctx.createSubcontext("jdbc");
//parse the jetty-web.xml file
Map<String, DataSource> dataSourceProperties = JettyWebParser.parse();
//add the data sources to the sub-context
for(String key : dataSourceProperties.keySet()) {
DataSource ds = dataSourceProperties.get(key);
ctx.bind(key, ds);
}
}
Have you considered using Mocks? If I recall correctly you use Interfaces to interact with JNDI. I know I've mocked them out at least once before.
As a fallback, you could probably use Tomcat. It's not a full blown J2EE impl, it starts fast, and is fairly easy to configure JNDI resources for. DataSource setup is well documented. It's sub-optimal, but should work.
You imply you've found non-working tutorials; that may mean you've already seen these:
J2EE or J2SE? JNDI works with both
Standalone JNDI server using jnpserver.jar
I had a quick go, but couldn't get this working. A little more perseverance might do it, though.
For local, one process standalone jar purpouses I would use spring-test package:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
SQLServerConnectionPoolDataSource myDS = new SQLServerConnectionPoolDataSource();
//setup...
builder.bind("java:comp/env/jdbc/myDS", myDS);
builder.activate();
startup log:
22:33:41.607 [main] INFO org.springframework.mock.jndi.SimpleNamingContextBuilder - Static JNDI binding: [java:comp/env/jdbc/myDS] = [SQLServerConnectionPoolDataSource:1]
22:33:41.615 [main] INFO org.springframework.mock.jndi.SimpleNamingContextBuilder - Activating simple JNDI environment
I have been looking for a similar simple starter solution recently. The "file system service provider from Sun Microsystems" has worked for me well. See https://docs.oracle.com/javase/jndi/tutorial/basics/prepare/initial.html.
The problem with the RMI registry is that you need a viewer - here you just need to look at file contents.
You may need fscontext-4.2.jar - I obtained it from http://www.java2s.com/Code/Jar/f/Downloadfscontext42jar.htm
Does anyone now a way to obtain server Context using Embeddable API (using org.glassfish.embeddable.GlassFish, not javax.ejb.embeddable.EJBContainer)?
It would be possible if there's a way to obtain EJBContainer from a running Glassfish, but I can't find even the list of services available for lookup.
Here's a workaround - we can obtain InitialContext as an external client.
For the full explanation check EJB_FAQ . This way at least remote EJBs could be tested:
So the full example will look like:
//Start GF
GlassFishRuntime gfRuntime = GlassFishRuntime.bootstrap();
GlassFish gf = gfRuntime.newGlassFish();
gf.start();
//Deploy application with EJBs
Deployer deployer = gf.getService(Deployer.class);
String deployedApp = deployer.deploy(new File(...), "--force=true");
//Create InitialContext
Properties props = new Properties();
props.setProperty("java.naming.factory.initial",
"com.sun.enterprise.naming.SerialInitContextFactory");
props.setProperty("java.naming.factory.url.pkgs",
"com.sun.enterprise.naming");
props.setProperty("java.naming.factory.state",
"com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl");
props.setProperty("org.omg.CORBA.ORBInitialHost", "localhost");
props.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
InitialContext ic = new InitialContext(props);
//Lookup EJBs
ic.lookup(...)
//Stop GF
gf.stop();
gfRuntime.shutdown();
//CORBA stuck thread, have to kill it manually
System.exit(0);
Note there's a System.exit(0) at the end - com.sun.corba.ee.impl.javax.rmi.CORBA.Util.KeepAlive thread is running even after the server stop preventing JVM from stopping...
As far as I know, you can initialize the InitialContext class to obtain a context, that can further be used to perform the lookup. This was tested, and found to work in the context of looking up an EJB, deployed in the embedded container. The EJB was not configured to allow access to specific roles, in which case the com.sun.appserv.security.ProgrammaticLogin class (not exposed via the Embeddable EJB API) might help; this was not tested, but is the recommended way to initialize the Principal for the thread accessing an EJB.
A more or less complete example that runs from Maven and uses the embedded Glassfish dependency in a POM (not reproduced here, for brevity) follows:
The EJB interface:
public interface EchoManager
{
String echo(String message);
}
The Session Bean:
#Local(EchoManager.class)
#Stateless
#EJB(name="java:global/glassfish-ejb-validation/EchoManager",beanInterface=EchoManager.class)
public class EchoManagerBean implements EchoManager
{
public String echo(String message)
{
return message;
}
}
The unit test:
public class EchoManagerTest
{
#Rule
public TestName testMethod = new TestName();
private static final Logger logger = Logger.getLogger(EchoManagerTest.class.getName());
#Test
public void testEchoWithGlassfishRuntime() throws Exception
{
logger.info("Starting execution of test" + testMethod.getMethodName());
GlassFish glassFish = null;
Deployer deployer = null;
String appName = null;
try
{
//Setup
BootstrapProperties bootstrapProps = new BootstrapProperties();
GlassFishRuntime glassFishRuntime = GlassFishRuntime.bootstrap(bootstrapProps);
GlassFishProperties gfProps = new GlassFishProperties();
glassFish = glassFishRuntime.newGlassFish(gfProps);
glassFish.start();
deployer = glassFish.getDeployer();
ScatteredArchive archive = new ScatteredArchive("glassfish-ejb-validation", Type.JAR);
archive.addClassPath(new File("target", "classes"));
archive.addClassPath(new File("target", "test-classes"));
appName = deployer.deploy(archive.toURI(), "--force=true");
// Setup the context
InitialContext context = new InitialContext();
//Execute (after lookup the EJB from the context)
EchoManager manager = (EchoManager) context.lookup("java:global/glassfish-ejb-validation/EchoManager");
String echo = manager.echo("Hello World");
//Verify
assertEquals("Hello World", echo);
}
finally
{
if(deployer != null && appName != null)
{
deployer.undeploy(appName);
}
if(glassFish != null)
{
glassFish.stop();
glassFish.dispose();
}
logger.info("Ending execution of test" + testMethod.getMethodName());
}
}
}
Note that the EJB is deployed with a explicit portable JNDI name (via the #EJB annotation), as I have other tests that use the public embeddable EJB API in other tests, and it is more or less difficult to specify an application name in such tests; each test execution might result in a different JNDI name for the EJB, thus necessitating an explicit JNDI name to be specified.
I am trying to look up a QueueConnectionFactory and Queue via Geronimo's JNDI. The Queue gets returned fine, but the QueueConnectionFactory lookup always returns null. It doesn't throw a NamingException, which is what I'd expect if the JNDI name was incorrect.
Can anyone see what I'm doing wrong? The test code below outputs:
true
false
import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class JndiTest
{
private final static String QUEUE_NAME = "jca:/org.apache.geronimo.configs/activemq-ra/JCAAdminObject/SendReceiveQueue";
private final static String FACTORY_NAME = "jca:/org.apache.geronimo.configs/activemq-ra/JCAManagedConnectionFactory/DefaultActiveMQConnectionFactory";
public static void main(String[] args) throws NamingException
{
InitialContext ctx = new InitialContext();
QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup(FACTORY_NAME);
Queue queue = (Queue)ctx.lookup(QUEUE_NAME);
System.out.println(factory == null);
System.out.println(queue == null);
}
}
In case it makes a difference: I've added openejb-client-3.0.1.jar, geronimo-ejb_3.0_spec-1.0.1.jar and activemq-core-4.1.2-G20090207.jar to my class path, and my jndi.properties file has the properties:
java.naming.factory.initial = org.apache.openejb.client.RemoteInitialContextFactory
java.naming.provider.url = ejbd://127.0.0.1:4201
The reason why it is not throwing an exception is that - there is a ClassLoadException that comes when the resource is accessed.
And the reason why that is happening because the class : com.sun.jndi.url.jca.jcaURLContextFactory is being searched for by the ClassLoader called from ResourceManager.
If you change the Factory name to some other name then you shall see the NamingException - but in the case of lookup , for Exceptions such as ClassNotFound/IllegalState - no exceptions are raised.
The dependencies of ActiveMQ thus need to be analysed.
Update1: One of the possible reasons is that the factory object can only be instantiated in a managed environment. Are you running your code as an application client?.
Update2: Some other pointers found for the cause of this behavior:
the openejb jndi implementation only
exposes ejbs, not any other resources.
If you have a j2ee app client, and
you wish to use jms, you need to
deploy a copy of the activemq adapter
on the client. You can then use the
j2ee java:comp/env context to find
your stuff.
Found this on ActiveMQ site:
ActiveMQ's JNDI Implementation does NOT talk to the naming server. It's
a stripped down version of a JNDI client that just allows to get Topics and
Queues directly from a JMS instance. So, instead of supplying the naming server address, you have to supply the JMS server address.Most JNDI implementations use the java.naming.provider.url property to specify the naming server's address. ActiveMQ uses the brokerURL one. Using the java.naming.provider.url one instead will result in ActiveMQ trying to load the whole Broker.
See more on how to Connect using JNDI:
The initial context factory used in the explanation is: org.apache.activemq.jndi.ActiveMQInitialContextFactory
Some sample code to test with JNDI can be found here
I wrote a simple java client - note below the provider url is the brokerURL that is being used.
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.activemq.jndi.ActiveMQInitialContextFactory");
//props.put(Context.PROVIDER_URL,"vm://localhost");//Either this or below
props.put(Context.PROVIDER_URL,"tcp://localhost:65432");
props.put("queue.SendReceiveQueue",
"org.apache.geronimo.configs/activemq-ra/JCAAdminObject/SendReceiveQueue");
InitialContext context = new InitialContext(props);
QueueConnectionFactory connectionFactory = (QueueConnectionFactory)context.lookup
("ConnectionFactory");
Queue q = (Queue) context.lookup("SendReceiveQueue");
System.out.println("conn is : " + connectionFactory.getClass().getName());
System.out.println("queue is : " + q.getQueueName());
This program gives the output:
conn is : org.apache.activemq.ActiveMQConnectionFactory
queue is : org.apache.geronimo.configs/activemq-ra/JCAAdminObject/SendReceiveQueue
I have a somewhat equivalent configuration Tomcat/Geronimo J2EE jar / Geronimo JMS Jar / ActiveMQ 4
And i'm a little bit confused about your jndi.propertie file.
Mine looks like this :
java.naming.factory.initial = org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url = tcp://localhost:61616
connectionFactoryNames = connectionFactory , TopicConnectionFactory
The big difference is obviousely that your initial context is remote. Beside that, i must provide a connectionFactoryNames, or i get a NamingException.
I don't know why, but for me, using a context didn't work. It seems that the message is sent, but the onMessage of my consumer is not called.
Using a context don't throw exception but don't work :
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
public class HelloClient {
public static void main(String[] args) throws Exception {
Properties ppt2 = new Properties();
ppt2.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.activemq.jndi.ActiveMQInitialContextFactory");
ppt2.put(Context.PROVIDER_URL, "tcp://localhost:61616");
ppt2.put("topic.MessageDestinationTopic", "console.jms/TopicQueue/JCAAdminObject/MessageDestinationTopic");
Context ctx2 = new InitialContext(ppt2);
TopicConnectionFactory factory = (TopicConnectionFactory) ctx2.lookup("ConnectionFactory");
TopicConnection connection = factory.createTopicConnection();
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = (Topic) ctx2.lookup("MessageDestinationTopic");
MessageProducer producer = session.createProducer(topic);
TextMessage msg = session.createTextMessage();
msg.setText("this is a test message");
producer.send(msg);
producer.close();
session.close();
System.out.println("Message published. Please check application server's console to see the response from MDB");
ctx2.close();
System.exit(0);
}
}
Using the code below ( without context ) works well :
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicSession;
public class HelloClient {
public static void main(String[] args) throws Exception {
TopicConnectionFactory factory = new org.apache.activemq.ActiveMQConnectionFactory("tcp://localhost:61616");
TopicConnection connection = factory.createTopicConnection();
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("MessageDestinationTopic");
MessageProducer producer = session.createProducer(topic);
TextMessage msg = session.createTextMessage();
msg.setText("this is a test message");
producer.send(msg);
producer.close();
session.close();
System.out.println("Message published. Please check application server's console to see the response from MDB");
System.exit(0);
}
}
There's two participants here, you're looking in JNDI for something. Somebody else had to put it there. I don't know the specifics of your environment but my approach to such problems is
explore the namespace - what's there? Do you have any JNDI browing tools?
look carfeully in the logs for the service that is supposed to be registering with JNDI, does it report any errors?