明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

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

[摘要]歡迎大家指正。璊2EE or J2SE? JNDI works with bothJNDI(Java Naming and Directory Interface), 允許應(yīng)用通過(guò)一個(gè)通用的接口來(lái)訪問(wèn)各種命名和目錄服務(wù)。下面的特征表征了JN...
歡迎大家指正!
-----------------------------
J2EE or J2SE? JNDI works with both

JNDI(Java Naming and Directory Interface), 允許應(yīng)用通過(guò)一個(gè)通用的接口來(lái)訪問(wèn)各種命名和目錄服務(wù)。下面的特征表征了JNDI的架構(gòu)。如同JDBC(Java Database Connectivity),JNDI并不是一種服務(wù),而是一套接口;它允許應(yīng)用使用標(biāo)準(zhǔn)API來(lái)訪問(wèn)很多不同的目錄服務(wù)供應(yīng)商。盡管Sun公司提供了適配器,比如LADP、DNS和CORBA,以連接已有的目錄服務(wù)供應(yīng)商,但是就如同JDK包含JDBC一樣,它包含了JNDI接口,但并不包含JNDI服務(wù)供應(yīng)商。然而,您可以選擇使用免費(fèi)或者開源JNDI供應(yīng)商中的一種到您的J2SE應(yīng)用中去。

JNDI是整合J2EE應(yīng)用的黏合劑。JNDI被設(shè)計(jì)用來(lái)支持具有較高動(dòng)態(tài)組裝和部署的那些應(yīng)用,可以在不用重建整個(gè)系統(tǒng)的條件下經(jīng)常的添加和更新各種組件。命名服務(wù)則通過(guò)作為一個(gè)組件注冊(cè)中心來(lái)幫助組織企業(yè)級(jí)的應(yīng)用。J2EE應(yīng)用一般在如下一些方面使用JNDI:

# 在一個(gè)集中、分級(jí)數(shù)據(jù)庫(kù)中儲(chǔ)存應(yīng)用配置信息

# 作為在不同應(yīng)用組件間共享的活動(dòng)對(duì)象的倉(cāng)庫(kù),這些組件能夠運(yùn)行在不同的JVM或者不同的系統(tǒng)中

# 作為已有目錄服務(wù)例如LDAP的接口(通過(guò)使用特別的方式來(lái)訪問(wèn)外部的服務(wù))

象J2EE應(yīng)用一樣,更大、更多的動(dòng)態(tài)J2SE應(yīng)用將得益于由動(dòng)態(tài)目錄服務(wù)帶來(lái)的松偶合以及動(dòng)態(tài)綁定特點(diǎn)。


一個(gè)簡(jiǎn)單的JNDI例子

從JNDI命名空間中儲(chǔ)存和回溯對(duì)象是簡(jiǎn)單和易懂的;首先,獲得JNDI的命名上下文(naming context),然后,使用bind()和lookup()方法來(lái)儲(chǔ)存和回溯對(duì)象,就象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操作:創(chuàng)建一個(gè)JNDI上下文,為該上下文綁定對(duì)象,從該上下文中回溯對(duì)象。需要注意的是,JNDI命名空間以及客戶端可能駐留在不同的JVM中,因此bind()和lookup()方法的調(diào)用可能發(fā)生在不同的JVM中。JNDI提供商通過(guò)使用一系列的技術(shù),包括序列化,來(lái)保證對(duì)象能夠在不同的JVM之間移動(dòng),并且可以回溯為它原來(lái)的形式。

Listing 1的代碼片斷有幾個(gè)隱藏的假設(shè):JNDI是如何知道在創(chuàng)建上下文的時(shí)候該使用哪個(gè)提供商?對(duì)于需要安全驗(yàn)證的提供商,信任書又從哪里來(lái)呢?一般來(lái)說(shuō),您可以通過(guò)在系統(tǒng)屬性中設(shè)定JNDI相關(guān)的屬性,或者把它們?cè)O(shè)定到j(luò)ndi.properties文件中來(lái)制定提供商以及其它連接是所需要的參數(shù)。當(dāng)然,任何的提供商都不能隨意的綁定和回溯對(duì)象。例如,一些提供商,比如DNS提供商,對(duì)象類型是只讀的,而有些提供商卻可以提供更加靈活的對(duì)象類型。大多數(shù)賣方提供的供應(yīng)商能儲(chǔ)存和回溯那些實(shí)現(xiàn)了以下任何一個(gè)接口的對(duì)象:java.io.Serializable, java.rmi.Remote, 或者 javax.naming.Referenceable。

隱藏的JNDI供應(yīng)商
具有以上特征的JNDI供應(yīng)商有相同的地方:它們都通過(guò)代理來(lái)訪問(wèn)外部的目錄服務(wù),比如LADP。然而,這里面沒有包含一種重要的JNDI供應(yīng)商類型:內(nèi)嵌在J2EE容器中的JNDI供應(yīng)商,它把目錄信息儲(chǔ)存在內(nèi)部的數(shù)據(jù)庫(kù)中。當(dāng)我們討論在J2EE應(yīng)用中使用JNDI時(shí),通常指的就是這種容器提供的供應(yīng)商。


