明辉手游网中心:是一个免费提供流行视频软件教程、在线学习分享的学习平台!

J2EE or J2SE? JNDI works with both(from javaworld)

[摘要]欢迎大家指正!-----------------------------J2EE or J2SE? JNDI works with bothJNDI(Java Naming and Directory Interface), 允许应用通过一个通用的接口来访问各种命名和目录服务。下面的特征表征了JN...
欢迎大家指正!
-----------------------------
J2EE or J2SE? JNDI works with both

JNDI(Java Naming and Directory Interface), 允许应用通过一个通用的接口来访问各种命名和目录服务。下面的特征表征了JNDI的架构。如同JDBC(Java Database Connectivity),JNDI并不是一种服务,而是一套接口;它允许应用使用标准API来访问很多不同的目录服务供应商。尽管Sun公司提供了适配器,比如LADP、DNS和CORBA,以连接已有的目录服务供应商,但是就如同JDK包含JDBC一样,它包含了JNDI接口,但并不包含JNDI服务供应商。然而,您可以选择使用免费或者开源JNDI供应商中的一种到您的J2SE应用中去。

JNDI是整合J2EE应用的黏合剂。JNDI被设计用来支持具有较高动态组装和部署的那些应用,可以在不用重建整个系统的条件下经常的添加和更新各种组件。命名服务则通过作为一个组件注册中心来帮助组织企业级的应用。J2EE应用一般在如下一些方面使用JNDI:

# 在一个集中、分级数据库中储存应用配置信息

# 作为在不同应用组件间共享的活动对象的仓库,这些组件能够运行在不同的JVM或者不同的系统中

# 作为已有目录服务例如LDAP的接口(通过使用特别的方式来访问外部的服务)

象J2EE应用一样,更大、更多的动态J2SE应用将得益于由动态目录服务带来的松偶合以及动态绑定特点。


一个简单的JNDI例子

从JNDI命名空间中储存和回溯对象是简单和易懂的;首先,获得JNDI的命名上下文(naming context),然后,使用bind()和lookup()方法来储存和回溯对象,就象Listing 1中显示的那样:

Listing 1. Store and retrieve objects from a JNDI namespace

import javax.naming.*;

public void createName() throws NamingException {
Context context = new InitialContext();
context.bind("/config/applicationName", "MyApp");
}

public String getName() throws NamingException {
Context context = new InitialContext();
return (String) context.lookup("/config/applicationName");
}

Listing 1演示的是最通常的JNDI操作:创建一个JNDI上下文,为该上下文绑定对象,从该上下文中回溯对象。需要注意的是,JNDI命名空间以及客户端可能驻留在不同的JVM中,因此bind()和lookup()方法的调用可能发生在不同的JVM中。JNDI提供商通过使用一系列的技术,包括序列化,来保证对象能够在不同的JVM之间移动,并且可以回溯为它原来的形式。

Listing 1的代码片断有几个隐藏的假设:JNDI是如何知道在创建上下文的时候该使用哪个提供商?对于需要安全验证的提供商,信任书又从哪里来呢?一般来说,您可以通过在系统属性中设定JNDI相关的属性,或者把它们设定到jndi.properties文件中来制定提供商以及其它连接是所需要的参数。当然,任何的提供商都不能随意的绑定和回溯对象。例如,一些提供商,比如DNS提供商,对象类型是只读的,而有些提供商却可以提供更加灵活的对象类型。大多数卖方提供的供应商能储存和回溯那些实现了以下任何一个接口的对象:java.io.Serializable, java.rmi.Remote, 或者 javax.naming.Referenceable。

隐藏的JNDI供应商
具有以上特征的JNDI供应商有相同的地方:它们都通过代理来访问外部的目录服务,比如LADP。然而,这里面没有包含一种重要的JNDI供应商类型:内嵌在J2EE容器中的JNDI供应商,它把目录信息储存在内部的数据库中。当我们讨论在J2EE应用中使用JNDI时,通常指的就是这种容器提供的供应商。


