JNDI全面總結

this_heart_add_add發表於2014-05-07

原理:
         在DataSource中事先建立多個資料庫連線,儲存在資料庫連線池中。當程式訪問資料庫時,只用從連線池中取空閒狀態的資料庫連線即可,訪問結束,銷燬資源,資料庫連線重新回到連線池,這與每次去直接訪問資料庫相比,會節省大量時間和資源。
         JNDI( Java Naming and Directory Interface ),是Java平臺的一個標準擴充套件,提供了一組介面、類和關於名稱空間的概念。如同其它很多Java技術一樣,JDNI是provider-based的技術,暴露了一個 API和一個服務供應介面(SPI)。這意味著任何基於名字的技術都能通過JNDI而提供服務,只要JNDI支援這項技術。JNDI目前所支援的技術包括 LDAP、CORBA Common Object Service(COS)名字服務、RMI、NDS、DNS、Windows登錄檔等等。很多J2EE技術,包括EJB都依靠JNDI來組織和定位實體。可以把它理解為一種將物件和名字捆綁的技術,物件工廠負責生產出物件,這些物件都和唯一的名字綁在一起,外部資源可以通過名字獲得某物件的引用。

          在javax.naming的包包中提供Context介面,提供了兩個很好用的方法:
<1> void bind( String name , Object object )
        將名稱繫結到物件。所有中間上下文和目標上下文(由該名稱最終原子元件以外的其他所有元件指定)都必須已經存在。 
<2>Object lookup( String name ) 
       檢索指定的物件。如果 name為空,則返回此上下文的一個新例項(該例項表示與此上下文相同的命名上下文,但其環境可以獨立地進行修改,而且可以併發訪問)。

執行機制: 
1、 首先程式程式碼獲取初始化的 JNDI 環境並且呼叫 Context.lookup() 方法從 JNDI 服務提供者那裡獲一個 DataSource 物件

2、 中間層 JNDI 服務提供者返回一個 DataSource 物件給當前的 Java 應用程式這個 DataSource 物件代表了中間層服務上現存的緩衝資料來源

 3、 應用程式呼叫 DataSource 物件的 getConnection() 方法

4、 當 DataSource 物件的 getConnection() 方法被呼叫時,中間層伺服器將查詢資料庫 連線緩衝池中有沒有 PooledConnection 介面的例項物件。這個 PooledConnection 物件將被用於與資料庫建立物理上的資料庫連線

5、 如果在緩衝池中命中了一個 PooledCoonection 物件那麼連線緩衝池將簡單地更 新內部的緩衝連線佇列並將該 PooledConnection 物件返回。如果在緩衝池內沒 有找到現成的 PooledConnection 物件,那麼 ConnectionPoolDataSource 介面將會被 用來產生一個新的 PooledConnection 物件並將它返回以便應用程式使用

6。 中間層伺服器呼叫 PooledConnection 物件的 getConnection() 方法以便返還一個 java.sql.Connection 物件給當前的 Java 應用程式

7、 當中間層伺服器呼叫 PooledConnection 物件的 getConnection() 方法時, JDBC 資料 庫驅動程式將會建立一個 Connection 物件並且把它返回中間層伺服器

8、 中間層伺服器將 Connection 物件返回給應用程式 Java 應用程式,可以認為這個 Connection 物件是一個普通的 JDBC Connection 物件使用它可以和資料庫建立。事 實上的連線與資料庫引擎產生互動操作 。

9、 當應用程式不需要使用 Connection 物件時,可以呼叫 Connection 介面的 close() 方 法。請注意這種情況下 close() 方法並沒有關閉事實上的資料庫連線,僅僅是釋 放了被應用程式佔用的資料庫連線,並將它還給資料庫連線緩衝池,資料庫連線 緩衝池會自動將這個資料庫連線交給請求佇列中下一個的應用程式使用。

 

現在,資料庫的連線沒有用到連線池幾乎很少很少,每個專案組都可能有自己的資料庫連線池元件,各容器提供商也提供了各自的資料庫連線池,下面介紹一下tomcat的資料庫連線管理。
tomcat6 資料來源配置(server.xml方式和context.xml方式)