J2EE是如何使用JNDi的?

J2EE應(yīng)用是以組件形式組裝起來(lái)的。組件的配置以及互相之間的連接的方式是在部署的時(shí)候指定的;大多數(shù)的信息被儲(chǔ)存在JNDI的命名空間中。J2EE應(yīng)用使用JNDI來(lái)儲(chǔ)存配置信息(比如string和numeric類型的常量),無(wú)狀態(tài)的對(duì)象(包括對(duì)象工廠),以及EJB的home接口。

JNDI可以看做是黏合劑,它允許您以組件形式比如servlets、Jsps以及EJBs,構(gòu)建J2EE應(yīng)用。在J2EE應(yīng)用中,每個(gè)組件不是通過(guò)靜態(tài)的鏈接而是通過(guò)JNDI查找來(lái)找尋其它的組件的。J2EE應(yīng)用還允許在維護(hù)類型和鏈接安全性時(shí)進(jìn)行部署期的綁定,這是通過(guò)每個(gè)組件報(bào)告一份它將需要的其它組件以及資源列表實(shí)現(xiàn)的。部署保證在應(yīng)用中每次導(dǎo)入都有相關(guān)正確的組件類型。J2EE容器提供工具來(lái)幫助您正確的完成這項(xiàng)工作。


How can J2SE apps use JNDI?

如同J2EE應(yīng)用一樣,J2SE應(yīng)用能夠?qū)NDI作為一個(gè)儲(chǔ)存了命名配置參數(shù)、對(duì)象以及對(duì)象工廠的倉(cāng)庫(kù)。工廠創(chuàng)建模式和JNDI可以很好的配合工作,你能夠在JNDI命名空間中只保存工廠對(duì)象。J2SE應(yīng)用能夠使用JNDI來(lái)替換RMI(遠(yuǎn)程方法調(diào)用)來(lái)達(dá)到更加強(qiáng)大、功能豐富以及集中化的特點(diǎn)。

大多數(shù)的J2SE應(yīng)用從配置文件中來(lái)獲取它們的配置信息,這些配置文件要么是屬性文件要么是XML文檔。這些配置文件指定了所有的配置信息;一些更加復(fù)雜的應(yīng)用為實(shí)例化對(duì)象的條件比如類名和構(gòu)造器參數(shù)在配置文件中儲(chǔ)存信息。

使用JNDI來(lái)儲(chǔ)存常量、對(duì)象以及對(duì)象工廠比傳統(tǒng)的配置存儲(chǔ)機(jī)制有一些優(yōu)勢(shì)。因?yàn)榇蠖鄶?shù)的JNDI供應(yīng)商都是支持網(wǎng)絡(luò)訪問(wèn)的,您就不必為保證每個(gè)分散的應(yīng)用主機(jī)上配置文件的一致性而花費(fèi)精力。此外,使用反射機(jī)制來(lái)實(shí)例化對(duì)象是件凌亂的事情,并且需要比簡(jiǎn)單的通過(guò)JNDI命名空間回溯對(duì)象更多的代碼(一般是更多的出錯(cuò)恢復(fù)代碼)。使用JNDI并不能避免從配置信息中實(shí)例化對(duì)象的需要(通常在應(yīng)用啟動(dòng),你獲得命名空間的時(shí)候),這種復(fù)雜程度已經(jīng)從大多數(shù)的應(yīng)用代碼中剝離出去了,這樣就能簡(jiǎn)化從JNDI命名空間中回溯期望的對(duì)象的程度。

另外一個(gè)在分布式應(yīng)用中使用JNDI而不是配置文件的原因是配置文件中的信息可能是敏感的信息,比如訪問(wèn)數(shù)據(jù)庫(kù)的密碼。將Datasource儲(chǔ)存在JNDI的名稱空間中,可以不用為整個(gè)應(yīng)用設(shè)置密碼就完成對(duì)整個(gè)應(yīng)用的數(shù)據(jù)庫(kù)連接。(比如,PoolMan連接池,一個(gè)廣泛被使用的開源項(xiàng)目,就是將Datasource對(duì)象儲(chǔ)存在JNDI名稱空間中的。)

為了在您的J2SE應(yīng)用中使用JNDI,您首先需要一個(gè)JNDI供應(yīng)商,因?yàn)镴DK并沒有包含。如果您只是想用JNDI來(lái)訪問(wèn)外部的目錄比如LDAP,那么您可以使用由Sun提供的供應(yīng)商中的一個(gè)。但是通常來(lái)說(shuō),您將希望擁有一個(gè)獨(dú)立的供應(yīng)商,可以有許多的選項(xiàng)供您選擇。


獨(dú)立的JNDI供應(yīng)商