J2EE是如何使用JNDi的?

J2EE应用是以组件形式组装起来的。组件的配置以及互相之间的连接的方式是在部署的时候指定的;大多数的信息被储存在JNDI的命名空间中。J2EE应用使用JNDI来储存配置信息(比如string和numeric类型的常量),无状态的对象(包括对象工厂),以及EJB的home接口。

JNDI可以看做是黏合剂,它允许您以组件形式比如servlets、Jsps以及EJBs,构建J2EE应用。在J2EE应用中,每个组件不是通过静态的链接而是通过JNDI查找来找寻其它的组件的。J2EE应用还允许在维护类型和链接安全性时进行部署期的绑定,这是通过每个组件报告一份它将需要的其它组件以及资源列表实现的。部署保证在应用中每次导入都有相关正确的组件类型。J2EE容器提供工具来帮助您正确的完成这项工作。


How can J2SE apps use JNDI?

如同J2EE应用一样,J2SE应用能够将JNDI作为一个储存了命名配置参数、对象以及对象工厂的仓库。工厂创建模式和JNDI可以很好的配合工作,你能够在JNDI命名空间中只保存工厂对象。J2SE应用能够使用JNDI来替换RMI(远程方法调用)来达到更加强大、功能丰富以及集中化的特点。

大多数的J2SE应用从配置文件中来获取它们的配置信息,这些配置文件要么是属性文件要么是XML文档。这些配置文件指定了所有的配置信息;一些更加复杂的应用为实例化对象的条件比如类名和构造器参数在配置文件中储存信息。

使用JNDI来储存常量、对象以及对象工厂比传统的配置存储机制有一些优势。因为大多数的JNDI供应商都是支持网络访问的,您就不必为保证每个分散的应用主机上配置文件的一致性而花费精力。此外,使用反射机制来实例化对象是件凌乱的事情,并且需要比简单的通过JNDI命名空间回溯对象更多的代码(一般是更多的出错恢复代码)。使用JNDI并不能避免从配置信息中实例化对象的需要(通常在应用启动,你获得命名空间的时候),这种复杂程度已经从大多数的应用代码中剥离出去了,这样就能简化从JNDI命名空间中回溯期望的对象的程度。

另外一个在分布式应用中使用JNDI而不是配置文件的原因是配置文件中的信息可能是敏感的信息,比如访问数据库的密码。将Datasource储存在JNDI的名称空间中,可以不用为整个应用设置密码就完成对整个应用的数据库连接。(比如,PoolMan连接池,一个广泛被使用的开源项目,就是将Datasource对象储存在JNDI名称空间中的。)

为了在您的J2SE应用中使用JNDI,您首先需要一个JNDI供应商,因为JDK并没有包含。如果您只是想用JNDI来访问外部的目录比如LDAP,那么您可以使用由Sun提供的供应商中的一个。但是通常来说,您将希望拥有一个独立的供应商,可以有许多的选项供您选择。


独立的JNDI供应商

开放原码J2EE服务器JBoss包含了一个JNDI供应商(JNP),它能作为独立服务运行;他提供非常出色的轻量级网络访问JNDI服务。JNP使用记忆在内存中的数据库来存储对象,所以命名空间的内容在服务重新启动时将不会保存。您可以非常容易的靠它自己来运行JNP,或者通过配置JBoss容器使只有在JNP服务的时候来启动它。

选择1:配置JNP服务以作为JBoss的一项服务
JBoss应用服务器建立在JMX(Java管理扩展)框架上;该框架允许将应用服务模块化为JMX mbeans,这样可以独立的启动和管理它们。文件jboss.jcml包含了在容器启动的时候装载的一个mbeans列表。JBoss缺省的配置包含超过50个mbeans,但是如果您配置jboss.jcml文件应该只包含单独的如Listing 2中的配置,然后启动JBoss服务器,那么服务器将只装载JNDI供应商,其它的任何J2EE应用服务将不会被装载:

