Section 21.3. What s in a Name? An Introduction to JNDI


21.3. What's in a Name? An Introduction to JNDI

JNDI abstracts a type of service known generically as a directory service. We need to introduce that concept and then describe a few common examples of such systems. Then we can explain how JNDI abstracts these various services.

21.3.1. Naming and Directory System Concepts

Directory services are one of the dark mysteries of modern computing. Why? Because if the people who developed these systems ever let on how simple they actually are, everyone would understand and be able to use them well. Then where would we be?

In practice, a naming system is what we programmers call an associative array, or, when we are feeling less verbose, a simple hash of name/value pairs. That's it. The core concept isn't any more complicated than that. The most familiar naming service out there (one that we are sure you use every day) is the Internet Domain Name Service, or DNS. This is a system that maps domain names (like www.somedumbnetwork.net) to IP addresses (like 205.117.29.1). In the world of directory services, such a name/value pair is called a binding.

Of course, the devil is in the details. DNS is implemented by a complex network of name servers that pass requests up and down a distributed hierarchy of name servers. That part can get quite complex, but the core idea is that you have a name (the domain name) and a value (an IP address) that you join together. DNS can actually bind other information, such multiple alias names for a single canonical name/IP pair, a mail handler name for a domain, and other general purpose data which the DNS administrator can choose to share.

So, naming services are a way to join names and values together.

Before we move on, let's make sure we understand how general and universal this concept is. A filesystem can be thought of as a naming service. A UNIX filename (like, say, /etc/inittab) can be thought of as a way of linking that name with the data it contains. So the key is the name (/etc/inittab) and the value could be either the data it contains, or perhaps a file handle that, when read, returns the data contained in the file.[3]

[3] The first case would be a name/value pair, the second case would be a name/reference pair. The distinction is often not important, but it does exist.

There are some other common features of naming systems that we should point out. They are frequently hierarchical. A domain name such as www.multitool.net actually indicates the host www in the multitool domain within the net domain. The name www.multitool.com is not related in any way with the name www.multitool.net. They are contained in different top-level domains. They do not intersect. Likewise, the name /etc/inittab would be completely unrelated to, say, /tmp/inittabbecause inittab is a file in the etc directory, and inittab is a file in the tmp directory. So, most naming systems are hierarchical. They differ in how the levels of the hierarchy are indicated, and in how absolute names are constructed from components of the hierarchy, but they share this common concept.

So, that's naming. Next come directory concepts.

A naming service is good, but what happens if you don't have the key and you need to go looking? That's what directories are for. Consider the ls command. Why do you need it? Have you ever run it? Of course you have. Why? Because you often don't know the exact name of something or where exactly it is in a naming system. You need to be able to look for what you want. That is the "directory" part of naming and directory services. You want something that lets you query and browse the naming system to find what you want.

The ls command will give you the complete contents of a directory, or it will allow you to query a directory by specifying wildcard names. These are examples of browse and query features. We'll talk more about these concepts in relation to naming and directory systems in general and to JNDI in particular.

Key to directory services is the concept of a context. A context is a set of bindings with a common name, expressed in a common way. In our filesystem example, /etc is a context. A context may contain other contexts that follow the same naming convention. For example, /etc/sysconfig is a context that is a subcontext of /etc. Likewise, multitool.net is a subcontext of the net context.

A context is distinguished by having a naming convention for itself and its subcontexts, and it must have means of creating bindings, removing bindings, and querying or listing bindings.

Since JNDI is designed to operate across multiple naming and directory systems, it is necessary to talk about naming systems and namespaces. A naming system is a connected set of contexts that use the same naming convention. Thus, Internet domain names are a naming system, UNIX filenames are a naming system, and so on. A namespace is a set of names in a naming system. These terms will have significance later when we'll talk about JNDI.

A naming system binds a name to a value. Directory services bind a directory object to one or more attributes. A naming service could be thought of as a simple case of a directory where "name" and "value" are the attributes of the directory object. A directory can store many more attributes (bindings) for a given name than can a naming service. Directory services also (in general) support the notion of searches and queries.

