网站开发属于无形资产吗,wordpress 防止恶意注册,企业网站建站 优帮云,wordpress点击图片缩放命名和目录操作
您可以使用JNDI执行以下操作#xff1a;读取操作和更新命名空间的操作。本节介绍这两个操作#xff1a;
l 查询对象
l 列出上下文内容
l 添加、覆盖和移除绑定
l 重命名对象
l 创建和销毁子上下文
配置
在命…命名和目录操作
您可以使用JNDI执行以下操作读取操作和更新命名空间的操作。本节介绍这两个操作
l 查询对象
l 列出上下文内容
l 添加、覆盖和移除绑定
l 重命名对象
l 创建和销毁子上下文
配置
在命名和目录服务中执行操作之前需要得到初始化上下文――命名空间的开始点。因为命名和目录服务的所有方法都相对于一些上下文执行。
为了得到初始化上下文必须执行以下步骤
1. 选择想要访问的访问提供者。
2. 指定需要的初始化上下文。
3. 调用InitialContext构造函数。
第一步为初始化上下文选择服务提供者
您可以为初始化上下文指定服务提供者创建一个环境变量集合Hashtable同时将服务提供者的名称加入其中。环境属性在JNDI教程中有详细的介绍。
如果您使用Sun的LDAP服务提供者代码如下所示 Hashtable env new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY,com.sun.jndi.ldap.LdapCtxFactory); 要指定Sun的文件系统服务提供者代码如下所示 Hashtable env new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY,com.sun.jndi.fscontext.RefFSContextFactory); 您可以使用一些系统属性描述使用的服务提供者。在JNDI教程中有详细描述。
第二步提供初始化上下文需要的信息
不同目录的客户端可能需要提供不同的信息用来连接目录。例如您需要指定服务器运行的机器以及识别目录中的用户。这些信息通过环境属性传递给服务提供者。JNDI指定服务提供者使用的一般环境参数。您的服务提供者文档会为需要提供的参数进行详细的说明。
LDAP提供者需要程序提供LDAP服务器的位置以及认证信息。要提供这些信息需要如下代码 env.put(Context.PROVIDER_URL, ldap://ldap.wiz.com:389); env.put(Context.SECURITY_PRINCIPAL, joeuser); env.put(Context.SECURITY_CREDENTIALS, joepassword); 本教程中使用Sun的LDAP服务提供者。例子中假设服务器设置在本机使用389端口根辨别名是“oJNDITutorial”修改目录不需要认证。这些信息是设置环境所需要的。 env.put(Context.PROVIDER_URL, ldap://localhost:389/oJNDITutorial); 如果您使用不同设置的目录需要设置相应的环境属性。您需要使用机器名称替换“localhost”。您可以在任何公共的目录服务器或在其他机器上的自己的服务器运行例子。您需要将“localhost”替换成那台机器的名字将oJNDITutorial替换成相应的命名上下文。
第三步创建初始化上下文
您已经创建了初始化上下文。为了做到这一点您将之前创建的环境属性放到InitialContext构造函数中 Context ctx new InitialContext(env); 现在您有了一个上下文对象的引用可以开始访问命名服务了。
为了完成目录操作需要使用InitialDirContext。为了做到这一点使用它的一个构造函数 DirContext ctx new InitialDirContext(env); 这句话返回了用来进行目录操作的DirContext对象引用。
命名异常
JNDI包中的很多方法当抛出异常时说明操作请求不能执行。一般情况下您将看到可以抛出NamingException的方法使用try/catch进行包装。 try { Context ctx new InitialContext(); Object obj ctx.lookup(somename); } catch (NamingException e) { // Handle the error System.err.println(e); } 异常类结构
JNDI有丰富的异常结构所有异常都从NamingException类中继承。异常类名都是自解释的在下文中进行列举。
如果要处理特定的NamingException子类需要分别catch子类。例如以下代码特别的对待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()并且指定最多返回多少结果那么search()最多返回n个结果。如果结果超过n个那么当第n1次请求NamingEnumeration.hasMore()时抛出SizeLimitExceededException。请参见本节中的有关limit的示例代码。
本手册中的例子
在本手册文件中的在线示例代码中通常为了便于阅读省略了try/catch语句。通常因为只有部分代码片段在这里展示所以只展示直接表示概念的行。如果您查看本教程附带的源码文件将看到try/catch语句的合适位置。
javax.naming包中异常在这里可以看到。
查询对象
要从命名服务中查询对象使用Context.lookup()方法并且传入您想取得的对象名。假设命名服务中有一个对象的名称是cnRosanna Lee,ouPeople。要取得这个对象您只需要编写 Object obj ctx.lookup(cnRosanna Lee,ouPeople); lookup()返回的对象类型依赖于命名服务以及对象关联的数据。命名服务可以包含许多不同类型的对象同时在系统的不同部分查询的对象可能得到不同的类型。例如“cnRosanna Lee,ouPeople”绑定到上下文对象中javax.naming.ldap.LdapContext。您可以对lookup()方法的结果cast成需要的类。
例如以下代码查询“cnRosanna Lee,ouPeople”对象并且cast成LdapContext。 import javax.naming.ldap.LdapContext; ... LdapContext ctx (LdapContext) ctx.lookup(cnRosanna Lee,ouPeople); 完整的例子在Lookup.java文件中。
在Java SE 6中查询名称引入了两个新的静态方法
l InitialContext.doLookup(Name name)
l InitialContext.doLookup(String name) 这些方法提供了不实例InitialContext查找对象的快捷方式。
列举上下文
代替Context.lookup()一次取得一个对象的方法您可以在一个单一的操作中列举整个剩下文。列举上下文有两个方法一个返回了绑定关系另一个只返回名-对象类型名。
Context.List()方法
Context.list()返回NameClassPair的枚举。每个NameClassPair包含对象名和对象类型名。下列代码列举了“ouPeople”目录的内容例如“ouPeople”目录中找到的文件和目录。 NamingEnumeration list ctx.list(ouPeople); while (list.hasMore()) { NameClassPair nc (NameClassPair)list.next(); System.out.println(nc); } 返回值如下 # java List cnJon Ruiz: javax.naming.directory.DirContext cnScott Seligman: javax.naming.directory.DirContext cnSamuel Clemens: javax.naming.directory.DirContext cnRosanna Lee: javax.naming.directory.DirContext cnMaxine Erlund: javax.naming.directory.DirContext cnNiels Bohr: javax.naming.directory.DirContext cnUri Geller: javax.naming.directory.DirContext cnColleen Sullivan: javax.naming.directory.DirContext cnVinnie Ryan: javax.naming.directory.DirContext cnRod Serling: javax.naming.directory.DirContext cnJonathan Wood: javax.naming.directory.DirContext cnAravindan Ranganathan: javax.naming.directory.DirContext cnIan Anderson: javax.naming.directory.DirContext cnLao Tzu: javax.naming.directory.DirContext cnDon Knuth: javax.naming.directory.DirContext cnRoger Waters: javax.naming.directory.DirContext cnBen Dubin: javax.naming.directory.DirContext cnSpuds Mackenzie: javax.naming.directory.DirContext cnJohn Fowler: javax.naming.directory.DirContext cnLondo Mollari: javax.naming.directory.DirContext cnTed Geisel: javax.naming.directory.DirContext Context.listBindings()方法
Context.listBindings()方法返回绑定的枚举。绑定是NameClassPair的子类。绑定不止包含对象名和对象类名还包含对象。以下代码片段枚举了“ouPeople”上下文打印出每一个绑定名称和对象。 NamingEnumeration bindings ctx.listBindings(ouPeople); while (bindings.hasMore()) { Binding bd (Binding)bindings.next(); System.out.println(bd.getName() : bd.getObject()); } 返回的结果如下 # java ListBindings cnJon Ruiz: com.sun.jndi.ldap.LdapCtx1d4c61c cnScott Seligman: com.sun.jndi.ldap.LdapCtx1a626f cnSamuel Clemens: com.sun.jndi.ldap.LdapCtx34a1fc cnRosanna Lee: com.sun.jndi.ldap.LdapCtx176c74b cnMaxine Erlund: com.sun.jndi.ldap.LdapCtx11b9fb1 cnNiels Bohr: com.sun.jndi.ldap.LdapCtx913fe2 cnUri Geller: com.sun.jndi.ldap.LdapCtx12558d6 cnColleen Sullivan: com.sun.jndi.ldap.LdapCtxeb7859 cnVinnie Ryan: com.sun.jndi.ldap.LdapCtx12a54f9 cnRod Serling: com.sun.jndi.ldap.LdapCtx30e280 cnJonathan Wood: com.sun.jndi.ldap.LdapCtx16672d6 cnAravindan Ranganathan: com.sun.jndi.ldap.LdapCtxfd54d6 cnIan Anderson: com.sun.jndi.ldap.LdapCtx1415de6 cnLao Tzu: com.sun.jndi.ldap.LdapCtx7bd9f2 cnDon Knuth: com.sun.jndi.ldap.LdapCtx121cc40 cnRoger Waters: com.sun.jndi.ldap.LdapCtx443226 cnBen Dubin: com.sun.jndi.ldap.LdapCtx1386000 cnSpuds Mackenzie: com.sun.jndi.ldap.LdapCtx26d4f1 cnJohn Fowler: com.sun.jndi.ldap.LdapCtx1662dc8 cnLondo Mollari: com.sun.jndi.ldap.LdapCtx147c5fc cnTed Geisel: com.sun.jndi.ldap.LdapCtx3eca90 结束NamingEnumeration
NamingEnumeration可以通过三种方式终止一般的显式的非显式的。
l 当NamingEnumeration.hasMore()返回false枚举结束同时终止。
l 您可以在枚举终止前请求NamingEnumeration.close()方法显式的终止一个枚举。这样做提示底层实现释放任何和枚举有关的资源。
l 如果hasMore()或next()方法抛出任何异常枚举立即终止。
不管如何终止枚举枚举一旦被终止就不能再使用。在一个已终止的枚举中请求任何方法都会导致不确定的结果。
为什么使用两个不同的方法
list()为浏览类型的应用程序准备只返回上下文中对象的名字。例如浏览器可能列出上下文中的名字期待用户选择一个或多个显示的名称来进行后续操作。这些应用程序一般不需要访问上下文中所有的对象。
listBindings()为需要在上下文对象中进行操作的应用程序准备。例如备份程序需要在文件目录中所有对象中执行“file stats”操作。或者一个打印机管理员程序可能想要重启建筑物内的所有打印机。为了执行这些操作需要得到上下文中的所有对象。因此将对象作为枚举的一部分返回是权宜之计。
应用程序可以依据需要的信息类型选择list()或listBindings()。
添加、替换、或删除绑定
Context接口包含在上下文中添加、替换、删除绑定的方法。
添加绑定
Context.bind()为了向上下文中添加绑定它以对象类型以及需要绑定的对象作为参数。 在继续前本教程中的例子需要您对架构做额外的修改。您必须关闭LDAP服务器的架构检测或将符合本教程的架构添加到服务器中。这种工作一般由目录服务器管理员执行。请看课程。 // Create the object to be bound Fruit fruit new Fruit(orange); // Perform the bind ctx.bind(cnFavorite Fruit, fruit); 这个例子创建了一个Fruit类的对象同时在上下文ctx中将他绑定到名称“cnFavorite Fruit”中。如果您随后在ctx中查询“cnFavorite Fruit”那么您将得到fruit兑现。注意编译Fruit类需要FruitFactory类。
如果您运行这个例子两次那么第二次会因为NameAlreadyBoundException异常失败。因为“cnFavorite Fruit”已经绑定了。要第二次运行时不失败需要使用rebind()。
添加或修改绑定
rebind()用来添加或替换绑定。它的参数列表和bind()一样但如果名称已经绑定那么首先会unbound然后再重新绑定新的对象。 // Create the object to be bound Fruit fruit new Fruit(lemon); // Perform the bind ctx.rebind(cnFavorite Fruit, fruit); 当您运行这个例子时将会替换bind()例子中已经创建的绑定关系。
删除绑定
要删除绑定使用unbind()。 // Remove the binding ctx.unbind(cnFavorite Fruit); 当这个例子运行时删除bind()或unbind()创建的绑定关系。
重命名
您使用Context.rename()对上下文中的对象进行重命名。 // Rename to Scott S ctx.rename(cnScott Seligman, cnScott S); 这个例子将绑定到“cnScott Seligman”的对象绑定到了“cnScott S”中。在验证对象被重命名后程序再将其重命名回原来的名字“cnScott Seligman”如下所示 // Rename back to Scott Seligman ctx.rename(cnScott S, cnScott Seligman); 更多关于LDAP中重命名的例子请参考LDAP的高级注意。
创建和销毁子上下文
Context接口包含创建和销毁一个子上下文。子上下文是绑定到其他上下文的上下文。
这个例子使用一个有属性的对象然后在目录中创建子上下文。您可以使用DirContext的方法将属性和对象在绑定或子上下文添加到名字空间时进行关联。例如您可以创建Person对象然后在为Person对象关联属性的同时将他绑定到命名空间中。命名等于没有任何属性。
createSubcontext()和bind()不同他创建了一个新对象例如一个要绑定到目录中的新上下文但bind()在目录中绑定了给定的对象。 创建上下文
要创建命名上下文您向createSubcontext()提供要创建上下文的名称。要创建有属性的上下文向DirContext.createSubcontext()提供想要创建上下文的名称以及需要的属性。 在继续前本教程中的例子需要您对架构做额外的修改。您必须关闭LDAP服务器的架构检测或将符合本教程的架构添加到服务器中。这种工作一般由目录服务器管理员执行。请看课程。 // 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); 这个例子创建了名称为“ouNewO”的上下文并且有属性“objectclass”属性“top”和“organizationalUnit”其中“objectclass”有两个值。 # java Create ouGroups: javax.naming.directory.DirContext ouPeople: javax.naming.directory.DirContext ouNewOu: javax.naming.directory.DirContext 这个例子创建了一个新的上下文叫“NewOu”是ctx的子上下文。
销毁上下文
要销毁上下文需要向destroySubcontext()提供需要销毁上下文的名称。 // Destroy the context ctx.destroySubcontext(NewOu); 这个例子在上下文ctx中删除上下文“NewOu”。
属性名
属性由属性标识符和一组属性值组成。属性表示符叫属性名是表示属性的字符串。属性值是属性的内容它的类型不一定是字符串。当您想要指定获取、搜索、或修改指定属性时您使用属性名。名称同时在返回属性的操作中返回例如当您执行目录的读取或搜索操作时。
当使用属性名时您需要知道特定目录服务器的特性所以您不会为结果惊奇。这些特定在下一子节中描述。
属性类型
在诸如LDAP之类的目录中属性名表示了属性的类型通常叫做属性类型名。例如属性名“cn”同时叫做属性类型名。属性类型定义了属性值的语法是否允许多值相等性以及对属性值执行比较和排序时的排序规则
属性子类
一些目录实现支持目录子类型就是服务器允许属性类型使用其他属性类型定义。例如“name”属性可能是所有name相关属性的超类型“commonName”是name的子类。对于支持这种特斯娜格的目录实现访问“name”属性可能返回“commonName”属性。
当访问支持子类型的属性的目录时要知道服务器可能返回和您请求不一致的类型。为了减少这种几率使用最派生类。
属性名同义词
一些目录实现支持属性名的同义词。例如“cn”可能是“commonName”的同义词。所以请求“cn”属性可能返回“commonName”属性。
当访问支持属性同义词的目录您必须意识到服务器可能返回和您请求不同的属性名。要防止这种情况发生使用官方的属性名代替使用同义词。官方的属性名是定义属性的属性名同义词是是定义中引用官方属性名的名称。
语言参数选择
LDAP v3的扩展RFC 2596允许您和属性名一起指定语言编码。类似于子类属性一个属性名可以表示多个不同的属性。例如“description”属性有两个不同的语言变体 description: software description;lang-en: software products description;lang-de: Softwareprodukte 对“description”属性的请求会返回所有三种属性。当访问支持这种特性的目录时您必须意识到服务器可能返回和请求时不同的名称。
读取属性
为了从目录中读取对象的属性使用DirContext.getAttributes()并且将您想读取的属性名称传递进去就可以了。假设在命名服务中的一个对象的名称是“cnTed Geisel, ouPeople”。要获取对象的属性要使用如下代码 Attributes answer ctx.getAttributes(cnTed Geisel, ouPeople); 您可以按照如下方式打印应答的内容 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: [B1dacd78b attribute: mail value: Ted.GeiselJNDITutorial.com attribute: facsimiletelephonenumber value: 1 408 555 2329 attribute: telephonenumber value: 1 408 555 5252 attribute: cn value: Ted Geisel 返回选中属性
为了读取选中子集的属性您需要提供想要获取的属性标识符的数组。 // Specify the ids of the attributes to return String[] attrIDs {sn, telephonenumber, golfhandicap, mail}; // Get the attributes requested Attributes answer ctx.getAttributes(cnTed Geisel, ouPeople, attrIDs); 这个例子请求对象“cnTed Geisel, ouPeople”的“sn”,“telephonenumber”“golfhandicap”和“mail”属性所以应答中返回这三个属性。
以下是输出结果 # java Getattrs attribute: sn value: Geisel attribute: mail value: Ted.GeiselJNDITutorial.com attribute: telephonenumber value: 1 408 555 5252 修改属性
DirContext接口包含修改目录中对象的属性和属性值的方法。
使用修改列表
修改对象属性的一个方法是提供修改列表ModificationItem。每一个ModificationItem包含数字常量表示修改的类型以及描述需要修改的属性。以下是修改的类型。
l ADD_ATTRIBUTE
l REPLACE_ATTRIBUTE
l REMOVE_ATTRIBUTE
修改以列表中提供的类型进行。或者所有的修改都执行或者一个都不执行。
以下代码创建修改列表。它将“mail”属性值替换成geiselwizards.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, geiselwizards.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)); Windows活动目录活动目录将“telephonenumber”属性定义为单值属性这和RFC 2256不符。为了让这个例子在活动目录中执行您必须或者使用其他属性代替“telephonenumber”或将DirContext.ADD_ATTRIBUTE改为DirContext.REPLACE_ATTRIBUTE。 在创建修改列表后您可以按照如下方式提供给modifyAttributes() // Perform the requested modifications on the named object ctx.modifyAttributes(name, mods); 使用属性
可选的您可以通过指定修改类型以及修改属性的方式进行修改。
例如以下行使用orig中的name关联需要替换的属性 ctx.modifyAttributes(name, DirContext.REPLACE_ATTRIBUTE, orig); 其他属性的名称没有改变。
两种对于modifyAttributes()的使用在示例程序中都有。使用修改列表修改属性的程序在第二部分modifyAttributes()中恢复了原来的属性。
有属性的添加、删除绑定
命名的例子讨论如何使用bind()和unbind()DirContext这两方法的重载版本。您使用DirContext的方法关联对象的属性在绑定或子上下文时将她们添加到名字空间中。例如您可能创建了Person对象然后在为Person对象关联属性时将他绑定到名字空间中。
添加有属性的绑定
DirContext.bind()用来将属性绑定添加到上下文中。它的参数为需要绑定的对象名称 以及属性集合。 // Create the object to be bound Fruit fruit new Fruit(orange); // Create attributes to be associated with the 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.bind(oufavorite, ouFruits, fruit, attrs); 这个例子创建了Fruit类的对象并且将他绑定到“ouFruits”上下文中名称为“oufavorite”。绑定有“objectclass”属性。如果接下来在ctx中查询“oufavorite, ouFruits”那么您将得到fruit对象。如果您想、得到“oufavorite, ouFruits”的属性您将得到刚才为对象添加的属性。以下是例子的输出 # java Bind orange attribute: objectclass value: top value: organizationalUnit value: javaObject value: javaNamingReference attribute: javaclassname value: Fruit attribute: javafactory value: FruitFactory attribute: javareferenceaddress value: #0#fruit#orange attribute: ou value: favorite 显示的多于属性使用来保存关于对象fruit的一些信息。这些多于信息随后会进行详细介绍。
如果您两次运行这个例子那么第二次运行时将会失败并抛出NameAlreadyBoundException。这是因为“oufavorite”已经绑定到上下文“ouFruits”中。如果要成功需要使用rebind()。
替换有属性的绑定
DirContext.rebind()的作用是添加或修改绑定以及属性。它接受和bind()一样的参数。然而使用rebind()时如果名称已经存在那么将会首先Unbind然后再绑定新的对象和属性。 // Create the object to be bound Fruit fruit new Fruit(lemon); // Create attributes to be associated with the 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(oufavorite, ouFruits, fruit, attrs); 运行这个例子时它替换了bind()例子中创建的绑定关系。 # java Rebind lemon attribute: objectclass value: top value: organizationalUnit value: javaObject value: javaNamingReference attribute: javaclassname value: Fruit attribute: javafactory value: FruitFactory attribute: javareferenceaddress value: #0#fruit#lemon attribute: ou value: favorite 搜索
目录提供的最有用的好处就是黄页功能或搜索服务。您可以组合一个包含条目属性的查询然后提交查询到目录中。然后目录返回满足查询的条目列表。例如您可以访问目录查询出保龄球平均成绩大于200的条目或所有姓以“Sch”开头的人。
DirContext接口提供查询条目的方法这些方法很复杂也很强大。搜索目录的不同方面在以下章节中描述
l 基本搜索
l 搜索过滤器
l 搜索控制
基本搜索
最简单的查询需需要您指定条目必须含有的属性集合以及进行查询的目标上下文。
以下代码创建了属性集合matchAttrs其中有两个属性“sn”和“mail”。表名搜索条目必须有姓sn属性其值为“Geisel”以及“mail”属性可以是任意值。然后调用DirContext.search()查询上下文“ouPeople”中和matchAttrs匹配的条目。 // 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(ouPeople, matchAttrs); You can then print the results as follows. while (answer.hasMore()) { SearchResult sr (SearchResult)answer.next(); System.out.println( sr.getName()); printAttrs(sr.getAttributes()); } printAttrs()方法和getAttributes()方法打印出属性集合类似。
返回结果如下 # java SearchRetAll cnTed Geisel attribute: sn value: Geisel attribute: objectclass value: top value: person value: organizationalPerson value: inetOrgPerson attribute: jpegphoto value: [B1dacd78b attribute: mail value: Ted.GeiselJNDITutorial.com attribute: facsimiletelephonenumber value: 1 408 555 2329 attribute: cn value: Ted Geisel attribute: telephonenumber value: 1 408 555 5252 返回选中属性
上一个例子返回满足指定查询条件条目的所有属性。您可以通过向search()传递属性标识符数组的方式选择想要包含在结果集中的属性。在创建matchAttrs只有您应该创建属性标识符的数组如下所示 // 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(ouPeople, matchAttrs, attrIDs); 这个例子返回条目的属性“sn”“telephonenumber”“golfhandicap”以及“mail”其中条目有属性“mail”并且“sn”属性的值是“Geisel”。这个例子产生如下结果。条目中没有“golfhandicap”属性所以没有返回。 # java Search cnTed Geisel attribute: sn value: Geisel attribute: mail value: Ted.GeiselJNDITutorial.com attribute: telephonenumber value: 1 408 555 5252 过滤器
除了使用指定属性集合进行搜索外您可以用搜索过滤器的形式进行搜索。搜索过滤器是一种搜索用的逻辑表达式。DirContext.search()可接受的过滤器语法在RFC 2254中定义。
以下搜索过滤器指定了满足查询条件的条目必须有值为“Geisel”的“sn”属性以及值为任意的“mail”属性 ((snGeisel)(mail*)) 以下代码创建了过滤器以及默认的SearchControls使用他们执行查询。这个查询的结果和基本查询中的一致。 // 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 ((snGeisel)(mail*)); // Search for objects using the filter NamingEnumeration answer ctx.search(ouPeople, filter, ctls); 搜索结果如下 # java SearchWithFilterRetAll cnTed Geisel attribute: sn value: Geisel attribute: objectclass value: top value: person value: organizationalPerson value: inetOrgPerson attribute: jpegphoto value: [B1dacd75e attribute: mail value: Ted.GeiselJNDITutorial.com attribute: facsimiletelephonenumber value: 1 408 555 2329 attribute: cn value: Ted Geisel attribute: telephonenumber value: 1 408 555 5252 搜索过滤器语法概述
搜索过滤器是前缀标记的搜索表达式逻辑运算符在表达式前面。下表列举了创建过滤器使用的符号。 符号 描述 与列表中所有项必须为true | 或列表中至少一个必须为true ! 非求反的项不能为true 相等根据属性的匹配规则 ~ 近似等于根据属性的匹配规则 大于根据属性的匹配规则 小于根据属性的匹配规则 * 存在条目中必须有这个属性但值不做限制 * 通配符表示这个位置可以有一个或多个字符当指定属性值时用到 \ 转义符当遇到“*”“”“”时进行转义 过滤器中的每一个条目使用属性标识符和属性值或符号表示属性值组成。例如项“snGeisel”表示“sn”属性的值必须为“Geisel”同时项“mail*”表示“mail”属性必须存在。
每项必须包含在括号之内例如“(snGeisel)”。这些项使用逻辑运算符例如创建逻辑表达式例如“( (snGeisel) (mail*))”。
每一个逻辑表达式可以进一步组成其他项例如“(| ( (snGeisel) (mail*)) (snL*))”。这个例子请求的条目中或者含有值为“Geisel”的“sn”属性和“mail”属性或者“sn”属性以字母“L”开头。
关于语法的详细描述请参考RFC 2254。
返回选中属性
上一个例子返回满足指定过滤器的条目中的所有属性。您可以通过设置搜索控制参数的方法选择返回属性。您创建想要包含在结果中的属性标识符集合然后将他传递到SearchControls.setReturningAttributes()中。如下所示 // Specify the ids of the attributes to return String[] attrIDs {sn, telephonenumber, golfhandicap, mail}; SearchControls ctls new SearchControls(); ctls.setReturningAttributes(attrIDs); 这个例子和基本搜索一节中返回选择的属性部分的结果一致。运行后的结果如下。这个条目没有“golfhandicap”属性所以没有返回。 # java SearchWithFilter cnTed Geisel attribute: sn value: Geisel attribute: mail value: Ted.GeiselJNDITutorial.com attribute: telephonenumber value: 1 408 555 5252 范围
默认的SearchControls指定搜索只在命名空间中进行SearchControls.ONELEVEL_SCOPE。这个默认选项在搜索过滤器一节中使用。
除了默认选项之外您可以指定搜索在整个子树或只在命名对象中执行。
搜索子树
对于整个子树的搜索不但搜索命名对象而且搜索它的后代。要按照这种方式进行搜索按照下面的方式向SearchControls.setSearchScope()中传递SearchControls.SUBTREE_SCOPE参数 // Specify the ids of the attributes to return String[] attrIDs {sn, telephonenumber, golfhandicap, mail}; SearchControls ctls new SearchControls(); ctls.setReturningAttributes(attrIDs); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); // Specify the search filter to match // Ask for objects that have the attribute sn Geisel // and the mail attribute String filter ((snGeisel)(mail*)); // Search the subtree for objects by using the filter NamingEnumeration answer ctx.search(, filter, ctls); 这个例子搜索了ctx上下文的子树得到满足搜索过滤器的条目。它在子树中找到了满足过滤器的“cn Ted Geisel, ouPeople”条目。 # java SearchSubtree cnTed Geisel, ouPeople attribute: sn value: Geisel attribute: mail value: Ted.GeiselJNDITutorial.com attribute: telephonenumber value: 1 408 555 5252 搜索命名对象
您也可以搜索命名对象。这样做是很有用的例如测试命名对象是否满足搜索过滤器。为了搜索命名对象将SearchControls.OBJECT_SCOPE传递到setSearchScope()中。 // Specify the ids of the attributes to return String[] attrIDs {sn, telephonenumber, golfhandicap, mail}; SearchControls ctls new SearchControls(); ctls.setReturningAttributes(attrIDs); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); // Specify the search filter to match // Ask for objects that have the attribute sn Geisel // and the mail attribute String filter ((snGeisel)(mail*)); // Search the subtree for objects by using the filter NamingEnumeration answer ctx.search(cnTed Geisel, ouPeople, filter, ctls); 这个例子测试是否对象“cnTed Geisel, ouPeople”满足给定的过滤器。 # java SearchObject attribute: sn value: Geisel attribute: mail value: Ted.GeiselJNDITutorial.com attribute: telephonenumber value: 1 408 555 5252 例子找到了一个结果并进行打印。注意结果中的名称字段为空。因为对象的名称是进行查询的上下文cnTed Geisel, ouPeople。
结果数量
有时查询可能返回了太多的结果同时您想限制结果集的大小。这时您可以使用限制数量的搜索控制。默认情况下搜索没有数量限制――它将会返回找到的所有结果。要设置搜索的数量限制将数字传递到SearchControls.setCountLimit()中。
以下例子设置数量限制为1。 // Set the search controls to limit the count to 1 SearchControls ctls new SearchControls(); ctls.setCountLimit(1); 如果程序尝试得到更多的结果那么会抛出SizeLimitExceededException。如果程序设置数量限制那么或者将这个异常和NamingExceptions异常区别对待或者按照数量限制的大小不请求超过数量的结果。
满足搜索数量限制是控制程序消耗资源的一种方法例如内存和网络带宽。其他控制资源的方法是尽量使用小的搜索过滤器在合适的上下文中开始搜索使用合适的范围。
时间限制
搜索时间限制是搜索操作等待应答的时间上限。当您不想为应答等待太长时间时这很有用。如果搜索操作超过时间限制还没完成那么将会抛出TimeLimitExceededException异常。
为了设置搜索的时间限制将毫秒数传递到SearchControls.setTimeLimit()即可。以下例子将时间限制设置为1秒。 // Set the search controls to limit the time to 1 second (1000 ms) SearchControls ctls new SearchControls(); ctls.setTimeLimit(1000); 要让这个例子运行超时需要重新配置或者使用一个慢的服务器或这使用有很多条目的服务器。您还可以使用其他方式让搜索超过1秒。
时间限制为0表示不进行时间限制这样请求将会进行无限等待。
常见问题解答
当您使用JNDI类运行成功编译的程序时可能遇到的主要问题如下 l 没有初始化上下文
l 连接被拒绝
l 连接失败
l 程序挂起
l 名字没有找到
l 不能连接任意主机
l 不能配置访问系统属性
l 不能使用CRAM-MD5认证 1. 得到NoInitialContextException
原因您没有指定使用的初始化上下文。特别的Context.INITIAL_CONTEXT_FACTORY环境变量没有设置成为初始化上下文的工厂类名。后者找不到Context.INITIAL_CONTEXT_FACTORY配置的可得到的服务提供者。
解决方案将环境变量Context.INITIAL_CONTEXT_FACTORY设置为您使用的初始化上下文类名。详细信息请参考配置一节。
如果属性已经设置那么确认类名没有输入错误并且类在您的程序中可见或者在classpath中或者在JRE的jre/lib/ext目录下。Java平台包含服务提供者有LDAPCOS命名DNS以及RMI注册。所有其他的服务提供者必须安装并且添加到执行环境中。
2. 得到CommunicationException异常表示“连接被拒绝”。
原因Context.PROVIDER_URL环境参数表示的服务器和端口没有提供访问。可能有人禁用或关闭了服务。或者输入了错误的服务器名称或端口号。
解决方案检查端口上确实运行了服务如果需要就重启服务器。这种检查依赖于您使用的LDAP服务器。通常可以使用管理控制台或工具管理服务器。您可以使用工具确认服务器的状态。
3. LDAP服务器向其他工具应答例如管理控制台但不应答您程序的请求。
原因服务器没有应答LDAP v3的连接全逆光球。一些服务器尤其是公共服务器不能正确的应答LDAP v3使用忽略的方式代替拒绝。同时一些LDAP v3服务器有错误处理机制Sun的LDAP服务提供者自动发送并且通常返回特定服务器错误码。
解决方案尝试设置环境参数“java.naming.ldap.version”为“2”。LDAP服务提供者默认尝试使用LDAP v3连接LDAP服务器然后使用LDAP v2。如果服务器静默忽略v3的请求那么提供者假设请求生效了。使用这种服务器您必须显式的设置协议版本确保服务器有正确的行为。
如果服务器是v3服务器那么尝试在创建初始化上下文之前设置这些环境参数 env.put(Context.REFERRAL, throw); 这样关闭了LDAP提供者自动发送的控制更多信息请参考JNDI教程。
4. 程序挂起。
原因当您尝试执行的查实会生成太多结果或需要服务器查询很多条目才能生成结果时服务器尤其是公共的不应答不是一个失败应答。这种服务器基于预请求计算花费的方式尝试减少资源消耗。
或者您尝试使用安全套接字层SSL但服务器/端口不支持反之您尝试使用普通套接字与SSL端口对话。
最终服务器或者因为负载原因非常慢的应答或完全不应答某些请求。
解决方案如果程序因为服务器为了减少资源消耗而挂起那么重试请求会得到单一应答或只有很少的应答。这样可以帮助您判断服务器是否还在活动。这样您可以加宽原有查询重新发送。
如果您的程序因为SSL问题挂起那么您需要找到SSL端口然后正确设置Context.SECURITY_PROTOCOL环境参数。如果端口是SSL端口那么这个参数应该设置成“ssl”。如果不是SSL端口这个参数不应该设置。
如果程序不是因为上述原因挂起使用com.sun.jndi.ldap.read.timeout表示读取超时。这个参数的值是一个字符串表示LDAP请求读取超时的毫秒数。如果LDAP提供者不能在周期内得到应答那么放弃读取尝试。数字应该大于0。等于或小于0表示不指定读取超时时间等于无限等待得到应答。
如果没有指定这个参数默认情况下会一直等待直到得到应答。
例如 env.put(com.sun.jndi.ldap.read.timeout, 5000); 表示如果LDAP服务器不能在5秒中内应答将放弃读取请求。
5. 您得到NameNotFoundException异常。
原因当您为LDAP初始化了初始上下文提供了根辨别名。例如如果您为初始上下文设置Context.PROVIDER_URL为“ldap://ldapserver:389/oJNDITutorial”然后提供名称“cnJoe,cus”那么您向LDAP服务传递的全名为“cnJoe,cus,oJNDITutorial”。如果这确实是您想要的名称那么您应该检验服务器确定包含这个条目。
同时如果您在认证时提供错误的辨别名Sun Java目录服务器返回错误。例如如果您设置Context.SECURITY_PRINCIPAL 环境参数为“cnAdmin, oTutorial”并且“cnAdmin, oTutorial”不是LDAP服务器的条目LDAP提供者将要抛出NameNotFoundException。Sun Java目录服务器返回的实际上是一些认证异常不是“name not found”。
解决方案确认您提供的名字是服务器上存在的。您可以通过列举父上下文的所有条目或使用其他工具例如服务器的管理员控制台来确认。 以下是在部署applet时可能遇到的问题。 6. 当您的applet尝试连接目录服务器时得到AppletSecurityException异常服务器正运行在和下载applet不同的机器上。
原因applet没有签名所以只能连接到加载它的机器。或者如果appet已经签名浏览器没有授予applet连接目录服务器的权限。
解决方案如果您想允许applet连接任意机器上的目录服务器那么需要签名整个applet以及applet使用的JNDI jar。关于jar的签名请参考签名和验证jar文件。
7. 当您的applet尝试使用系统属性设置环境属性时出现AppletSecurityException异常。
原因浏览器限制访问系统参数并且当您尝试读取时抛出SecurityException。
解决方案如果您需要为applet得到输入尝试使用applet params代替。
8. 当applet运行在Firefox中尝试使用CRAM-MD5向LDAP进行认证时抛出AppletSecurityException。
原因Firefox禁止访问java.security包。LDAP提供者使用java.security.MessageDigest提供的信息摘要功能来实现CRAM-MD5。
解决方案使用Java插件。 转自https://blog.csdn.net/liumm0000/article/details/7932019