在 GT4 中使用 Derby 有几个原因:
- Derby 是使用 Java 编程语言编写的,这意味着它不需要对每个支持的平台都进行编译。
- Derby 设计用来嵌入到大型系统中,例如 GT4。
- 实际上不需要设置。嵌入式的 Derby DBMS 可以自动启动。
- Derby 非常小。核心的 jar 文件不到 2MB。
- Derby 支持大部分 SQL 标准,完全可以用作 RDBMS 系统。如果需要,可以很容易地切换到其他的 DBMS。
- Derby 和 GT4 都是遵循 Apache 许可证发布的。
您需要做的修改主要有 3 类:模式、配置和代码。
- 在模式中,所支持的数据类型
BOOLEAN/BIT和TEXT中存在区别。 - 在代码中,修改主要是对 null 的处理、可更新的游标以及对模式的修改。
- 从配置的观点来看,修改更加简单了。我们将 Derby 作为一个嵌入式数据库运行,因此只需要很少的设置。
这些修改在将来的 GT4 发行版中应该就可以使用了。有关 GT4 中对 Derby 支持的可用性的更多信息,请访问 The Globus Alliance。问题和意见可以通过 GT4 Friends 讨论列表提出。
注意:我使用了 V10.1.1.0 版本的 Derby,这可以从 Apache.org 上下载。
安装 Globus Toolkit V4.0.1,并将 GLOBUS_LOCATION 环境变量设置为您安装的根目录,然后:
- 解压 Derby 发行版,并将 derby.jar、derbyclient.jar 和 derbytools.jar 拷贝到 $GLOBUS_LOCATION/lib/ 中
- 创建一个目录 $GLOBUS_LOCATION/database/
- 将下面的代码添加到 etc/globus-user-env.sh 中:
清单 1. globus-user-env.sh 中添加的内容# #The following statement will set the jvm options for the database # export GLOBUS_OPTIONS=-Dderby.system.home=$GLOBUS_LOCATION/database/ # export CLASSPATH=$GLOBUS_LOCATION/lib/derby.jar: $GLOBUS_LOCATION/lib/derbytools.jar:$GLOBUS_LOCATION/lib/derbyclient.jar
- 导出 GLOBUS_LOCATION 的设置,并引用 globus-user-env.sh 文件。要确保环境变量都正确设置了,请检查
CLASSPATH和GLOBUS_*环境变量。例如:
清单 2. 检查环境变量env | grep CLASS CLASSPATH=/opt/gt401f/lib/derby.jar:/opt/gt401f/lib/derbytools.jar:/ opt/gt401f/lib/derbyclient.jar env | grep GLOBUS GLOBUS_PATH=/opt/gt401f GLOBUS_LOCATION=/opt/gt401f GLOBUS_OPTIONS=-Dderby.system.home=/opt/gt401f/database
- 要连接到 Derby 而不是其他数据库上,请修改 etc/globus_wsrf_rft/jndi-config.xml 文件中
driverName和connectionString的设置:org.apache.derby.jdbc.EmbeddedDriver jdbc:derby:directory:rftDatabase
我们首先从 GT4 MySQL 模式开始入手,这可以在文件 GLOBUS_LOCATION/share/globus_wsrf_rft/rft_schema_mysql.sql 中找到,我们将其拷贝到 rft_schema_derby.sql 文件。与 PostgreSQL 模式相比,MySQL 模式的语义与 Derby 更为接近。具体来说,PostgreSQL 模式包含了 TEXT 类型的域。它们可以转换成 MySQL 模式中的标准 SQL VARCHAR 数据类型,因此也可以使用 Derby。
MySQL 模式中有几个使用 BIT 数据类型定义的域。在 Derby 中我们将把这些域重新定义为 CHAR(1)。以下域在 MySQL 模式中被定义为 bit:
清单 3. bit 域
started bit(1) default 0, all_or_none bit(1) default 0, dcau bit(1) default 0, notpt bit(1) default 0, binary_mode bit(1) default 1, ignore_file_perm_errors bit(1) default 0 |
我们还需要对两处进行修改。首先,权限域被定义为 INT。将其修改为 VARCHAR。(这些修改会反映到将来的 Globus 发行版中)。其次,主键约束名重名了。对于 Derby 来说,主键约束名要求是惟一的。一个完整的 RFT SQL 模式如下所示:
清单 4. Derby 的完整 RFT SQL 模式
CREATE TABLE "GLOBUS"."REQUEST" (
"ID" INTEGER NOT NULL,
"CONCURRENCY" INTEGER DEFAULT 1,
"TERMINATION_TIME" DOUBLE,
"PROXY_LOC" VARCHAR(200),
"USERNAME" VARCHAR(200),
"STARTED" CHAR(1),
"ALL_OR_NONE" CHAR(1),
"MAXATTEMPTS" INTEGER DEFAULT 1,
"DELEGATED_EPR" VARCHAR(1255),
"USER_SUBJECT" VARCHAR(200));
CREATE TABLE "GLOBUS"."TRANSFER" (
"ID" INTEGER NOT NULL,
"REQUEST_ID" INTEGER NOT NULL,
"SOURCE_URL" VARCHAR(200) NOT NULL,
"DEST_URL" VARCHAR(200),
"STATUS" INTEGER DEFAULT 4,
"ATTEMPTS" INTEGER DEFAULT 0,
"USER_NAME" VARCHAR(100) DEFAULT NULL,
"DCAU" CHAR(1),
"PARALLEL_STREAMS" INTEGER DEFAULT 1,
"TCP_BUFFER_SIZE" INTEGER DEFAULT 0,
"BLOCK_SIZE" INTEGER DEFAULT 16000,
"NOTPT" CHAR(1),
"BINARY_MODE" CHAR(1),
"SOURCE_SUBJECT" VARCHAR(200),
"DEST_SUBJECT" VARCHAR(200),
"RETRY_TIME" DOUBLE DEFAULT 0,
"SIZE" DOUBLE DEFAULT 0,
"FAULT" VARCHAR(255),
"PERMISSIONS" VARCHAR(100),
"IGNORE_FILE_PERM_ERRORS" CHAR(1)
);
CREATE TABLE "GLOBUS"."TRANSFERID" (
"TRANSFER_ID" INTEGER NOT NULL
);
CREATE TABLE "GLOBUS"."FACTORY" (
"TOTAL_TRANSFERS" DOUBLE,
"TOTAL_BYTES" DOUBLE
);
CREATE TABLE "GLOBUS"."RESTART" (
"TRANSFER_ID" INTEGER,
"MARKER" VARCHAR(200)
);
CREATE TABLE "GLOBUS"."REQUESTID" (
"REQUEST_ID" INTEGER NOT NULL
);
-- ----------------------------------------------
-- DDL Statements for keys
-- ----------------------------------------------
-- primary/unique
ALTER TABLE "GLOBUS"."TRANSFER"
ADD CONSTRAINT "TRANSFER_PK_CONS" PRIMARY KEY ("ID");
ALTER TABLE "GLOBUS"."REQUEST"
ADD CONSTRAINT "REQUEST_PK_CONS" PRIMARY KEY ("ID");
ALTER TABLE "GLOBUS"."TRANSFERID"
ADD CONSTRAINT "TRANSFER_ID_PK_CONS" PRIMARY KEY ("TRANSFER_ID");
ALTER TABLE "GLOBUS"."REQUESTID"
ADD CONSTRAINT "REQUEST_ID_PK_CONS" PRIMARY KEY ("REQUEST_ID");
|
使用 IJ 创建数据库,这是 Derby 的交互式 JDBC 脚本工具。
清单 5. 使用 IJ 创建数据库
java -classpath $GLOBUS_LOCATION/lib/derby.jar:
$GLOBUS_LOCATION/lib/derbytools.jar
-Dderby.infolog.append=true
-Dij.protocol=jdbc:derby:directory:$GLOBUS_LOCATION/database/
-D'ij.database=rftDatabase;create=true' org.apache.derby.tools.ij
$GLOBUS_LOCATION/share/globus_wsrf_rft/rft_schema_derby.sql
|
所有与数据库有关的代码都包含在两个类中,可以在 src/org/globus/transfer/reliable/service/ 目录中找到这两个类:
- RFTResourceManager.java
- ReliableFileTransferDbAdapter.java
对 RFTResourceManager 的修改如下:
-
SQL 限制子句 在 Derby 中并不受支持。相反,我们使用
Statement.getMaxRows()方法。 -
可更新游标 —— 在撰写本文时,Derby 还不支持可更新的游标。因此我们使用一个新方法来替换
updateInt()和updateRow()调用,这就是updateTransferJob(),它使用一个新的 PreparedStatement 来实现更新。
它与 1.22.4.5 版本的 RFTResourceManager.java 之间的区别如下:
清单 6. 对 RFTResourceManager.java 的修改
93,96c93
+ + " order by id ";
+
+ private static final String updateTransferQuery =
+ "UPDATE transfer set status=? where id=?";
---
- + " order by id limit ?";
223,255d219
+ public
+ synchronized void updateTransferJob(int transferId, int status)
+ throws RftDBException {
+ Connection c = null;
+ PreparedStatement preparedStatement = null;
+ try {
+ c = RFTDatabaseSetup.getDBConnection();
+ preparedStatement =
+ c.prepareStatement(updateTransferQuery);
+ preparedStatement.setObject(
+ 1, new Integer(transferId));
+ preparedStatement.setObject(2,
+ new Integer(status));
+ preparedStatement.executeUpdate();
+ } catch (SQLException e) {
+ logger.error(i18n.getMessage("dbUpdateErr",
+ "" + requestId), e);
+ throw new RftDBException(i18n.getMessage(
+ "dbUpdateErr", "" + requestId), e);
+ } finally {
+ try {
+ if (preparedStatement != null) {
+ preparedStatement.close();
+ }
+ if (c != null) {
+ RFTDatabaseSetup.returnDBConnection(c);
+ }
+ } catch (SQLException sql) {
+ logger.warn(i18n.getMessage(
+ "dbStatementErr"), sql);
+ }
+ }
+ }
269c237
+ statement.setMaxRows(this.cacheSize);
---
- statement.setObject(3, new Integer(this.cacheSize));
271d238
+ for (int i=0;i<this.cacheSize;i++) {
274,275c241
+ int tempTransferId = rs.getInt(1);
+ transfer.setTransferId(new Integer(tempTransferId));
---
- transfer.setTransferId(new Integer(rs.getInt(1)));
298,299c264,265
+ this.updateTransferJob(tempTransferId,
+ RFTConstants.STATUS_CACHED);
---
- rs.updateInt("status",RFTConstants.STATUS_CACHED);
- rs.updateRow();
306d271
+ }
|
对 ReliableFileTransferDbAdapter.java 的修改
对 ReliableFileTransferDbAdapter.java 的修改如下:
-
null 的处理 —— 在 Derby 中,需要显式地在
PreparedStatements中将对象设置成 null,这需要使用一个诸如pstmt.setObject(5, "null");之类的调用。以前,代码允许在处理可为空的域时采用缺省的行为。现在每个可空的域都会进行检查,并设置适当的缺省值。 -
可更新的游标 —— 与 RFTResourceManager 中的修改类似,但是使用新的语句替换了
updateInt()和updateRow()调用。 -
Boolean 域 —— 回想一下我们将 SQL 模式的某些域从
BIT修改成了CHAR。这样,JDBC 调用将CHAR域设置成一个 Java Boolean 值,就会导致这个CHAR域被设置为0或1。在 MySQL 中可以处理BIT域的代码依然可以处理 Derby 中的CHAR域。例如,pstmt.setObject(6, new Boolean(true))会将CHAR域在数据库中设置为1。
清单 7. 对 ReliableFileTransferDbAdapter.java 的修改
184c184,188
- pstmt.setObject(5, request.getMaxAttempts());
---
+ if (request.getMaxAttempts() == null) {
+ pstmt.setObject(5, new Integer(1));
+ } else {
+ pstmt.setObject(5, request.getMaxAttempts());
+ }
324a329
+ Statement tempStatement = null;
335c340
- Statement tempStatement = c.createStatement();
---
+ tempStatement = c.createStatement();
348,349c353,357
- rs.updateInt("request_id",nextRequestId);
- rs.updateRow();
---
+ String updateQuery =
+ "update requestid set request_id=";
+ updateQuery = updateQuery + nextRequestId;
+ tempStatement = c.createStatement();
+ tempStatement.executeUpdate(updateQuery);
402a410
+ Statement tempStatement = null;
416c424
- Statement tempStatement = c.createStatement();
---
+ tempStatement = c.createStatement();
427,428c435,437
- rs.updateInt("transfer_id",nextTransferId);
- rs.updateRow();
---
+ tempStatement = c.createStatement();
+ tempStatement.executeUpdate(
+ "update transferid set " + "transfer_id= "+ nextTransferId);
482c491,495
- pstmt.setObject(6, tempUserName);
---
+ if (tempUserName == null) {
+ pstmt.setObject(6, "null");
+ } else {
+ pstmt.setObject(6, tempUserName);
+ }
489c502,506
- pstmt.setObject(5, tempUserName);
---
+ if(tempUserName == null) {
+ pstmt.setObject(5, "null");
+ } else {
+ pstmt.setObject(5, tempUserName);
+ }
852,861c869,918
- pstmt.setObject(5, rftOptions.getUserName());
- pstmt.setObject(6, rftOptions.getDcau());
- pstmt.setObject(7, rftOptions.\
getParallelStreams());
- pstmt.setObject(8, rftOptions.\
getTcpBufferSize());
- pstmt.setObject(9, rftOptions.getBlockSize());
- pstmt.setObject(10, rftOptions.getNotpt());
- pstmt.setObject(11, rftOptions.getBinary());
- pstmt.setObject(12, rftOptions.\
getSourceSubjectName());
- pstmt.setObject(13, rftOptions.\
getDestinationSubjectName());
- pstmt.setObject(14, rftOptions.\
getIgnoreFilePermErr());
---
+ if(rftOptions.getUserName() == null) {
+ pstmt.setObject(5, "null");
+ } else {
+ pstmt.setObject(5,\
rftOptions.getUserName());
+ }
+ if(rftOptions.getDcau() == null) {
+ pstmt.setObject(6, new Boolean(true));
+ } else {
+ pstmt.setObject(6, rftOptions.getDcau());
+ }
+ if(rftOptions.getParallelStreams() == null) {
+ pstmt.setObject(7, new Integer(1));
+ } else {
+ pstmt.setObject(7, rftOptions.\
getParallelStreams());
+ }
+ if(rftOptions.getTcpBufferSize() == null) {
+ pstmt.setObject(8, new Integer(16384));
+ } else {
+ pstmt.setObject(8, rftOptions.\
getTcpBufferSize());
+ }
+ if (rftOptions.getBlockSize() == null) {
+ pstmt.setObject(9, new Integer(16384));
+ } else {
+ pstmt.setObject(9,\
rftOptions.getBlockSize());
+ }
+ if (rftOptions.getNotpt() == null) {
+ pstmt.setObject(10, new Boolean(false));
+ } else {
+ pstmt.setObject(10, rftOptions.getNotpt());
+ }
+ if (rftOptions.getBinary() == null) {
+ pstmt.setObject(11, new Boolean(true));
+ } else {
+ pstmt.setObject(11, rftOptions.getBinary());
+ }
+ if (rftOptions.getSourceSubjectName() == null) {
+ pstmt.setObject(12, "null");
+ } else {
+ pstmt.setObject(12, rftOptions.\
getSourceSubjectName());
+ }
+ if (rftOptions.getDestinationSubjectName() == null) {
+ pstmt.setObject(13, "null");
+ } else {
+ pstmt.setObject(13, rftOptions.\
getDestinationSubjectName());
+ }
+ if (rftOptions.getIgnoreFilePermErr() == null) {
+ pstmt.setObject(14, new Boolean(false));
+ } else {
+ pstmt.setObject(14, rftOptions.\
getIgnoreFilePermErr());
+ }
2070c2127,2128
- query.append("' limit 1");
---
+ query.append("' ");
+ statement.setMaxRows(1);
|
可以运行 RFT 测试工具来验证这些修改是否有效。这可以参考 RFT 管理手册中的提示。
特别感谢 Argonne National Laboratory 的 Ravi Madduri。他是 RFT 的作者,也是本文中介绍的大部分代码的作者。还要感谢 IBM 的 Sneha Varghese 和 Shama Ghulamhussain,他们贡献了 Globus Toolkit 中对早期版本的 Cloudscape 的支持。
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
-
Cloudscape Version 10:技术概述 是一份非常简明的概述。
-
在 Globus Toolkit Version 4: Software for Service-Oriented Systems(I. Foster 著,IFIP International Conference on Network and Parallel Computing,Springer-Verlag LNCS 3779,pp 2-13,2005 年)中对基于 Web 服务的 GT4 的基本特性进行了介绍。
-
有关 Globus 的所有知识,请参阅 A Globus Toolkit Primer(I. Foster 著,2005 年)。
-
请访问 developerWorks 网格计算专区,这里有丰富的 how-to 信息和工具文档,可以帮助您利用网格计算进行开发。
获得产品和技术
-
IBM DB2 Universal Database Enterprise Server Edition V8.2 for Linux® 是一个试用软件,可以从 developerWorks 上下载。
-
在您的下一个开放源码开发项目中采用 IBM 试用软件,这些软件可以下载,也可以通过 DVD 获得。
讨论
-
通过参与 developerWorks blogs 加入 developerWorks 社区。
