Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Mastering Enterprise JavaBeans™ and the Java 2 Platform, Enterprise Edition - Roman E

..pdf
Скачиваний:
41
Добавлен:
24.05.2014
Размер:
6.28 Mб
Скачать

584 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

/*

* 2) Get the NameParser from the service provider. */

parser = ctx.getNameParser("");

/*

*3) Use the NameParser to create the initial location

*of where we are in the directory structure. Because

*the NameParser is service provider specific, the

*resulting Name object will be formatted to a

*specific directory syntax.

*/

currName = parser.parse("");

}

/**

*Call to begin browsing the file system directory structure.

*This method isn't important, it just interacts with the user.

*/

public void browse() {

/*

* Start reading input from standard input */

String line = null, command = null, args = null;

StringTokenizer tokens = null;

BufferedReader reader =

new BufferedReader(new InputStreamReader(System.in));

while (true) {

/*

*Print prompt, read next input line,

*and get command

*/ try {

System.out.println();

System.out.print(currName + "> ");

line = reader.readLine();

System.out.println();

tokens =

new StringTokenizer(line, " ", false);

//Get command. e.g. "cd" in "cd .." command = tokens.nextToken();

//Get arguments. e.g. ".." in "cd .." if (tokens.hasMoreElements()) {

Source B.2 Browser.java (continues).

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 585

args = line.substring( command.length()+1, line.length());

}

}

catch (Exception e) { continue;

}

/*

*Do case analysis based on command. Call

*the corresponding JNDI function.

*/ try {

if (command.equals("ls")) { ls();

}

else if (command.equals("mv")) { /*

*Figure out the name of the

*context the user is trying

*to rename (mv)

*/

String oldStr = null, newStr = null;

try {

StringTokenizer argtokens = new StringTokenizer(args, " ", false);

oldStr = argtokens.nextToken(); newStr = argtokens.nextToken();

}

catch (Exception e) {

throw new Exception("Syntax: mv <old context> <new context>");

}

mv(oldStr, newStr);

}

else if (command.equals("cd")) { cd(args);

}

else if (command.equals("mkdir")) { mkdir(args);

}

else if (command.equals("rmdir")) { rmdir(args);

}

Source B.2 Browser.java (continues).

Go back to the first page for a quick link to buy this book online!

586 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

else if (command.equals("cat")) { cat(args);

}

else if (command.equals("quit")) { System.exit(0);

}

else { System.out.println("Syntax:

[ls|mv|cd|mkdir|rmdir|cat|quit] [args...]");

}

}

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

}

}

}

/**

* Lists the contents of the current context (folder) */

private void ls() throws Exception {

//Get an enumeration of Names bound to this context NamingEnumeration e = ctx.list(currName);

//Each enumeration element is a NameClassPair.

//Print the Name part.

while (e.hasMore()) {

NameClassPair p = (NameClassPair) e.next();

System.out.println(p.getName());

}

}

/**

* Navigate the directory structure */

private void cd(String newLoc) throws Exception { if (newLoc == null) {

throw new Exception("You must specify a folder");

}

//Save the old Name, in case the user tries to cd

//into a bad folder.

Name oldName = (Name) currName.clone();

try { /*

*If the user typed "cd ..", pop up one

*directory by removing the last element from

Source B.2 Browser.java (continues).

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 587

* the Name. */

if (newLoc.equals("..")) {

if (currName.size() > 0) { currName.remove(currName.size()-1);

}

else { System.out.println(

"Already at top level.");

}

}

/*

*Otherwise, the user typed "cd <folder>". Go

*deeper into the directory structure.

*

*Note that we use "addAll()" which will add every

*atomic folder name into the total name. This means

*when we type "cd .." later, we will only traverse

*down one folder.

*/ else {

currName.addAll(parser.parse(newLoc));

}

/*

*Confirm our traversal by trying to do a lookup()

*operation after we've popped out a directory. If

*lookup() fails, we need to restore the directory

*Name to what it was, and throw an exception.

*/

ctx.lookup(currName);

}

catch (Exception e) { currName = oldName;

throw new Exception("Cannot traverse to desired directory: " + e.toString());

}

}

/**

*Renames (moves) one context to a new name.

*@param oldStr the old context

*@param newStr the new context

*/

private void mv(String oldStr, String newStr) throws Exception {

Source B.2 Browser.java (continues).

Go back to the first page for a quick link to buy this book online!

588 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

/*

* Navigate to the current context (folder) */

Context currCtx = (Context) ctx.lookup(currName);

/*

* Rename the subcontext */

currCtx.rename(oldStr, newStr);

}

/**

* Makes a subfolder (subcontext) */

