1. 程式人生 > >JNDI 命名和目錄操作

JNDI 命名和目錄操作

  • 查詢物件
  • 列出Context的內容
  • 新增,覆蓋,去除繫結
  • 重新命名物件
  • 建立和銷燬subcontext

配置

在進行任何操作的命名或目錄服務之前,您需要先獲取inital Context,即開始進入名稱空間的起始點。這是因為所有的命名和目錄服務的方法都是在某些Context中進行的。要獲得inital Context,您必須遵循以下步驟。

  • 選擇要訪問的相應服務的服務提供程式
  • 指定inital Context需要的配置。
  • 呼叫InitialContext建構函式

第一步:選擇inital Context的服務供應商

您可以通過指定一組 environment properties (Hashtable型別)集合為服務提供商建立inital Context,或者向inital Context新增服務提供程式類的名稱
.這個連結提供environment properties 詳細的使用教程.

https://docs.oracle.com/javase/jndi/tutorial/beyond/env/index.html
如果您使用的是包含在JDK的LDAP服務提供商,那麼你的程式碼如下所示:

Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");

在JDK中指定檔案系統服務提供商,那麼你的程式碼如下所示:

Hashtable<String
, Object> env = new Hashtable>String, Object>(); env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContextFactory");

第二步:提供inital Context所需要的資訊

不同目錄的客戶端可能需要不同的資訊來聯絡目錄。例如,您可能需要指明那個正在執行的伺服器,以及需要什麼樣的資訊來識別使用者到該目錄。這樣的資訊通過 environment properties傳遞給服務提供商. JNDI指定一些通用的 environment properties讓服務提供商使用.您的服務提供程式文件將提供有關這些屬性的詳細資訊。
LDAP提供程式要求程式指定LDAP伺服器的位置,以及使用者身份資訊。要提供此資訊,您可以編寫如下程式碼:

env.put(Context.PROVIDER_URL, "ldap://ldap.wiz.com:389");
env.put(Context.SECURITY_PRINCIPAL, "joeuser");
env.put(Context.SECURITY_CREDENTIALS, "joepassword");

下面示例使用的LDAP服務提供者在JDK。這個例子假設伺服器已經啟動並使用了本地機器上的埠389與使用者名稱字”o=JNDITutorial”.這是更新目錄不需要認證。它們包括下列程式碼以建立環境

env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");

如果您正在使用一個設定不同的目錄,那麼您就需要相應地設定這些環境屬性。你將需要更換機器名”localhost”,您可以執行這些示例在任何非公共目錄伺服器或您自己的伺服器上。你將需要更換機器名”localhost”和使用者名稱o=JNDITutorial。

第三步:建立inital Context

您現在已經準備好建立inital Context。這樣做,將先前建立的環境屬性提供給InitialContext建構函式:

Context ctx = new InitialContext(env);

現在,您有一個Context 物件的引用,您可以開始訪問命名服務。
執行目錄操作,你需要使用一個InitialDirContext。要做到這一點,需使用它的建構函式:

DirContext ctx = new InitialDirContext(env);

這個語句返回一個可執行目錄操作的DirContext引用物件。

命名異常

當需要的請求操作無法執行時,很多在JNDI包中的方法都有丟擲NamingException。通常,你會看到一個try/catch程式碼塊,或者丟擲一個NamingException:

try {
    Context ctx = new InitialContext();
    Object obj = ctx.lookup("somename");
} catch (NamingException e) {
    // Handle the error
    System.err.println(e);
}

異常類的層次結構

JNDI具有豐富的異常層次結構,所有異常都有一個共同的基類:NamingException .
下面是一個使用子類AuthenticationException的例子:

try {
    Context ctx = new InitialContext();
    Object obj = ctx.lookup("somename");
} catch (AuthenticationException e) {
    // attempt to reacquire the authentication information
    ...
} catch (NamingException e) {
    // Handle the error
    System.err.println(e);
}

列舉

例如Context.list() 和DirContext.search()都會返回一個NamingEnumeration.在這種情況下,如果發生了一個錯誤,不返回任何結果,這時NamingException 或其相應的子類將被丟擲.
如果出現錯誤,但也有一些結果會被返回,返回一個NamingEnumeration 以便你能得到這些結果。當所有的結果都用完後,呼叫NamingEnumeration.hasMore()會造成NamingException (或它的一個子類)被丟擲。在這點上,列舉無效,並且沒有其他方法能被呼叫。
例如,如果你執行一個search()並指定一個數(N)限制返回多少個結果集,那麼search()將返回一個包含最多N結果的結果集。如果結果數超過N,然後NamingEnumeration.hasMore()為n + 1次呼叫時,會丟擲一個SizeLimitExceededException 。
所有的異常說明可以在這裡找到:
https://docs.oracle.com/javase/8/docs/api/javax/naming/package-summary.html

查詢物件

通過您要檢索的物件的名稱,使用 Context.lookup()查詢命名服務中的物件。假設有一個物件命名服務中的名稱:cn=Rosanna Lee,ou=People。要檢索該物件,您將寫