在server.xml下配置你必需重啟伺服器才能生效,而context.xml配置儲存後tomcat會自動載入無需重啟 
在JNDI配配置資料來源中需注意:專案下需要引入資料庫驅動包,並且TOMCAT下也需要引入,不然會報錯的
1.context.xml方式
Tomcat-6.0.26\conf\context.xml檔案當新增以下的配置資訊:
<Resource name="jdbc/mysql" auth="Container" type="javax.sql.DataSource"
               maxActive="100" maxIdle="30" maxWait="60" wait_timeout="18800" timeBetweenEvictionRunsMillis="300000" minEvictableIdleTimeMillis="600000"
               username="root" password="jdzxdb" driverClassName="com.mysql.jdbc.Driver"
               url="jdbc:mysql://localhost:3306/sxtele?comautoReconnect=true&amp;failOverReadOnly=false"  removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"/>
<Resource name="jdbc/db2" auth="Container" type="javax.sql.DataSource"
               maxActive="100" maxIdle="30" maxWait="60" wait_timeout="18800" timeBetweenEvictionRunsMillis="300000" minEvictableIdleTimeMillis="600000"
               username="lcgluser" password="lcgluser" driverClassName="com.ibm.db2.jcc.DB2Driver"
               url="jdbc:db2://133.64.46.65:50000/STEDWDB"  removeAbandoned="true" removeAbandonedTimeout="60" logAbandoned="true"/>
 其中:
name 表示指定的jndi名稱
auth 表示認證方式,一般為Container
type 表示資料來源床型,使用標準的javax.sql.DataSource
maxActive 表示連線池當中最大的資料庫連線
maxIdle 表示最大的空閒連線數
maxWait 當池的資料庫連線已經被佔用的時候,最大等待時間
logAbandoned 表示被丟棄的資料庫連線是否做記錄,以便跟蹤
username 表示資料庫使用者名稱
password 表示資料庫使用者的密碼
driverClassName 表示JDBC DRIVER
url 表示資料庫URL地址

 

注意,這裡你配置的name值要和程式中使用的是一樣的,比如按照這個例子,程式就應該是這樣的

Java程式碼 
String gENV = "java:comp/env/";   
Context ctx = new InitialContext();     
      DataSource ds = (DataSource)ctx .lookup(gENV+"jdbc/mysql");   
      Connection conn = ds.getConnection(); 

String gENV = "java:comp/env/";
Context ctx = new InitialContext();
      DataSource ds = (DataSource)ctx.lookup(gENV+"jdbc/db2");
      Connection conn = ds.getConnection();

 

關於獲取資料來源的語法,大體有(javax.sql.DataSource) ctx.lookup ("java:comp/env/XXX")和(javax.sql.DataSource) ctx.lookup("XXX")兩種寫法,好多人以為這兩種寫法是相同的,以為都是通過JNDI來獲取資料來源。其實java:comp/env和 JNDI是不同的,java:comp/env 是環境命名上下文(environment naming context(ENC)),是在EJB規範1.1以後引入的,引入這個是為了解決原來JNDI查詢所引起的衝突問題,也是為了提高EJB或者J2EE應用的移植性。ENC是一個引用,引用是用於定位企業應用程式的外部資源的邏輯名。引用是在應用程式部署描述符檔案中定義的。在部署時,引用被繫結到目標可操作環境中資源的物理位置(JNDI名)。使用ENC是把對其它資源的JNDI查詢的硬編碼解脫出來,通過配置這個引用可以在不修改程式碼的情況下,將引用指向不同的EJB(JNDI)。 在J2EE中的引用常用的有: 
---------JDBC 資料來源引用在java:comp/env/jdbc 子上下文中宣告 
---------JMS 連線工廠在java:comp/env/jms 子上下文中宣告 
---------JavaMail 連線工廠在java:comp/env/mail 子上下文中宣告 
---------URL 連線工廠在 java:comp/env/url子上下文中宣告 


 

2.在server.xml檔案中配置資料來源
Tomcat-6.0.26\conf\context.xml中的配置:在<Host></Host>中新增如下配置
  <Context path="/sljx" reloadable="true"  docBase="sljx"   crossContext="true" 
debug="0">
               <Resource 
                   name="jdbc/oracle"
                   type="javax.sql.DataSource"
                   auth="Container"
                   username="sljx"
                   password="sljx"
                   driverClassName="oracle.jdbc.driver.OracleDriver"
                   maxIdle="3000"
                   maxWait="5000"
                   url="jdbc:oracle:thin:@localhost:1521:jadetrus"
                   autoReconnect="true"
                   maxActive="1000"/>
      </Context>
  具體屬性解釋和JAVA獲取資料來源跟上面一樣

 

3.在hibernate中配置JNDI也很簡單在hibernate.cfg.xml中
<property name="connection.datasource">java:/comp/env/jdbc/mysql</property>   
    <property name="dialect">   
        org.hibernate.dialect.MySQLDialect   
    </property> 