private void mkdir(String str) throws Exception { Context currCtx = (Context) ctx.lookup(currName); currCtx.createSubcontext(str);

}

/**

* Removes a subfolder (subcontext) */

private void rmdir(String str) throws Exception { Context currCtx = (Context) ctx.lookup(currName); currCtx.destroySubcontext(str);

}

/**

* displays a file (specific to File System service provider) */

private void cat(String fileStr) throws Exception {

/*

* Append the filename to the folder string */

Name fileName = (Name) currName.clone(); fileName.addAll(parser.parse(fileStr));

/*

* Use JNDI to get a File Object reference */

File f = (File) ctx.lookup(fileName);

/*

* Print out file contents */

FileReader fin = new FileReader(f);

Writer out = new PrintWriter(System.out);

Source B.2 Browser.java (continues).

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 589

char[] buf = new char[512];

int howmany;

while ((howmany = fin.read(buf)) >= 0) {

out.write(buf, 0, howmany);

}

out.flush();

fin.close();

}

}

Source B.2 Browser.java (continued).

We then acquire a NameParser object and use it to create the currName object, which is of type Name. currName identifies where we are in the directory structure, such as /usr/bin. Objects of type Name can be either compound or composite names—the Name interface abstracts that out.

Why do we use NameParser to create currName? Can’t we create Name objects directly, by using, for example, the CompoundName object constructor? Well, remember that CompoundName objects can be composed of more than one atomic name—/usr/bin has two atomic names. CompoundName objects also expose methods such as add(), which will add other names to lengthen the total compound name. But if we’re going to construct a compound name from individual atomic names, we’re going to need to specify the particular naming convention for the service provider being used. For example, in the LDAP string cn=Ed Roman, ou=People, o=Middleware-Company.com, the string cn=Ed Roman is an atomic name, but the comma “,” is a separator character that is specific to LDAP. The File System service provider, in comparison, might have a compound name such as /usr/bin, where a different separator character, “/”, demarcates the atomic name boundaries.

To achieve service provider independence, we acquire a NameParser object from the Initial Context, which knows how to take a java.lang.String and convert it into a Name. Once we’ve got the Name, we can pass it around without knowing what particular directory structure is being used. For example, let’s compare running the cd operation on a file system versus an LDAP directory. Here’s the result of navigating a file system:

java -Djava.naming.factory.initial =

com.sun.jndi.fscontext.RefFSContextFactory

-Djava.naming.provider.url =

file:c:\ com.wiley.compBooks.Browser.Browser

> ls

Go back to the first page for a quick link to buy this book online!

590 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

AUTOEXEC.BAT

COMMAND.COM

Program Files

WINNT

> cd Program Files

Program Files> ls

Accessories

Internet Explorer

Java Plug-in 1.1

Microsoft Visual Studio

MSMQ

NetMeetingNT winamp

Program Files> cd winamp

Program Files\winamp> ls

maps pics Plugins Skins tabs

winamp.ini

winamp.m3u

Program Files\winamp> cat winamp.ini

[3Dfx Squiggle] Screen_x=50527560 Screen_y=50527580

Program Files\winamp> cd ..

Program Files\winamp> cd ..

Program Files> cd ..

> quit

And here’s the result of navigating an LDAP tree:

java -Djava.naming.factory.initial =

com.sun.jndi.ldap.LdapCtxFactory

-Djava.naming.provider.url =

ldap://louvre:389/o=Airius.com

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 591

com.wiley.compBooks.Browser.Browser

> ls

ou=Groups

ou=People ou=Special Users ou=Netscape Servers

> cd ou=Groups

ou=Groups> ls

cn=Directory Administrators cn=Accounting Managers cn=HR Managers

cn=QA Managers cn=PD Managers

ou=Groups> cd cn=Directory Administrators

cn=Directory Administrators,ou=Groups> cd ..

ou=Groups> cd ..

> quit

Notice that in the File System example, we recursed into the subfolder Program Files/winamp, while in the LDAP tree, we recursed into cn=Directory Administrators, ou=Groups. The LDAP string is in the reverse order! But that’s OK, because this is the LDAP convention. When we perform our cd .. operation, the LDAP service provider correctly removes the cn=Directory Administrators atomic name because the LDAP service provider is smart enough to know that LDAP strings are in reverse order. Our client code, however, was never aware of it. We simply added the next context on. This is the power of JNDI in action, separating us from the specific directory protocol being used.

Next, let’s take a look at some of the directory operations we implemented. The cd method is fairly straightforward: It modifies the currName object based on where the user wants to go. It then checks to make sure the updated currName is a valid subcontext by performing a Naming.lookup() operation, which will throw an exception if currName is not a valid context. The ls method has the following body:

//Get an enumeration of Names bound to this context NamingEnumeration e = ctx.list(currName);

//Each enumeration element is a NameClassPair.

//Print the Name part.

Go back to the first page for a quick link to buy this book online!

592 M A S T E R I N G E N T E R P R I S E J A V A B E A N S

while (e.hasMore()) {

NameClassPair p = (NameClassPair) e.next();

System.out.println(p.getName());

}

With ls, we first performed a list() operation, which returns all of the bound objects (including subcontexts) in the current context. The returned results are a set of NameClassPair objects, which identify the Name of the bound object and the class of the object to which the Name is bound. By printing out the Names, we get the desired functionality of ls. Notice that we used list(), as opposed to its cousin method, listBindings(). listBindings() is a less efficient method that returns the bound objects themselves, not just their classes.

mkdir, rmdir, and mv are also straightforward. The one interesting case is cat. cat works only with the File System service provider. It retrieves a whole bound object (not just its class). The File System service provider’s specific implementation returns a File object, which can be treated like any regular Java File object. That is why we can print out the File object—it was located and returned

Does JNDI Truly Bridge Arbitrary Naming and

Directory Services?

JNDI is wonderful because you can now access different directories through one standard API. But does it truly bridge naming and directory services, allowing your client code to remain constant if you switch directories? The answer, unfortunately, is no. The JNDI API is a standard interface for you to work with, separating you from proprietary APIs and protocols. Each directory, though, must be navigated according to a different syntax. How can we then keep our client code static?

There is no easy answer to this. As it stands, JNDI cannot eliminate fully your client code changes if you switch to a different directory. But by using a few tricks, you can minimize these client code changes.

Let’s say you want to reference a directory entry deep within a tree. If you specify the entry using a long string such as “cn=Ed Roman, ou=People, o=wiley.com, c=us”, you are committing to a specific syntax (here, LDAP’s). If you switch from LDAP, you will need to use a different syntax. How can you avoid this problem?

Well, one technique you can use is to acquire a list of all contexts at every level of the directory tree. Instead of identifying an absolute location, such as “cn=Ed Roman, ou=People, o=wiley.com, c=us”, you can start at the root node (“o=wiley.com, c=us”) and get a list of all subcontexts. From there, you can choose the context “People” and get a list of all subcontexts there. Then you will finally arrive at the “Ed Roman” object. This eliminates the need for directory-specific syntax. Unfortunately, it is a costly technique because you need to access the directory many times (once at each level of the tree)

Go back to the first page for a quick link to buy this book online!

Understanding the Java Naming And Directory Interface (JNDI) 593

to us through the File System naming service. This illustrates what JNDI can do—you can store many different kinds of objects using JNDI, and you can use it as a generic lookup service for your objects.

One take-away point from this example is that we were able to plug in the LDAP provider with zero code changes. Slap on a JFC/Swing UI, and you’ve got a Windows NT explorer written in JNDI that can work with any kind of directory protocol, not just a file system. Now that we’ve written this browser, we can use it to debug any JNDI code you write or to see how objects are bound in the other examples in this appendix.

Advanced JNDI: Combining JNDI with JDBC

With EJB, you can use Java Database Connectivity (JDBC) operations from inside your beans to perform relational database access operations. JDBC is essentially a portable Java-based interface to relational databases. In fact, JDBC and JNDI are much alike.

rather than just once. You also need to perform list operations, which are expensive, especially if the list is long. In reality, if you want an efficient client, this scheme is not practical.

You can also keep your client code as portable as possible between directories, by keeping the directory syntax-specific section of your client code as small and mutable as possible, preferably by using properties files. But the real answer to this problem is to use Name objects rather than Strings. Name objects can be constructed with direc- tory-specific syntax from NameParser objects that know a particular service provider’s convention. Always use Name objects—they enable JNDI to be a closer to a true common directory API.

Of course, JNDI will never truly be able to bridge all naming and directory service technologies because of unsupported methods in certain service providers. If you want to use the qualities of service that a certain directory vendor uses to distinguish itself from other directory services, you may need to write directory-specific code. This is an unfortunate consequence of the nature of a competitive industry. It is also true with EJB—by using proprietary features that certain EJB container or server vendors provide, you are tying yourself to those vendors.

Realistically, this is the best that any kind of software that provides a common API bridge over arbitrary technologies is going to do. You have to remember that these directory protocols were invented totally independently. Just the mere existence of JNDI is a huge win. To address the needs of directory service vendors, JNDI encompasses a subset of the union of features that all vendors provide. And while the JNDI cannot possibly cover everything, the bases that it does cover are the most important ones. You can reap many benefits with the intelligent use of JNDI.

Go back to the first page for a quick link to buy this book online!