jndi step by step(2)
(4) 目录服务操作
1、目录服务的操作
我们会用LDAP作为例子来讲解目录服务的操作。与命名服务不同,目录服务的内容上下文的初始化方式需要
改变:
- // Set up the environment for creating the initial context
- Hashtable env = new Hashtable();
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
- DirContext ctx = new InitialDirContext(env);
1.1、如何读取属性
你可以使用 DirContext.getAttributes() 方法来读取一个对象的属性,你需要给这个方法传递一个对象的
名字作为参数。例如一个对象在命名系统中的名字是“cn=Ted Geisel, ou=People”,那么我们可以使用如下
的代码得到它的属性:
- Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People");
你也可以输出 “answer”来看看:
- for (NamingEnumeration ae = answer.getAll(); ae.hasMore();) {
- Attribute attr = (Attribute)ae.next();
- System.out.println("attribute: " + attr.getID());
- /* Print each value */
- for (NamingEnumeration e = attr.getAll(); e.hasMore();
- System.out.println("value: " + e.next()))
- ;
- }
输出结果是:
# java GetattrsAll
attribute: sn
value: Geisel
attribute: objectclass
value: top
value: person
value: organizationalPerson
value: inetOrgPerson
attribute: jpegphoto
value: [B@1dacd78b
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: facsimiletelephonenumber
value: +1 408 555 2329
attribute: telephonenumber
value: +1 408 555 5252
attribute: cn
value: Ted Geisel
1.1.1、返回需要的属性
有的时候我们只是需要得到一些属性,而不是全部的属性。这样,你可以把属性作为一个数组,把这个数组
作为参数传递给那个方法。
- // Specify the ids of the attributes to return
- String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"};
- // Get the attributes requested
- Attributes answer = ctx.getAttributes("cn=Ted Geisel, ou=People", attrIDs);
假设我们这个例子里的这个对象,拥有 "sn", "telephonenumber"和"mail"属性,但是没有“golfhandicap”
属性,那么上面的代码返回的结果就应该是:
# java Getattrs
attribute: sn
value: Geisel
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: telephonenumber
value: +1 408 555 5252
1.2、改变属性
DirContext 接口有一些改变对象属性的方法。
1.2.1、批量改变属性
改变属性的方式之一就是批量改变属性,也就是使用许多 ModificationItem 对象来修改属性。
每个 ModificationItem 对象都会有一个常量,来表示对属性进行什么样的操作。这些常量如下:
ADD_ATTRIBUTE
REPLACE_ATTRIBUTE
REMOVE_ATTRIBUTE
对属性的改变会按照队列的顺序来执行,要么所有的改变都生效,要么都不生效。
下面的代码演示了一个例子。它把“mail”这个属性的值,改变成了“geisel@wizards.com”,给“telephonenumber”
属性增加了一个值,并且删除了“jpegphoto”属性。
- // Specify the changes to make
- ModificationItem[] mods = new ModificationItem[3];
- // Replace the "mail" attribute with a new value
- mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
- new BasicAttribute("mail", "geisel@wizards.com"));
- // Add an additional value to "telephonenumber"
- mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
- new BasicAttribute("telephonenumber", "+1 555 555 5555"));
- // Remove the "jpegphoto" attribute
- mods[2] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
- new BasicAttribute("jpegphoto"));
上面的代码中,我们创建了一个修改属性的 ModificationItem 对象的列表(其实就是一个数组),然后执行
modifyAttributes() 方法来修改属性。
- // Perform the requested modifications on the named object
- ctx.modifyAttributes(name, mods);
1.2.2 只修改某几个属性
你可以不使用上面的方式,而对属性进行某一种操作:
- // Save original attributes
- Attributes orig = ctx.getAttributes(name,
- new String[]{"mail", "telephonenumber", "jpegphoto"});
- 。。。。 。。。
- // Revert changes
- ctx.modifyAttributes(name, DirContext.REPLACE_ATTRIBUTE, orig);
1.3、在目录服务中使用搜索功能
1.3.1、基本的搜索功能
最基本的搜索功能是可以指定一个对象的名字,和一些要搜索的属性的名字。
下面的代码演示了这个功能。我们要进行这么一个搜索:对象必须有“sn”属性,而且数值必须是“Geisel”,
而且必须有“mail”这个属性。
- // Specify the attributes to match
- // Ask for objects that has a surname ("sn") attribute with
- // the value "Geisel" and the "mail" attribute
- Attributes matchAttrs = new BasicAttributes(true); // ignore attribute name case
- matchAttrs.put(new BasicAttribute("sn", "Geisel"));
- matchAttrs.put(new BasicAttribute("mail"));
- // Search for objects that have those matching attributes
- NamingEnumeration answer = ctx.search("ou=People", matchAttrs);
你可以打印出这个结果:
- while (answer.hasMore()) {
- SearchResult sr = (SearchResult)answer.next();
- System.out.println(">>>" + sr.getName());
- printAttrs(sr.getAttributes());
- }
输出结果:
# java SearchRetAll
>>>cn=Ted Geisel
attribute: sn
value: Geisel
attribute: objectclass
value: top
value: person
value: organizationalPerson
value: inetOrgPerson
attribute: jpegphoto
value: [B@1dacd78b
attribute: mail
value: Ted.Geisel@JNDITutorial.com
attribute: facsimiletelephonenumber
value: +1 408 555 2329
attribute: cn
value: Ted Geisel
attribute: telephonenumber
value: +1 408 555 5252
1.3.2、返回指定的属性
上一个例子返回了满足条件的全部属性,当然你也可以只返回需要的属性,这仅仅需要把需要返回的属性,
作为一个数组参数传递给 search() 方法。
- // Specify the ids of the attributes to return
- String[] attrIDs = {"sn", "telephonenumber", "golfhandicap", "mail"};
- // Search for objects that have those matching attributes
- NamingEnumeration answer = ctx.search("ou=People", matchAttrs, attrIDs);
1.4、搜索过滤
在这里,你可以学到一个高级点的搜索方式,就是在搜索中使用过滤。在搜索中实现过滤,我们需要使用
表达式来实现这个功能。下面的这个表达式就表示搜索条件是:对象必须有“sn”属性,而且数值必须是
“Geisel”,而且必须有“mail”这个属性:
(&(sn=Geisel)(mail=*))
下面的例子告诉你如何使用表达式来搜索:
- // Create the default search controls
- SearchControls ctls = new SearchControls();
- // Specify the search filter to match
- // Ask for objects that have the attribute "sn" == "Geisel"
- // and the "mail" attribute
- String filter = "(&(sn=Geisel)(mail=*))";
- // Search for objects using the filter
- NamingEnumeration answer = ctx.search("ou=People", filter, ctls);
下面这个列表有助你使用表达式:
符号 描述
& conjunction (i.e., and -- all in list must be true)
| disjunction (i.e., or -- one or more alternatives must be true)
! negation (i.e., not -- the item being negated must not be true)
= equality (according to the matching rule of the attribute)
~= approximate equality (according to the matching rule of the attribute)
>= greater than (according to the matching rule of the attribute)
<= less than (according to the matching rule of the attribute)
=* presence (i.e., the entry must have the attribute but its value is irrelevant)
* wildcard (indicates zero or more characters can occur in that position);
used when specifying attribute values to match
\ escape (for escaping '*', '(', or ')' when they occur inside an attribute value)
表达式中的每一个项目,都必须使用属性的名字,也可以使用属性的数值。例如“sn=Geisel”意味着属性
“sn”的值为“Geisel”,而 "mail=*" 意味着属性 “mail” 必须存在,但是可以为任意值。
每一个项目必须是在括号中,例如 "(sn=Geisel)"。不同的括号间使用逻辑判断符号来连接起来。
例如 "(| (& (sn=Geisel) (mail=*)) (sn=L*))"。这表示 属性中 sn 必须等于Geisel 并且有 mail 这个
属性 或者 有 sn 这个属性。
详细的内容,请参考 http://www.ietf.org/rfc/rfc2254.txt
当然了,你也可以只返回指定的属性,而不是全部的属性。
1.5 搜索控制
在上面的章节中,我们已经看到了一个类:SearchControls,通过这个类,你可以控制搜索的行为。这里
我们就来仔细地看看这个类。
1.5.1 搜索范围
SearchControls 类默认会在整个内容上下文(SearchControls.ONELEVEL_SCOPE)搜索对象,通过设置,你
可以在某一个范围内搜索对象。
1.5.1.1 在一个子树上搜索
通过下面的代码你可以清晰地了解这一功能:
1.5.1.2 根据名字来搜索
通过下面的代码你可以清晰地了解这一功能:
1.5.2 数量的限制
通过下面的代码你可以控制返回结果的数量:
1.5.3 时间的限制
如果一个搜索耗费了很长的时间,那可不是一个好方法。这里你可以设置超时的时间。
参数的单位是毫秒。
如果发生超时现象,那么就会抛出 TimeLimitExceededException。
1.6 结合命名服务和目录服务的操作
我们已经这样的一个概念,就是目录服务是命名服务的一个扩展。例如,之前我们说过命名服务具有 bind(),
rebind(), createSubcontext() 等方法,但是在目录服务里却没有介绍这些方法。
其实目录服务里也有这些方法。下面就用 LDAP 作为例子,介绍一下这些方法。
1.6.1 创建一个具有属性的内容上下文
1.6.2 增加一个具有属性的绑定
1.6.3 替换一个具有属性的绑定
import javax.naming.*; import javax.naming.directory.*; import java.util.Hashtable; /** * Demonstrates how to replace a binding and its attributes to a context. * (Use after Bind example; use Unbind to remove binding.) * * usage: java Rebind */ class Rebind { public static void main(String[] args) { // Set up the environment for creating the initial context Hashtable env = new Hashtable(11); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial"); try { // Create the initial context DirContext ctx = new InitialDirContext(env); // Create object to be bound Fruit fruit = new Fruit("lemon"); // Create attributes to be associated with object Attributes attrs = new BasicAttributes(true); // case-ignore Attribute objclass = new BasicAttribute("objectclass"); objclass.add("top"); objclass.add("organizationalUnit"); attrs.put(objclass); // Perform bind ctx.rebind("ou=favorite, ou=Fruits", fruit, attrs); // Check that it is bound Object obj = ctx.lookup("ou=favorite, ou=Fruits"); System.out.println(obj); // Get its attributes Attributes retattrs = ctx.getAttributes("ou=favorite, ou=Fruits"); GetattrsAll.printAttrs(retattrs); // Close the context when we're done ctx.close(); } catch (NamingException e) { System.out.println("Operation failed: " + e); } } }
(5) 高级应用之Name
1、jndi 高级应用之 Name
1.1、什么是 Name?
这之前的文档中,我们所用的例子里,对象的名字都是 java.lang.String 类型的,也就是字符串类型。
在这个文档里,我们则会介绍一些其他的对象名字类型,如 Name,以及他们的使用方法。
我们首先会讲讲什么是字符串的名字和结构化的名字,以及它们的存在的必要性。
然后,我们会介绍2个结构化名字的类:(复合名字)composite 和 (混合名字)compound。
最后,我们介绍如何在对象名字中使用特殊字符,以及如何解析和构成一个复杂的对象的名字。
1.2、字符串名字 vs 结构化名字
在 Context 和 DirContext 接口里,每一个命名服务的方法都有2种方式,一个是接受字符串类型的名字,
一个是接受结构化的名字(Name 类的对象)。例如:
lookup(java.lang.String)
lookup(javax.naming.Name)
1.2.1、字符串名字
使用字符串类型的名字,可以让你不必再生成一个CompositeName类的对象。例如下面的代码是相同的:
- Object obj1 = ctx.lookup("cn=Ted Geisel, ou=People, o=JNDITutorial");
- CompositeName cname = new CompositeName(
- "cn=Ted Geisel, ou=People, o=JNDITutorial");
- Object obj2 = ctx.lookup(cname);
1.2.1、结构化的名字
结构化的名字的对象可以是 CompositeName 或 CompoundName 类的对象,或者是任何一个实现了 “Name ”
接口的类。
如果是 CompositeName 类的实例,那么就被认为是一个复合的名字(composite name)。所谓的复合名字就
是可以在一个名字里使用多个命名服务系统,而不是仅仅一个。
如果是 compound 类的实例,那么就被认为是混合的名字(compound name)。混合的名字只包含一个命名服务
系统。
1.2.2、那么该使用哪种名字呢?
一般来说,如果用户可以提供字符串类型的名字,那么就使用字符串类型的名字;可是如果用户提供的是一
个组合的名字,那么就应该使用结构化的名字了。
例如一个应用如果会涉及多个命名服务系统,那么就应该使用结构化的名字了。
1.3 复合名字(composite name)
复合名字就是跨越多个命名系统的名字。例如:
cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt
这个名字里包含了两个命名系统:一个 LDAP 系统 "cn=homedir,cn=Jon Ruiz,ou=People" 和一个文件系统
"tutorial/report.txt"。当你把这个字符串传递给 Context 的 look 方法,那么就会从 LDAP 系统里查找
那个文件,然后返回那个对象(一个文件对象的实例)。当然,这取决于特定的SPI。例如:
- File f = (File)ctx.lookup(
- "cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt");
1.3.1、字符串的表现形式
我们可以这样想像,一个复合的名字是由不同的“组件”组成的。所谓的“组件”,我们可以想象成是一个名字
的一小部分,每一个组件表示一个命名服务系统。每一个组件都由正斜杠“/”分开。
例如 :cn=homedir,cn=Jon Ruiz,ou=People/tutorial/report.txt
包含三个组件:
cn=homedir,cn=Jon Ruiz,ou=People
tutorial
report.txt
第一个组件属于LDAP命名服务系统,第2和第3个组件属于文件命名服务系统。正如我们从这个例子里看到的,
多个组件(tutorial 和 report.txt)可以属于同一个命名服务系统,但是一个组件是不能跨越多个命名服务
系统的。
除了使用正斜杠“/”外,复合名字还允许使用其它三个特殊符号:反斜杠“\",单引号"'",双引号" " "。
这三个符号是内置的符号,也就是说他们三个具有特殊的用途。
反斜杠“\”的作用是转译字符。反斜杠“\”之后的字符,会被认为是普通字符。
例如“a\/b”,其中的“/”是不会被认为是不同的命名服务系统的分隔符号。
双引号和单引号可以让引号内的字符成为普通字符,例如下面的用法都是一样的:
a\/b\/c\/d
"a/b/c/d"
'a/b/b/d'
可见,有的时候使用引号还是很方便的。
复合的名字也可以是空的,空的复合名字意味着没有组件,一般用空的字符串表示。
复合名字的组件也可以是空的,例如:
/abc
abc/
abc//xyz
1.3.2、复合名字的类
CompositeName 类是用来构成复合名字的类。你可以给它的构造函数传递一个复合名字的字符串。
例如:
- import javax.naming.CompositeName;
- import javax.naming.InvalidNameException;
- /**
- * Demonstrates how to construct a composite name given its
- * string representation
- *
- * usage: java ConstructComposite
- */
- class ConstructComposite {
- public static void main(String[] args) {
- if (args.length != 1) {
- System.out.println("usage: java ConstructComposite <string></string>");
- System.exit(-1);
- }
- String name = args[0];
- try {
- CompositeName cn = new CompositeName(name);
- System.out.println(cn + " has " + cn.size() + " components: ");
- for (int i = 0; i < cn.size(); i++) {
- System.out.println(cn.get(i));
- }
- } catch (InvalidNameException e) {
- System.out.println("Cannot parse name: " + name);
- }
- }
- }
运行这个例子,输出的结果就是:
a/b/c has 3 components:
a
b
c
CompositeName 类有许多方法,例如查看,修改,比较,以及得到一个复合名字的字符串表现形式。
1.3.3、访问复合名字里的组件
可以通过下列方法访问复和名字里的组件:
get(int posn)
getAll()
getPrefix(int posn)
getSuffix(int posn)
clone()
如果你想得到特定位置的组件的名字,那么使用 get(int posn) 方法就非常合适。
getAll() 方法可以返回全部的组件的名字。
例如:
- try {
- CompositeName cn = new CompositeName(name);
- System.out.println(cn + " has " + cn.size() + " components: ");
- for (Enumeration all = cn.getAll(); all.hasMoreElements();) {
- System.out.println(all.nextElement());
- }
- } catch (InvalidNameException e) {
- System.out.println("Cannot parse name: " + name);
- }
你可以用 getPrefix(int posn) 和 getSuffix(int posn)来从前端或者后端查找组件的名字,如:
- CompositeName cn = new CompositeName("one/two/three");
- Name suffix = cn.getSuffix(1); // 1 <= index < cn.size()
- Name prefix = cn.getPrefix(1); // 0 <= index < 1
运行结果:
two/three
one
1.3.4、修改一个复合名字
你可以通过下列方法修改一个复合名字:
add(String comp)
add(int posn, String comp)
addAll(Name comps)
addAll(Name suffix)
addAll(int posn, Name suffix)
remove(int posn)
当你创建了一个复合名字实例后,你可以对它进行修改。看看下面的例子:
- CompositeName cn = new CompositeName("1/2/3");
- CompositeName cn2 = new CompositeName("4/5/6");
- System.out.println(cn.addAll(cn2)); // 1/2/3/4/5/6
- System.out.println(cn.add(0, "abc")); // abc/1/2/3/4/5/6
- System.out.println(cn.add("xyz")); // abc/1/2/3/4/5/6/xyz
- System.out.println(cn.remove(1)); // 1
- System.out.println(cn); // abc/2/3/4/5/6/xyz
1.3.4、比较复合名字
你可以通过以下的方法对复合名字进行比较:
compareTo(Object name)
equals(Object name)
endsWith(Name name)
startsWith(Name name)
isEmpty()
你可以使用 compareTo(Object name) 方法对一个复合名字的列表进行排序。下面是一个例子:
- import javax.naming.CompositeName;
- import javax.naming.InvalidNameException;
- /**
- * Demonstrates how to sort a list of composite names.
- *
- * usage: java SortComposites [<name></name>]*
- */
- class SortComposites {
- public static void main(String[] args) {
- if (args.length == 0) {
- System.out.println("usage: java SortComposites [<names></names>]*");
- System.exit(-1);
- }
- CompositeName[] names = new CompositeName[args.length];
- try {
- for (int i = 0; i < names.length; i++) {
- names[i] = new CompositeName(args[i]);
- }
- sort(names);
- for (int i = 0; i < names.length; i++) {
- System.out.println(names[i]);
- }
- } catch (InvalidNameException e) {
- System.out.println(e);
- }
- }
- /**
- * Use bubble sort.
- */
- private static void sort(CompositeName[] names) {
- int bound = names.length-1;
- CompositeName tmp;
- while (true) {
- int t = -1;
- for (int j=0; j < bound; j++) {
- int c = names[j].compareTo(names[j+1]);
- if (c > 0) {
- tmp = names[j];
- names[j] = names[j+1];
- names[j+1] = tmp;
- t = j;
- }
- }
- if (t == -1) break;
- bound = t;
- }
- }
- }
equals() 方法可以让你比较两个复合名字是否相同。只有两个复合名字有相同的组件,而且顺序一样,
会返回 true。
使用 startsWith() 和 endsWith()方法,你可以判断复合名字是以什么字符串开头和以什么字符串结尾。
isEmpty() 方法可以让你知道一个复合名字是否为空。你也可以使用 size() == 0 来实现同样的功能。
下面是一些例子:
- CompositeName one = new CompositeName("cn=fs/o=JNDITutorial/tmp/a/b/c");
- CompositeName two = new CompositeName("tmp/a/b/c");
- CompositeName three = new CompositeName("cn=fs/o=JNDITutorial");
- CompositeName four = new CompositeName();
- System.out.println(one.equals(two)); // false
- System.out.println(one.startsWith(three)); // true
- System.out.println(one.endsWith(two)); // true
- System.out.println(one.startsWith(four)); // true
- System.out.println(one.endsWith(four)); // true
- System.out.println(one.endsWith(three)); // false
- System.out.println(one.isEmpty()); // false
- System.out.println(four.isEmpty()); // true
- System.out.println(four.size() == 0); // true
1.3.5、复合名字的字符串表现形式
你可以使用 toString() 方法来实现这个功能。
下面是一个例子:
- import javax.naming.CompositeName;
- import javax.naming.InvalidNameException;
- /**
- * Demonstrates how to get the string representation of a composite name.
- *
- * usage: java CompositeToString
- */
- class CompositeToString {
- public static void main(String[] args) {
- if (args.length != 1) {
- System.out.println("usage: java CompositeToString <string></string>");
- System.exit(-1);
- }
- String name = args[0];
- try {
- CompositeName cn = new CompositeName(name);
- String str = cn.toString();
- System.out.println(str);
- CompositeName cn2 = new CompositeName(str);
- System.out.println(cn.equals(cn2)); // true
- } catch (InvalidNameException e) {
- System.out.println("Cannot parse name: " + name);
- }
- }
- }
1.3.5、复合名字作为Context的参数
直接看一个例子,非常简单:
- // Create the initial context
- Context ctx = new InitialContext(env);
- // Parse the string name into a CompositeName
- Name cname = new CompositeName(
- "cn=homedir,cn=Jon Ruiz,ou=people/tutorial/report.txt");
- // Perform the lookup using the CompositeName
- File f = (File) ctx.lookup(cname);
1.4、混合名字
混合名字不是跨越命名系统的,例如:
cn=homedir,cn=Jon Ruiz,ou=People
它和复合名字有些类似,我们也可以把它看成是由不同的组件组成的,这个名字里包含了三个组件:
ou=People
cn=Jon Ruiz
cn=homedir
1.4.1、混合名字和复合名字的关系
当你给 Context.lookup() 传递一个字符串的时候,首先 lookup 方法会把这个字符串作为复合名字来看
待,这个复合名字可能只包含一个组件。但是一个组件可能会包含几个混合名字。
1.4.2、混合名字的字符串表现方式
正如上面所说的,一个混合名字是由很多的组件组成的。组件之间的分隔符号依赖于特定的命名服务系统。
例如在 LDAP 里,分隔符好就是“,”,因此,下面的这个混合名字
ou=People
cn=Jon Ruiz
cn=homedir
的字符串形式就是 cn=homedir,cn=Jon Ruiz,ou=People
1.4.3、混合名字的类
处理混合名字,我们可以使用 CompoundName 类。你可以向它的构造函数传递混合名字的字符串,并且还
得设置一些必要的属性,这些属性一般都是特定的命名服务系统的一些规则。
实际上,只有当你准备编写一个SPI的时候,才会去使用装个构造函数。作为一个开发者,一般你只是会
涉及混合名字里的各个组件而已。
下面是一个例子:
- import javax.naming.*;
- import java.util.Hashtable;
- /**
- * Demonstrates how to get a name parser and parse a name.
- *
- * usage: java ParseCompound
- */
- class ParseCompound {
- public static void main(String[] args) {
- // Set up environment for creating initial context
- Hashtable env = new Hashtable(11);
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
- try {
- // Create the initial context
- Context ctx = new InitialContext(env);
- // Get the parser for this namespace
- NameParser parser = ctx.getNameParser("");
- // Parse name
- Name compoundName = parser.parse("cn=John,ou=People,ou=Marketing");
- // List components in name
- for (int i = 0; i < compoundName.size(); i++) {
- System.out.println(compoundName.get(i));
- }
- // Close ctx when done
- ctx.close();
- } catch (NamingException e) {
- System.out.println(e);
- }
- }
- }
1.4.4、操作混合名字类
注意,上面的例子中,我们使用 NameParser.parse() 来返回一个实现了Name接口的对象。这个接口可以
是 CompositeName类 或 CompoundName类。这就意味着,你可以访问或者修改一个混合名字对象,就好
像我们在复合名字里做的一样!
下面是一个例子:
- // Get the parser for this namespace
- NameParser parser = ctx.getNameParser("");
- // Parse the name
- Name cn = parser.parse("cn=John,ou=People,ou=Marketing");
- // Remove the second component from the head
- System.out.println(cn.remove(1)); // ou=People
- // Add to the head (first)
- System.out.println(cn.add(0, "ou=East")); // cn=John,ou=Marketing,ou=East
- // Add to the tail (last)
- System.out.println(cn.add("cn=HomeDir")); // cn=HomeDir,cn=John,ou=Marketing,ou=East
输出结果:
ou=People
cn=John,ou=Marketing,ou=East
cn=HomeDir,cn=John,ou=Marketing,ou=East
需要的注意的是,LDAP系统里,组件的顺序是从右到左的。也就是右边是名字的开头,而左边是名字
的结尾!
下面这个例子是修改混合名字的:
- import javax.naming.*;
- import java.util.Hashtable;
- import java.io.File;
- /**
- * Demonstrates how to modify a compound name by adding and removing components.
- * Uses file system syntax.
- *
- * usage: java ModifyCompoundFile
- */
- class ModifyCompoundFile {
- public static void main(String[] args) {
- // Set up environment for creating initial context
- Hashtable env = new Hashtable(11);
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.fscontext.RefFSContextFactory");
- try {
- // Create the initial context
- Context ctx = new InitialContext(env);
- // Get the parser for this namespace
- NameParser parser = ctx.getNameParser("");
- // Parse name
- Name cn = parser.parse("Marketing" + File.separator +
- "People" + File.separator +
- "John");
- // Remove 2nd component from head
- System.out.println(cn.remove(1)); // People
- // Add to head (first)
- System.out.println(cn.add(0, "East"));
- // East/Marketing/John
- // Add to tail (last)
- System.out.println(cn.add("HomeDir"));
- // /East/Marketing/John/HomeDir
- // Close ctx when done
- ctx.close();
- } catch (NamingException e) {
- System.out.println(e);
- }
- }
- }
这个例子使用了文件系统,而不是LDAP系统,输出结果:
People
East/Marketing/John
East/Marketing/John/HomeDir
1.4.5、混合名字作为Context的参数
我们只用一个例子就可以了:
- // Create the initial context
- Context ctx = new InitialContext(env);
- // Get the parser for the namespace
- NameParser parser = ctx.getNameParser("");
- // Parse the string name into a compound name
- Name compound = parser.parse("cn=Jon Ruiz,ou=people");
- // Perform the lookup using the compound name
- Object obj = ctx.lookup(compound);
1.4.6、取得完整的混合名字
有的时候,你可能需要根据一个混合名字来的完整的名字。例如一个DNS,你只知道一部分如“www.abc.com”,
但实际上它的完整的DNS是“www.abc.com.cn”。
其实这个功能已经超出了jndi 的 api 所控制的范围了,因为 jndi 是不能决定完整的名字到底是什么样子,
那必须依赖特定的命名服务系统。
但是,jndi 仍然提供了一个接口 Context.getNameInNamespace()。这个方法的返回结果,依赖于你所使用
的命名服务系统。
下面是一个例子:
- // Create the initial context
- Context ctx = new InitialContext(env);
- // Perform the lookup
- Context jon = (Context)ctx.lookup("cn=Jon Ruiz,ou=people");
- String fullname = jon.getNameInNamespace();
输出结果:
cn=Jon Ruiz,ou=people,o=JNDItutorial
1.5、名字解析
名字解析的意思就是根据一个名字的字符串形式,把它解析成结构化的形式,如混合名字或复合名字。
jndi API里包含了这样的接口,其接口的实现,依赖于特定的SPI。
1.5.1、解析复合名字
下面是一个例子:
- // Parse the string name into a CompositeName
- Name cname = new CompositeName(
- "cn=homedir,cn=Jon Ruiz,ou=people/tutorial/report.txt");
其实这个用法我们早就在前面看过了。
1.5.2、解析混合名字
为了解析混合名字,你必须使用 NameParser 接口,这个接口有一个方法:
- Name parse(String name) throws InvalidNameException
首先,你必须从 SPI 里得到一个 NameParser 接口的实现:
- // Create the initial context
- Context ctx = new InitialContext();
- // Get the parser for LDAP
- NameParser ldapParser =
- ctx.getNameParser("ldap://localhost:389/o=jnditutorial");
- // Get the parser for filenames
- NameParser fsParser = ctx.getNameParser("file:/");
一旦你得到了 NameParser 实例,你就可以用它来把字符串形式的名字转换成结构化的名字。
- // Parse the name using the LDAP parser
- Name compoundName = ldapParser.parse("cn=John Smith, ou=People, o=JNDITutorial");
- // Parse the name using the LDAP parser
- Name compoundName = fsParser.parse("tmp/tutorial/beyond/names/parse.html");
如果解析错误,那么会得到 InvalidNameException 。
尽管 parse() 方法可以返回一个结构化的名字,但是我们还是建议仅仅用它来处理混合名字,而不要
处理复合名字。
parse()返回的对象未必一定是 CompoundName 类,只要返回的对象实现了 Name 接口就可以--这依赖
于特定的SPI。
1.6、动态生成复合名字
之前我们介绍了访问,操作复合名字的方法。但是还有一些难以处理的情况需要我们去面对。
例如,如果你要给一个复合名字增加一个组件的话,你是增加复合名字的组件还是混合名字的组件?
如果是混合名字的组件,那么使用什么规则呢?
例如我们有这样一个复合名字:
cn=homedir,cn=Jon Ruiz/tutorial
现在你需要增加一个文件名字,如果是windows系统,那么就是:
cn=homedir,cn=Jon Ruiz/tutorial\report.txt
然后我们再增加一个LDAP混合名字 ou=People,那么就需要使用LDAP的规则:
cn=homedir,cn=Jon Ruiz,ou=People/tutorial\report.txt
在这个例子中,我们使用了不同的命名服务系统,我们需要知道什么时候因该使用什么样的命名系统
的规则,这真的很麻烦。
为了解决这个问题,jndi API 提供了一个接口 Context.composeName(),它用来动态的组装名字,
当然依赖于特定的SPI了。
你需要给它提供两个参数:一个是需要追加的组件,一个是被追加的组件的名字。
下面是一个例子:
- // Create the initial context
- Context ctx = new InitialContext(env);
- // Compose a name within the LDAP namespace
- Context ldapCtx = (Context)ctx.lookup("cn=Jon Ruiz,ou=people");
- String ldapName = ldapCtx.composeName("cn=homedir", "cn=Jon Ruiz,ou=people");
- System.out.println(ldapName);
- // Compose a name when it crosses into the next naming system
- Context homedirCtx = (Context)ctx.lookup(ldapName);
- String compositeName = homedirCtx.composeName("tutorial", ldapName);
- System.out.println(compositeName);
- // Compose a name within the File namespace
- Context fileCtx = (Context)ctx.lookup(compositeName);
- String fileName = fileCtx.composeName("report.txt", compositeName);
- System.out.println(fileName);
(6) 高级应用之 环境属性
1、环境属性
在之前的文档里,我们已经学会如何去初始化一个内容上下文,例如:
- Hashtable env = new Hashtable();
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.put(Context.PROVIDER_URL, "ldap://localhost:389/o=JNDITutorial");
- Context ctx = new InitialContext(env);
这个例子中的 Hashtable,就是一个环境属性,或者简单的说是“环境”。
这个章节里,我们要介绍的就是这个“环境属性”,看看他们是如何发挥作用的,以及如何使用的等等。
JNDI其实只是一个接口,为了访问一个命名/目录服务,你必须依赖特定的SPI。这就需要进行一些配置,
你得告诉JNDI,你需要的SPI是什么。
下面是一些不同种类的环境属性:
a> 标准的
b> 描述服务的
c> 描述特性的
d> 描述提供者的。
下面是标准的环境属性:
1.1、环境属性
配置环境属性可以通过两种方式:一个是把一个Hashtable传递给InitialContext的构造函数,另一个
是用一个 .properties 文件。
一些JNDI的环境属性还可以通过系统变量或者Applet参数来设置。
1.1.1、应用程序资源文件(.properties文件)
你可以在 .properties文件 里指定JNDI的配置。这个文件的名字应该是 jndi.properties。例如下面
就是一个 jndi.properties的例子:
java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person
java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person
java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
java.naming.provider.url=ldap://localhost:389/o=jnditutorial
com.sun.jndi.ldap.netscape.schemaBugs=true
首先,应用程序会从classpath 里面载入 jndi.properties,如果没有发现,那么会载入JAVA_HOME/lib/
jndi.properties。
注意:如果你使用这种方式配置JNDI,那么当找不到装个文件的时候,会抛出异常。
当然,我们初始化内容上下文的用法也需要修改一下:
- InitialContext ctx = new InitialContext();
1.1.2、通过系统变量设置JNDI
我们可以使用如下的方式来实现这个功能:
# java -Djava.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory \
-Djava.naming.provider.url=ldap://localhost:389/o=jnditutorial \
List
1.1.3、Applet的方式设置JDNI
看这个例子:
- name=java.naming.factory.initial
- value=com.sun.jndi.ldap.LdapCtxFactory>
- name=java.naming.provider.url
- value=ldap://localhost:389/o=jnditutorial>
同时初始内容上下文也需要修改:
- // Put this applet instance into the environment
- Hashtable env = new Hashtable();
- env.put(Context.APPLET, this);
- // Pass the environment to the initial context constructor
- Context ctx = new InitialContext(env);
- // List the objects
- NamingEnumeration enum = ctx.list(target);
- while (enum.hasMore()) {
- out.println(enum.next());
- }
- ctx.close();
1.2、内容上下文环境的探讨
我们已经知道可以通过三种方式来设置JDNI的属性。但是,如果我们同时使用了两种方式会
怎么样呢?看看下面的例子:
- // Initial environment with various properties
- Hashtable env = new Hashtable();
- env.put(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.fscontext.FSContextFactory");
- env.put(Context.PROVIDER_URL, "file:/");
- env.put(Context.OBJECT_FACTORIES, "foo.bar.ObjFactory");
- env.put("foo", "bar");
- // Call the constructor
- Context ctx = new InitialContext(env);
- // See what environment properties you have
- System.out.println(ctx.getEnvironment());
我们同时配置一个jndi.properties在classpath里:
java.naming.factory.object=com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person
java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person
java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
java.naming.provider.url=ldap://localhost:389/o=jnditutorial
com.sun.jndi.ldap.netscape.schemaBugs=true
然后看看运行的结果:
com.sun.jndi.ldap.netscape.schemaBugs=true
java.naming.factory.object=foo.bar.ObjFactory:com.sun.jndi.ldap.AttrsToCorba:com.wiz.from.Person
java.naming.factory.initial=com.sun.jndi.fscontext.FSContextFactory
foo=bar
java.naming.provider.url=file:/
java.naming.factory.state=com.sun.jndi.ldap.CorbaToAttrs:com.wiz.from.Person
java.naming.factory.control=com.sun.jndi.ldap.ResponseControlFactory
下面来分析一下这个结果:
a> 在 Hashtable 里的“foo”项,和在文件里的 “com.sun.jndi.ldap.netscape.schemaBugs”,
都出现在结果里。
b> “java.naming.factory.object” 项是两者的组合。
c> 其他的属性如“java.naming.factory.initial” 都是用的 Hashtable里的。
是不是有些混乱?如果我们要使用多个SPI该怎么办?没关系,我们在下一个小结里介绍这个问题。
1.3、定制使用SPI
你可以通过一个SPI的属性文件,来为某一个SPI进行单独的设置。这个文件也是 .properties 文件。
SPI的属性文件应该类似下面:
前缀/jndiprovider.properties
这个前缀是什么呢?就是这个SPI实现的内容上下文类(Context)的包的结构。
例如,我们要使用的是 com.sun.jndi.ldap.LdapCtx 这个内容上下文,那么对应于它的属性配置
文件就应该是:"com/sun/jndi/ldap/jndiprovider.properties"。
一个应用可以使用多个这样的属性配置文件。
那么我们为什么要使用属性配置文件?
有两个原因。第一,我们可以单独的配置某一个命名/服务系统。第二,部署的时候会有用。例如,
你可以单独配置一个针对LDAP的属性,而不用去修改 jndi.properties,或者增加系统变量。
但是,我们也并不是可以在SPI属性配置文件里设置全部的属性,我们可以设置的属性如下:
java.naming.factory.object
java.naming.factory.state
java.naming.factory.control
java.naming.factory.url.pkgs
不过并不象 jndi.properties 或者 那个Hastable,SPI属性配置文件里的属性不会自动载入到环境
里,只有SPI调用了下列方法时才会这样做:
NamingManager.getObjectInstance(Object, Name, Context, Hashtable)
DirectoryManager.getObjectInstance(Object, Name, Context, Hashtable, Attributes)
NamingManager.getStateToBind(Object, Name, Context, Hashtable)
DirectoryManager.getStateToBind(Object, Name, Context, Hashtable, Attributes)
ControlFactory.getControlInstance(Control, Context, Hashtable)
例如,假设我们使用一个LDAP服务,它的实现类是com.sun.jndi.ldap.LdapCtx,当这个类调用
DirectoryManager.getObjectInstance() 方法时,JNDI将会从com/sun/jndi/ldap/jndiprovider.properties
里找到“java.naming.factory.object”项,然后把它追加到环境里已经定义的项里(例如那个Hashtable
或者 jndi.properties)。
转载于:https://www.cnblogs.com/aurawing/articles/1887036.html
jndi step by step(2)相关推荐
- Linux内核同步机制之(四):spin lock【转】
转自:http://www.wowotech.net/kernel_synchronization/spinlock.html 一.前言 在linux kernel的实现中,经常会遇到这样的场景:共享 ...
- Linux 内核同步(二):自旋锁(Spinlock)
自旋锁 内核当发生访问资源冲突的时候,可以有两种锁的解决方案选择: 一个是原地等待 一个是挂起当前进程,调度其他进程执行(睡眠) Spinlock 是内核中提供的一种比较常见的锁机制,自旋锁是&quo ...
- ElasticSearch:为中文分词器增加对英文的支持(让中文分词器可以处理中英文混合文档)(转)
本文地址,需转载请注明出处: http://blog.csdn.net/hereiskxm/article/details/47441911 当我们使用中文分词器的时候,其实也希望它能够支持对于英文的 ...
- 自旋锁使用场景和实现分析(转载)
自旋锁 最近看到的一篇文章,觉得写的很清晰,通过场景应用解答了我对自旋锁使用的一些疑问,推荐给大家. 引入问题: (1)如果cpu0持有锁,cpu1一直不释放锁怎么办? (2)什么场景下必须要用自旋锁 ...
- python入门(中)
python入门(中) 一.简介 二.列表 1.列表的定义 2.列表的创建 3.向列表中添加元素 4.删除列表中的元素 5.获取列表中的元素 6.列表的常用操作符 7.列表的其它方法 三.元组 1.创 ...
- 机器学习 基础理论 学习笔记 (8)特征选择(feature selection)(一)
0.说明 本文也许比较乱,请看目录再食用. 后续会 出文 机器学习 基础理论 学习笔记 (8)特征选择(feature selection)(二) 将 分类问题 和 回归问题 分开总结. 以及或将出文 ...
- 基于卷积神经网络的多目标图像检测研究(四)
第4章 Faster R-CNN网络模型设计 4.1 Faster RCN文件结构介绍 本文在介绍具体的训练模型之前,先介绍Faster RCNN的文件结构.如图4.1所示,主要包括了7个文件夹和4个 ...
- 【第七周:Python(一)】7周成为数据分析师
本课程共七个章节,课程地址:7周成为数据分析师(完结)_哔哩哔哩_bilibili 数据分析思维 业务知识 Excel 数据可视化 SQL 统计学 Python 第七周:Python(P86-P143 ...
- tf.estimator API技术手册(16)——自定义Estimator
tf.estimator API技术手册(16)--自定义Estimator (一)前 言 (二)自定义estimator的一般步骤 (三)准备训练数据 (四)自定义estimator实践 (1)创建 ...
- NHibernate Step by Step:序篇 (转)
原文出处:http://www.cnblogs.com/abluedog/archive/2006/04/15/375676.html 原文作者:abluedog 很久以前,你可能习惯了connect ...
最新文章
- 黑苹果没有找到触控板为什么还是能用_为什么Macbook触控板体验领先Windows那么多,却难以取代鼠标?...
- 《剑指offer》-斐波那契数列
- QgridLayout例子,在qlayout添加button实例(添加窗体过程类似)
- Mybatis 系列2-配置文件
- vue怎么获取用户的位置经纬度_vue 实现Web端的定位功能 获取经纬度
- 栈的应用--中缀表达式转后缀表达式
- java fx 重绘_如何重绘JAVA FX 2.2中的窗口(舞台)
- IEEE1588 ( PTP ) 协议简介
- 开源无国界,从openEuler Maintainer到Spark Committer的贡献开源之路
- MongoBD命令大全
- MMDetection3D 1.1:统一易用的 3D 感知平台
- 微信支付宝收款二维码还能用吗?权威解读
- JWT手动签发| 自动签发
- 大数据风控是在做什么?
- [Python] 相关性分析
- pes时处理PTS和DTS的方法
- 旋转接头出现故障的原因分享
- Linux 系统假死的解决方案
- 前端初学之利用html,css,js实现车牌摇号程序(一)
- EasyUI表单验证
热门文章
- webpack打包告警We noticed you‘re using the `useBuiltIns` option without declaring a core-js version.
- 162天,从华为外包5k转岗正式员工15k,心酸只有自己知道
- 英国脱离欧盟Brexit目前票数:脱欧阵营51.4%领先!!!
- 截止失真放大电路_【电子干货377】晶体三极管的一些常见应用电路
- 职业等级计算机操作员,计算机操作员是什么职业?
- unity游戏开发需要学什么?
- 微信小程序在线考试项目开发-用户授权登录、身份信息获取
- 如何设计高扩展的在线网页制作平台
- 软件测试工程师APUS一面二面汇总
- matlab心电显示,请问如何在GUI界面中打开并显示心电信号