開放原碼J2EE服務(wù)器JBoss包含了一個(gè)JNDI供應(yīng)商(JNP),它能作為獨(dú)立服務(wù)運(yùn)行;他提供非常出色的輕量級(jí)網(wǎng)絡(luò)訪問(wèn)JNDI服務(wù)。JNP使用記憶在內(nèi)存中的數(shù)據(jù)庫(kù)來(lái)存儲(chǔ)對(duì)象,所以命名空間的內(nèi)容在服務(wù)重新啟動(dòng)時(shí)將不會(huì)保存。您可以非常容易的靠它自己來(lái)運(yùn)行JNP,或者通過(guò)配置JBoss容器使只有在JNP服務(wù)的時(shí)候來(lái)啟動(dòng)它。

選擇1:配置JNP服務(wù)以作為JBoss的一項(xiàng)服務(wù)
JBoss應(yīng)用服務(wù)器建立在JMX(Java管理擴(kuò)展)框架上;該框架允許將應(yīng)用服務(wù)模塊化為JMX mbeans,這樣可以獨(dú)立的啟動(dòng)和管理它們。文件jboss.jcml包含了在容器啟動(dòng)的時(shí)候裝載的一個(gè)mbeans列表。JBoss缺省的配置包含超過(guò)50個(gè)mbeans,但是如果您配置jboss.jcml文件應(yīng)該只包含單獨(dú)的如Listing 2中的配置,然后啟動(dòng)JBoss服務(wù)器,那么服務(wù)器將只裝載JNDI供應(yīng)商,其它的任何J2EE應(yīng)用服務(wù)將不會(huì)被裝載:

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服務(wù)器使得它單獨(dú)運(yùn)行
JNP還可以作為一個(gè)單獨(dú)存在的應(yīng)用服務(wù)器。為達(dá)到這個(gè)目的,您需要下面兩個(gè)jar配置文件:

# jnpserver.jar -- 在lib/ext目錄
# log4j.jar -- 廣泛被使用的日志工具,來(lái)自Apache Jakarta項(xiàng)目;在JBoss的lib/ext目錄

此外,您還需要一個(gè)log4j.properties文件;Listing 3顯示了一個(gè)簡(jiǎn)單的情況。該文件可以通過(guò)如下的途徑來(lái)訪問(wèn):

Listing 3. 簡(jiǎn)單的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

為了在運(yùn)行模式下運(yùn)行JNP服務(wù)器,請(qǐng)確保log4j.jar文件、jnpserver.jar文件以及包含log4j.properties文件的目錄都可以在您的classpath找到。然后,運(yùn)行如下命令啟動(dòng)JNP服務(wù)器:

java org.jnp.server.Main

您也可以在您應(yīng)用的JVM中使用org.jnp.server對(duì)象來(lái)啟動(dòng)JNDI服務(wù)。


使用JNDI服務(wù)

一旦JNP服務(wù)器運(yùn)行起來(lái),您可以通過(guò)配置jnp-clinent.jar文件包含在classpath中,以及在系統(tǒng)屬性或者jndi.properties文件中指定java.naming.provider.url和java.naming.factory.initial屬性,以讓您的應(yīng)用使用JNP。Listing 4顯示了一個(gè)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

下面的例子顯示了在應(yīng)用啟動(dòng)的時(shí)候如何使用在屬性文件中定義好的string、integer這些對(duì)象值相關(guān)的參數(shù)來(lái)獲得JNDI命名空間,以及應(yīng)用組件回溯這些屬性。簡(jiǎn)短來(lái)說(shuō),Listing 5a假設(shè)解析XML配置文件的方法并且省略了出錯(cuò)處理的代碼:

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中回溯和使用對(duì)象

 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中回溯對(duì)象是非常簡(jiǎn)單和易懂的。通過(guò)使用JNDI來(lái)存儲(chǔ)配置信息、無(wú)狀態(tài)對(duì)象或者對(duì)象工廠,您可以輕松的創(chuàng)建靈活的應(yīng)用,同時(shí)將負(fù)責(zé)的配置信息包含在一個(gè)單一的地方,甚至對(duì)于分布式的應(yīng)用也是可以的。(如果您的組件是通過(guò)JNDI命名空間來(lái)訪問(wèn)對(duì)象的,那么將它們之間的依賴關(guān)系文檔化到這些組件的Javadoc中。)

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

盡管JNDI客戶端接口也是J2SE的一部分,但是大多數(shù)的J2SE應(yīng)用并沒有使用JNDI。那些使用JNDI的通常只是利用它來(lái)訪問(wèn)外部的目錄服務(wù)比如LDAP。然而,J2SE應(yīng)用還可以使用那些到目前為止只有J2EE應(yīng)用使用的部署時(shí)綁定的特征。隨著越來(lái)越多的JNDI供應(yīng)商實(shí)現(xiàn)例如JNP的出現(xiàn),任何需要命名服務(wù)的應(yīng)用都可以擁有它們了。