级别: 中级 Vladimir Silva (vladimir_silva@hotmail.com), 软件工程师, Consultant
2006 年 12 月 29 日 Jakarta Project Commons Virtual File System(VFS)为访问各种不同的文件系统提供了单一的应用程序编程接口(API)。Common VFS 为从本地文件、FTP 服务器、SSH、WebDAV、HTTP、HTTPS Windows 共享等资源获得的文件提供了统一的视图。VFS 支持很多文件系统,不过却缺少对网格计算协议(例如 GridFTP)的支持。本文将介绍一种可在 Common VFS 内使用的 GridFTP 提供者的实现。
Commons VFS 支持的文件系统
Commons VFS 具有以下特性:
- 为访问各种文件类型提供了单一一致的 API
- 对 Java 虚拟机(JVM)或本地文件系统中的文件信息进行缓存
- 事件递送
- 集成到应用程序中的一些实用工具,例如 ClassLoader 和 URLStreamHandlerFactory
表 1. Commons VFS 所支持的部分文件系统列表
| 名字 | URI 格式 | 文件系统 |
|---|
| local | file:///somedir/file.txt | 提供对本地物理文件系统上的文件的访问 | | Common Internet File System (CIFS)) | smb://[ username[: password]@] hostname[: port][ absolute-path] | Samba 服务器,或 Windows 共享 | | FTP/SFTP | ftp://[ username[: password]@] hostname[: port][ absolute-path]
sftp://[ username[: password]@] hostname[: port][ absolute-path]
| FTP,Secure FTP 服务器 | | HTTP/HTTPS | http://[ username[: password]@] hostname[: port][ absolute-path]
https://[ username[: password]@] hostname[: port][ absolute-path
| HTTP 服务器 |
它还支持很多其他的文件系统,包括 WebDA、诸如 ZIP、JAR、TAR、GZIP、BZIP2 之类的压缩格式、资源(RES)和 RAM(关于完整的文件系统列表,请参看 参考资料)。
网格文件系统
那么这个非常棒的 API 究竟缺少什么呢?它所缺少是对在网格环境中使用的最常见的两个文件系统 —— GridFTP(也称为 GsiFTP)和 Storage Resource Broker —— 的支持。在本文中,将介绍使用 VFS API 连接 GridFTP 服务器所需要的一些步骤。下一篇文章将讨论 Storage Resource Broker 方面的内容。
GsiFTP 文件系统实现类
这个文件系统必须作为一个新提供者实现,它使用了一组对一系列抽象对象进行扩展的类来为新文件系统提供一个通用视图。这个新提供者然后又通过定义一个将新类加载到通用 VFS 中所使用的配置 XML 文件进行集成。
清单 1. GsiFTP 提供者配置文件 META-INF/vfs-providers.xml
<providers>
<!-- Vladimir GsiFTP aka GridFTP -->
<provider
class-name="org.apache.commons.vfs.provider.gsiftp.GsiFtpFileProvider">
<scheme name="gsiftp"/>
</provider>
</providers>
|
表 2 给出了为 GsiFTP 提供者所定义的类。
表 2. GsiFTP 提供者实现类
| 名字 | 说明 |
|---|
| GsiFtpClientFactory | 创建 GridFtpClient 实例 | | GsiFtpFileNameParser | 设置协议的默认值,例如将端口设置为 2811 | | GsiFtpFileObject | 扩充 AbstractFileObject;代表一个文件,并被用来访问这个文件的内容和结构 | | GsiFtpFileSystem | 代表 GsiFTP 服务器上的文件 | | ProxyTool | 创建网格代理证书使用的实用工具类;使用 CoG 工具包配置文件 $HOME/.globus/cog.properties;用户证书和加密后的私钥必须安装到 $HOME/.globus 中 |
图 1. Eclipse 下 GsiFTP 提供者实现类
图 1 给出了实现 GsiFTP VFS 提供者所需要的类。它们都是在包 org.apache.commons.vfs.provider.gsiftp 下面创建的。请注意实用工具类 ProxyTool.java。它的角色是创建认证所需要的代理证书。用户证书和私钥必须安装才能工作。还要注意 META-INF/vfs-providers.xml 文件的位置。这个文件定义了新提供者的配置,它一放到 META-INF 文件夹中就会被 VFS 自动加载。
GsiFTP 客户机工厂
对于任何新 VFS 提供者都有一些关键的类。第一个类是一个客户机工厂。其角色是创建到目标服务器的客户机连接,在这种情况下就是 GsiFTP。这个类依赖于实用工具类 ProxyTool.java 来确定代理是否过期。如果代理已经过期,它就会创建一个新代理证书并返回。代理又会被 GsiFTP 客户机用来对远程服务器进行认证。
清单 2. 创建 GsiFTP 客户机实例的 Commons VFS 工厂类
package org.apache.commons.vfs.provider.gsiftp;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileSystemOptions;
import org.globus.ftp.GridFTPClient;
import java.io.IOException;
/**
* Create a GridFtpClient instance
*
* @author Mario Ivankovits
* @author Vladimir Silva
* @version $Revision$ $Date: 2005-10-14 10:59:47 -0700 (Fri, 14 Oct 2005) $
*/
public class GsiFtpClientFactory
{
static String Host;
static int Port;
private GsiFtpClientFactory()
{
}
/**
* Creates a new connection to the server.
*/
public static GridFTPClient createConnection(String hostname
, int port
, String username, String password
, FileSystemOptions fileSystemOptions)
throws FileSystemException
{
Host = hostname;
Port = port;
try
{
if ( password == null)
throw new Exception("Password cannot be null");
// Create a proxy cert (if missing)
new ProxyTool().createProxy(password);
final GridFTPClient client =
new GridFTPClient(hostname, port);
try
{
// Authenticate w/ user credentials
// defined in $HOME/.globus/cog.properties
client.authenticate(null);
}
catch (final IOException e)
{
if (client != null )
client.close();
throw e;
}
return client;
}
catch (final Exception exc)
{
throw new FileSystemException(
"vfs.provider.gsiftp/connect.error"
, new Object[]{hostname}, exc);
}
}
}
|

 |

