通过LDAP初步理解JNDI

LDAP与JNDI模型对比
jndi-ldap-model

1、LdapBinder
这个类的主要功能是,把消息放到一个预设的LDAP路径

package com.neohope.jndi.test;

import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class LdapBinder {

    public static void main(String[] args) {
        try {
            final Hashtable jndiProperties = new Hashtable();
            jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
            jndiProperties.put(Context.PROVIDER_URL, "file:///d:/Downloads/ldap");
            //jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            //jndiProperties.put(Context.PROVIDER_URL, "ldap://localhost:389");
            //jndiProperties.put(Context.SECURITY_PRINCIPAL,"cn=Directory Manager");
            //jndiProperties.put(Context.SECURITY_CREDENTIALS,"password");

            DirContext ctx = new InitialDirContext(jndiProperties);
            NeoLdapMsgRef msgRef = new NeoLdapMsgRef("Ldap Text");
            ctx.bind("cn=anobject", msgRef);
            //ctx.unbind("cn=anobject");

            /*
            NamingEnumeration list = ctx.list("/");
            while (list.hasMore()) {
                NameClassPair nc = (NameClassPair) list.next();
                System.out.println(nc);
            }
            */

            NamingEnumeration list = ctx.listBindings("/");
            while (list.hasMore()) {
                Binding binding = (Binding)list.next();
                System.out.println(binding.getName() + " " +binding.getObject()
                );
            }

            ctx.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2、LdapReader
这个类的主要功能是,从预设的LDAP路径读取消息

package com.neohope.jndi.test;

import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class LdapReader {

    public static void main(String[] args) {
        try {
            final Hashtable jndiProperties = new Hashtable();
            jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.fscontext.RefFSContextFactory");
            jndiProperties.put(Context.PROVIDER_URL, "file:///d:/Downloads/ldap");

            //jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            //jndiProperties.put(Context.PROVIDER_URL, "ldap://localhost:389");
            //jndiProperties.put(Context.SECURITY_PRINCIPAL,"cn=Directory Manager");
            //jndiProperties.put(Context.SECURITY_CREDENTIALS,"password");

            DirContext ctx = new InitialDirContext(jndiProperties);
            NeoLdapMsgRef msgRef = (NeoLdapMsgRef)ctx.lookup("cn=anobject");
            ctx.close();

            System.out.println(msgRef.message);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3、LdapMonitor
这个类的主要功能是,监视LDAP路径下内容变动

package com.neohope.jndi.test;

import javax.naming.Context;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.event.*;
import javax.naming.ldap.UnsolicitedNotificationEvent;
import javax.naming.ldap.UnsolicitedNotificationListener;
import java.util.Hashtable;

/**
 * Created by Hansen
 * 条件所限,没有进行测试
 */
public class LdapMonitor {

    public static void main(String[] args) {
        try {
            final Hashtable jndiProperties = new Hashtable();
            jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            jndiProperties.put(Context.PROVIDER_URL, "ldap://localhost:389");
            jndiProperties.put(Context.SECURITY_PRINCIPAL,"cn=Manager");
            jndiProperties.put(Context.SECURITY_CREDENTIALS,"password");

            DirContext ctx = new InitialDirContext(jndiProperties);
            EventDirContext enentCtx=(EventDirContext)(ctx.lookup("/"));

            NamingListener unsolListener = new UnsolicitedNotificationListener() {
                public void notificationReceived(UnsolicitedNotificationEvent evt) {
                    System.out.println("received: " + evt + ",notification:" + evt.getNotification());
                }

                public void namingExceptionThrown(NamingExceptionEvent evt) {
                    System.out.println(">>> UnsolListener got an exception");
                    evt.getException().printStackTrace();
                }
            };

            NamingListener namespaceListener = new NamespaceChangeListener() {
                public void objectAdded(NamingEvent evt) {
                    System.out.println("objectAdded: " + evt.getOldBinding() + "\n=> " + evt.getNewBinding());
                    System.out.println("\tchangeInfo: " + evt.getChangeInfo());
                }

                public void objectRemoved(NamingEvent evt) {
                    System.out.println("objectRemoved: " + evt.getOldBinding() + "\n=> " + evt.getNewBinding());
                    System.out.println("\tchangeInfo: " + evt.getChangeInfo());
                }

                public void objectRenamed(NamingEvent evt) {
                    System.out.println("objectRenamed: " + evt.getOldBinding() + "\n=> " + evt.getNewBinding());
                    System.out.println("\tchangeInfo: " + evt.getChangeInfo());
                }

                public void namingExceptionThrown(NamingExceptionEvent evt) {
                    System.err.println(">>>NamespaceChangeListener Exception");
                    evt.getException().printStackTrace();
                }
            };

            NamingListener objectListener = new ObjectChangeListener() {
                public void objectChanged(NamingEvent evt) {
                    System.out.println("objectChanged: " + evt.getOldBinding() + "\n\t=> " + evt.getNewBinding());
                    System.out.println("\tchangeInfo: " + evt.getChangeInfo());
                }

                public void namingExceptionThrown(NamingExceptionEvent evt) {
                    System.err.println(">>>ObjectChangeListener Exception");
                    evt.getException().printStackTrace();
                }
            };

            enentCtx.addNamingListener("", EventContext.SUBTREE_SCOPE, unsolListener);
            enentCtx.addNamingListener("", EventContext.SUBTREE_SCOPE, namespaceListener);
            enentCtx.addNamingListener("", EventContext.SUBTREE_SCOPE, objectListener);

            System.in.read();

            //enentCtx.close();
            ctx.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4、NeoLdapMsgRef

package com.neohope.jndi.test;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;

/**
 * Created by Hansen
 */
public class NeoLdapMsgRef implements Referenceable {
    public String message = "";

    public NeoLdapMsgRef(String message)
    {
        this.message = message;
    }

    @Override
    public Reference getReference() throws NamingException {
        Reference ref = new Reference(this.getClass().getName(), NeoLdapMsgRefFactory.class.getName(), null);
        ref.add(new StringRefAddr("msg", message));
        return ref;
    }
}

5、NeoLdapMsgRefFactory

package com.neohope.jndi.test;

import javax.naming.*;
import javax.naming.spi.ObjectFactory;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class NeoLdapMsgRefFactory implements ObjectFactory {
    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        if (obj instanceof Reference) {
            Reference ref = (Reference) obj;
            String msg = (String) ref.get("msg").getContent();
            NeoLdapMsgRef msgRef = new NeoLdapMsgRef(msg);
            return msgRef;
        }
        else {
            return null;
        }
    }
}

LADP常用函数

LADP操作 解释 JNDI函数
Search Search directory for matching directory entries DirContext.search()
Compare Compare directory entry to a set of attributes DirContext.search()
Add Add a new directory entry DirContext.bind(), DirContext.createSubcontext()
Modify Modify a particular directory entry DirContext.modifyAttributes()
Delete Delete a particular directory entry Context.unbind(), Context.destroySubcontext()
Rename Rename or modify the DN Context.rename()
Bind Start a session with an LDAP server new InitialDirContext()
Unbind End a session with an LDAP server Context.close()
Abandon Abandon an operation previously sent to the server Context.close(), NamingEnumneration.close()
Extended Extended operations command LdapContext.extendedOperation()

LADP查询常用符号

o Organization
ou Organizational unit
cn Common name
sn Surname
givenname First name
uid Userid
dn Distinguished name
mail Email address

LADP查询常用操作符

符号 含义 示例 匹配示例
~ Approximate (sn~=Tyagi) Tyagi or variations in spelling
= Equality (sn=Tyagi) Surname of Tyagi only
> Greater than (sn=Tyagi) Any surname that alphabetically follows Tyagi
>= Greater than or equal to (sn>=Tyagi) Any surname that includes or alphabetically follows Tyagi
< Less than (sn Any surname that alphabetically precedes Tyagi
<= Less than or equal to (sn<=Tyagi) Any surname that includes or alphabetically precedes Tyagi
=* Presence (sn=*) All surnames (all entries with the sn attribute)
Substring (sn=Tya*), (sn=*yag*), (sn=Ty*g*) Any matching string, substring, or superstring that matches Tyagi
& And (&(sn=Tyagi) (cn=Sameer Tyagi)) Any entry that matches both surname of Tyagi and a common name of Sameer Tyagi
| Or (|(sn=Tyagi) (cn=Sameer Tyagi)) Any entry that matches either surname of Tyagi or a common name of Sameer Tyagi
! Not (!(sn=Tyagi)) Any entry other than that with a surname of Tyagi

通过JMS初步理解JNDI

JNDI服务模型
jndi-model

1、服务端

package com.neohope.jndi.test;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Hashtable;

/**
 * Created by Hansen on 2016/5/4.
 */
public class Server {
    private static InitialContext ctx;

    public static void initJNDI() {
        try {
            LocateRegistry.createRegistry(1234);
            final Hashtable jndiProperties = new Hashtable();
            jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
            jndiProperties.put(Context.PROVIDER_URL, "rmi://localhost:1234");
            ctx = new InitialContext(jndiProperties);
        } catch (NamingException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public static void bindJNDI(String name, Object obj) throws NamingException {
        ctx.bind(name, obj);
    }

    public static void unInitJNDI() throws NamingException {
        ctx.close();
    }

    public static void main(String[] args) throws NamingException, IOException {
        initJNDI();
        NeoMessage msg = new NeoMessage("Just A Message");
        bindJNDI("java:com/neohope/jndi/test01", msg);
        System.in.read();
        unInitJNDI();
    }
}

2、客户端

package com.neohope.jndi.test;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.util.Hashtable;

/**
 * Created by Hansen
 */
public class Client {
    public static void main(String[] args) throws NamingException {
        final Hashtable jndiProperties = new Hashtable();
        jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
        jndiProperties.put(Context.PROVIDER_URL, "rmi://localhost:1234");

        InitialContext ctx = new InitialContext(jndiProperties);
        NeoMessage msg = (NeoMessage) ctx.lookup("java:com/neohope/jndi/test01");
        System.out.println(msg.message);
        ctx.close();
    }
}

3、NeoMessage

package com.neohope.jndi.test;

import java.io.Serializable;
import java.rmi.Remote;

/**
 * Created by Hansen
 */
public class NeoMessage implements Remote, Serializable {
    public String message = "";

    public NeoMessage(String message)
    {
        this.message = message;
    }
}

大家可以看出,在这个简单的例子中:
1、服务端仅仅是把数据生成好,放到了LocateRegistry中。
2、而客户端,通过JNDI查到消息,获取到了对应的数据。
3、LocateRegistry完成了跨JVM/主机通讯的任务

反过来思考一下,对于JNDIL是不是更清楚一些了呢?
那再思考一下,那J2EE容器中的数据源是如何统一管理的呢?