Java 中文官方教程 2022 版(四十八)(2)https://developer.aliyun.com/article/1488516
类似地,当上下文方法从 list()、listBindings() 或 search() 操作返回结果时,可以通过调用getNameInNamespace()
检索 DN。可以直接从 DN 构造 LdapName,如示例中所示,RetrievingLdapName
:
while (answer.hasMore()) { SearchResult sr = (SearchResult) answer.next(); String name = sr.getNameInNamespace(); System.out.println(name); LdapName dn = new LdapName(name); // do something with the dn
操作相对独立名称(RDN)
类javax.naming.ldap.Rdn
表示根据RFC 2253指定的相对独立名称(RDN)。RDN 表示 DN 的组件,如操作 LdapName 课程中所解释的那样。RDN 由类型和值对组成。RDN 的示例包括:
OU=Sun
OU=Sales+CN=J.Smith
。
上面的示例显示了多值 RDN 的表示。
Rdn
类提供了访问 RDN 的名称/值对、获取其字符串表示、检索Attributes
视图、比较和确定 RDN 的相等性以及转义和取消转义 RDN 值部分的方法。
Rdn
类是不可变的。
构造 Rdn
如果是单个名称/值对的 RDN,可以使用指定的名称和值对构造 Rdn。对于多值 RDN,应创建包含所有名称/值对的属性集,并使用以Attributes
为参数的构造函数。还可以根据RFC 2253中指定的字符串表示创建 Rdn。最后,可以使用其复制构造函数克隆 Rdn。这里是一个使用不同类型构造函数创建 RDN 的示例
。
Rdn rdn1 = new Rdn("ou= Juicy\\, Fruit"); System.out.println("rdn1:" + rdn1.toString()); Rdn rdn2 = new Rdn(rdn1); System.out.println("rdn2:" + rdn2.toString()); Attributes attrs = new BasicAttributes(); attrs.put("ou", "Juicy, Fruit"); attrs.put("cn", "Mango"); Rdn rdn3 = new Rdn(attrs); System.out.println("rdn3:" + rdn3.toString()); Rdn rdn4 = new Rdn("ou", "Juicy, Fruit"); System.out.println("rdn4:" + rdn4.toString());
访问 RDN 的类型/值对
可以使用以下方法获取 RDN 的类型/值:
对于由单个类型/值对组成的 RDN,getType()
方法返回类型,getValue()
方法返回 RDN 的值。方法toAttributes()
返回类型/值对的属性视图。下面的示例
打印了 RDN 的类型/值对。
Attributes attrs = new BasicAttributes(); attrs.put("o", "Yellow"); attrs.put("cn", "Mango"); // create a binary value for the RDN byte[] mangoJuice = new byte[6]; for (int i = 0; i < mangoJuice.length; i++) { mangoJuice[i] = (byte) i; } attrs.put("ou", mangoJuice); Rdn rdn = new Rdn(attrs); System.out.println(); System.out.println("size:" + rdn.size()); System.out.println("getType(): " + rdn.getType()); System.out.println("getValue(): " + rdn.getValue()); // test toAttributes System.out.println(); System.out.println("toAttributes(): " + rdn.toAttributes());
获取字符串表示
为了按照RFC 2253中指定的语法格式化 RDN 的字符串表示,可以使用:
当您使用接受String
参数的 Rdn 构造函数时,您提供了 RDN 的字符串表示,并获得了一个 Rdn 实例。要做相反的操作,即获取 Rdn 实例的字符串表示,您可以使用 toString()。toString()的结果可以馈送回 Rdn 构造函数,以产生与原始 Rdn 实例相等的 Rdn 实例。
这里有一个示例
:
Rdn rdn = new Rdn("cn=Juicy\\,Fruit"); String str = rdn.toString(); System.out.println(str); Rdn rdn2 = new Rdn(str); System.out.println(rdn.equals(rdn2)); // true
比较 RDNs
下面的方法使 RDN 的比较成为可能:
您可以使用 compareTo()对 Rdn 实例列表进行排序。equals()让您确定两个 Rdns 在语法上是否相等。如果两个 Rdns 都具有相同(大小写匹配)的类型/值对,则它们是相等的。多值 RDN 中组件的顺序不重要。
这里有一个示例
:
Rdn one = new Rdn("ou=Sales+cn=Bob"); Rdn two = new Rdn("cn=Bob+ou=Sales"); Rdn three = new Rdn("ou=Sales+cn=Bob+c=US"); Rdn four = new Rdn("cn=lowercase"); Rdn five = new Rdn("cn=LowerCASE"); System.out.println(one.equals(two)); // true System.out.println(two.equals(three)); // false System.out.println(one.equals(three)); // false System.out.println(four.equals(five)); // true
转义和反转义特殊字符
Rdn 类的最佳用途之一是处理包含特殊字符的 DN。它会自动处理特殊字符的转义和反转义。像 ‘’(反斜杠)、‘,’(逗号)、+(加号)等字符在RFC 2253中有特定的语义。您可以在 RFC2253 中找到所有特殊字符的列表。当这些字符作为 DN 中的文字时,必须用 ‘’(反斜杠)进行转义。
例如,考虑一个 RDN:cn=Juicy, Fruit
,出现在 Juicy 和 Fruit 之间的逗号,是一个需要用 ‘’(反斜杠)转义的特殊字符。经过语法格式化后的 RDN 如下所示:cn=Juicy\, Fruit
然而, ‘’(反斜杠)字符本身是 Java 语言字符串语法中的一个特殊字符,需要再次用 ‘’(反斜杠)转义。Java 语言字符串格式和RFC 2253都使用 ‘’(反斜杠)来转义特殊字符。因此,Java 格式化的 RDN 字符串如下所示:cn=Juicy\\, Fruit
请注意,上述提到的格式化规则仅适用于 Rdn 的值组件。Rdn
类提供了两个static
方法来处理 RDN 值的自动转义和反转义:
下面的示例
显示了如何获取 DN 的字符串表示,而无需处理RFC 2253中定义的处理特殊字符的语法。
// DN with ',' (comma) String unformatted = "Juicy, Fruit"; String formatted = Rdn.escapeValue(unformatted); LdapName dn = new LdapName("cn=" + formatted); System.out.println("dn:" + dn); unformatted = "true+false"; formatted = Rdn.escapeValue(unformatted); dn = new LdapName("cn=" + formatted); System.out.println("dn:" + dn); // DN with a binary value as one of its attribute values byte[] bytes = new byte[] {1, 2, 3, 4}; formatted = Rdn.escapeValue(bytes); System.out.println("Orig val: " + bytes + "Escaped val: " + formatted);
同样地,使用静态的unescapeValue()
方法,可以从格式化值中获取原始字符串。这里
是一个检索原始值的示例。
// DN with ',' (comma) String unformatted = "Juicy, Fruit"; String formatted = Rdn.escapeValue(unformatted); System.out.println("Formatted:" + formatted); Object original = Rdn.unescapeValue(formatted); System.out.println("Original:" + original); // DN with a '+' (plus) unformatted = "true+false"; formatted = Rdn.escapeValue(unformatted); System.out.println("Formatted:" + formatted); original = Rdn.unescapeValue(formatted); System.out.println("Original:" + original); // DN with a binary value as one of its attribute values byte[] bytes = new byte[] {1, 2, 3, 4}; formatted = Rdn.escapeValue(bytes); System.out.println("Formatted:" + formatted); original = Rdn.unescapeValue(formatted); System.out.println("Original:" + original);
设置 LDAP 操作的超时时间
原文:
docs.oracle.com/javase/tutorial/jndi/newstuff/readtimeout.html
当客户端向服务器发出 LDAP 请求并且服务器由于某种原因未响应时,客户端将永远等待服务器响应,直到 TCP 超时。在客户端端,用户体验实际上是一个进程挂起。为了及时控制 LDAP 请求,自 Java SE 6 以来可以为 JNDI/LDAP 服务提供程序配置读取超时。
新的环境属性:
com.sun.jndi.ldap.read.timeout
可用于指定 LDAP 操作的读取超时时间。该属性的值是表示 LDAP 操作的读取超时时间(以毫秒为单位)的整数的字符串表示。如果 LDAP 提供程序在指定的时间内未收到 LDAP 响应,则会中止读取尝试。整数应大于零。小于或等于零的整数表示未指定读取超时,相当于无限等待响应直到接收到响应,这是默认行为。
如果未指定此属性,默认情况下将等待响应直到接收到响应。
例如,env.put("com.sun.jndi.ldap.read.timeout", "5000");
会导致 LDAP 服务提供程序在服务器在 5 秒内未响应时中止读取尝试。
这里有一个示例,ReadTimeoutTest
,使用一个不响应 LDAP 请求的虚拟服务器,展示了当将此属性设置为非零值时的行为。
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put("com.sun.jndi.ldap.read.timeout", "1000"); env.put(Context.PROVIDER_URL, "ldap://localhost:2001"); Server s = new Server(); try { // start the server s.start(); // Create initial context DirContext ctx = new InitialDirContext(env); System.out.println("LDAP Client: Connected to the Server"); : : } catch (NamingException e) { e.printStackTrace(); }
由于服务器甚至未响应 LDAP 绑定请求,当创建 InitialDirContext 时,上述程序打印下面的堆栈跟踪。客户端在等待服务器响应时超时。
Server: Connection accepted javax.naming.NamingException: LDAP response read timed out, timeout used:1000ms. : : at javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:82) at ReadTimeoutTest.main(ReadTimeoutTest.java:32)
请注意,此属性与另一个环境属性 com.sun.jndi.ldap.connect.timeout
不同,后者设置连接到服务器的超时时间。读取超时适用于与服务器建立初始连接后从服务器接收的 LDAP 响应。
Trail: Sockets Direct Protocol
Sockets Direct Protocol(SDP)提供了对高性能网络连接的访问,例如 InfiniBand 提供的连接。本教程展示了如何启用 SDP,涉及哪些 Java API,并解释了它的工作原理。
课程:理解套接字直接协议
对于高性能计算环境,快速高效地在网络中传输数据是必需的。这样的网络通常被描述为需要高吞吐量和低延迟。高吞吐量指的是在长时间内可以提供大量处理能力的环境。低延迟指的是处理输入并提供输出之间的最小延迟,就像您在实时应用程序中所期望的那样。
在这些环境中,使用套接字流进行传输数据可能会导致瓶颈。InfiniBand(IB)由InfiniBand 贸易协会于 1999 年推出,旨在满足高性能计算的需求。IB 的最重要特性之一是远程直接内存访问(RDMA)。RDMA 使得可以直接从一台计算机的内存移动数据到另一台计算机,绕过两台计算机的操作系统,从而实现显著的性能提升。
套接字直接协议(SDP)是一种网络协议,旨在支持在 InfiniBand 结构上的流连接。SDP 支持是在 Java 平台标准版(“Java SE 平台”)的 JDK 7 版本中引入的,用于在 Solaris 操作系统(“Solaris OS”)和 Linux 操作系统上部署的应用程序。Solaris OS 自 Solaris 10 5/08 版本以来一直支持 SDP 和 InfiniBand。在 Linux 上,InfiniBand 软件包称为 OFED(OpenFabrics 企业发行版)。JDK 7 版本支持 OFED 的 1.4.2 和 1.5 版本。
概述
原文:
docs.oracle.com/javase/tutorial/sdp/sockets/overview.html
SDP 支持本质上是一种 TCP 绕过技术。
当启用 SDP 并且应用程序尝试打开 TCP 连接时,TCP 机制将被绕过,通信直接传输到 IB 网络。例如,当您的应用程序尝试绑定到 TCP 地址时,基础软件将根据配置文件中的信息决定是否应重新绑定到 SDP 协议。此过程可以在绑定过程或连接过程中发生(但每个套接字仅发生一次)。
在您的代码中利用 SDP 协议无需进行 API 更改:实现是透明的,并受经典网络(java.net
)和新 I/O(java.nio.channels
)包的支持。查看支持的 Java API 部分,了解支持 SDP 协议的类列表。
SDP 支持默认情况下是禁用的。启用 SDP 支持的步骤如下:
- 创建一个 SDP 配置文件。
- 设置指定配置文件位置的系统属性。
创建一个 SDP 配置文件
一个 SDP 配置文件是一个文本文件,您可以决定这个文件将存放在文件系统的哪个位置。配置文件中的每一行都是一个注释或一个规则。注释以井号字符(#)开头,井号字符后的所有内容将被忽略。
有两种类型的规则,如下:
- "bind"规则表示当 TCP 套接字绑定到与规则匹配的地址和端口时,应使用 SDP 协议传输。
- "connect"规则表示当未绑定的 TCP 套接字尝试连接到与规则匹配的地址和端口时,应使用 SDP 协议传输。
一个规则的形式如下:
("bind"|"connect")1*LWSP-char(hostname|ipaddress)["/"prefix])1*LWSP-char("*"|port)["-"("*"|port)]
解码符号:
1LWSP-char表示任意数量的线性空白字符(制表符或空格)可以分隔标记。方括号表示可选文本。符号(xxx | yyy)表示标记将包括xxx或yyy*中的一个,但不会同时包括两者。引号中的字符表示文字。
第一个关键字指示规则是bind还是connect规则。下一个标记指定主机名或文字 IP 地址。当指定文字 IP 地址时,您还可以指定前缀,表示 IP 地址范围。第三个也是最后一个标记是端口号或端口号范围。
在这个示例配置文件中考虑以下符号:
# Use SDP when binding to 192.0.2.1 bind 192.0.2.1 * # Use SDP when connecting to all application services on 192.0.2.* connect 192.0.2.0/24 1024-* # Use SDP when connecting to the http server or a database on examplecluster connect examplecluster.example.com 80 connect examplecluster.example.com 3306
示例文件中的第一条规则指定 SDP 用于本地 IP 地址192.0.2.1
上的任何端口(*)。对于每个分配给 InfiniBand 适配器的本地地址,您将添加一个绑定规则。(InfiniBand 适配器相当于 InfiniBand 的网络接口卡(NIC)。)如果您有多个 IB 适配器,您将为分配给这些适配器的每个地址使用一个绑定规则。
示例文件中的第二条规则指定每当连接到192.0.2.*
并且目标端口大于或等于 1024 时,将使用 SDP。IP 地址前缀/24
表示 32 位 IP 地址的前 24 位应与指定地址匹配。IP 地址的每个部分使用 8 位,因此 24 位表示 IP 地址应匹配192.0.2
,最后一个字节可以是任何值。端口标记上的-*
符号指定“及以上”。端口范围,例如 1024—2056,也是有效的,并且将包括指定范围的端点。
示例文件中的最后规则指定一个主机名(examplecluster
),首先分配给 http 服务器的端口(80),然后分配给数据库的端口(3306)。与文字 IP 地址不同,主机名可以转换为多个地址。当指定主机名时,它匹配主机名在名称服务中注册的所有地址。
启用 SDP 协议
SDP 支持默认情况下是禁用的。要启用 SDP 支持,请设置com.sun.sdp.conf
系统属性,提供配置文件的位置。以下示例启动一个使用名为sdp.conf
的配置文件的应用程序:
% java -Dcom.sun.sdp.conf=sdp.conf -Djava.net.preferIPv4Stack=true *ExampleApplication*
ExampleApplication指的是试图连接到 IB 适配器的客户端应用程序。
请注意,此示例指定另一个系统属性java.net.preferIPv4Stack
。有关为什么使用此属性的更多信息,请参见 Issues 部分。
调试 SDP
你可以通过使用-Dcom.sun.sdp.debug
标志来启用调试消息。如果你指定一个文件,消息将被输出到该文件。否则,消息将被打印到标准输出。
这个第一个示例展示了打印到标准输出的示例消息:
% java -Dcom.sun.sdp.conf=sdp.conf -Dcom.sun.sdp.debug *ExampleApplicaton* BIND to 192.0.2.1:5000 (socket converted to SDP protocol) CONNECT to 129.156.232.160:80 (no match) CONNECT to 192.0.2.2:80 (socket converted to SDP protocol)
这个第二个示例展示了输出重定向到名为debug.log
的文件:
% java -Dcom.sun.sdp.conf=sdp.conf -Dcom.sun.sdp.debug=debug.log *ExampleApplication* [1] 27310 % tail -f debug.log BIND to 192.0.2.1:5000 (socket converted to SDP protocol)
SDP 的技术问题
- IPv4 和 IPv6 不兼容
互联网协议第 4 版(IPv4)长期以来一直是 Internet 协议(IP)的行业标准版本,用于在 Internet 上传输数据。互联网协议第 6 版(IPv6)是下一代 Internet 层协议。今天仍在使用这两个版本的 IP。
IPv4 地址是 32 位长,以十进制格式编写,并用句点分隔。IPv6 地址是 128 位长,以十六进制格式编写,并用冒号分隔。IPv4 地址不能直接在 IPv6 中使用,但 IPv6 支持一种特殊类别的地址:IPv4 映射地址。在 IPv4 映射地址中,前 80 位设置为零,接下来的 16 位设置为 1,最后 32 位表示 IPv4 地址。
例如,这里是相同的 IP 地址以两种格式表示:
IPv4 address IPv4-mapped address (for use in IPv6) 192.0.2.1 ::ffff:192.0.2.1
- 默认情况下,如果 IB 适配器中启用了 IPv6,则 Java 平台将使用 IPv6。然而,在 Solaris 操作系统或 Linux 下目前不支持 IPv4 映射地址。因此,如果您想在 JDK 7 下使用 IPv4 地址格式,必须指定
java.net.preferIPv4Stack
属性,如下例所示:
% java -Dcom.sun.sdp.conf=sdp.conf -Djava.net.preferIPv4Stack=true *MyApplication*
- 错误
在早期 InfiniBand 实现中发现了一些错误。这些错误在 Solaris 10 10/09 版本中已修复。请确保您至少使用此版本。
Solaris 和 Linux 支持
Solaris 10
要测试 SDP 是否已启用,请使用sdpadm
(1M)命令:
% /usr/sbin/sdpadm status SDP is Enabled
你可以使用 grep 命令在/etc/path_to_inst
文件中搜索字符串"ibd",以查看网络上支持的 IB 适配器列表。
其他你可能会发现有用的命令包括ib
(7D)、ibd
(7D)和sdp
(7D)。
Solaris 11
你可以使用以下命令获取 InfiniBand 分区链接信息:
% dladm show-port -o LINK
其他你可能会发现有用的命令包括ib
(7D)、ibd
(7D)和sdp
(7D)。
Linux
你可以使用以下命令获取 InfiniBand 适配器的列表:
% egrep "^[ \t]+ib" /proc/net/dev
支持的 Java API
原文:
docs.oracle.com/javase/tutorial/sdp/sockets/supported.html
所有使用 TCP 的 API 都可以使用 SDP,特别包括以下类:
java.net
包
Socket
ServerSocket
java.nio.channels
包:
SocketChannel
ServerSocketChannel
AsynchronousSocketChannel
AsynchronousServerSocketChannel
当启用 SDP 支持时,它会在不需要对您的代码进行任何更改的情况下正常工作。不需要编译。然而,重要的是要知道套接字只绑定一次。连接是隐式绑定。因此,如果套接字之前没有绑定并且调用了 connect
,则绑定将在那个时候发生。
例如,考虑以下代码片段:
AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); ch.bind(local); Future<Void> result = ch.connect(remote);
在此代码片段中,当在套接字上调用 bind
时,异步套接字通道将绑定到本地 TCP 地址。然后,代码尝试使用相同的套接字连接到远程地址。如果远程地址使用配置文件中指定的 InfiniBand,则连接不会转换为 SDP,因为套接字先前已绑定。
更多信息请参考
- Alan Bateman 的博客:套接字直接协议
- Nitin Hande 的博客:在 OpenSolaris 上使用 IPoIB
- Anish Gupta 的博客关于InfiniBand 支持
教程:Java Architecture for XML Binding
Java Architecture for XML Binding (JAXB) 教程介绍了 Java Architecture for XML Binding (JAXB) 技术,通过 JAXB 应用示例。
在阅读本教程之前
要理解并使用 Java Architecture for XML Binding (JAXB) 教程中的信息,您应该具备以下技术知识:
- Java 编程语言及其开发环境
- 可扩展标记语言(XML)
本教程详细讨论了仅与 JAXB 相关的代码。
简要描述了 JAXB 技术,包括其目的和主要特点。
Java 中文官方教程 2022 版(四十八)(4)https://developer.aliyun.com/article/1488524