2015年8月14日星期五

JNDI Basics


Overview

    Finding resources is of particular importance in large-scale enterprise environments, where the applications you build may depend on services provided by applications written by other groups in other departments. A well-designed naming infrastructure makes such projects possible -- and the lack of one makes them impossible. In fact, many business-process reengineering efforts begin with the design and implementation of a robust, enterprise-wide naming and directory infrastructure.

   The Java Naming and Directory Interface™ (JNDI) is an application programming interface (API) that provides naming and directory functionality to applications written using the Java™ programming language. It provides a common-denominator interface to many existing naming services. As such, JNDI was not designed to replace existing technology; instead, it provides a common interface to existing naming services.

  The primary function of a naming system is to bind names to objects (or, in some cases, to references to objects -- more on which in a moment). In order to be a naming service, a service must at the very least provide the ability to bind names to objects and to look up objects by name. JNDI provides an interface that supports all this common functionality.

Architecture
 
The JNDI architecture consists of an API and a service provider interface (SPI). Java applications use the JNDI API to access a variety of naming and directory services. The SPI enables a variety of naming and directory services to be plugged in transparently, thereby allowing the Java application using the JNDI API to access their services.


    JNDI is included in the Java SE Platform. To use the JNDI, you must have the JNDI classes and one or more service providers. The JDK includes service providers for the following naming/directory services:

  • Lightweight Directory Access Protocol (LDAP) : Developed by the University of Michigan; as its name implies, it is a lightweight version of DAP (Directory Access Protocol), which in turn is part of X.500, a standard for network directory services. 
  • Common Object Request Broker Architecture (CORBA) Common Object Services (COS) name service : The naming service for CORBA applications; allows applications to store and access references to CORBA objects.
  • Java Remote Method Invocation (RMI) Registry : A bootstrap naming service that is used by RMI servers on the same host to bind remote objects to names. Clients on local and remote hosts can then look up remote objects and make remote method invocations.
  • Domain Name Service (DNS) : The Internet's naming service; maps people-friendly names into computer-friendly IP addresses. DNS is a distributed naming service, meaning that the service and its underlying database is spread across many hosts on the Internet.
  • Network Information System ( NIS and NIS+ ) : Network naming services developed by Oracle. Both allow users to access files and applications on any host with a single ID and password.

Other service providers can be downloaded from the JNDI page or obtained from other vendors.


An introduction to naming services

    Naming Concepts

    A naming service's primary function is to map people friendly names to objects, such as addresses, identifiers, or objects typically used by computer programs. Clients use the naming service to locate objects by name.



For example, the Internet Domain Name System (DNS) maps machine names to IP Addresses:

www.example.com ==> 192.0.2.5

A file system maps a filename to a file reference that a program can use to access the contents of the file.

c:\bin\autoexec.bat ==> File Reference

    Names

To look up an object in a naming system, you supply it the name of the object. The naming system determines the syntax that the name must follow. This syntax is sometimes called the naming systems naming convention. All objects in a naming system are named in the same way (that is, they subscribe to the same naming convention). A name is made up components. A name's representation consist of a component separator marking the components of the name.

The most noticeable difference among each service is the way each naming service requires names to be specified -- its naming convention:

Naming SystemComponent SeparatorNames
UNIX file system"/"/usr/hello
DNS"."seanzhou1023.blogspot.com
LDAP"," and "="cn=Sean, o=Paypal, c=US


The UNIX file system's naming convention is that a file is named from its path relative to the root of the file system, with each component in the path separated from left to right using the forward slash character ("/"). The UNIX pathname, /usr/hello, for example, names a file hello in the file directory usr, which is located in the root of the file system.

DNS naming convention calls for components in the DNS name to be ordered from right to left and delimited by the dot character ("."). The name "seanzhou1023.blogspot.com" names a machine called "seanzhou1023" in the "blogspot.com" domain. Likewise, the name "blogspot.com" names the domain "blogspot" in the top-level domain "com."

The Lightweight Directory Access Protocol (LDAP) naming convention orders components from right to left, delimited by the comma character (","). Components in an LDAP name must be specified as name/value pairs with the name and value separated by an equals character ("="). The name "cn=Sean, o=Paypal, c=US" names the person "cn=Sean" in the organization "o=Paypal, c=US." Likewise, the name "o=Paypal, c=US" names the organization "o=Paypal" in the country "c=US."

JNDI solves this problem with the Name class and its subclasses and helper classes. The Name class represents a name composed of an ordered sequences of subnames, and provides methods for working with names independent of the underlying naming service.

    Bindings

The association of a name with an object is called a binding. A naming service maintains a set of bindings. A file name is bound to a file. The DNS contains bindings that map machine names to IP addresses. An LDAP name is bound to an LDAP entry.

    References and Addresses

