IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Grid computing | Open source  >

在 GT4 中使用 IBM Cloudscape/Apache Derby 作为 RFT 数据库

简化网格的 Globus Toolkit 设置

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

Tom Seelbach (seelbach@us.ibm.com), 资深软件工程师, IBM

2005 年 11 月 14 日

本文介绍了在使用 Apache Derby 开放源码数据库管理系统(DBMS)作为 Globus Toolkit 4.0(GT4)中的 Reliable File Transfer(RFT)服务的数据库管理系统时,所要对代码和配置进行的修改。GT4 目前可以支持 PostgreSQL 和 MySQL DBMS。由于 Derby 是一个 100% 的 Java™ 技术系统,因此它可以嵌入到 GT4 发行版中,这可以极大地简化网格的安装、配置和操作过程。GT4 中需要 DBMS 的核心组件是 RFT。

为什么要使用 Derby?

在 GT4 中使用 Derby 有几个原因:

  • Derby 是使用 Java 编程语言编写的,这意味着它不需要对每个支持的平台都进行编译。
  • Derby 设计用来嵌入到大型系统中,例如 GT4。
  • 实际上不需要设置。嵌入式的 Derby DBMS 可以自动启动。
  • Derby 非常小。核心的 jar 文件不到 2MB。
  • Derby 支持大部分 SQL 标准,完全可以用作 RDBMS 系统。如果需要,可以很容易地切换到其他的 DBMS。
  • Derby 和 GT4 都是遵循 Apache 许可证发布的。




回页首


修改概述

您需要做的修改主要有 3 类:模式、配置和代码。

  • 在模式中,所支持的数据类型 BOOLEAN/BITTEXT 中存在区别。
  • 在代码中,修改主要是对 null 的处理、可更新的游标以及对模式的修改。
  • 从配置的观点来看,修改更加简单了。我们将 Derby 作为一个嵌入式数据库运行,因此只需要很少的设置。

这些修改在将来的 GT4 发行版中应该就可以使用了。有关 GT4 中对 Derby 支持的可用性的更多信息,请访问 The Globus Alliance。问题和意见可以通过 GT4 Friends 讨论列表提出。





回页首


GT4 配置的修改

安装并配置 Derby

注意:我使用了 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 文件。要确保环境变量都正确设置了,请检查 CLASSPATHGLOBUS_* 环境变量。例如:
    清单 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 文件中 driverNameconnectionString 的设置:
    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





回页首


RFT 代码的修改

所有与数据库有关的代码都包含在两个类中,可以在 src/org/globus/transfer/reliable/service/ 目录中找到这两个类:

  • RFTResourceManager.java
  • ReliableFileTransferDbAdapter.java

对 RFTResourceManager.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 域被设置为 01。在 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 测试工具来验证这些修改是否有效。这可以参考 RFT 管理手册中的提示。





回页首


致谢

特别感谢 Argonne National Laboratory 的 Ravi Madduri。他是 RFT 的作者,也是本文中介绍的大部分代码的作者。还要感谢 IBM 的 Sneha Varghese 和 Shama Ghulamhussain,他们贡献了 Globus Toolkit 中对早期版本的 Cloudscape 的支持。



参考资料

学习

获得产品和技术

讨论


关于作者

Tom Seelbach

Tom Seelbach 作为一名开发人员、测试人员以及 IBM Grid Toolbox 开发组的负责人,已经具有很多年使用 Globus Toolkit 的经验。在过去的几年中,他是 Tivoli Personalized Services Manager(WebSphere Everyplace Suite 产品的一部分)的负责人。他还为 http://movies.excite.com 开发了电影搜索和个性化设置,并为 http://money.excite.com 开发了内容管理。在娱乐方面,他喜欢踢英式足球,并花了一些时间来耕种农作物,并尽量与自己的妻子和 3 个女儿待在一起。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?




回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款