常用獲取示例
package com.cn.util.db;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class DBCache {
 private InitialContext ic;

 private Map<String,DataSource> cache;

 private static DBCache instance = new DBCache();

 private DBCache() {
  try {
   ic = new InitialContext();
  } catch (NamingException e) {
   e.printStackTrace();
  }
  cache = Collections.synchronizedMap(new HashMap<String,DataSource>());
 }

 public static DBCache getInstance() {
  return instance;
 }

 
 public Connection getDBConn(String dataSourceName) throws SQLException,
   NamingException {
  Connection conn = null;
        conn=getDataSource(dataSourceName).getConnection();
       return conn;
 }

 public DataSource getDataSource(String dataSourceName)
   throws NamingException {

  if (cache.containsKey(dataSourceName)) {
   return cache.get(dataSourceName);
  } else {

   DataSource newDataSource = (DataSource) ic.lookup(dataSourceName);
   cache.put(dataSourceName, newDataSource);
   return newDataSource;
  }

 }
 public void removeDateSouceOffCache(String dataSourceName) {
  try {
   DataSource ds = cache.get(dataSourceName);
   ds.getConnection().close();
   cache.remove(dataSourceName);
  } catch (Exception ex) {
  }
 }
 
 
 public static void freeConn(Connection conn) {
  freeConn(conn, null, null);
 }

 public static void freeConn(Connection connection, Statement statement) {
  freeConn(connection, statement, null);
 }

 
 public static void freeConn(Connection conn, Statement ps, ResultSet rs) {
  try {
   try {
    if (rs != null)
     rs.close();
   } finally {
    try {
     if (ps != null)
      ps.close();
    } finally {
     if (conn != null)
      conn.close();
    }
   }
  } catch (SQLException ex) {
   ex.printStackTrace();
  }
 }
}

如果獲取連線呼叫getDBConn傳入下面兩值中任意一個即可
 public static final String DATA_SOURCE = "java:comp/env/jdbc/localhostMysql";
 public static final String DATA_SOURCE_DB2 = "java:comp/env/jdbc/db2";
        private static final String DATA_SOURCE="java:comp/env/jdbc/oracle";

 

