虽然 J2EE 平台提高了普通企业开发人员的生活水平,但是这种提高是以不得不学习许多规范和技术为代价的,这些规范和技术则是 J2EE 为了成为无所不包的分布式计算平台而整合进来的。Dolly Developer 是众多开发人员中的一员,她已经发现了一个特性,该特性有助于缓解随企业级应用程序部署而带来的负担,这个特性就是 JNDI,即 Java 命名与目录接口(Java Naming and Directory Interface)。让我们来看看 Dolly 在没有 JNDI 的时候是怎么做的,以及她是如何正确地应用 JNDI 来改善其状况的。
Dolly Developer 正在编写使用 JDBC 数据源的 Web 应用程序。她知道自己正在使用 MySQL,所以她将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到其 Web 应用程序中的数据库。她认识到数据库连接池的重要性,所以她包含了一个连接池包,并把它配置成最多使用 64 个连接;她知道数据库服务器已经被设置成最多允许 128 台客户机进行连接。
在开发阶段,每件事都进行得很顺利。但是,在部署的时候,开始失控。Dolly 的网络管理员告诉她,她不能从她的桌面机访问生产服务器或登台服务器(staging server),所以她不得不为每个部署阶段开发不同的代码版本。因为这种情况,她需要一个新的 JDBC URL,所以还要为测试、阶段和生产进行独立的部署。(一听到要在每个环境中建立单独部署,熟悉配置管理的人会战战兢兢的,但是既然这是种非常普遍的情况,所以他们也只好硬着头皮上了。)
就在 Dolly 认为通过不同的 URL 建立彼此独立的部署已经解决了自己的配置问题时,她发现她的数据库管理员不想在生产环境中运行 MySQL 实例。他说,MySQL 用作开发还可以,但是对于任务关键型数据而言,业务标准是 DB2®。现在她的构建不仅在数据库 URL 方面有所不同,而且还需要不同的驱动程序。
事情越变越糟。她的应用程序非常有用,并且变得非常关键,以致于它从应用服务器那里得到了故障恢复的能力,并被复制到 4 个服务器集群。但是数据库管理员提出了抗议,因为她的应用程序的每个实例都要使用 64 个连接,而数据库服务器总共只有 200 个可用连接 —— 全部都被 Dolly 的应用程序占用了。更麻烦的是,DBA 已经确定 Dolly 的应用程序只需要 32 个连接,而且每天只有一个小时在使用。随着她的应用程序规模扩大,应用程序遇到了数据库级的争用问题,而她的惟一选择就是改变集群的连接数量,而且还要做好准备,在集群数量增长或者应用程序复制到另一个集群时再重复一次这样的操作。看来她已经决定了如何配置应用程序,应用程序的配置最好是留给系统管理员和数据库管理员来做。
如果 Dolly 在开发应用程序时了解 J2EE 所扮演的角色,那么她就可能避免遭遇这种困境。J2EE 规范把职责委托给多个开发角色:组件提供者(Component Provider)、应用程序组装者(Application Assembler)、部署人员(Deployer)和系统管理员(System Administrator)。(在许多公司中,组件提供者和组件组装者的角色是融合在一起的,部署人员和系统管理员的角色是融合在一起的。)在真正了解 J2EE 中的 JNDI 角色之前,掌握 J2EE 角色的作用非常重要。
假设有一个企业应用程序,该应用程序包含一个 Web 应用程序,还有一个负责业务逻辑和持久性的 EJB 组件。开发这个应用程序的组件供应商可能有许多,但是在许多情况下,可以由一个人来承担全部职责。组件可以包含数据传输对象(一个 JAR 文件)、EJB 接口(另一个 JAR 文件)、EJB 实现本身(另一个 JAR 文件),以及用户界面组件 —— servlet、JSP、HTML 页面和其他静态 Web 内容。用户界面组件被进一步打包成 Web 应用程序,其中包含 servlet 类、JSP 文件、静态内容,以及其他必需组件的 JAR(包括 EJB 接口)。
这听起来好像用到的组件太多了,几乎超出了人的想像范围,尤其是在考虑构建一个典型的 Web 应用程序需要使用多少个 JAR 文件的时候。但是,重要的是认识到在这里必须小心地管理依赖性。接口和传输对象是 Web 应用程序和 EJB 实现可以依赖的对象,但是依赖性的方向应该是相同的;还要避免产生循环依赖。J2EE 组件(例如 WAR 文件和 EJB JAR 文件)必须在它们的部署单元之外声明它们在资源上的依赖性。
应用程序组装者负责把 Web 应用程序中的依赖内容包含进来,并把它们整体打包成单个企业应用程序。工具在这里帮助很大。IDE 可以协助创建反映模块和 JAR 依赖性的项目结构,还允许您随意指定包含或排除的模块。
部署人员负责确保部署环境中存在组件所需的资源,并将组件绑定到平台的可用资源上。例如,Web 应用程序中的外部 EJB 引用(部署描述符中的 ejb-ref
)就是在此时被绑定到实际部署的 EJB 组件 —— 而且是立即绑定。
任何不平凡(nontrivial)的 J2EE 应用程序都需要访问描述它期望使用环境的信息。这意味着开发和测试组件时,为了临时测试代码,开发人员要承担一些部署方面的职责。重要的是要理解:这么做的时候,您就走出了开发人员的领域。否则,可以试着依靠 JDBC 驱动程序,或 URL、JMS 队列名称,或者其他具有无意识的、偶尔可能是灾难性暗示的机器资源。
Dolly 的问题的解决方案是从她的应用程序中清除所有对数据存储的直接引用。没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。Dolly 需要编写代码来忽略将要访问的特定外部资源,只需要知道其他人会提供使用这些外部资源所需的链接即可。这允许部署人员(任何处在这个角色的人)把数据库连接分配给 Dolly 的应用程序。Dolly 没有必要参与其中。(从数据库安全性到遵守 Sarbanes-Oxley 法案,她都没有参与进来,她这样做也有充足的业务理由。)
许多开发人员知道:代码和外部资源之间的紧密耦合是潜在的问题,但是在实践中却经常忘记角色的划分。在小型开发工作中(指的是团队规模或部署规模),即使忽视角色划分也能获得成功。(毕竟,如果应用程序只是个人的应用程序,而且您不准备依靠它,那么把应用程序锁定在特定的 PostgreSQL 实例上也挺好的。)
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。
现在我们重新来看一下 Dolly 的情况。在其简单的 Web 应用程序中,她直接从应用程序代码中使用了一个 JDBC 连接。参见清单 1,我们可以看出,Dolly 显式地把 JDBC 驱动程序、数据库 URL 以及她的用户名和口令编码到了 servlet 中:
Connection conn=null; try { Class.forName("com.mysql.jdbc.Driver", true, Thread.currentThread().getContextClassLoader()); conn=DriverManager.getConnection("jdbc:mysql://dbserver?user=dolly&password=dagger"); /* use the connection here */ c.close(); } catch(Exception e) { e.printStackTrace(); } finally { if(conn!=null) { try { conn.close(); } catch(SQLException e) {} } } |
如果不用这种方式指定配置信息,Dolly(以及她的同伴们)使用 JNDI 来查找 JDBC DataSource
会更好一些,如清单 2 所示:
Connection conn=null; try { Context ctx=new InitialContext(); Object datasourceRef=ctx.lookup("java:comp/env/jdbc/mydatasource"); DataSource ds=(Datasource)datasourceRef; Connection c=ds.getConnection(); /* use the connection */ c.close(); } catch(Exception e) { e.printStackTrace(); } finally { if(conn!=null) { try { conn.close(); } catch(SQLException e) { } } } |
为了能够得到 JDBC 连接,首先要执行一些小的部署配置,这样我们才可以在本地组件的 JNDI 下文中查找 DataSource
。这可能有点烦琐,但是很容易学。不幸的是,这意味着即使是为了测试组件,开发人员也必须涉足部署人员的领地,并且还要准备配置应用服务器。
为了让 JNDI 解析 java:comp/env/jdbc/mydatasource
引用,部署人员必须把 <resource-ref>
标签插入 web.xml 文件(Web 应用程序的部署描述符)。 <resource-ref>
标签的意思就是“这个组件依赖于外部资源”。清单 3 显示了一个示例:
<resource-ref> <description>Dollys DataSource</description> <res-ref-name>jdbc/mydatasource</res-ref-name> <res-ref-type>javax.sql.DataSource</res-ref-type> <res-auth>Container</res-auth> </resource-ref> |
<resource-ref>
入口告诉 servlet 容器,部署人员要在 组件命名上下文(component naming context) 中设置一个叫做 jdbc/mydatasource
的资源。组件命名上下文由前缀 java:comp/env/
表示,所以完全限定的本地资源名称是: java:comp/env/jdbc/mydatasource
.
这只定义了到外部资源的本地引用,还没有创建引用指向的实际资源。(在 Java 语言中,类似的情况可能是: <resource-ref>
声明了一个引用,比如 Object foo
,但是没有把 foo
设置成实际引用任何 Object
。)
部署人员的工作就是创建 DataSource
(或者是创建一个 Object
对象,让 foo
指向它,在我们的 Java 语言示例中就是这样)。每个容器都有自己设置数据源的机制。例如,在 JBoss 中,是利用服务来定义数据源(请参阅 $JBOSS/server/default/deploy/hsqldb-ds.xml,把它作为示例),它指定自己是 DataSource
的全局 JNDI 名称(默认情况下是 DefaultDS
)。在创建资源之后,第三步仍然很关键:把资源连接或者绑定到应用程序组件使用的本地名称。在使用 Web 应用程序的情况下,是使用特定于供应商的部署描述符扩展来指定这个绑定,清单 4 中显示了一个这样的例子。(JBoss 用称为 jboss-Web.xml
的文件作为特定于供应商的 Web 应用程序部署描述符。)
清单 4. 用特定于供应商的部署描述符将资源绑定到 JDI 名称
<resource-ref> <res-ref-name>jdbc/mydatasource</res-ref-name> <jndi-name>java:DefaultDS</jndi-name> </resource-ref> |
这表明应该将本地资源引用名称( jdbc/mydatasource
)映射到名为 java:DefaultDS
的全局资源。如果全局资源名称出于某种原因发生了变化,而应用程序的代码无需变化,那么只需修改这个映射即可。在这里,有两个级别的间接寻址:一个定义并命名资源( java:DefaultDS
),另一个把特定于本地组件的名称( jdbc/mydatasource
)绑定到命名的资源。(实际上,当您在 EAR 级别上映射资源时,可能还存在第三级别的间接寻址。)
当然,J2EE 中的资源并不局限于 JDBC 数据源。引用的类型有很多,其中包括资源引用(已经讨论过)、环境实体和 EJB 引用。特别是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一项关键角色:查找其他应用程序组件。
试想一下这种情况:当一家公司从 Order Ontology Processing Services(OOPS)购买了一个可部署的 EJB 组件来处理客户订单时,会发生什么。为了便于举例说明,我们把它叫做 ProcessOrders V1.0。ProcessOrders 1.0 有两部分:一组接口和支持类(home 和 remote 接口,以及支持的传输类);实际 EJB 组件自身。选择 OOPS 是因为它在这个领域的专业性。
该公司遵照 J2EE 规范,编写使用 EJB 引用的 Web 应用程序。公司的部署人员把 ProcessOrders 1.0 绑定到 JNDI 树中,将它用作 ejb/ProcessOrders/1.0
,并解析 Web 应用程序的资源名称,以指向这个全局 JNDI 名称。目前为止,这些都是 EJB 组件非常普通的用法。但是,当我们考虑公司的开发周期与公司供应商之间的交互时,事情就变得复杂起来。在这里,JNDI 也能帮助我们。
我们假设 OOPS 发布了一个新版本,即 ProcessOrders V1.1。这个新版本有一些新功能,公司内部的一个新应用程序需要这些新功能,而且很自然地扩展了 EJB 组件的业务接口。
在这里,公司有以下几个选择:可以更新所有应用程序来使用新版本,也可以编写自己的版本,或者使用 JNDI 的引用解析,允许每个应用程序在不影响其他应用程序的情况下使用自己的 EJB 组件版本。立刻更新所有应用程序对维护来说是一场噩梦,它要求对所有组件都进行完整的回归测试,这通常是一项艰巨的任务,而且如果发生任何功能测试失败的话,那么还要进行另一轮调试。
编写内部(in-house)组件常常是没有必要的重复工作。如果组件是由在这个业务领域内具有专业知识的公司编写的,那么给定的 IT 商店不可能像专业的组件供应商那样精通业务功能。
正如您可能已经猜到的那样,最好的解决方案是用 JNDI 解析。EJB 的 JNDI 引用非常类似于 JDBC 资源的引用。对于每个引用,部署人员都需要把新组件按特定的名称(比如说 ejb/ProcessOrders/1.1
)绑定到全局树中,对于需要 EJB 组件的其他每个组件,还要为组件在部署描述符中解析 EJB 引用。依赖于 V1.0 以前的应用程序不需要进行任何修改,也不需要重新测试,这缩短了实现的时间、降低了成本并减少了复杂性。在服务趋于转换的环境中,这是一种很有效的方法。可以对应用程序架构中所得到的所有组件进行这类配置管理,从 EJB 组件到 JMS 队列和主题,再到简单配置字符串或其他对象,这可以降低随时间的推移服务变更所产生的维护成本,同时还可以简化部署,减少集成工作。
有一个古老的计算机科学笑话:每个编程问题都可以仅仅用一个抽象层(或间接的)来解决。在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,但还没有紧到无法让人很容易地把它们分开并重新装配。JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和预先考虑,这个承诺是完全可以实现的。实际上,它要比许多人想像的容易得多。
<!-- CMA ID: 58202 --><!-- Site ID: 10 --><!-- XSLT stylesheet used to transform this file: dw-article-6.0-beta.xsl -->
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
- 从 Sun Microsystems 公司的 The JNDI Tutorial 可以学习更多 JNDI 的知识。
- “ Using JNDI Outside J2EE”摘自 Teach Yourself J2EE in 21 Days(Sams Publishing,2004 年),演示了如何定义正在使用的名称服务类型,以及如何定义网络上的服务器的主机名称和端口号。
-
developerWorks 专栏作家 Brian Goetz 在他的文章“ J2EE or J2SE? JNDI works with both”( JavaWorld,2002 年 4 月)中,讨论了如何使用 JNDI 和 J2SE 进行应用程序配置。
- Daniel 通过解释 J2EE 应用程序中使用的 JNDI 命名上下文,展示了如何“ 走出 JNDI 迷宫”。
-
将 JNDI 用于 WebSphere Application Server J2EE 瘦客户端应用程序 展示了如何在 J2EE 应用程序客户机中使用 JNDI,以便与远程 JNDI 提供者进行通信。
- 在 developerWorks Java 技术专区 可以找到数百篇有关 Java 各个方面的技术文章。
- 请访问 Developer Bookstore,以获得技术书籍的完整清单,其中包括数百本 Java 相关主题 的书籍。
Kirk Pepperdine 是 JavaPerformanceTuning.com 的首席技术官,过去 15 年里,他一直专注于对象技术和性能调整。他还是 Ant Developer's Handbook 一书(MacMillan)的合著者之一。
发表评论
-
命名和目录服务
2012-05-02 15:50 933命名和目录服务是计算的基础。命名服务是一种有用的工具,可编程的 ... -
配置 jndi
2012-04-21 07:37 14091)JNDI JNDI ---- Java 命名与目录接口 ... -
JNDI学习笔记一(Me)
2012-04-16 13:46 11421. 使用JNDI需要先到Sun官方网站现在相应的jar 例 ... -
JNDI入门1(转)
2012-04-16 13:34 1119原文:http://bbs.chinaunix.net/for ... -
JNDI 是什么+JBoss下的简单配置
2012-04-16 13:13 1220JNDI是 Java 命名与目录接口(Java Na ...
相关推荐
另一个涉及JNDI的高知名度漏洞是Log4j 2.x的CVE-2021-44228,也被称为“Log4Shell”漏洞。Log4j是一个广泛使用的日志记录框架,该漏洞允许攻击者通过在日志消息中插入特定的JNDI引用,从而执行任意代码。由于Log4j的...
2. **注入攻击**:攻击者通过诱使目标应用调用这个恶意的JNDI引用,通常通过输入参数或者请求数据来实现。 3. **远程代码执行**:当目标应用解析这个引用时,JNDI会尝试连接到指定的服务器并下载并执行代码。 为了...
2. **创建DLL绑定**:在JNDI服务器上,你需要创建一个条目,将DLL的引用绑定到一个有意义的名字。这通常涉及到将DLL的路径或者调用接口的信息注册到命名上下文中。 3. **编写Java代码**:在Java应用中,首先导入...
- **转介**:当客户端请求的对象不在当前上下文中时,JNDI 可以自动转至其他上下文进行查找。 2. **`javax.naming.directory` 包** - **目录对象**:包括 `DirContext` 接口,扩展了 `Context` 的功能。 - **...
Java 通过JNDI(Java Naming and Directory Interface)访问数据库是一种常见的做法,它提供了一种标准的方法来查找和管理资源,如数据源、EJB、邮件服务器等。JNDI在企业级应用中广泛使用,因为它允许应用程序与...
2. **Spring中的数据源配置**: Spring提供了多种数据源实现,如`org.springframework.jdbc.datasource.DriverManagerDataSource`、`com.zaxxer.hikari.HikariDataSource`等。这些数据源可以直接在Spring配置文件中...
2. **配置 JNDI 连接** - 使用文本编辑器打开 Kettler 的配置文件,找到关于 JNDI 的配置部分。 - 如果 Kettler 配置文件中已有 JNDI 配置,则根据上述步骤进行相应的修改;如果没有,则需要新增配置项。 #### ...
2. **查找EJB**:客户端或者服务器内的其他组件可以通过JNDI查找API来查找EJB。这通常涉及到调用`InitialContext`的`lookup`方法,传入EJB的JNDI名称。 3. **获取EJB引用**:查找成功后,返回的是EJB的引用,而不是...
2. `commons-dbcp-1.2.1.jar`:Apache Commons DBCP(Database Connection Pool)是Java数据库连接池的实现,它为JNDI环境下的数据源管理提供了基础。通过DBCP,应用程序可以高效地管理和复用数据库连接,减少数据库...
2. **编写Servlet**:创建一个名为`JNDITest`的Servlet类,该类的主要功能是从JNDI资源中获取`DataSource`对象,并通过该对象建立数据库连接。以下是`JNDITest`类的具体实现: ```java package com.Test; import ...
2. **DNS (Domain Name System)**:DNS是互联网上的一个关键服务,它将易于记忆的域名转换为网络可识别的IP地址。在Java中,`dns.jar` 可能包含了一个实现DNS查询的库,使得Java程序能够方便地执行DNS解析。例如,...
2. **查找条目**:使用JNDI的`DirContext.search()`方法,我们可以根据特定的过滤器找到需要修改密码的条目。例如,我们可能需要搜索DN(Distinguished Name)与特定用户关联的条目。 3. **编码密码**:在修改密码...
2. **JNDI查找**:在Java EE应用中,通过JNDI查找可以获取到EJB的引用。首先,我们需要创建一个NamingContext对象,然后调用其lookup()方法,传入EJB的JNDI名称。这个名称通常由两部分组成:全局上下文名(如"java:...
在帆软报表FineReport中,若要实现与Websphere应用服务器的JNDI连接,需要进行一系列配置,以确保报表能够利用JNDI获取数据源,并最终在WEB环境中通过浏览器访问报表。 首先,需要了解JNDI(Java Naming and ...
确认后,转到“数据源”部分进行配置。 在数据源配置中,需要设置连接池参数,如最小和最大连接数。这些值可以根据应用的预期负载和数据库的并发处理能力来设定。通常,为了保持性能和资源的有效利用,最小连接数和...
#### 2. 构建上下文实现 JNDI SPI提供了构建具体命名和目录服务实现的基础框架。开发者可以根据需求选择性地实现其中的部分或全部接口。 ##### 2.1 参数所有权 实现上下文时,需要注意参数的所有权问题。即当上...
4. **db2jcc_javax.jar**:这个文件可能包含了一些与javax相关的接口或类,这些接口或类对于DB2 JDBC驱动的某些功能是必要的,比如JNDI查找服务或者其他的Java EE相关特性。 5. **rdbmswriter-0.0.1-SNAPSHOT.jar**...
- JNDI(Java Naming and Directory Interface):在应用服务器环境中,可以通过JNDI查找预配置的数据源,简化配置。 - 配置参数:DB2驱动支持多种配置参数,如超时设置、最大连接数等,以优化性能和稳定性。 5. ...
首先,我们需要理解什么是乱码。乱码通常发生在计算机系统处理字符编码不一致的情况下,即数据的输入、存储和显示使用了不同的字符集。中文字符集主要有GBK、GB2312、UTF-8等,如果它们之间不匹配,就会导致字符无法...
标题所提及的"Java-SVG转图片所需jar包"是用于解决这个问题的关键组件。描述中指出,这个压缩包包含了14个jar文件,这些文件提供了必要的API和工具来处理SVG到图片的转换。 首先,我们来看一下关键的几个jar包: 1...