J2EE or J2SE? JNDI works with both(from javaworld)
發(fā)表時間:2024-02-23 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]歡迎大家指正。璊2EE or J2SE? JNDI works with bothJNDI(Java Naming and Directory Interface), 允許應(yīng)用通過一個通用的接口來訪問各種命名和目錄服務(wù)。下面的特征表征了JN...
歡迎大家指正!
-----------------------------
J2EE or J2SE? JNDI works with both
JNDI(Java Naming and Directory Interface), 允許應(yīng)用通過一個通用的接口來訪問各種命名和目錄服務(wù)。下面的特征表征了JNDI的架構(gòu)。如同JDBC(Java Database Connectivity),JNDI并不是一種服務(wù),而是一套接口;它允許應(yīng)用使用標(biāo)準(zhǔn)API來訪問很多不同的目錄服務(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è)計用來支持具有較高動態(tài)組裝和部署的那些應(yīng)用,可以在不用重建整個系統(tǒng)的條件下經(jīng)常的添加和更新各種組件。命名服務(wù)則通過作為一個組件注冊中心來幫助組織企業(yè)級的應(yīng)用。J2EE應(yīng)用一般在如下一些方面使用JNDI:
# 在一個集中、分級數(shù)據(jù)庫中儲存應(yīng)用配置信息
# 作為在不同應(yīng)用組件間共享的活動對象的倉庫,這些組件能夠運(yùn)行在不同的JVM或者不同的系統(tǒng)中
# 作為已有目錄服務(wù)例如LDAP的接口(通過使用特別的方式來訪問外部的服務(wù))
象J2EE應(yīng)用一樣,更大、更多的動態(tài)J2SE應(yīng)用將得益于由動態(tài)目錄服務(wù)帶來的松偶合以及動態(tài)綁定特點(diǎn)。
一個簡單的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操作:創(chuàng)建一個JNDI上下文,為該上下文綁定對象,從該上下文中回溯對象。需要注意的是,JNDI命名空間以及客戶端可能駐留在不同的JVM中,因此bind()和lookup()方法的調(diào)用可能發(fā)生在不同的JVM中。JNDI提供商通過使用一系列的技術(shù),包括序列化,來保證對象能夠在不同的JVM之間移動,并且可以回溯為它原來的形式。
Listing 1的代碼片斷有幾個隱藏的假設(shè):JNDI是如何知道在創(chuàng)建上下文的時候該使用哪個提供商?對于需要安全驗證的提供商,信任書又從哪里來呢?一般來說,您可以通過在系統(tǒng)屬性中設(shè)定JNDI相關(guān)的屬性,或者把它們設(shè)定到j(luò)ndi.properties文件中來制定提供商以及其它連接是所需要的參數(shù)。當(dāng)然,任何的提供商都不能隨意的綁定和回溯對象。例如,一些提供商,比如DNS提供商,對象類型是只讀的,而有些提供商卻可以提供更加靈活的對象類型。大多數(shù)賣方提供的供應(yīng)商能儲存和回溯那些實(shí)現(xiàn)了以下任何一個接口的對象:java.io.Serializable, java.rmi.Remote, 或者 javax.naming.Referenceable。
隱藏的JNDI供應(yīng)商
具有以上特征的JNDI供應(yīng)商有相同的地方:它們都通過代理來訪問外部的目錄服務(wù),比如LADP。然而,這里面沒有包含一種重要的JNDI供應(yīng)商類型:內(nèi)嵌在J2EE容器中的JNDI供應(yīng)商,它把目錄信息儲存在內(nèi)部的數(shù)據(jù)庫中。當(dāng)我們討論在J2EE應(yīng)用中使用JNDI時,通常指的就是這種容器提供的供應(yīng)商。
J2EE是如何使用JNDi的?
J2EE應(yīng)用是以組件形式組裝起來的。組件的配置以及互相之間的連接的方式是在部署的時候指定的;大多數(shù)的信息被儲存在JNDI的命名空間中。J2EE應(yīng)用使用JNDI來儲存配置信息(比如string和numeric類型的常量),無狀態(tài)的對象(包括對象工廠),以及EJB的home接口。
JNDI可以看做是黏合劑,它允許您以組件形式比如servlets、Jsps以及EJBs,構(gòu)建J2EE應(yīng)用。在J2EE應(yīng)用中,每個組件不是通過靜態(tài)的鏈接而是通過JNDI查找來找尋其它的組件的。J2EE應(yīng)用還允許在維護(hù)類型和鏈接安全性時進(jìn)行部署期的綁定,這是通過每個組件報告一份它將需要的其它組件以及資源列表實(shí)現(xiàn)的。部署保證在應(yīng)用中每次導(dǎo)入都有相關(guān)正確的組件類型。J2EE容器提供工具來幫助您正確的完成這項工作。
How can J2SE apps use JNDI?
如同J2EE應(yīng)用一樣,J2SE應(yīng)用能夠?qū)NDI作為一個儲存了命名配置參數(shù)、對象以及對象工廠的倉庫。工廠創(chuàng)建模式和JNDI可以很好的配合工作,你能夠在JNDI命名空間中只保存工廠對象。J2SE應(yīng)用能夠使用JNDI來替換RMI(遠(yuǎn)程方法調(diào)用)來達(dá)到更加強(qiáng)大、功能豐富以及集中化的特點(diǎn)。
大多數(shù)的J2SE應(yīng)用從配置文件中來獲取它們的配置信息,這些配置文件要么是屬性文件要么是XML文檔。這些配置文件指定了所有的配置信息;一些更加復(fù)雜的應(yīng)用為實(shí)例化對象的條件比如類名和構(gòu)造器參數(shù)在配置文件中儲存信息。
使用JNDI來儲存常量、對象以及對象工廠比傳統(tǒng)的配置存儲機(jī)制有一些優(yōu)勢。因為大多數(shù)的JNDI供應(yīng)商都是支持網(wǎng)絡(luò)訪問的,您就不必為保證每個分散的應(yīng)用主機(jī)上配置文件的一致性而花費(fèi)精力。此外,使用反射機(jī)制來實(shí)例化對象是件凌亂的事情,并且需要比簡單的通過JNDI命名空間回溯對象更多的代碼(一般是更多的出錯恢復(fù)代碼)。使用JNDI并不能避免從配置信息中實(shí)例化對象的需要(通常在應(yīng)用啟動,你獲得命名空間的時候),這種復(fù)雜程度已經(jīng)從大多數(shù)的應(yīng)用代碼中剝離出去了,這樣就能簡化從JNDI命名空間中回溯期望的對象的程度。
另外一個在分布式應(yīng)用中使用JNDI而不是配置文件的原因是配置文件中的信息可能是敏感的信息,比如訪問數(shù)據(jù)庫的密碼。將Datasource儲存在JNDI的名稱空間中,可以不用為整個應(yīng)用設(shè)置密碼就完成對整個應(yīng)用的數(shù)據(jù)庫連接。(比如,PoolMan連接池,一個廣泛被使用的開源項目,就是將Datasource對象儲存在JNDI名稱空間中的。)
為了在您的J2SE應(yīng)用中使用JNDI,您首先需要一個JNDI供應(yīng)商,因為JDK并沒有包含。如果您只是想用JNDI來訪問外部的目錄比如LDAP,那么您可以使用由Sun提供的供應(yīng)商中的一個。但是通常來說,您將希望擁有一個獨(dú)立的供應(yīng)商,可以有許多的選項供您選擇。
獨(dú)立的JNDI供應(yīng)商
開放原碼J2EE服務(wù)器JBoss包含了一個JNDI供應(yīng)商(JNP),它能作為獨(dú)立服務(wù)運(yùn)行;他提供非常出色的輕量級網(wǎng)絡(luò)訪問JNDI服務(wù)。JNP使用記憶在內(nèi)存中的數(shù)據(jù)庫來存儲對象,所以命名空間的內(nèi)容在服務(wù)重新啟動時將不會保存。您可以非常容易的靠它自己來運(yùn)行JNP,或者通過配置JBoss容器使只有在JNP服務(wù)的時候來啟動它。
選擇1:配置JNP服務(wù)以作為JBoss的一項服務(wù)
JBoss應(yīng)用服務(wù)器建立在JMX(Java管理擴(kuò)展)框架上;該框架允許將應(yīng)用服務(wù)模塊化為JMX mbeans,這樣可以獨(dú)立的啟動和管理它們。文件jboss.jcml包含了在容器啟動的時候裝載的一個mbeans列表。JBoss缺省的配置包含超過50個mbeans,但是如果您配置jboss.jcml文件應(yīng)該只包含單獨(dú)的如Listing 2中的配置,然后啟動JBoss服務(wù)器,那么服務(wù)器將只裝載JNDI供應(yīng)商,其它的任何J2EE應(yīng)用服務(wù)將不會被裝載:
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還可以作為一個單獨(dú)存在的應(yīng)用服務(wù)器。為達(dá)到這個目的,您需要下面兩個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
為了在運(yùn)行模式下運(yùn)行JNP服務(wù)器,請確保log4j.jar文件、jnpserver.jar文件以及包含log4j.properties文件的目錄都可以在您的classpath找到。然后,運(yùn)行如下命令啟動JNP服務(wù)器:
java org.jnp.server.Main
您也可以在您應(yīng)用的JVM中使用org.jnp.server對象來啟動JNDI服務(wù)。
使用JNDI服務(wù)
一旦JNP服務(wù)器運(yùn)行起來,您可以通過配置jnp-clinent.jar文件包含在classpath中,以及在系統(tǒng)屬性或者jndi.properties文件中指定java.naming.provider.url和java.naming.factory.initial屬性,以讓您的應(yīng)用使用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
下面的例子顯示了在應(yīng)用啟動的時候如何使用在屬性文件中定義好的string、integer這些對象值相關(guān)的參數(shù)來獲得JNDI命名空間,以及應(yīng)用組件回溯這些屬性。簡短來說,Listing 5a假設(shè)解析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來存儲配置信息、無狀態(tài)對象或者對象工廠,您可以輕松的創(chuàng)建靈活的應(yīng)用,同時將負(fù)責(zé)的配置信息包含在一個單一的地方,甚至對于分布式的應(yīng)用也是可以的。(如果您的組件是通過JNDI命名空間來訪問對象的,那么將它們之間的依賴關(guān)系文檔化到這些組件的Javadoc中。)
JNDI并不是只為了J2EE而存在的
盡管JNDI客戶端接口也是J2SE的一部分,但是大多數(shù)的J2SE應(yīng)用并沒有使用JNDI。那些使用JNDI的通常只是利用它來訪問外部的目錄服務(wù)比如LDAP。然而,J2SE應(yīng)用還可以使用那些到目前為止只有J2EE應(yīng)用使用的部署時綁定的特征。隨著越來越多的JNDI供應(yīng)商實(shí)現(xiàn)例如JNP的出現(xiàn),任何需要命名服務(wù)的應(yīng)用都可以擁有它們了。