關於JNDI的更多用法
基於JNDI的應用開發 
    JNDI(The Java Naming and Directory Interface,Java命名和目錄介面)是一組在Java應用中訪問命名和目錄服務的API.命名服務將名稱和物件聯絡起來,使得我們可以用名稱訪問物件。目錄服務是一種命名服務,在這種服務裡,物件不但有名稱,還有屬性。

    命名或目錄服務使你可以集中儲存共有資訊,這一點在網路應用中是重要的,因為這使得這樣的應用更協調、更容易管理。例如,可以將印表機設定儲存在目錄服務中,以便被與印表機有關的應用使用。


  JNDI概述 
    我們大家每天都不知不覺地使用了命名服務。例如,當你在web瀏覽器輸入URL,http://java.sun.com時,DNS(Domain Name System,域名系統)將這個符號URL名轉換成通訊標識(IP地址)。命名系統中的物件可以是DNS記錄中的名稱、應用伺服器中的EJB元件(Enterprise JavaBeans Component)、LDAP(Lightweight Directory Access Protocol)中的使用者Profile.

    目錄服務是命名服務的自然擴充套件。兩者之間的關鍵差別是目錄服務中物件可以有屬性(例如,使用者有email地址),而命名服務中物件沒有屬性。因此,在目錄服務中,你可以根據屬性搜尋物件。JNDI允許你訪問檔案系統中的檔案,定位遠端RMI註冊的物件,訪問象LDAP這樣的目錄服務,定位網路上的EJB元件。

    對於象LDAP 客戶端、應用launcher、類瀏覽器、網路管理實用程式,甚至地址薄這樣的應用來說,JNDI是一個很好的選擇。

    JNDI架構

    JNDI架構提供了一組標準的獨立於命名系統的API,這些API構建在與命名系統有關的驅動之上。這一層有助於將應用與實際資料來源分離,因此不管應用訪問的是LDAP、RMI、DNS、還是其他的目錄服務。換句話說,JNDI獨立於目錄服務的具體實現,只要你有目錄的服務提供介面(或驅動),你就可以使用目錄。如圖1所示。 圖1:JNDI架構

    關於JNDI要注意的重要一點是,它提供了應用程式設計介面(application programming interface,API)和服務提供者介面(service provider interface,SPI)。這一點的真正含義是,要讓你的應用與命名服務或目錄服務互動,必須有這個服務的JNDI服務提供者,這正是JNDI SPI發揮作用的地方。服務提供者基本上是一組類,這些類為各種具體的命名和目錄服務實現了JNDI介面?很象JDBC驅動為各種具體的資料庫系統實現了JDBC介面一樣。作為一個應用開發者,你不必操心JNDI SPI.你只需要確認你要使用的每一個命名或目錄服務都有服務提供者。

    J2SE和JNDI

    Java 2 SDK 1.3及以上的版本包含了JNDI.對於JDK 1.1和1.2也有一個標準的擴充套件。Java 2 SDK 1.4.x的最新版本包括了幾個增強和下面的命名/目錄服務提供者:

    l LDAP(Lightweight Directory Access Protocol)服務提供者 l CORBA COS(Common Object Request Broker Architecture Common Object Services)命名服務提供者 l RMI(Java Remote Method Invocation)註冊服務提供者 l DNS(Domain Name System)服務提供者

    更多的服務提供者

    可以在如下網址找到可以下載的服務提供者列表:

    http://java.sun.com/products/jndi/serviceproviders.html 特別有意思的或許是如下網址提供的Windows 登錄檔JNDI服務提供者:http://cogentlogic.com/cocoon/CogentLogicCorporation/JNDI.xml 這個服務提供者使你可以訪問Windows XP/2000/NT/Me/9x的windows登錄檔。

      也可以在如下網址下載JNDI/LDAP Booster Pack:http://java.sun.com/products/jndi/ 這個Booster Pack包含了對流行的LDAP控制的支援和擴充套件。它代替了與LDAP 1.2.1服務提供者捆綁在一起的booster pack.關於控制和擴充套件的更多資訊可以在如下網站看到: http://java.sun.com/products/jndi/tutorial/ldap/ext/index.html 另一個有趣的服務提供者是Sun的支援DSML v2.0(Directory Service Markup Language,目錄服務標記語言)的服務提供者。DSML的目的是在目錄服務和XML之間架起一座橋樑。

    JNDI API

    JNDI API由5個包組成:

    l Javax.naming:包含了訪問命名服務的類和介面。例如,它定義了Context介面,這是命名服務執行查詢的入口。 l Javax.naming.directory:對命名包的擴充,提供了訪問目錄服務的類和介面。例如,它為屬性增加了新的類,提供了表示目錄上下文的DirContext介面,定義了檢查和更新目錄物件的屬性的方法。 l Javax.naming.event:提供了對訪問命名和目錄服務時的時間通知的支援。例如,定義了NamingEvent類,這個類用來表示命名/目錄服務產生的事件,定義了偵聽NamingEvents的NamingListener介面。 l Javax.naming.ldap:這個包提供了對LDAP 版本3擴充的操作和控制的支援,通用包javax.naming.directory沒有包含這些操作和控制。 l Javax.naming.spi:這個包提供了一個方法,通過javax.naming和有關包動態增加對訪問命名和目錄服務的支援。這個包是為有興趣建立服務提供者的開發者提供的。

    JNDI 上下文

    正如在前面提到的,命名服務將名稱和物件聯絡起來。這種聯絡稱之為繫結(binding)。一組這樣的繫結稱之為上下文(context),上下文提供瞭解析(即返回物件的查詢操作)。其他操作包括:名稱的繫結和取消繫結,列出繫結的名稱。注意到一個上下文物件的名稱可以繫結到有同樣的命名約定的另一個上下文物件。這稱之為子上下文。例如,如果UNIX中目錄/home是一個上下文,那麼相對於這個目錄的子目錄就是子上下文?例如,/home/guests中guests就是home的子上下文。在JNDI中,上下文用介面javax.naming.Context表示,這個介面是與命名服務互動的關鍵介面。在Context(或稍後討論的DirContext)介面中的每一個命名方法都有兩種過載形式:

    Lookup(String name):接受串名 l
    Lookup(javax.naming.Name):接受結構名,
    例如,CompositeName(跨越了多個命名系統的名稱)或CompondName(單個命名系統中的名稱);它們都實現了Name介面。Compound name的一個例子是:cn=mydir,cn=Q Mahmoud,ou=People,composite name的一個例子是:cn=mydir,cn=Q Mahmoud,ou=People/myfiles/max.txt(這裡,myfiles/max.txt是表示第二部分的檔名)

        Javax.naming.InitialContext是實現了Context介面的類。用這個類作為命名服務的入口。為了建立InitialContext物件,構造器以java.util.Hashtable或者是其子類(例如,Properties)的形式設定一組屬性。下面給出了一個例子:

    Hashtable env = new Hashtable(); // select a service provider factory env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContext"); // create the initial context Context contxt = new InitialContext(env);

    INITIAL_CONTEXT_FACTORY指定了JNDI服務提供者中工廠類(factory class)的名稱。Factory負責為其服務建立適當的InitialContext物件。在上面的程式碼片斷中,為檔案系統服務提供者指定了工廠類。表1給出了所支援的服務提供者的工廠類。要注意的是檔案系統服務提供者的工廠類需要從Sun公司單獨下載,J2SE 1.4.x沒有包含這些類。

    表1:上下文INITIAL_CONTEXT_FACTORY的值       Name Service Provider Factory 
               File System                                                               com.sun.jndi.fscontext.RefFSContextFactory 
               LDAP                                                                       com.sun.jndi.ldap.LdapCtxFactory 
               RMI                                                                         com.sun.jndi.rmi.registry.RegistryContextFactory 
               CORBA                                                                   com.sun.jndi.cosnaming.CNCtxFactory 
               DNS                                                                         com.sun.jndi.dns.DnsContextFactory

    為了用名稱從命名服務或目錄中取得或解析物件,使用Context的lookup方法:Object obj=contxt.lookup(name)。Lookup方法返回一個物件,這個物件表示的是你想要找的上下文的兒子。