A directory object represents an object in the computing environment. This might be a server, a printer, a user, a router, whatever. Each object would have a set of attributes that describe the object. A directory is a connected set of directory objects.

In the directories we know about (see Sections 21.3.2.4 and 21.3.2.5 for the limits of our knowledge), directory objects are arranged in a hierarchy, so that they serve as naming contexts as well as directory objects.

21.3.2. Common Directory Services

Now that you have seen the concepts, we can cover a few common implementations of naming and directory services.

21.3.2.1 Domain Name Service (DNS)

This is probably the most familiar naming and directory system. It is used all the time to resolve Internet host names to IP addresses, and it is commonly used to obtain the names of mail servers for domains. It also has less often used features to look up arbitrary data for domains. These features are not used often because standard DNS has no authentication and authorization controls. Information in DNS is, inherently, public information.

21.3.2.2 Filesystems

The UNIX filesystems, NTFS, FAT, and other filesystems provide name-to-data mappings that are compatible with JNDI. When they are combined with networked filesystems, such as SMB, CIFS, NFS, and even rsync and FTP, files can be made available over the network through JNDI.

21.3.2.3 LDAP

LDAP is the "Lightweight Directory Access Protocol." There is an old joke that a platypus is a swan put together by a committee. If that is so, then it often seems that LDAP is the platypus of name and directory services.

To be fair, LDAP has the heavy burden that goes with any standards that are produced by a large committee-driven process. It has to try to be all things to all people. LDAP is a query and transport protocol specification of the ISO X.500 naming and directory service standard.[4] Like other ISO and ANSI standards, the specification is robust to the point of uselessness. LDAP is designed to allow every possible name system in the Universe to be subsumed into a single, uniquely addressable Directory Information Tree. Every entry in LDAP has a distinguished name, which is an unambiguous specification of the name from the root of the tree. So far, this is like the other naming systems. There is a root, there are nodes at each layer, and then, at the bottom, there is data. What makes X.500 and LDAP different is that each node consists of not just a name, but of a type/name pair. An example of an LDAP name might be:

[4] If you are dying to know, X.500 is a naming and directory services standard from the International Standards Organization (ISO), an international technical standards body. X.500 has a transport and query protocol specification of its own, but it uses the ISO OSI (Open Systems Interconnection) network protocol standard. OSI is rarely used because TCP/IP took off first and has been hacked and hacked again to keep it alive and well. At one time, it looked like IP address space limitations would push the world to OSI protocols, but hacks like CIDR, private subnets, and now the (less hackish) IPv6 make it look like TCP/IP will be here for quite a while. In a sense, then, LDAP is X.500 over TCP/IP. Or, to put it another way, LDAP is a TCP/IP implementation of ISO X.500.

 url=http://www.multitool.net/,cn=M. Schwarz,o=MAS Consulting,st=MN,c=us 

At each node there is a type (c, cn, url, and so on) and a name (or value) for that type. The definitions of these types and the lists of types permitted at a particular level depend on a schema which is controlled by whoever controls the server that serves the given level of the hierarchy. In other words, as with DNS, if you want to be part of the public, global namespace, you have to play by the rules of the ancestor nodes. You can do what you want with your point of control and below, but you must obey the naming schema of all of your ancestors.[5]

[5] We want to be clear: You only have to do this if you wish to give those ancestors and outside users access to your directories. You are free to create entirely private directory structures that need not conform to anyone else's schema. It all depends on the purpose and audience of your directory.

This explains why so few organizations actually use LDAP globally (i.e., integrating directly with all other public LDAP servers in the world). Instead, they tend to use LDAP by setting up schema and servers that are completely internal and private so that they do not have to use the many required parent nodes it would take to hook up to the global LDAP namespace.[6]

[6] Another reason is that LDAP itself has no cryptographically secure authentication or transport mechanisms. That means that hooking up all your directory data to the global Internet gives hackers a one-stop opportunity to steal your data. Not good. Of course, as with other protocols, there are several add-on security mechanisms for LDAP.