|
加载用户证书并创建网格代理
图 2. ProxyTool.java 实用工具类
这个过程中另外一个关键步骤是检测用户代理是否已经过期,如果已经过期,就通过加载用户根目录中的用户证书和私钥来创建新代理。这通过使用 Java™ CoG 工具包很容易就可以实现,Globus 就使用它通过 Java 编程语言实现了 Grid Security Infrastructure API。
用户证书可以通过调用 CertUtil.loadCertificate() 从默认位置进行加载。私钥通过 BouncyCastleOpenSSLKey 类进行封装,可以使用这个类的一个实例进行加载和解密。
清单 3. 加载用户证书和私钥
/*
* Load a cert from a given path
*/
private void loadCertificates(String path)
throws GeneralSecurityException, IOException
{
certificates = new X509Certificate[] { CertUtil
.loadCertificate(path) };
}
/**
* Load user key and decrypt it
* @param keyPath path to the private key ($HOME/.globus/userkey.pem)
* @param pwd User key decryption passphrase
* @throws GeneralSecurityException
*/
private void loadKey(String keyPath, String pwd)
throws GeneralSecurityException
{
try {
OpenSSLKey key = new BouncyCastleOpenSSLKey(keyPath);
if (key.isEncrypted()) {
key.decrypt(pwd);
}
userKey = key.getPrivateKey();
} catch (IOException e) {
throw new GeneralSecurityException
("Error: Failed to load key: " + keyPath);
} catch (GeneralSecurityException e) {
throw new GeneralSecurityException
("Error: Wrong pass phrase");
}
}
|
如果需要验证,私钥和公钥的模数必须匹配。如果所剩下的时间为 0,代理就过期了,如下所示。
清单 4. 代理验证和过期检查
/*
* Verify proxy by comparing the modulus of the public and
* private keys
*/
private void verify() throws GeneralSecurityException
{
RSAPublicKey pkey = (RSAPublicKey)
this.certificates[0].getPublicKey();
RSAPrivateKey prkey = (RSAPrivateKey) userKey;
if (!pkey.getModulus().equals(prkey.getModulus())) {
throw new GeneralSecurityException(
"Certificate and private key specified do not match");
}
}
/**
* Check for expired proxy
* @return true if expired proxy
* @throws GlobusCredentialException if no proxy found
*/
public boolean proxyExpired()
{
try {
long t = new GlobusCredential(
CoGProperties.getDefault().getProxyFile()).
getTimeLeft();
return (t == 0) ? true : false;
} catch (GlobusCredentialException e) {
return true;
}
}
|
VFS 文件对象
图 3. VFS GsiFTP 文件对象类
任何新 VFS 提供者的另外一个关键元素是实际对象的实现。这个类实现以下功能(另外还会做很多事情):
- 将文件对象附加到它的文件资源上
- 为该文件获取文件信息(名字、大小、数据等)
- 跟踪这个对象中包含的任何孩子
- 执行各种文件操作,包括创建文件/文件夹、删除文件、分离文件等。
- 接收影响自己或孩子的事件 —— 例如,当这个文件的类型或内容发生变化(
onChange)或这个文件的孩子发生变化(onChildrenChanged)时
测试新提供者
使用 FileSystemManager 接口来访问 Commons VFS。最简单的方法是使用 VFS.getManager() 方法,它会返回默认的实现。使用这个接口,可以定位文件并创建文件系统。
清单 5. 测试新提供者的样例代码
/**
* Grid Security Infrastructure (GSI) Authentication tokens
*/
String user = "vsilva"; // user name
String pwd = "2p2dkdt"; // user key pass phrase (used to decrypt it)
String path = ""; // Server Path
String host = "ebony"; // GridFTP host name
int port = 2811; // default port
/*
* Obtain a FileSystem Manager: used o resolve URIs or
* create file systems
*/
FileSystemManager fsManager = VFS.getManager();
//
// Build a VFS GSIFTP URI
//
String uri = "gsiftp://" + user + ":" + pwd + "@" + host
+ ":" + port + "/" + path;
//
// A File Object for the GsiFTP server
//
FileObject fo = fsManager.resolveFile(uri);
// List the contents of the file object
FileObject[] children = fo.getChildren();
FileObject f = null;
FileContent c = null;
System.out.println( "Children of " + fo.getName() );
for ( int i = 0; i < children.length; i++ )
{
f = children[ i ];
c = f.getContent();
final long size = ( f.getType() == FileType.FILE ) ? c.getSize() : -1;
final long date = ( f.getType() == FileType.FILE ) ? c.getLastModifiedTime() : -1;
System.out.println( f.getName().getPath()
+ " date:" + date
+ " Size:" + size );
}
|

 |