----------------------------------------------------------------------------------------------------------------------------
對jndi總體的理解: jndi(java naming and directory Interface)它提供了一套使用命名和目錄服務的介面。使用者可以通過它來使用命名和目錄服務。就像jdbc一樣。jndi包括命名服務和目錄服務兩部分,其中目錄服務包含目錄物件directory object,它包含若干屬性物件。提供了對屬性的很多操作。

命名和目錄服務: 命名和目錄服務我們一直在使用,如作業系統的檔案系統,它給我們提供對檔案的操作,查詢,新增刪除等功能。DNS服務將url同ip地址繫結在了一起。命名和目錄系統的最主要的功能是將name和物件繫結。在它的基礎之上還提供更多的功能如lookup,search.而且儲存的物件是有一定層次結構的。使用這樣的服務我們可以對物件更加有效的管理和操作。

命名服務將一個名稱對映到一個物件。RMI Registry和CORBA Naming Service都是命名服務。
目錄服務也存放物件,但目錄服務識別這些物件的相關屬性。可以用專案屬性來搜尋目錄。

在20世紀90年代早期,輕量級的目錄訪問協議(LightWeightDiretoryAccessProtocol,LDAP)被作為一種標準的目錄協議被開發出來,JNDI能夠訪問LDAP。
j2se為jndi提供了5個擴充套件包:
javax.naming;為訪問命名服務提供的api
javax.naming.directory;為訪問目錄服務提供了基本的介面
javax.naming.event;支援命名和目錄服務中的事件通知
javax.naming.ldap; 支援ldap的包,
javax.naming.spi;提供了不同命名和目錄服務可以掛接他們的實現的方法。


context: context是一套name-to-object的繫結(bindings),可以理解為層次或目錄,它還可已包括下一層subContext。在使用命名和目錄服務時獲得initial context是對整個名字空間操作的入口。在目錄服務中是DirContext.
----------------------------------------------------------------------------------------------------------------------------

JNDI(Java Naming and Directory Interface)是一個應用程式設計的API,為開發人員提供了查詢和訪問各種命名和目錄服務的通用、統一的介面,類似JDBC都是構建在抽象層上。

JNDI可訪問的現有的目錄及服務有:
DNS、XNam 、Novell目錄服務、LDAP(Lightweight Directory Access Protocol 輕型目錄訪問協議)、 CORBA物件服務、檔案系統、Windows XP/2000/NT/Me/9x的登錄檔、RMI、DSML v1&v2、NIS。

JNDI優點:
包含了大量的命名和目錄服務,使用通用介面來訪問不同種類的服務;可以同時連線到多個命名或目錄服務上;建立起邏輯關聯,允許把名稱同Java物件或資源關聯起來,而不必指導物件或資源的物理ID。

JNDI程式包:
javax.naming:命名操作;
javax.naming.directory:目錄操作;
javax.naming.event:在命名目錄伺服器中請求事件通知;
javax.naming.ldap:提供LDAP支援;
javax.naming.spi:允許動態插入不同實現。

利用JNDI的命名與服務功能來滿足企業級APIs對命名與服務的訪問,諸如EJBs、JMS、JDBC 2.0以及IIOP上的RMI通過JNDI來使用CORBA的命名服務。