Listing 2. 仅包含JNDI的jboss.jcml文件

<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="org.jboss.naming.NamingService"
 name="DefaultDomain:service=Naming">
<attribute name="Port">1099</attribute>
</mbean>
</server>

选择2:配置JNP服务器使得它单独运行
JNP还可以作为一个单独存在的应用服务器。为达到这个目的,您需要下面两个jar配置文件:

# jnpserver.jar -- 在lib/ext目录
# log4j.jar -- 广泛被使用的日志工具,来自Apache Jakarta项目;在JBoss的lib/ext目录

此外,您还需要一个log4j.properties文件;Listing 3显示了一个简单的情况。该文件可以通过如下的途径来访问:

Listing 3. 简单的log4j.properties文件

# Use a ConsoleAppender -- write log data to standard out
log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

为了在运行模式下运行JNP服务器,请确保log4j.jar文件、jnpserver.jar文件以及包含log4j.properties文件的目录都可以在您的classpath找到。然后,运行如下命令启动JNP服务器:

java org.jnp.server.Main

您也可以在您应用的JVM中使用org.jnp.server对象来启动JNDI服务。


使用JNDI服务

一旦JNP服务器运行起来,您可以通过配置jnp-clinent.jar文件包含在classpath中,以及在系统属性或者jndi.properties文件中指定java.naming.provider.url和java.naming.factory.initial属性,以让您的应用使用JNP。Listing 4显示了一个jndi.properties文件的例子:

Listing 4. jndi.properties文件

java.naming.provider.url=jnphost.mycompany.com:1099
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming

下面的例子显示了在应用启动的时候如何使用在属性文件中定义好的string、integer这些对象值相关的参数来获得JNDI命名空间,以及应用组件回溯这些属性。简短来说,Listing 5a假设解析XML配置文件的方法并且省略了出错处理的代码:

Listing 5a. 从配置文件中装载JNDI命名空间

 public void loadJNDI() {
Context context = new InitialContext();
ConfigItem[] items = getConfigItems();
for (int i=0; i<items.length; i++) {
Object o=null;

if (items[i].getType().equals("Integer"))
o = Integer.decode(items[i].getValue());
else if (items[i].getType().equals("String"))
o = items[i].getValue();
else if (items[i].getType().equals("Object"))
o = Class.forName(items[i].getValue()).newInstance();

context.bind(items[i].getName(), o);
}
}


Listing 5b. XML配置文件示例

 <config>
<item name="config/screen/resolutionX" type="Integer"
value="1024" />
<item name="config/screen/resolutionY" type="Integer"
value="768" />
<item name="converters/html" type="Object"
value="com.mycompany.converters.HtmlRenderer" />
<item name="converters/pdf" type="Object"
value="com.pdfmonger.PdfRenderer" />
</config>


Listing 5c. 在JNDI中回溯和使用对象

 public void convert(InputStream in, OutputStream out) {
// Retrieve the converter object from JNDI
Context context = new InitialContext();
Renderer renderer = (Renderer) context.lookup("converters/html");

// Use the converter object
renderer.convert(in, out);
}


正如您看到的,从JNDI中回溯对象是非常简单和易懂的。通过使用JNDI来存储配置信息、无状态对象或者对象工厂,您可以轻松的创建灵活的应用,同时将负责的配置信息包含在一个单一的地方,甚至对于分布式的应用也是可以的。(如果您的组件是通过JNDI命名空间来访问对象的,那么将它们之间的依赖关系文档化到这些组件的Javadoc中。)

JNDI并不是只为了J2EE而存在的

尽管JNDI客户端接口也是J2SE的一部分,但是大多数的J2SE应用并没有使用JNDI。那些使用JNDI的通常只是利用它来访问外部的目录服务比如LDAP。然而,J2SE应用还可以使用那些到目前为止只有J2EE应用使用的部署时绑定的特征。随着越来越多的JNDI供应商实现例如JNP的出现,任何需要命名服务的应用都可以拥有它们了。