Object obj = ctx.lookup("cn=Rosanna Lee,ou=People");

lookup()返回的目標型別取決於底層的命名系統和物件本身相關的資料。一個命名系統可以包含許多不同型別的物件,並且在系統的不同部分中查詢物件可能會產生不同型別的物件。在上面的例子中, “cn=Rosanna Lee,ou=People”恰好是繫結到Context的物件((javax.naming.ldap.LdapContext)。你可以用lookup()找到目標類:

例如,下面的程式碼查詢物件 “cn=Rosanna Lee,ou=People”,並轉換為LdapContext。

import javax.naming.ldap.LdapContext;
...
LdapContext ctx = (LdapContext) ctx.lookup("cn=Rosanna Lee,ou=People");

這裡有2個新的靜態方法可查詢一個名字在Java SE 6中:
InitialContext.doLookup(Name name)
InitialContext.doLookup(String name)
這些方法提供了一個尋找無需例項化的InitialContext的快捷方式

列出Context的內容

如果不使用Context.lookup()一次得到一個單一的物件,你可以通過使用一個單一的操作列出Context的內容。有兩種方法用於列出Context:一個是返回繫結的方法,一個返回name-to-object 類。

  • Context.List()
    返回一個NameClassPair列舉,每個NameClassPair包含物件名和類名,下面的程式碼片段列出了”ou=People”目錄的內容(即在”ou=People”目錄中找到的相應的檔案和目錄)。
NamingEnumeration list = ctx.list("ou=People");

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

執行這個例子產生以下輸出:

# java List
cn=Jon Ruiz: javax.naming.directory.DirContext
cn=Scott Seligman: javax.naming.directory.DirContext
cn=Samuel Clemens: javax.naming.directory.DirContext
cn=Rosanna Lee: javax.naming.directory.DirContext
cn=Maxine Erlund: javax.naming.directory.DirContext
cn=Niels Bohr: javax.naming.directory.DirContext
cn=Uri Geller: javax.naming.directory.DirContext
cn=Colleen Sullivan: javax.naming.directory.DirContext
cn=Vinnie Ryan: javax.naming.directory.DirContext
cn=Rod Serling: javax.naming.directory.DirContext
cn=Jonathan Wood: javax.naming.directory.DirContext
cn=Aravindan Ranganathan: javax.naming.directory.DirContext
cn=Ian Anderson: javax.naming.directory.DirContext
cn=Lao Tzu: javax.naming.directory.DirContext
cn=Don Knuth: javax.naming.directory.DirContext
cn=Roger Waters: javax.naming.directory.DirContext
cn=Ben Dubin: javax.naming.directory.DirContext
cn=Spuds Mackenzie: javax.naming.directory.DirContext
cn=John Fowler: javax.naming.directory.DirContext
cn=Londo Mollari: javax.naming.directory.DirContext
cn=Ted Geisel: javax.naming.directory.DirContext
  • Context.listBindings()
    返回一個Binding列舉,Binding是一個NameClassPair的子類.Binding包含物件的名稱和類名稱,但也包含物件本身。下面的程式碼列舉”ou=People”的Context,打印出每個繫結的名稱和物件。
NamingEnumeration bindings = ctx.listBindings("ou=People");

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

執行這個例子產生以下輸出:

# java ListBindings
cn=Jon Ruiz: com.sun.jndi.ldap.LdapCtx@1d4c61c
cn=Scott Seligman: com.sun.jndi.ldap.LdapCtx@1a626f
cn=Samuel Clemens: com.sun.jndi.ldap.LdapCtx@34a1fc
cn=Rosanna Lee: com.sun.jndi.ldap.LdapCtx@176c74b
cn=Maxine Erlund: com.sun.jndi.ldap.LdapCtx@11b9fb1
cn=Niels Bohr: com.sun.jndi.ldap.LdapCtx@913fe2
cn=Uri Geller: com.sun.jndi.ldap.LdapCtx@12558d6
cn=Colleen Sullivan: com.sun.jndi.ldap.LdapCtx@eb7859
cn=Vinnie Ryan: com.sun.jndi.ldap.LdapCtx@12a54f9
cn=Rod Serling: com.sun.jndi.ldap.LdapCtx@30e280
cn=Jonathan Wood: com.sun.jndi.ldap.LdapCtx@16672d6
cn=Aravindan Ranganathan: com.sun.jndi.ldap.LdapCtx@fd54d6
cn=Ian Anderson: com.sun.jndi.ldap.LdapCtx@1415de6
cn=Lao Tzu: com.sun.jndi.ldap.LdapCtx@7bd9f2
cn=Don Knuth: com.sun.jndi.ldap.LdapCtx@121cc40
cn=Roger Waters: com.sun.jndi.ldap.LdapCtx@443226
cn=Ben Dubin: com.sun.jndi.ldap.LdapCtx@1386000
cn=Spuds Mackenzie: com.sun.jndi.ldap.LdapCtx@26d4f1
cn=John Fowler: com.sun.jndi.ldap.LdapCtx@1662dc8
cn=Londo Mollari: com.sun.jndi.ldap.LdapCtx@147c5fc
cn=Ted Geisel: com.sun.jndi.ldap.LdapCtx@3eca90

終止一個NamingEnumeration

一個NamingEnumeration可以有三種方式:自然終止,明確,或意外。

  • 當 NamingEnumeration.hasMore()返回false,列舉是完整和有效的終止。
  • 你可以明確終止列舉通過呼叫 NamingEnumeration.close()。這樣做為底層實現提供一個提示,以釋放與列舉相關聯的任何資源。
  • 如果 hasMore()或next()丟擲一個NamingException,然後列舉有效終止。

為什麼有兩個不同的列表方法?

list()用於瀏覽模式的應用程式,只想在一個Context中顯示物件的名稱。例如,一個瀏覽器先用Context列出名稱,並等待使用者選擇一個或幾個然後顯示執行進一步操作的名稱。這樣的應用程式通常不需要在Context中訪問所有的物件。
listbindings()用於需要對Context的全體物件執行操作的應用程式。例如,備份應用程式可能需要在檔案目錄中的所有物件上執行“檔案統計”操作。或者印表機管理程式可能要重新啟動一個專案的所有印表機。要執行這樣的操作,這些應用程式需要獲得在Context中的所有物件。因此,返回物件作為列舉的一部分是更加有利.
應用程式可以使用list()或更昂貴的listBindings(),應根據資訊型別的需要.

新增,替換或刪除繫結

Context介面包含用於Context的新增、替換和刪除繫結的方法

  • 新增繫結
    Context.bind() 用於新增一個繫結到Context中,它接受被繫結的物件名稱和物件作為引數.
// 建立需要繫結的物件
Fruit fruit = new Fruit("orange");

// 執行繫結
ctx.bind("cn=Favorite Fruit", fruit);

此示例建立一個水果類的物件並將它繫結到”cn=Favorite Fruit”到名為ctx的Context中。如果你隨後用ctx的lookup名為:”cn=Favorite Fruit”,然後你會得到fruit 物件。
如果你執行這個例子兩次,然後將丟擲異常:NameAlreadyBoundException。這是因為這個名字”cn=Favorite Fruit”已經被綁定了。對於嘗試第二次成功,你將不得不使用rebind()。

新增或替換繫結:
rebind()用於新增或替換繫結。它接受和bind()相同的引數,但語義是這樣的,如果是已經繫結,那麼它將會繫結新的物件。

// Create the object to be bound
Fruit fruit = new Fruit("lemon");

// Perform the bind
ctx.rebind("cn=Favorite Fruit", fruit);

當你執行這個例子,它將取代bind()例子建立的物件
這裡寫圖片描述

移除繫結

移除繫結使用unbind().

// Remove the binding
ctx.unbind("cn=Favorite Fruit");

重新命名

你可以通過 Context.rename()重新命名Context中的物件

// Rename to Scott S
ctx.rename("cn=Scott Seligman", "cn=Scott S");

這個例子重新命名物件被繫結到”cn=Scott Seligman”到”cn=Scott S”如下:

// Rename back to Scott Seligman
ctx.rename("cn=Scott S", "cn=Scott Seligman");

建立和銷燬subcontext

Context介面包含用於建立和銷燬subcontext的方法,一個context必然和另一context 為同一型別.
這裡介紹的例子使用一個有屬性的物件和建立subcontext到目錄中。你可以使用DirContext 關聯屬相到物件中,同時增加binding和subcontext到名稱空間。例如,您可以建立一個物件,並將其繫結到名稱空間,並在同一時間關聯該物件的屬性。這樣命名等價沒有屬性。
createsubcontext()會建立一個新的物件,DirContext.createSubcontext()建立一個子物件,需要指定屬性和context

建立一個Context

建立一個Context,你提供給createsubcontext()Context的名稱。 createsubcontext()名稱的情況下,你想建立及其屬性。

// Create attributes to be associated with the new context
Attributes attrs = new BasicAttributes(true); // case-ignore
Attribute objclass = new BasicAttribute("objectclass");
objclass.add("top");
objclass.add("organizationalUnit");
attrs.put(objclass);

// Create the context
Context result = ctx.createSubcontext("NewOu", attrs);

此示例建立一個新的語境中被稱為”ou=NewOu” 有一個屬性”objectclass”的兩個值,”top”和 “organizationalUnit”,在CTX的Context。

# java Create
ou=Groups: javax.naming.directory.DirContext
ou=People: javax.naming.directory.DirContext
ou=NewOu: javax.naming.directory.DirContext

此示例建立一個新的背景下,被稱為”NewOu”,那是一個CTX的子Context。
這裡寫圖片描述

銷燬Context

銷燬Context,你提供給destroysubcontext()Context的名稱來摧毀。

// Destroy the context
ctx.destroySubcontext("NewOu");