JNDI與JDBC:
JNDI提供了一種統一的方式,可以用在網路上查詢和訪問服務。通過指定一個資源名稱,該名稱對應於資料庫或命名服務中的一個紀錄,同時返回資料庫連線建立所必須的資訊。
程式碼示例:
try{
Context cntxt = new InitialContext();
DataSource ds = (DataSource) cntxt.lookup("jdbc/dpt");
}
catch(NamingException ne){
...
}

JNDI與JMS:
訊息通訊是軟體元件或應用程式用來通訊的一種方法。JMS就是一種允許應用程式建立、傳送、接收、和讀取訊息的JAVA技術。
程式碼示例:
try{
Properties env = new Properties();
InitialContext inictxt = new InitialContext(env);
TopicConnectionFactory connFactory = (TopicConnectionFactory) inictxt.lookup("TTopicConnectionFactory");
...
}
catch(NamingException ne){
...
}

訪問特定目錄:舉個例子,人是個物件,他有好幾個屬性,諸如這個人的姓名、電話號碼、電子郵件地址、郵政編碼等屬性。通過getAttributes()方法
Attribute attr = directory.getAttributes(personName).get("email");
String email = (String)attr.get();
通過使用JNDI讓客戶使用物件的名稱或屬性來查詢物件:
foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls);

通過使用JNDI來查詢諸如印表機、資料庫這樣的物件,查詢印表機的例子:
Printer printer = (Printer)namespace.lookup(printerName);
printer.print(document);

瀏覽名稱空間:
NamingEnumeration list = namespace.list("o=Widget, c=US");
while (list.hasMore()) {
NameClassPair entry = (NameClassPair)list.next();
display(entry.getName(), entry.getClassName());
}

參考資料:
http://java.sun.com/products/jndi/examples.html
http://java.sun.com/products/jndi/serviceproviders.html

常用的JNDI操作:
void bind(String sName,Object object);――繫結:把名稱同物件關聯的過程
void rebind(String sName,Object object);――重新繫結:用來把物件同一個已經存在的名稱重新繫結
void unbind(String sName);――釋放:用來把物件從目錄中釋放出來
void lookup(String sName,Object object);――查詢:返回目錄總的一個物件
void rename(String sOldName,String sNewName);――重新命名:用來修改物件名稱繫結的名稱
NamingEnumeration listBinding(String sName);――清單:返回繫結在特定上下文中物件的清單列表
NamingEnumeration list(String sName);
程式碼示例:重新得到了名稱、類名和繫結物件。
NamingEnumeration namEnumList = ctxt.listBinding("cntxtName");
...
while ( namEnumList.hasMore() )  {
Binding bnd = (Binding) namEnumList.next();
String sObjName = bnd.getName();
String sClassName = bnd.getClassName();
SomeObject objLocal = (SomeObject) bnd.getObject();
}

----------------------------------------------------------------------------------------------------------------------------

瞭解名字服務和目錄服務的相關概念,有助於更好的使用JNDI。 Naming service       名字服務定義瞭如何將名字與物件關聯,並通過名字如何找到物件的方法。典型的例子如:DNS將域名與IP關聯,檔案系統將檔名與檔案相關聯。在名字服務中,主要的概念:

-          名字(Names),在名字系統中實際物件的代號,如檔名,域名等,它會被用來查詢關聯的物件。不同的系統中會有不同的命名規範,如檔案系統採用“\”來表示層級,而DNS則使用“.”。

-          繫結(Bindings),名字和實際物件的關聯。

-          引用和地址(References and Addresses),當物件不能直接被儲存在名字系統時,就必須使用引用,通過引用找到實際的物件。在系統中,儲存的引用的內容被稱為地址。引用還有另一個用處:在名字系統中,缺少象關聯式資料庫中外來鍵的概念。通過使用引用,可以作為外來鍵的一個取代辦法。

-          上下文(Context),它是一個名字-物件集合,提供了與名字系統互動的主要操作,如查詢、繫結、去繫結。子上下文(subcontext)與它的關係類似檔案系統中目錄和子目錄的關係,子上下文被包含在一個上下文中,通過父上下文中的一個名字與子上下文關聯。

-          名字系統和名字空間(Naming Systems and Namespaces),名字系統是相同型別的上下文的集合,它提供名字服務;名字空間,是名字系統中的名字集合,如檔案系統的檔名和目錄。

Directory service       目錄服務是名字服務的擴充套件,它除了關聯名字和物件,還允許物件包含屬性。目錄系統通常以層次結構組織資料。在目錄服務中的主要概念:

-          屬性(Attributes),它屬於目錄物件,它是(名字,值)對,屬性可以有多個值。