Depending on the naming service, some objects cannot be stored directly by the naming service; that is, a copy of the object cannot be placed inside the naming service. Instead, they must be stored by reference; that is, a pointer or reference to the object is placed inside the naming service. A reference represents information about how to access an object. Typically, it is a compact representation that can be used to communicate with the object, while the object itself might contain more state information. Using the reference, you can contact the object and obtain more information about the object.

For example, an airplane object might contain a list of the airplane's passengers and crew, its flight plan, and fuel and instrument status, and its flight number and departure time. By contrast, an airplane object reference might contain only its flight number and departure time. The reference is a much more compact representation of information about the airplane object and can be used to obtain additional information. A file object, for example, is accessed using a file reference. A printer object, for example, might contain the state of the printer, such as its current queue and the amount of paper in the paper tray. A printer object reference, on the other hand, might contain only information on how to reach the printer, such as its print server name and printing protocol.

Although in general a reference can contain any arbitrary information, it is useful to refer to its contents as addresses (or communication end points): specific information about how to access the object.

    Context

A context is a set of name-to-object bindings. Every context has an associated naming convention. A context always provides a lookup (resolution) operation that returns the object, it typically also provides operations such as those for binding names, unbinding names, and listing bound names. A name in one context object can be bound to another context object (called a subcontext) that has the same naming convention.



A file directory, such as /usr, in the UNIX file system represents a context. A file directory named relative to another file directory represents a subcontext (UNIX users refer to this as a subdirectory). That is, in a file directory /usr/bin, the directory bin is a subcontext of usr. A DNS domain, such as COM, represents a context. A DNS domain named relative to another DNS domain represents a subcontext. For the DNS domain Sun.COM, the DNS domain Sun is a subcontext of COM.

Finally, an LDAP entry, such as c=us, represents a context. An LDAP entry named relative to another LDAP entry represents a subcontext. For the LDAP entry o=sun,c=us, the entry o=sun is a subcontext of c=us.

Naming Systems and Namespaces

A naming system is a connected set of contexts of the same type (they have the same naming convention) and provides a common set of operations.

A system that implements the DNS is a naming system. A system that communicates using the LDAP is a naming system.

A naming system provides a naming service to its customers for performing naming-related operations. A naming service is accessed through its own interface. The DNS offers a naming service that maps machine names to IP addresses. LDAP offers a naming service that maps LDAP names to LDAP entries. A file system offers a naming service that maps filenames to files and directories.

A namespace is the set of all possible names in a naming system. The UNIX file system has a namespace consisting of all of the names of files and directories in that file system. The DNS namespace contains names of DNS domains and entries. The LDAP namespace contains names of LDAP entries.

An introduction to directory services

    Directory Concepts

A directory service is an extension to a naming service. It associates names with objects and also associates such objects with attributes:
directory service = naming service + objects containing attributes
You not only can look up an object by its name but also get the object's attributes or search for the object based on its attributes.




In fact, most existing products provide both sets of functionality. Naming services provide name-to-object mapping, and directory services provide information about the objects and tools for searching for them.

An example is the telephone company's directory service. It maps a subscriber's name to his address and phone number.

A directory object represents an object in a computing environment. A directory object can be used, for example, to represent a printer, a person, a computer, or a network. A directory object contains attributes that describe the object that it represents.

Attributes

A directory object can have attributes. For example, a printer might be represented by a directory object that has as attributes its speed, resolution, and color. A user might be represented by a directory object that has as attributes the user's e-mail address, various telephone numbers, postal mail address, and computer account information.

An attribute has an attribute identifier and a set of attribute values. An attribute identifier is a token that identifies an attribute independent of its values. For example, two different computer accounts might have a "mail" attribute; "mail" is the attribute identifier. An attribute value is the contents of the attribute. The email address, for example, might have:

Attribute Identifier : mail
Attribute Value : seanzhou1023@gmail.com

Directories and Directory Services

A directory is a connected set of directory objects. A directory service is a service that provides operations for creating, adding, removing, and modifying the attributes associated with objects in a directory. The service is accessed through its own interface.

Network Information Service (NIS) is a directory service available on the UNIX operating system for storing system-related information, such as that relating to machines, networks, printers, and users.

Oracle Directory Server is a general-purpose directory service based on the Internet standard LDAP.

Directories often arrange their objects in a hierarchy. For example, the LDAP arranges all directory objects in a tree, called a directory information tree (DIT). Within the DIT, an organization object, for example, might contain group objects that might in turn contain person objects. When directory objects are arranged in this way, they play the role of naming contexts in addition to that of containers of attributes.

Search Service