|
展开并安装源文件
图 4. Eclipse 中的项目文件
本文的代码是使用 Eclipse 创建的。这对于库有一些要求,在构建新 VFS 提供者时必须要考虑这个问题。Java CoG 工具包所使用的 Grid Security 库对于这个提供者来说就是必需的,如图 4 所示。其他通用的库需求包括 Commons 日志,当然还有通用的 VFS 核心库。
结束语
Commons VFS 为访问各种不同的文件系统提供了单一的 API。它为从各种源头 —— 例如本地磁盘、HTTP 服务器或压缩包 —— 获得的文件提供了统一的视图。本文的目的是介绍一种可在 Common VFS 内使用的 GridFTP 提供者的实现。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 示例脚本 | gr-jvfsftp.zip | 3MB | HTTP |
|---|
参考资料 学习
获得产品和技术
-
用 IBM 试用版软件 构建您的下一个开放源码开发项目,这些软件可以从 developerWorks 上直接下载。
讨论
关于作者  | |  | Vladimir Silva 出生在厄瓜多尔的基多,在这里他获得了 Polytechnic Institute of the Army 的工程师学位。然后他又在 Middle Tennessee State University 获得了计算机科学的硕士学位。毕业后,他加入了 IBM Web-Ahead 技术智囊团,在这里他以一名软件工程师的身份参加了很多项目,例如 IBM 内部网格和 IBM Grid Toolbox。他还为 developerWorks 发表了很多与网格有关的技术文章,他在服务器端安全性方面的一些工作(数字证书)已经集成到了最新的 Globus Toolkit(GT4)版本中。他是 Grid Computing for Developers 一书的作者。他的其他兴趣包括神经网络和人工智能。他还拥有包括 OCP、MCSD 和 MCP 在内的众多 IT 证书。 |
对本文的评价
|