LDAP can (and does) fill books of its own. The type/name pairs are bulky to type and hard to remember, but they allow you to easily map in entire other naming systems, by simply assigning a type to a naming system and allowing that system's names to be values at that level. Remember that these names are hierarchical, so everything under cn (normally used for "common name") applies to (in this case) Michael Schwarz. If I defined the schema for my space, I could put anything I wanted under that name.

A common use of LDAP is for centralizing authentication and authorization data for users. Users authenticate to LDAP and all systems in an organization can validate a single credential to authenticate the userthe holy grail of single sign-in. Alas, doing this right is nontrivial because LDAP doesn't specify any mandatory authentication and encryption scheme. (Thus it is often the hacker's holy grail of single sniff-in and 0wn3d systems.)

21.3.2.4 Novell Directory Service (NDS)

Novell, the folks behind Netware, came up with NDS, which provides full directory services like LDAP/X.500, but (according to the computer presslet us confess right now that we have never directly used NDS or Microsoft's Active Directory) with a simpler API and easier administration. We don't know enough about it to comment on it. But we do know that JNDI can access it.

21.3.2.5 Microsoft's Active Directory

We have to do the same hand-waving here. Active Directory provides similar functionality to NDS and LDAP. We don't know enough about it to comment on it. But, again, JNDI can talk to it.

21.3.3. Putting a Face to a Name: JNDI

The Java Naming and Directory Interface package is designed to provide a common way to access all of these disparate naming and directory services.

The JNDI architecture consists of the JNDI API, which provides a consistent API, and a Service Provider Interface (SPI), which requires an instance to connect to each naming service (such as DNS, LDAP, the RMI registry, and so on).

Basic naming system functionality is obtained through the javax.naming package. Directory services are provided by the javax.naming.directory package.

Since JNDI can span multiple naming and directory systems, there are no absolute root contexts, so the InitialContext class exists to provide a base from which all other names and directories may be looked up.

21.3.3.1 A Sample JNDI Program

The next couple of sections describe a very simple JNDI application that uses the DNS Service Provider Interface to do directory operations on a DNS domain. The source code for the class is shown in Example 21.1.

Example 21.1. A sample JNDI application
    import java.util.*;    import javax.naming.*;    import javax.naming.directory.*;  5    public class GetDomain {      private Hashtable env = new Hashtable();      private DirContext dctx;      private String domainQuery; 10      public GetDomain(String dom2Query) throws NamingException {        domainQuery = dom2Query;        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");        dctx = new InitialDirContext(env); 15   }      public NamingEnumeration getDomainMembers() throws NamingException {        return dctx.list(domainQuery);      } 20      public static void main(String[] args) {        GetDomain gd = null;        NamingEnumeration ne = null; 25     try {          gd = new GetDomain(args[0]);          ne = gd.getDomainMembers();          while (ne.hasMore()) { 30         Object o = ne.next();            System.out.println("Object ["+o+"]");          }        } catch (Exception e) { 35       e.printStackTrace();        }      }    } 40 

Example 21.2 is what we get when we run this program against one of the author's DNS domains.[7]

[7] Note that directory operations in the JNDI DNS Service Provider Interface are done with DNS zone transfers. Many domains, especially large domains, disable zone transfers either for security reasons, or because they generate a lot of network traffic and are a popular tool for Denial of Service (DoS) attacks on name servers. To put it simply: This program won't work on a lot of domains, especially from outside.

Example 21.2. Running GeTDomain against the multitool.net domain
 [mschwarz@cassidy simpleApp]$ java GetDomain multitool.net Object [baroni: java.lang.Object] Object [erik: java.lang.Object] Object [www: java.lang.Object] Object [class: java.lang.Object] Object [jboss: java.lang.Object] Object [penguin: java.lang.Object] Object [mail: java.lang.Object] Object [cvs: java.lang.Object] Object [stiletto: java.lang.Object] Object [penfold: java.lang.Object] Object [ns2: java.lang.Object] Object [ns1: java.lang.Object] Object [irc: java.lang.Object] [mschwarz@cassidy simpleApp]$ 

The Getdomain main() method

This is another "single class" program example. In this case, the main() method creates an instance of the class, passing the first command-line argument to the constructor. We'll cover the constructor in the next section. By now, you will recognize that this is one of the purely pedagogical examples. Note the complete lack of input validation and error checking on the number and content of the command-line arguments.

Establishing an initial context

For both naming and directory services, it is necessary to establish an initial context. A context is a collected set of names. A directory system is a connected set of contexts. Our example is for DNS. We must set an initial context for DNS. The class constructor (lines 1115) does that.

So what is going on here? This constructor is a bit unusual, isn't it? The InitialDirContext is a "context factory." It takes an "environment," which is a Hashtable, that provides the information needed to make the context. And what is that information? At a minimum, the constant value associated with Context.INITIAL_CONTEXT_FACTORY must be associated with the class name of the real context factory for the directory systemin this case, com.sun.jndi.dns.DnsContextFactory. If you are from a C/C++ background, think of this as a function pointer.[8]

[8] Of course, it is not. What really happens here is that the code in InitialDirContext uses the Class class to load the specified class by name. All JNDI context factory classes implement the Context interface, so InitialDirContext uses the ability of Class to load the class by its name as an instance of Context.

We now have an initial directory context, which we can use to search.

Going from the initial context to a DNS entry

Let's now consider a use case for this little program. The program begins at main(), line 21. We create an instance of our class and an instance of NamingEnumeration (which we will discuss in a moment). We do some very lazy error handling by enclosing the entire process in a simple try/catch block[9] and treating all exceptions the same. The first thing we do is construct an instance of our class, passing in the first command-line argument[10] as the domain name to use for setting the initial context.

[9] JNDI has a particularly rich collection of exceptions. When a naming or directory operation fails, it is usually possible to determine exactly how and why it failed from the type of Exception thrown. All JNDI Exceptions extend NamingException, so they also make it quite easy to handle them in a lazy manner. In a real production application, you should at least make some effort to differentiate between failures where the network is not working and failures where the network is working fine, but the named resource does not exist. Believe us, if you have to support your application in production you will care about the difference.

[10] Again, very bad production coding. Note that no attempt is made to check the number of arguments passed or their contents.

Next, we get an enumeration of all the names in that context. This is done through a method in our class that simply wraps the actual JNDI call that obtains this enumeration. The real event is on line 18. The list() method of the directory context returns a NamingEnumeration, which is an extension of the classic Enumeration Java class. With it, you can iterate over the contents of the contextwhich we do in lines 2933. We rely on our old Object method, toString(), to make these names readable for us. Of course, each entry in the enumeration is actually a binding that binds the name to either a name object or to a context object.

If, when you encounter a context, you save the current context, set the current context to the new context, and make the method recursive, you would walk from the present context on down. In theory, you could set your initial context to "." (which is the root of DNS) and this program would dump the whole domain name system to you.[11]

[11] If you are a relatively unschooled net hooligan, let us assure you that this is only "in theory." Before you go off and attempt a DoS attack on the entire Internet with a simple Java class like this, we have to tell you that JNDI DNS enumerations depend on a DNS protocol feature called zone transfers. Most high-level DNS servers will not do zone transfers at all, and many will only accept zone transfer requests from internal addresses. Sorry.

21.3.3.2 Learning More about JNDI

As with so much in this book, we have had time and space to cover only the basics. There is so much more to JNDI. For now we want to point you at Sun's excellent JNDI Tutorial.[12] JNDI is covered in more depth in many books, including JNDI API Tutorial and Reference: Building Directory-Enabled Java Applications by Rosanna Lee and Scott Seligman, ISBN 0201705028.

[12] http://java.sun.com/products/jndi/tutorial

21.3.4. Using JNDI with JBoss

For our purposes, it is important to know that JBoss uses JNDI to provide much of the EJB container infrastructure. The primary use is to look up the EJBs, as we shall see in the code examples in the following chapters.



    Java Application Development with Linux
    Java Application Development on Linux
    ISBN: 013143697X
    EAN: 2147483647
    Year: 2004
    Pages: 292

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net