JNDI(轉載)

轉自:http://javacrazyer.iteye.com/blog/759485html

原理:
         在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來組織和定位實體。能夠把它理解爲一種將對象和名字捆綁的技術,對象工廠負責生產出對象,這些對象都和惟一的名字綁在一塊兒,外部資源能夠經過名字得到某對象的引用。java

          在javax.naming的包包中提供Context接口,提供了兩個很好用的方法:
<1> void bind( String name , Object object )
        將名稱綁定到對象。全部中間上下文和目標上下文(由該名稱最終原子組件之外的其餘全部組件指定)都必須已經存在。 
<2>Object lookup( String name ) 
       檢索指定的對象。若是 name爲空,則返回此上下文的一個新實例(該實例表示與此上下文相同的命名上下文,但其環境能夠獨立地進行修改,並且能夠併發訪問)。mysql

運行機制: 
一、 首先程序代碼獲取初始化的 JNDI 環境而且調用 Context.lookup() 方法從 JNDI 服務提供者那裏獲一個 DataSource 對象web

二、 中間層 JNDI 服務提供者返回一個 DataSource 對象給當前的 Java 應用程序這個 DataSource 對象表明了中間層服務上現存的緩衝數據源sql

 三、 應用程序調用 DataSource 對象的 getConnection() 方法數據庫

四、 當 DataSource 對象的 getConnection() 方法被調用時,中間層服務器將查詢數據庫 鏈接緩衝池中有沒有 PooledConnection 接口的實例對象。這個 PooledConnection 對象將被用於與數據庫創建物理上的數據庫鏈接編程

五、 若是在緩衝池中命中了一個 PooledCoonection 對象那麼鏈接緩衝池將簡單地更 新內部的緩衝鏈接隊列並將該 PooledConnection 對象返回。若是在緩衝池內沒 有找到現成的 PooledConnection 對象,那麼 ConnectionPoolDataSource 接口將會被 用來產生一個新的 PooledConnection 對象並將它返回以便應用程序使用windows

6。 中間層服務器調用 PooledConnection 對象的 getConnection() 方法以便返還一個 java.sql.Connection 對象給當前的 Java 應用程序api

七、 當中間層服務器調用 PooledConnection 對象的 getConnection() 方法時, JDBC 數據 庫驅動程序將會建立一個 Connection 對象而且把它返回中間層服務器瀏覽器

八、 中間層服務器將 Connection 對象返回給應用程序 Java 應用程序,能夠認爲這個 Connection 對象是一個普通的 JDBC Connection 對象使用它能夠和數據庫創建。事 實上的鏈接與數據庫引擎產生交互操做 。

九、 當應用程序不須要使用 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&v二、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);

相關文章
相關標籤/搜索