本文是
JNDI Tutorial
系列文章的第二部分:
The Basics
,介绍了
JNDI
的一些基础知识,诸如
Naming
操作和
Directory
操作。介绍了如何通过编程的方式访问命名和目录服务,如何使用
JNDI
和目录进行交互。从准备环境到查找对象以及在目录中进行搜索等操作。
本部分主要介绍了关于命名的一些操作
(Naming Operations)
可以通过使用
JNDI
来进行命名操作,例如读操作和更新名称空间操作。本部分内容中将主要介绍以下操作:
- Looking up an object
- Listing the contents of a context
- Adding, overwriting, and removing a binding
- Renaming an object
- Creating and destroying subcontexts
在这些例子中使用下面的环境属性初始化
initial context
:
// Set up environment for creating the initial context
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file:
d:/workspace/JNDITutorial
/tmp/tutorial");
Context ctx = new InitialContext(env);
查找对象
:
使用
Context.lookup()
方法来从命名服务中查找对象,需要将对象的名字作为参数传递给
lookup
方法。假设在命名服务中有一个叫做
report.txt
的对象,那么获取这个对象的代码为:
Object obj=ctx.lookup(“report.txt”);
lookup()
方法返回的对象的类型由命名系统和对象自身关联的数据决定。一个命名系统可能包含多种不同类型的对象,并且根据对象在命名系统的位置的不同也会导致对象类型的不同。在本例中,
”report.txt”
绑定到一个
java.io.File
对象,可以进行强制类型转换。下面是查找操作的源代码:
import java.io.File;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
/**
* Demonstrates how to look up an object.
*
* usage: java Lookup
*/
class Lookup {
public static void main(String[] args) {
// Set up the environment for creating the initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL,
"file:d:/workspace/JNDITutorial/tmp/tutorial");
try {
// Create the initial context
Context ctx = new InitialContext(env);
// Perform lookup and cast to target type
File f = (File) ctx.lookup("report.txt");
System.out.println(f);
// Close the context when we're done
ctx.close();
} catch (NamingException e) {
System.out.println("Lookup failed: " + e);
}
}
}
输出结果为:
d:/workspace/JNDITutorial/tmp/tutorial/report.txt
(
注:需要将
service provider
的
jar
包加入到
classpath
中,文件为
fscontext.jar
和
providerutil.jar)
(
注:不能够将
file:d:/workspace/JNDITutorial/tmp/tutorial
中的
file:
去掉,因为第一个冒号前面的字符串为协议,冒号和
d
之间可以加多个
”/”)
枚举上下文:
和调用
Context.lookup()
方法每次获取一个对象不同的是,使用
Context.list()
方法可以获取一个名称
-
对象枚举,
Context.listBindings()
方法可以返回一个所有的绑定。
Context.list()
方法
Context.list()
方法返回一个
NameClassPair
的美剧。每个
NameClassPair
由对象名和其所属的类名组成。下面的代码片断列出了
awt
目录下的内容
(
文件和目录
)
:
NamingEnumeration list = ctx.list("awt");
// Go through each item in list
while (list.hasMore()) {
NameClassPair nc = (NameClassPair) list.next();
System.out.println(nc);
}
运行上面例子的结果如下:
accessibility: javax.naming.Context
......
swing: javax.naming.Context
(
它只列举出
awt
目录下面的直接目录或者文件。
)
Context.listBindings()
方法
Context.listBindings()
方法返回一个绑定的枚举。
Binding
类似
NameClassPair
的子类。一个绑定不仅仅包好对象名和类名,还包含对象本身。下面的代码列举出
awt
上下文中的所有绑定并打印:
NamingEnumeration bindings = ctx.listBindings("awt");
// Go through each item in list
while (bindings.hasMore()) {
Binding bd = (Binding) bindings.next();
System.out.println(bd.getName() + ": " + bd.getObject());
}
输出结果如下:
accessibility: com.sun.jndi.fscontext.RefFSContext@1cf8583
......
swing: com.sun.jndi.fscontext.RefFSContext@dbe178
终止
NamingEnumeration
一个
NamingEnumeration
可以有三种终止方式:自然终止、显式终止和异常终止。
¨
当
NamingEnumeration.hasMore()
方法返回
false
的时候,枚举已经完成,所以终止。
¨
可以在枚举完成之前显式的终止它,通过调用
NamingEnumeration.close()
方法。
¨
如果
hasMore()
方法或者
next()
方法抛出
NamingException
时,枚举终止。
无论以哪种方式终止
NamingEnumeration
,一旦终止后,就不能再被使用。调用已经终止的枚举对象会返回一个未定义的结果。
为什么有两种不同的列举方法
list()
方法的目的是提供给浏览器风格的应用程序,这些应用程序只希望显示上下文中的对象的名称。例如一个浏览器可能只希望列出上下文中的名字供用户选择以便执行更进一步的操作,这样的应用程序一般不需要访问上下文中的所有对象。
listBindings()
方法的目的是提供给应用程序,这些应用程序对上下文中的对象进行操作。例如,一个备份应用程序可能需要对一个文件目录中的所有对象执行
”file stats”
操作。又或者一个打印机管理程序可能希望重启整个大厦中的所有打印机。为了执行这些操作,应用程序需要获取上下文中绑定的所有对性。所以这个时候需要将对象本身作为结果返回。但是使用
listBindings()
方法的开销要比
list()
的大。
添加、替换和移除绑定
Context
接口包含添加、替换和删除绑定的方法。
添加绑定
Context.bind()
方法用于向上下文中添加一个绑定,它接受绑定的名字和要绑定的对象作为参数。
下面的例子演示了将一个
Fruit
对象和
favoriate
名字进行绑定:
package com.sun.jndi.examples.basics;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.naming.spi.ObjectFactory;
/**
* Demonstrates how to add a binding to a context. (Use Rebind example to
* overwrite binding; use Unbind to remove binding.)
*
* usage: java Bind
*/
public class Bind {
public static void main(String[] args) {
// Set up the environment for creating the initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.fscontext.RefFSContextFactory");
env.put(Context.PROVIDER_URL, "file:/tmp/tutorial");
try {
// Create the initial context
Context ctx = new InitialContext(env);
// Create the object to be bound
Fruit fruit = new Fruit("orange");
// Perform the bind
ctx.bind("favorite", fruit);
// Check that it is bound
Object obj = ctx.lookup("favorite");
System.out.println(obj);
// Close the context when we're done
ctx.close();
} catch (NamingException e) {
System.out.println("Operation failed: " + e);
}
}
}
class Fruit implements Referenceable {
String fruit;
public Fruit(String f) {
fruit = f;
}
public Reference getReference() throws NamingException {
return new Reference(Fruit.class.getName(), new StringRefAddr("fruit",
fruit)
, FruitFactory.class.getName(), null); // factory
// location
}
public String toString() {
return fruit;
}
}
public
class FruitFactory implements ObjectFactory {
public FruitFactory() {
}
public Object getObjectInstance(Object obj, Name name, Context ctx,
Hashtable env) throws Exception {
if (obj instanceof Reference) {
Reference ref = (Reference) obj;
if (ref.getClassName().equals(Fruit.class.getName())) {
RefAddr addr = ref.get("fruit");
if (addr != null) {
return new Fruit((String) addr.getContent());
}
}
}
return null;
}
}
(
注:本例中的三个类都是
public
类型的类,但是为了代码的简洁,笔者将其都写在了一个类中,并将
Fruit
类和
FruitFactory
类定义为了
friendly
类型的类,但是执行的时候抛出了异常,信息如下:
Operation failed: javax.naming.NamingException: unexpected exception [Root exception is java.lang.IllegalAccessException: Class javax.naming.spi.NamingManager can not access a member of class com.sun.jndi.examples.basics.FruitFactory with modifiers "public"]; remaining name 'favorite'
在
ObjectFactory
的类说明中有如下的说明:
An object factory must implement the ObjectFactory interface. In addition, the factory class must be public and must have a public constructor that accepts no parameters.
所以必须将
FruitFactory
类定义为
public
类型的类,并且提供一个
public
的无参构造方法。而
Fruit
类没有这个限制
)
上述例子创建了一个
Fruit
类的对象,然后将其和名称
”favorite”
进行绑定。如果接着执行检索
favorite
名称,那么将返回
fruit
对象。在编译
Fruit
类的时候,需要使用
FruitFactory
类。如果执行例子两次的时候,将会抛出异常,异常为
NameAlreadyBoundException
。这是由于
favorite
已经被绑定。为了使再次绑定成功,需要使用
rebind()
方法。或者先移除再重新绑定。
添加或者替换绑定
rebind()
方法用来添加或者替换一个存在的绑定。它接受和
bind()
方法相同的参数。但是执行的顺序是如果绑定存在的话,就先解除绑定,然后绑定到指定的新的对象。
Fruit fruit = new Fruit("lemon");
// Perform the bind
ctx.rebind("favorite", fruit);
// Check that it is bound
Object obj = ctx.lookup("favorite");
System.out.println(obj);
此时无论程序执行几次,都会打印出
lemon
作为结果。
执行完上述程序后,在
D:/workspace/JNDITutorial/tmp/tutorial
中生成
.bindings
文件,内容如下:
#This file is used by the JNDI FSContext.
#Tue Dec 04 15:47:06 JST 2007
favorite/RefAddr/0/Type=fruit
favorite/RefAddr/0/Content=lemon
favorite/FactoryName=com.sun.jndi.examples.basics.FruitFactory
favorite/RefAddr/0/Encoding=String
favorite/ClassName=com.sun.jndi.examples.basics.Fruit
移除绑定
通过使用
unbind()
方法移除绑定。
重命名对象
使用Context.rename()方法重命名对象:
// Rename to old_report.txt
ctx.rename("report.txt", "old_report.txt");
上面的代码将绑定到report.txt的对象绑定到old_report.txt。
创建和销毁上下文
// Rename to old_report.txt
ctx.rename("report.txt", "old_report.txt");
上面的代码将绑定到report.txt的对象绑定到old_report.txt。
创建和销毁上下文
Context
接口提供了创建或者销毁子上下文的接口,一个上下文可以被绑定到相同类型的其他上下文。对应到文件系统的话,也就是可以创建或者删除一个子目录。
创建上下文
通过传递上下文的名称给
createSubcontext()
方法来创建子上下文:
// Create the context
Context result=ctx.createSubcontext(“new”);
执行示例程序后,会在
tuitorial
目录下创建新的目录
new
。
销毁上下文
通过调用
destroySubcontext()
方法来销毁参数指定的上下文。
// Destroy the context
ctx.destroySubcontext(“new”);
结合创建和删除上下文,代码如下:
Context ctx = new InitialContext(env);
try {
ctx.lookup("new");
System.out.println("destroying context new...");
ctx.destroySubcontext("new");
} catch (NameNotFoundException e) {
}
// Create the context
System.out.println("creating context new...");
Context result = ctx.createSubcontext("new");
// Check that it was created by listing its parent
Context newCtx = (Context) ctx.lookup("new");
System.out
.println("the new context:" + newCtx.getNameInNamespace());
// Close the context when we're done
ctx.close();
连续执行两次,结果如下:
creating context new...
the new context:d:/workspace/JNDITutorial/tmp/tutorial/new
destroying context new...
creating context new...
the new context:d:/workspace/JNDITutorial/tmp/tutorial/new