You can look up a directory object by supplying its name to the directory service. Alternatively, many directories, such as those based on the LDAP, support the notion of searches. When you search, you can supply not a name but a query consisting of a logical expression in which you specify the attributes that the object or objects must have. The query is called a search filter. This style of searching is sometimes called reverse lookup or content-based searching. The directory service searches for and returns the objects that satisfy the search filter.

For example, you can query the directory service to find:

all users that have the attribute "age" greater than 40 years.
all machines whose IP address starts with "192.113.50".


Packaging

The JNDI is divided into five packages:

Naming Package

The javax.naming package contains classes and interfaces for accessing naming services. It defines a Context interface, which is the core interface for looking up, binding/unbinding, renaming objects, listing the bindings. A context represents a set of bindings within a naming service that all share the same naming convention. Some naming services also provide subcontext functionality. Much like a directory in a filesystem, a subcontext is a context within a context. This hierarchical structure permits better organization of information. For naming services that support subcontexts, the Context class also provides methods for creating and destroying subcontexts.

In the JNDI, all naming and directory operations are performed relative to a context. There are no absolute roots. Therefore the JNDI defines an InitialContext, which provides a starting point for naming and directory operations. Once you have an initial context, you can use it to look up other contexts and objects. The InitialContext class is instantiated with properties that define the type of naming service in use and, for naming services that provide security, the ID and password to use when connecting.

The JNDI also defines a class hierarchy for exceptions that can be thrown in the course of performing naming and directory operations. The root of this class hierarchy is NamingException. Programs interested in dealing with a particular exception can catch the corresponding subclass of the exception. Otherwise, they should catch NamingException.

The methods of Context:
  • void bind(String stringName, Object object): Binds a name to an object. The name must not be bound to another object. All intermediate contexts must already exist. 
  • void rebind(String stringName, Object object): Binds a new name to an object bound to an old name, and unbinds the old name. All intermediate contexts must already exist. 
  • Object lookup(String stringName): Returns the object bound to the specified name.
  • void unbind(String stringName): Unbinds the object bound to the specified name. 
  • void rename(String stringOldName, String stringNewName): Changes the name to which an object was bound. 
  • NamingEnumeration<Binding> listBindings(String stringName): Returns an enumeration containing the names bound to the specified context, along with the objects and the class names of the objects bound to them. 
  • NamingEnumeration<NameClassPair> list(String stringName): Returns an enumeration containing the names bound to the specified context, along with the class names of the objects bound to them.
The difference between bind and rebind method is that bind will complain if you try to bind an already bound name to a new object while rebind wont'.

Each of these methods has a sibling that takes a Name object instead of a String object. The Name class is an interface that represents a generic name--an ordered sequence of zero or more components. It allows a program to manipulate names without having to know as much about the specific naming service in use. The Naming Systems use this interface to define the names that follow its conventions.

Directory Package

The javax.naming.directory package extends the javax.naming package to provide functionality for accessing directory services in addition to naming services. This package allows applications to retrieve associated attributes with objects stored in the directory and to search for objects using specified attributes.

The DirContext class is the heart of JNDI directory services. It behaves as a naming context by extending the Context interface.and represents a directory context. It provides all of the standard naming service functionality, and can also work with attributes and search for directory entries.

The methods of DirContext:
  • void bind( String stringName, Object object, Attributes attrs): binds a name to an object, along with associated attributes. If attrs is null, the resulting binding will have the attributes associated with obj if obj is a DirContext, and no attributes otherwise. If attrs is non-null, the resulting binding will have attrs as its attributes; any attributes associated with obj are ignored.
  • void rebind( String stringName, Object object, Attributes attributes): binds a name to an object, along with associated attributes, overwriting any existing binding. If attrs is null and obj is a DirContext, the attributes from obj are used. If attrs is null and obj is not a DirContext, any existing attributes associated with the object already bound in the directory remain unchanged. If attrs is non-null, any existing attributes associated with the object already bound in the directory are removed and attrs is associated with the named object. If obj is a DirContext and attrs is non-null, the attributes of obj are ignored.
  • DirContext createSubcontext(String stringName, Attributes attrs): creates and binds a new context, along with associated attributes. This method creates a new subcontext with the given name, binds it in the target context (that named by all but terminal atomic component of the name), and associates the supplied attributes with the newly created object. All intermediate and target contexts must already exist. If attrs is null, this method is equivalent to Context.createSubcontext().
  • Attributes getAttributes(String stringName): retrieves all of the attributes associated with a named object.
  • Attributes getAttributes(String stringName, String[] attrIds): retrieves selected attributes associated with a named object. attrIds are the identifiers of the attributes to retrieve. null indicates that all attributes should be retrieved; an empty array indicates that none should be retrieved.
  • void modifyAttributes(String stringName, int mod_op, Attributes attrs): modifies the attributes associated with a named object. The order of the modifications is not specified. Where possible, the modifications are performed atomically. mod_op is the modification operation, one of: ADD_ATTRIBUTE, REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE.
  • void modifyAttributes(String stringName, ModificationItem[] mods): modifies the attributes associated with a named object using an ordered list of modifications. The modifications are performed in the order specified. Each modification specifies a modification operation code and an attribute on which to operate. Where possible, the modifications are performed atomically.
  • NamingEnumeration<SearchResult> search(String stringName, Attributes matchingAttributes): searches in a single context for objects that contain a specified set of attributes. This method returns all the attributes of such objects. stringName is the name of the context o search.
  • NamingEnumeration<SearchResult> search(String stringName, Attributes matchingAttributes, String[] attributesToReturn): searches in a single context for objects that contain a specified set of attributes, and retrieves selected attributes. stringName is the name of the context o search.
  • NamingEnumeration<SearchResult> search(Name name, String filter,                           SearchControls cons): searches in the named context or object for entries that satisfy the given search filter. Performs the search as specified by the search controls. The format and interpretation of filter follows RFC 2254. An instance of the SearchControls class controls key aspects of the search:
SearchControls{

   int getSearchScope(); //the scope of the search to either the object (OBJECT_SCOPE), the object and the level immediately below it (ONELEVEL_SCOPE), or the object and its entire subtree (SUBTREE_SCOPE).

   long getCountLimit(); //the maximum number of entries that the search will return.

   int getTimeLimit(); //the maximum number of milliseconds that the search will run.
   String []        rgstringAttributesToReturn, //which attributes should be returned along with the entries returned by the search.

    boolean getReturningObjFlag(); //whether or not the objects bound to selected entries should to be returned along with the entries returned by the search.
   
    boolean getDerefLinkFlag();//whether or not links should be dereferenced links (or followed to their ultimate destination) during the search. A link references another directory entry and can span multiple naming systems. The underlying JNDI service provider may or may not provide support for links.
}

As with the Context class, each of the methods above also has a variant that takes a Name object rather than a String object.

The Example

The example below illustrates how to connect to a naming service, list all of the bindings, or list a specific binding. It uses the filesystem service provider, which is one of the reference JNDI service-provider implementations provided by Sun. The filesystem service provider makes the filesystem look like a naming service (which it is, in many ways -- filenames like /foo/bar/baz are names and are bound to objects like files and directories).

public class HelloWorld
import java.util.Hashtable;
import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
public class JNDIExample {
  public static void main(String [] rgstring) {
    try {
      // Create the initial context.  The environment information specifies the JNDI provider to use
      // and the initial URL to use (in our case, a directory in URL form -- file:///...).
      Hashtable hashtableEnvironment = new Hashtable();
      hashtableEnvironment.put(
        Context.INITIAL_CONTEXT_FACTORY, 
        "com.sun.jndi.fscontext.RefFSContextFactory"
      );
      hashtableEnvironment.put(
        Context.PROVIDER_URL, 
        "file:///"
      );
      Context context = new InitialContext(hashtableEnvironment);
      // If you provide no other command line arguments,
      // list all of the names in the specified context and
      // the objects they are bound to.
      if (rgstring.length == 0) {
        NamingEnumeration namingenumeration = context.listBindings("");
        while (namingenumeration.hasMore()) {
          Binding binding = (Binding)namingenumeration.next();
          System.out.println(
            binding.getName() + " " +
            binding.getObject()
          );
        }
      }
      // Otherwise, list the names and bindings for the
      // specified arguments.
      else {
        for (int i = 0; i < rgstring.length; i++) {
          Object object = context.lookup(rgstring[i]);
          System.out.println(
            rgstring[i] + " " +
            object
          );
        }
      }
      context.close();
    }
    catch (NamingException namingexception) {
      namingexception.printStackTrace();
    }
  }
}

To run above codes , you need to download the deprecated File System Provider implemented by Sun from: http://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html


References

1. Trail: Java Naming and Directory Interface,  http://docs.oracle.com/javase/tutorial/jndi/index.html
2. JNDI Overview , http://www.javaworld.com/article/2076888/core-java/jndi-overview--part-1--an-introduction-to-naming-services.html
3. LDAP Naming Service Provider, https://docs.oracle.com/javase/7/docs/technotes/guides/jndi/jndi-ldap.html
4. Building a Service Provider, https://docs.oracle.com/javase/jndi/tutorial/provider/
5. The original LDAP site at the University of Michigan, http://www.umich.edu/~dirsvcs/ldap/
6. OpenLDAP, http://www.openldap.org/