-          目錄和目錄服務(Directories and Directory Services),目錄是目錄物件的集合;目錄服務則提供與目錄相關的服務,建立、刪除和修改存放在目錄中的物件的屬性。

-          查詢和查詢過濾器(Searches and Search Filters),獲取目錄物件的操作就是查詢;過濾器是類似查詢條件的物件。

基本使用 
 

²        註冊JNDI提供者

在使用JNDI之前,需要先獲取JNDI的提供者,並在系統註冊它。與JNDI相關的系統屬性在javax.naming.Context中定義,常用的屬性:

-          java.naming.factory.initial,服務提供者用來建立InitialContext的類名。

-          java.naming.provider.url,用來配置InitialContext的初始url

-          java.naming.factory.object,用來建立name-to-object對映的類,用於NameClassPair和References。

-          java.naming.factory.state,用來建立jndi state的類

對於目錄服務,由於一般需要安全設定,還通常使用:

-          java.naming.security.authentication,安全型別,三個值:none,simple或strong。

-          java.naming.security.principal,認證資訊。

-          java.naming.security.credentials,證照資訊。

-          java.naming.security.protocol,安全協議名。

使用System.setProperty註冊,如果程式不顯示說明,那麼java會在classpath內查詢jdni.properties檔案來完成註冊。jdni.properties例子:

java.naming.factory.initial=com.codeline.db.MockInitialContextFactory

 

 【連線服務】

註冊之後,就可以實施服務連線了。對於名字服務由InitialContext開始,目錄服務則使用InitialDirContext。它們分別實現了Context和DirContext,這兩個介面分別對應名字服務和目錄服務的介面,也是JNDI中最重要的兩個介面。

連線名字服務:                 
System.setProperty(Context.INITIAL_CONTEXT_FACTORY," 
com.sun.jndi.fscontext.FSContextFactory"); 
InitialContext ctx = new InitialContext();

連線目錄服務:
  Hashtable env = new Hashtable(); 
   env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); 
   env.put(Context.PROVIDER_URL, "ldap://myserver.com/");
   env.put(Context.SECURITY_AUTHENTICATION, "simple"); 
    //登入ldap server需要的使用者名稱 
   env.put(Context.SECURITY_PRINCIPAL, "ldapuser"); 
  //登入ldap server需要的密碼 
   env.put(Context.SECURITY_CREDENTIALS, "mypassword"); 
InitialDirContext ctx = new InitialDirContext(env);

 

多服務提供者:如果應用包含多個服務提供者,在連線時略有不同。以名字服務為例
Hashtable env = new Hashtable(); 
  env.put(Context.INITIAL_CONTEXT_FACTORY, 
"com.sun.jndi.rmi.registry.RegistryContextFactory"); 
  env.put(Context.PROVIDER_URL, "rmi://myserver.com:1099"); 
  //使用不同的建構函式 
InitialContext ctx = new InitialContext(env); 
 

【查詢物件】

不論名字服務還是目錄服務,都是使用lookup來查詢物件的。除了可以使用String作為引數之外,lookup還可使用Name介面作為引數。
Greeter greeter = (Greeter)ctx.lookup("SayHello"); 
如果想要獲得上下文中所有的物件名字,就使用lis返回NameClassPair列表。NameClassPair包含物件名字和物件類名。如果想要獲得實際的物件例項列表,就使用listBindings,它返回Binding列表。Binding是NameClassPair的子類,它包含物件的例項。 
-  list

NamingEnumeration list = ctx.list("awt"); 
while (list.hasMore()) { 
    NameClassPair nc = (NameClassPair)list.next(); 
    System.out.println(nc); 
}

-  listBindings

NamingEnumeration bindings = ctx.listBindings("awt"); 
while (bindings.hasMore()) { 
    Binding bd = (Binding)bindings.next(); 
    System.out.println(bd.getName() + ": " + bd.getObject()); 

 

【物件繫結】

- 使用bind新增繫結
Fruit fruit = new Fruit("orange");
ctx.bind("favorite", fruit);

- 使用rebind修改繫結
Fruit fruit = new Fruit("lemon");
ctx.rebind("favorite", fruit);

- 使用unbind去除繫結。
ctx.unbind("favorite"); 
 

【物件改名】

使用rename可以給一個在上下文中的物件改名
ctx.rename("report.txt", "old_report.txt");

- 獲取屬性

屬性相關的介面是Attribute和Attributes,它們都在javax.naming.directory包內。通過DirContext的getAttributes方法就可以獲得物件的屬性集合,然後使用Attributes的get方法獲得對應的屬性,最後通過Attribute的get方法就可以獲得屬性值。


String dn = "uid=me, dc=mycompany, dc=com, ou=customer, o=ExampleApp"; 
Context user = (Context)ctx.lookup(dn); 
//獲得所有屬性 
Attributes attrs = user.getAttributes(""); 
Attribute test= attrs .get("test"); 
Object testValue= test.get(); 
上例中獲得的是user的所有屬性,在實際使用過程中,考慮網路頻寬的影響,可以設定獲取要獲取的屬性列表:


String reqd_attrs = new String[] { "surname", "initials","title", "rfc822mailalias"}; 
Attributes attrs = user.getAttributes("", reqd_attrs); 
 

【查詢和過濾】

使用search方法完成。
    public DirContext[] findUser(String initials,String surname,String country,String phone) { 
       //構造條件 
         BasicAttributes search_attrs = new BasicAttributes(); 
        search_attrs.put("initials", initials); 
        search_attrs.put("sn", surname); 
        search_attrs.put("c", country); 
       if(phone != null) 
          search_attrs.put("phonenumber", phone); 
        NamingEnumeration results = initial_ctx.search("ou=Customer,o=ExampleApp", search_attrs); 
        LinkedList found = new LinkedList(); 
        while(results.hasMore()) { 
            SearchResults sr = (SearchResults)results.next(); 
            String name = sr.getName(); 
            Object ctx = sr.getObject(); 
           if((ctx == null) || !(ctx instanceof DirContext)) 
               found.add(initial_ctx.lookup(name)); 
           else 
               found.add(ctx); 
       } 
       DirContext[] ret_val = new DirContext[found.size()]; 
        found.toArray(ret_val); 
       return ret_val; 
  } 
 

DirContext介面主要過濾方式: 
1.使用過濾字串 
String reqd_attrs = new String[] { "cn", "uid","rfc822mailalias" }; 
NamingEnumeration results = initial_ctx.search("ou=Customer, o=ExampleApp",search_attrs,reqd_attrs); 
 
2.使用SearchControls,獲得更多的控制 
SearchControls ctrls = new SearchControls(); 
ctrls.setCountLimit(20); 
ctrls.setTimeLimit(5000); 
ctrls.setSearchScope(SearchControls.SUBTREE_SCOPE); 
NamingEnumeration results = initial_ctx.search("cat=books,ou=Products, 
o=ExampleApp","title=*Java*",ctrls); 
 【修改屬性 】
使用DirContext和InitialDirContext的modifyAttributes方法完成。所謂的修改過程,實際就是先構造要修改的屬性列表,然後使用上述方法提交。對於屬性包含多個值時,需要把屬性的不修改的值也要包含,否則伺服器會認為那些值不再需要而刪除它們。
 public void updateAddress(String dn,String address, String country, String phone) { 
        BasicAttributes mod_attrs = new BasicAttributes(); 
        if(address != null) 
            mod_attrs.put("address", address); 
         if(country != null) 
             mod_attrs.put("c", country); 
        if(phone != null) 
             mod_attrs.put("phonenumber", phone); 
         if(mod_attrs.size() != 0) 
            initial_ctx.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, mod_attrs); 

使用ModificationItem,也可一次進行多個不同的修改操作: 
ModificationItem[] mod_items = new ModificationItems[2]; 
Attribute email = new BasicAttribute("rfc822mailalias", new_email); 
ModificationItem email_mod = new ModificationItem(DirContext.ADD_ATTRIBUTE, email); 
Attribute addr = new BasicAttribute("address", address); 
ModificationItem addr_mod = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, addr); 
mod_items[0] = email_mod; 
mod_items[1] = addr_mod; 
initial_ctx.modifyAttributes(dn, mod_items);

 【建立上下文】
使用createSubcontext方法完成。
 BasicAttributes attrs = new BasicAttributes(); 
attrs.put("initials", initials); 
 attrs.put("sn", surname); 
 attrs.put("rfc822mailalias", email); 
 if(address != null) 
     attrs.put("address", address); 
 if(country != null) 
     attrs.put("c", country); 
 if(phone != null) 
    attrs.put("phonenumber", phone); 
initial_ctx.createSubcontext(dn, attrs); 
 

【刪除上下文】

使用destroySubcontext方法完成。
initial_ctx.destroySubcontext(dn); 
 

資料:
jndi學習筆記  http://hankun.blogbus.com/logs/2005/12/1774694.html
 [精華] JNDI   http://www.chinaunix.net/jh/26/30421.html

相關文章