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

developerWorks 中国  >  WebSphere  >

使用WebSphere Adapter Toolkit开发符合J2EE标准的资源适配器: 第2部分

构建FTP适配器的outbound场景

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

里 金千, IBM中国软件开发中心任软件工程师

2005 年 12 月 12 日

在商业整合开发中,J2EE 应用程序经常需要与企业信息系统(EIS)交互,以获得所需的资源。J2EE Connector Architecture (JCA) 1.5 标准定义了资源适配器(Resource Adapter)组件规范,使 J2EE 应用程序可以通过标准接口和 EIS 系统交互。IBM 提供了工具软件 WebSphere Adapter Toolkit 6.0 (WAT)以及 Adapter Foundation Classes(AFC) 基础类库来帮助开发者快速的构建符合 JCA 标准的资源适配器。本文将通过具体的实例(WebSphere Demo FTP Adapter)来说明整个开发流程。

1 FTP资源适配器Outbound场景设计

本场景主要是通过SCA组件与FTP服务器的连接。通过使用FTP适配器,用户可以通过Web客户端(http),从FTP服务器上下载或者上传文件。这个过程模拟了命令模式下的建立(Create)和获取(Retrieve)命令。

1.1 架构

整体构架与数据流如下:
JSP Client (creating BO) -> Simple SCA component -> Importing -> WebSphere FTP Adapter -> FTP Server (EIS)


图一:Outbound场景体系结构
图一:Outbound场景体系结构

上图中,JSP客户端、导入及SCA组件被打包成一个完整的WAS应用(.ear)。WebSphere FTP适配器被单独出来构成WebSphere资源适配器包。

这里, Web容器直接连到FTP服务器上,这是为了快速刷新文件列表。这样设计是为了简化该场景设计。

1.2 JSP Client

这个JSP页面中包括三个部分。第一部分是输入域,这部分用来接收用户的输入信息。它们是:FTP服务器IP,端口,用户名称,用户密码, 访问FTP服务器的目录。第二部分是上传区域,这部分包括了输入上传文件名称的文本框,一个浏览按钮和一个上传按钮。第三部分是从FTP服务器取得的一个文件列表和一个刷新按钮。点击第三部分包含的每一个文件都可以将它们从FTP服务器上下载下来,点击第三部分包含的每一个目录可以进入相应的FTP服务器目录。下图是这个页面的截图。


图二:Outbound场景JSP客户端
图二:Outbound场景JSP客户端

在使用客户端时,用户需要先在第一部分的输入域中输入信息。接着点击第三部分的刷新(Refresh File List)按钮,这会更新相应的文件列表。刷新文件列表功能直接由JSP提供而不是通过SCA组件和FTP适配器。最后,用户可以通过点击文件列表中的目录名称改变目录,也可以修改输入的目录名再点击刷新(Refresh File List)按钮来改变目录。

模拟获取命令,用户可以点击文件列表中的文件。此时会弹出一个对话框提示保存该文件。模拟创建命令,用户点击第二部分中的浏览按钮,指定一个想要上传的本地文件,点击上传(Upload)按钮。如果成功上传,摘要信息将会在页面上显示出来。如果失败,页面上将会显示出相应的错误信息。上传、下载过程都通过SCA组件和FTP适配器实现。

1.3 SCA组件

SCA组件包括wsdl接口和相关的SCA组件。因为该场景组关注的是JCA适配器,所以SCA组件只是简单的从JSP客户端传一个WBIRecord到FTP适配器。其中没有任何附加的业务逻辑。

1.4导入配置

这部分包括三个子组件:BG/BO,JCA适配器输入描述符和数据绑定的实现。

这个场景主要用于设计使用适配器基本类库(AFC)的命令模式,所以可使用的BG和BO必须继承自基本BO,即 DataGraph.xsd 和 BusinessGraph.xsd。这里,outbound场景中的BO、BG都位于到FTPOperation.xsd文件,这些BO都继承自上述两个BO。FTPOperation BO由JSP客户端创建,传给SCA组件,然后再被传到FTP适配器中。BO元素中所包含的信息如下:

  • FTP服务器IP
  • FTP服务器端口
  • FTP服务器路径
  • 本地路径
  • 用户名
  • 密码
  • 文件名

导入描述符需要手动创建,它描述了一个适配器是如何被导入到SCA组件中去的。这个.import文件包括适配器名称,连接工厂类,绑定方法和数据绑定类等信息。

在JSP客户端,我们创建了数据对象来打包信息。但是在WebSphere适配器端,只接受Record输入。所以,数据绑定类,即DataBindingImpl.class,必须可以进行WBIRecord和DataObject之间的数据类型转换。

1.5 FTP适配器

所有相关的outbound类和命令模式类都在FTP适配器中运行,这是整个outbound场景中最重要的功能模块。FTP适配器从SCA组件中获得WBIRecord类型的输入数据。接着,FTP适配器通过从WBIRecord中获取的信息,连接到FTP服务器上并作出相应的操作。

1.6 FTP服务器

任何FTP服务器都可在这个场景中使用。





回页首


2 构建Outbound场景

2.1创建FTP Adapter

1) 指定到WID的安装目录,在命令行中运行wid.exe -clean启动WID。

2)在菜单项中点击File -> New -> Project,会出现如下的面板,选择Adapter Toolkit -> J2C Resource Adapter Project,点击Next。


图三:WAT-新项目页
图三:WAT-新项目页

3) 在J2C Resource Adapter Project面板中输入如图所示的Name及EAR Project名称。


图四:WAT-项目页
图四:WAT-项目页

4) 在Adapter属性页中输入如下的Adapter Name, Package Name, Class Name Prefix。


图五:WAT-名称页
图五:WAT-名称页

5) 在Generation Options面板中,在Adapter Specification下拉列表中选择IBM WebSphere Resource Adapter,在Available components中将除了Generate Bidi Support classes的其它选项都选中。 完成后点击Finish,WebSphere资源适配器工程会被自动建立。


图六:WAT-组件页
图六:WAT-组件页

2.2实现FTP适配器Outbound类

1) 在上节中,当WebSphere资源适配器工程成功创建以后,在WID的主窗口会出现部署描述文件即ra.xml的编辑器。在WID主窗口的左侧会出现所有自动产生的Java类的列表。


图七:WAT-新项目生成
图七:WAT-新项目生成

2) 点击编辑器的Outbound Adapter标签页,为连接工厂添加一些属性。


图八:WAT-Outbound标签页
图八:WAT-Outbound标签页

默认的会有三个属性:"password", "userName" 和 "reauthSupported",选择某个属性并点击Edit按钮可以修改该属性的值。"userName" 和 "password"用于J2C的安全认证。可以将其设置为任意你所希望的字符串,例如可以都设置为jql,在随后部署的时候会用到这两个属性。

点击Add按钮添加某个属性会出项一个对话框,在对话框中,你可以输入添加的属性的名称,类型,值及其描述。输入完成点击Finish,新加的属性就会出现在属性列表中。

根据上面的方法继续添加下表列出的所有属性。

Property NameTypeValue
ipString9.181.27.26
portString21
remote_dirString/WAT_test
local_dirString  
remote_filenameString 
local_filenameString 

属性添加完成以后,你可以在ra.xml ,J2CA_ftpConnectionRequestInfo.java和 J2CA_ftpManagedConnectionFactory.java文件中找到相应属性的定义。

3)实现J2CA_ftpResourceAdapter.java 我们需要在这个类中为outbound场景实现几个方法,适配器基本类库(AFC)已经为我们实现了一些方法。


// This method returns the meta data of our resource adapter
public com.ibm.j2ca.base.WBIResourceAdapterMetadata getResourceAdapterMetadata()
			throws javax.resource.ResourceException {
return new WBIResourceAdapterMetadata("FTP RA","IBM","0.1",false);
}

4)实现J2CA_ftpManagedConncetion.java 和 J2CA_ftpManagedConnectionFactory.java 这两个类分别封装了EIS Connection和ConncetionFactory,除了系统自动生成的代码,下面是我们必须自己实现的一些方法。


J2CA_ftpManagedConncetion.java
private J2CA_ftpConnection ftpConnection;
private J2CA_ftpConnectionRequestInfo cri;
J2CA_ftpConnectionFactory factory;
// construct function which is called by ManagedConnectionFactory
public J2CA_ftpManagedConnection(
			com.ibm.j2ca.base.WBIManagedConnectionFactory mfc,
			javax.security.auth.Subject subject,
			com.ibm.j2ca.base.WBIConnectionRequestInfo cri)
			throws javax.resource.ResourceException {
	super(mfc, subject, cri);
	this.cri = (J2CA_ftpConnectionRequestInfo)cri;
	System.out.println("-> Enter J2CA_ftpManagedConnection construct, super finish");
	J2CA_ftpConnectionFactory factory = new J2CA_ftpConnectionFactory(null,mfc);
	System.out.println("### factory created");
}
// this function returns the outbound runtime properties
public J2CA_ftpConnectionRequestInfo getFTPConnectionRequestInfo()
{
return cri;
}
// This function returns the EIS Connection
public java.lang.Object getWBIConnection(
	javax.resource.spi.security.PasswordCredential arg0, boolean arg1)
		throws javax.resource.ResourceException {
	return new J2CA_ftpConnection(this);
}


J2CA_ftpManagedConncetionFactory.java
// Create ManagedConnection Instance, which is called by WBI runtime.
public javax.resource.spi.ManagedConnection createManagedConnection(
	javax.security.auth.Subject subject,
	javax.resource.spi.ConnectionRequestInfo connectionRequestInfo)
		throws javax.resource.ResourceException {
System.out.println("-> Enter createManagedConnection, Class: ManangedConnectionFactory");
		
this.setReauthSupported(false);
J2CA_ftpManagedConnection connection = new J2CA_ftpManagedConnection(
	 (WBIManagedConnectionFactory)this, subject, (WBIConnectionRequestInfo) connectionRequestInfo);
return connection;
}
// Create ConnectionFactory Instance, which is called by WBI runtime.
public java.lang.Object createConnectionFactory(
			javax.resource.spi.ConnectionManager connMgr)
			throws javax.resource.ResourceException {
System.out.println("-> Enter createConnectionFactory, Class: ManangedConnectionFactory 2");
return new J2CA_ftpConnectionFactory(connMgr,(WBIManagedConnectionFactory)this);
}

5)实现J2CA_ftpConnectionFactory.java 和 J2CA_ftpConnection.java 这两个Java类包括了一个真正的EIS连接的所有功能。J2CA_ftpConnection.java包括了一些如建立、断开连接,上传、下载文件等真实FTP的所具有的功能,为了简化代码我们直接调用IBM JDK 1.4.2中的API(sun.net.ftp)来实现。


J2CA_ftpConnectionFactory.java
J2CA_ftpManagedConnectionFactory mcf;
J2CA_ftpConnection cf;
// Construct function
public J2CA_ftpConnectionFactory(javax.resource.spi.ConnectionManager cm,
	com.ibm.j2ca.base.WBIManagedConnectionFactory mcf) {
		super(cm, mcf);
		this.mcf = (J2CA_ftpManagedConnectionFactory)mcf;
		System.out.println("### J2CA_ftpConnectionFactory construct");
}
// This method returns the real EIS connection which will be used during the whole outbound scenario.
public J2CA_ftpConnection getConnection(J2CA_ftpManagedConnection mc) throws ResourceException
{
    	System.out.println("getConnection() - EIS connection");
    	cf = new J2CA_ftpConnection((WBIManagedConnection)mc);
	return cf;
 }


J2CA_ftpConnection.java
FtpClient ftpConn= null; 
int ch; 
String hostname="";
String message="";
	
J2CA_ftpConnectionRequestInfo cri;
J2CA_ftpManagedConnection ftpMC;
J2CA_ftpManagedConnectionFactory mcf;
String ip = "";
String port = "";
String localdir = "";
String user = "";
String passwd = "";
// Construct function
public J2CA_ftpConnection(com.ibm.j2ca.base.WBIManagedConnection mc)
			throws javax.resource.ResourceException {
	super(mc);
	System.out.println("<- enter J2CA_ftpConnection Construct, finish super");
	ftpMC = (J2CA_ftpManagedConnection)mc;
	cri = ftpMC.getFTPConnectionRequestInfo();
	mcf = (J2CA_ftpManagedConnectionFactory)ftpMC.getManagedConnectionFactory();
	}
// Create Interaction to do the real job. Called by WBI runtime.
public javax.resource.cci.Interaction createInteraction()
			throws javax.resource.ResourceException {
		super.checkValidity();
		return new J2CA_ftpInteraction(this);
	}
// Connect to FTP Server
public FtpClient connect(String FileDir,String hostname,int port,String uid,String pwd) 
	{
	  this.hostname = hostname;
	  System.out.println("Connecting to "+hostname+".....");
	  try{ 
	   ftpConn= new FtpClient(hostname,port); 
	   ftpConn.login(uid,pwd); 
	   ftpConn.binary();
	   System.out.println("Connect to host: "+hostname+" success!"); 
	  } 
	  catch(Exception e){ 
	   System.out.println("login to: "+hostname+"failed! "+e);
	   ftpConn= null;
  } 
	  return ftpConn; 
	} 
       // Disconnect to FTP server
	public void stop(String FileDir) 
	{ 
	  try { 
	   if(ftpConn!=null){
		ftpConn.closeServer();
		ftpConn= null;
		System.out.println("Connection to "+hostname+" is close"); 
	   }
	  } 
	  catch(IOException e) 
	  { 
	   System.out.println("Stop: Connect to "+hostname+"failed!"+e); 
	  } 
	} 
       // download file from the connected FTP server
	public boolean downloadFile(String FileDir,String filepathname){ 
	  boolean result=true; 
	  if (ftpConn!= null) 
	  { 
	   System.out.println("downloading "+filepathname+"......"); 
	   String strdir = "";
	   try{ 
		File fi = new File(FileDir + filepathname);
		RandomAccessFile remoteFile = new RandomAccessFile(fi,"rw");
		remoteFile.seek(0);
		TelnetInputStream fget=ftpConn.get(strdir+filepathname);
		DataInputStream puts = new DataInputStream(fget);
		while ((ch = puts.read()) >= 0) {
		 remoteFile.write(ch);
		}
		fget.close();
		remoteFile.close();
		System.out.println("Download "+filepathname+" to "+ FileDir +" success!"); 
	   } 
	   catch(IOException e){ 
		System.out.println("Download "+filepathname+" to "+ FileDir +" failed! "+e); 
		result = false ; 
	   } 
	  } 
	  else{     result = false;   } 
	  return result; 
	} 
    // Upload file to the connected FTP server
	public boolean uploadFile(String FileDir,String filepathname){ 
	  boolean result=true; 
	  String message = "";
	  if (ftpConn!= null) 
	  { 
	   System.out.println("uploading "+FileDir+" "+filepathname+"...."); 
	   try{ 
		String filename = "";
		filename = filepathname;
		RandomAccessFile sendFile = new RandomAccessFile(FileDir+filepathname,"r"); 
		sendFile.seek(0); 
		TelnetOutputStream outs = ftpConn.put(filename); 
		DataOutputStream outputs = new DataOutputStream(outs); 
		while (sendFile.getFilePointer() < sendFile.length() ) 
		{ 
		 ch = sendFile.read(); 
		 outputs.write(ch); 
		} 
		outs.close(); 
		sendFile.close(); 
    
		System.out.println("upload "+filepathname+" success!"); 
	   } 
	   catch(IOException e){ 
		System.out.println("upload "+filepathname+"failed! "+e); 
		result = false ; 
	   } 
	  } 
	  else{    result = false;   } 
	  return result; 
	} 
         // get File List from FTP server
	public ArrayList getFileList() throws IOException {
	  BufferedReader dr = new BufferedReader(new InputStreamReader(ftpConn.list()));
	  ArrayList list = new ArrayList();
	  String s = "";
	  while ( (s = dr.readLine()) != null) {
	  list.add(s);
	  }
	  return list;
	}
          // set Current Directory of FTP server
	public void setPath(String path) throws IOException {
	  if (ftpConn== null)
	  ;  else {
	   ftpConn.cd(path);  }
	}
          // get and set interface of EIS connection
	public FtpClient getftpConn ()
 { return ftpConn;	}
	public void setftpConn (FtpClient client) 
{  ftpConn= client; }

6)实现J2CA_ftpInteraction.java
这个类在运行时会根据传过来的WBIRecord执行相应动作。我们必须首先根据Record生成BO,并根据BO的verb调用相应的方法。

这里我们使用Command Pattern API来选择执行相应动作的方法,因此在J2CA_ftpInteraction.java的构造函数中我们需要初始化CommandManager。


J2CA_ftpInteraction.java
	J2CA_ftpConnection ftpConnection;
J2CA_ftpManagedConnection mc;
Interpreter interpreter;
CommandManager commandMgr;
J2CA_ftpCommandFactoryImpl cfi;
LogUtils log;
// Construct Function
	public J2CA_ftpInteraction(com.ibm.j2ca.base.WBIConnection connection) {
		super(connection);	
		System.out.println("-> Enter create cdl FTP Interaction");
		ftpConnection = (J2CA_ftpConnection)connection;
		
		try {
mc = (J2CA_ftpManagedConnection)ftpConnection.getManagedConnection();
		} catch (ResourceException e) {
			e.printStackTrace(); 	}
		
		log = mc.getManagedConnectionFactory().getLogUtils();
		interpreter = new Interpreter(log);
		J2CA_ftpResourceAdapter ra =
		(J2CA_ftpResourceAdapter)mc.getManagedConnectionFactory().getResourceAdapter();
		J2CA_ftpCommandFactoryImpl comf = new J2CA_ftpCommandFactoryImpl();
		commandMgr = new CommandManager(comf, ftpConnection, log);
                      cfi = new J2CA_ftpCommandFactoryImpl();
		System.out.println("<- Out create cdl FTP Interaction");
	}
	// do the real job based on the passed-in Record
	public javax.resource.cci.Record execute(
			javax.resource.cci.InteractionSpec iSpec,
			javax.resource.cci.Record record)
			throws javax.resource.ResourceException {
	System.out.println(" >>> cdl FTP AdapterInteraction.execute() ");
	WBIInteractionSpec wbiSpec = (WBIInteractionSpec) iSpec;
	com.ibm.j2ca.extension.commandpattern.Command command = null;
	DataObject bo;
String verb;
	if(record==null){  System.out.println("record==null");  return null; }
	System.out.println("record name:"+((WBIRecord)record).getRecordName());
	System.out.println("BO:"+((WBIRecord)record).getDataObject());
System.out.println("string:"+((WBIRecord)record).getDataObject().getString("verb"));
	
         // extract verb and decide the operation
	verb = ((WBIRecord)record).getDataObject().getString("verb");
	wbiSpec.setFunctionName(verb);
	if(verb.equalsIgnoreCase("Retrieve"))
	{
	bo = ((WBIRecord)record).getDataObject().getDataObject("FTPOperation");
	command = cfi.createCommand("Retrieve",bo);
	command.setConnection(ftpConnection);
	} else {
	System.out.println(" Try to get the Command, function: "+verb);
	command = commandMgr.produceCommands((WBIRecord)record, wbiSpec.getFunctionName());
	System.out.println(" Got the Command, function: "+verb);
	}
         // Do the corresponding operation
	command.execute(((WBIRecord)record).getDataObject());
System.out.println(" <<< cdl FTP JDBCAdapterInteraction.execute() ");
	return record;
	}
	

7)实现 J2CA_ftpCommandFactoryImpl.java, J2CA_ftpCreateCommand.java 及 J2CA_ftpRetrieveCommand.java 这三个类是命令模式API的具体实现。CommandFactoryImpl有一个createCommand()的方法,这个方法根据BO的verb生成命令的实例。另外两个类是真正执行命令的类,它们都继承自BaseCommand。


2CA_ftpCommandFactoryImpl.java
// create Command Instance based on functionName
public com.ibm.j2ca.extension.commandpattern.Command createCommand(
		java.lang.String functionName, commonj.sdo.DataObject arg1)
			throws javax.resource.ResourceException {
	J2CA_ftpBaseCommand command = null;
	if(functionName.equals(WBIInteractionSpec.CREATE_OP)){
			command = new J2CA_ftpCreateCommand();
	}else if(functionName.equals(WBIInteractionSpec.DELETE_OP)){
			command = new J2CA_ftpDeleteCommand();
	}else if(functionName.equals(WBIInteractionSpec.UPDATE_OP)){
			command = new J2CA_ftpUpdateCommand();		
	}else if(functionName.equals(WBIInteractionSpec.RETRIEVE_OP)){
			command = new J2CA_ftpRetrieveCommand();
	}else	    command = new J2CA_ftpNoOperationCommand();
		System.out.println("functionName = "+functionName);
		return command;
	}


public DataObject execute(DataObject Object) throws ResourceException 
{
J2CA_ftpConnection connection = (J2CA_ftpConnection)this.getConnection();
J2CA_ftpManagedConnection mc = (J2CA_ftpManagedConnection)connection.getManagedConnection();
J2CA_ftpManagedConnectionFactory mcf = (J2CA_ftpManagedConnectionFactory)mc.getManagedConnectionFactory();
	   	  
String ip = mcf.getIp();
String port = mcf.getPort();
String dir = mcf.getRemote_dir();
String user = mcf.getUserName();
String passwd = mcf.getPassword();
String filename = mcf.getRemote_filename();
String localdir = mcf.getLocal_dir();
	   	
DataObject inputObject = Object.getDataObject("FTPOperation");
System.out.println("Got FTPOperationBO");
if(inputObject.getString("ip")!=null && inputObject.getString("ip").length()!=0)
 ip = inputObject.getString("ip");
if(inputObject.getString("port")!=null && inputObject.getString("port").length()!=0) port =
inputObject.getString("port");
if(inputObject.getString("dir")!=null && inputObject.getString("dir").length()!=0) 
dir = inputObject.getString("dir");
if(inputObject.getString("user")!=null && inputObject.getString("user").length()!=0) user =
inputObject.getString("user");
if(inputObject.getString("passwd")!=null&&inputObject.getString("passwd").length()!=0)  
passwd = inputObject.getString("passwd");
if(inputObject.getString("filename")!=null&&inputObject.getString("filename").length()!=0) 
filename = inputObject.getString("filename");
if(inputObject.getString("localdir")!=null&&inputObject.getString("localdir").length()!=0)   
localdir = inputObject.getString("localdir");
	   		
try {  	// try 3 times
	for(int i=0;i<3;i++)
	{
	connection.connect(localdir,ip,Integer.parseInt(port),user,passwd);
	 if(connection.getftpConn ()!=null) break;
	try {Thread.sleep(3000);} catch (InterruptedException e1) {}
	}
// set the directory of FTP server
	connection.setPath(dir);
	System.out.println("dir="+dir);
If(!localdir.endsWith("/") && !localdir.endsWith("\\")) localdir = localdir + "/";
          // Upload file operation
	connection.uploadFile(localdir,filename);
	System.out.println("Success Upload file"+filename);
	connection.stop(null);
} catch (IOException e) {
	System.out.println("Error Upload file"+filename);
	e.printStackTrace();
}
// delete the temp file after uploading
File file = new File(localdir +filename);
if(file.exists()) file.delete();
return null;
}


public DataObject execute(DataObject Object) throws ResourceException
{
J2CA_ftpConnection connection = (J2CA_ftpConnection)this.getConnection();
J2CA_ftpManagedConnection mc = (J2CA_ftpManagedConnection)connection.getManagedConnection();
J2CA_ftpManagedConnectionFactory mcf =
(J2CA_ftpManagedConnectionFactory)mc.getManagedConnectionFactory();
	   	  
String ip = mcf.getIp();
String port = mcf.getPort();
String dir = mcf.getRemote_dir();
String user = mcf.getUserName();
String passwd = mcf.getPassword();
String filename = mcf.getRemote_filename();
String localdir = mcf.getLocal_dir();
	   	  
DataObject inputObject = Object.getDataObject("FTPOperation");
System.out.println("Got FTPOperationBO");
if(inputObject.getString("ip")!=null && inputObject.getString("ip").length()!=0) ip =
inputObject.getString("ip");
if(inputObject.getString("port")!=null && inputObject.getString("port").length()!=0) port =
inputObject.getString("port");
if(inputObject.getString("dir")!=null && inputObject.getString("dir").length()!=0) 
dir = inputObject.getString("dir");
if(inputObject.getString("user")!=null && inputObject.getString("user").length()!=0) 
user = inputObject.getString("user");
if(inputObject.getString("passwd")!=null&&inputObject.getString("passwd").length()!=0)   
passwd = inputObject.getString("passwd");
if(inputObject.getString("filename")!=null&&inputObject.getString("filename").length()!=0)  
filename = inputObject.getString("filename");
if(inputObject.getString("localdir")!=null&&inputObject.getString("localdir").length()!=0)   
localdir = inputObject.getString("localdir");
	   		
try {
	// try 3 times
	for(int i=0;i<3;i++)
	{
	connection.connect(localdir,ip,Integer.parseInt(port),user,passwd);
	 if(connection.getftpConn ()!=null) break;
	try {Thread.sleep(3000);} catch (InterruptedException e1) {}
	 }
           // set directory of FTP server
	connection.setPath(dir);
	System.out.println("dir="+dir);
If(!localdir.endsWith("/") && !localdir.endsWith("\\")) localdir = localdir + "/";
          // download file from FTP server
	connection.downloadFile(localdir,filename);
	System.out.println("Success Down file"+filename);
	connection.stop(null);
} catch (IOException e) {
	System.out.println("Error Upload file"+filename);
	e.printStackTrace();
}
return null;
}

到此为止,outbound场景的所有FTP适配器代码都开发完毕。

2.3创建Outbound场景的应用程序

这篇文档主要关注的是如何开发JCA Adapter,因此关于如何在WID中建立WebSphere Business Integration (WBI) Project这里并不详细讨论。如果感兴趣可以阅读WebSphere Process Server - First Step的相关文档,里面详细介绍了如何在WID中创建基于WebSphere Process Server的应用程序。

下面列出了所有重要的文件,并给出了简要的描述。这样我们可以对每个文件的作用有个基本的了解,并且在必要的时候修改这些文件。所有这些列出的文件都可以在附件cdlFTP.jar中找到。

1) cdlFTP.jar /sca.module

这是应用程序的描述文件,通常我们不需要修改它。


<?xml version="1.0" encoding="UTF-8"?>
<scdl:module name="cdlFTP"
    xmlns:scdl="http://www.ibm.com/xmlns/prod/websphere/scdl/6.0.0">
</scdl:module>

2) cdlFTP.jar /sca.references

这是SCA组件的stand-alone方式的引用文件,SCA组件与适配器的import相连。JSP客户端可以直接调用此项服务来使用导入的适配器的outbound功能。


<?xml version="1.0" encoding="UTF-8"?>
<scdl:references xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:scdl="http://www.ibm.com/xmlns/prod/websphere/scdl/6.0.0"
	xmlns:wsdl="http://www.ibm.com/xmlns/prod/websphere/scdl/wsdl/6.0.0"
	xmlns:java="http://www.ibm.com/xmlns/prod/websphere/scdl/java/6.0.0"
	xmlns:ftpOperation="http://cdlFTP.Operation"
	xsi:schemaLocation="http://cdlFTP.Operation cdlFTP/FTPOperation.wsdl">
	<reference name="FTP">
		<interface xsi:type="wsdl:WSDLPortType" portType="ftpOperation:FTPOperation"/>
		<wire  target="cdlFTP/cdlFTP"/>
	</reference>
</scdl:references>

3) cdlFTP.jar /cdlFTP/FTPOperation.wsdl

这个文件定义了应用和导入服务的接口。


<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="FTPOperation"
	targetNamespace="http://cdlFTP.Operation"
	xmlns:tns="http://cdlFTP.Operation"
	xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:Operation="http://cdlFTP.Operation"
	xmlns:data="http://cdlFTP.Operation.data">
	<wsdl:types>
		<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
			<xsd:import namespace="http://cdlFTP.Operation.data"
				schemaLocation="FTPOperation.xsd">
			</xsd:import>
		</xsd:schema>
	</wsdl:types>
	<wsdl:message name="OperationRequest">
		<wsdl:part name="request" element="data:Operation"></wsdl:part>
	</wsdl:message>
	<wsdl:message name="OperationResponse">
		<wsdl:part name="response" element="data:Operation"></wsdl:part>
	</wsdl:message>
	
	<wsdl:portType name="FTPOperation">
		<wsdl:operation name="OperationFTP">
			<wsdl:input message="tns:OperationRequest"></wsdl:input>
			<wsdl:output message="tns:OperationResponse"></wsdl:output>
		</wsdl:operation>
	</wsdl:portType>
</wsdl:definitions>

4) cdlFTP.jar  /cdlFTP/FTPOperation.xsd
               /cdlFTP/ASITypes.xsd
               /cdlFTP/BaseAdapterMetadata.xsd
               /cdlFTP/BusinessGragh.xsd
               /cdlFTP/DataGraph.xsd

FTPOperation.xsd文件定义了这个outbound场景所用的BO和BG。另外四个xsd文件分别定义了这些BO和BG的基本元素,我们自己定义的BO和BG必须继承自这些基本元素才能使用command pattern API。

FTPOperation.xsd文件包含了BO和BG,它定义了所有如FTP服务器的IP,端口等重要元素。这些元素用于FTP适配器从JSP客户端获取相应的信息。

由于这个文件比较长,在此并没有将文件的内容都列出来,你可以在cdlFTP.jar中找到这些xsd文件。

5) cdlFTP.jar /cdlFTP/cdlFTP.import

这个.import文件定义了adapter如何与SCA组件建立连接。这里你必须指定一些信息,如:

  • Service interface,
  • Binding method,
  • Binding type,
  • Resource Adapter Name,
  • ManagedConnectionFactory Class,
  • InteractionSpec Class,
  • Data Binding Class.

<?xml version="1.0" encoding="UTF-8"?>
<scdl:import name="cdlFTP/cdlFTP"
	xmlns:scdl="http://www.ibm.com/xmlns/prod/websphere/scdl/6.0.0"
	xmlns:policy="http://www.ibm.com/xmlns/prod/websphere/scdl/policy/plugin/6.0.0"
	xmlns:java="http://www.ibm.com/xmlns/prod/websphere/scdl/java/6.0.0"
	xmlns:wsdl="http://www.ibm.com/xmlns/prod/websphere/scdl/wsdl/6.0.0"
	xmlns:eis="http://www.ibm.com/xmlns/prod/websphere/scdl/eis/6.0.0"
	xmlns:ftpOperation="http://cdlFTP.Operation"
	xsi:schemaLocation="http://cdlFTP.Operation FTPOperation.wsdl">
	<interfaces>
		<interface xsi:type="wsdl:WSDLPortType" portType="ftpOperation:FTPOperation"/>
	</interfaces>
	
	<esbBinding xsi:type="eis:EISImportBinding">
		<resourceAdapter name="IBM cdl FTP Adapter"/>
		<connection type="cdlFTP.outbound.J2CA_ftpManagedConnectionFactory" shared="Unshareable"> 
		</connection>
		<methodBinding method="OperationFTP" interactionType="cdlFTP.outbound.J2CA_ftpInteractionSpec"
			inDataBindingType="cdlFTP.DataBindingImpl"
			outDataBindingType="cdlFTP.DataBindingImpl">
		</methodBinding>
	</esbBinding>
</scdl:import>

6) cdlFTP.jar /cdlFTP/DataBindingImpl.java

JSP客户端提供的是DataObject,而WebSphere资源适配器只能接收WBIRecord对象,因此我们需要DataBindingImpl这个数据绑定类对这两种对象进行转换。DataBindingImpl类必须实现RecordHolderDataBinding接口,有如下一些方法。


	public Record getRecord() {
	System.out.println("DataBingingImpl: getRecord");
	if(record==null) System.out.println("DataBingingImpl: record==null");
	return (Record)record;
	}
public void setRecord(Record aRecord) {
	System.out.println("DataBingingImpl: getRecord");
	if(record==null) System.out.println("DataBingingImpl: record==null");
	if(aRecord==null) System.out.println("DataBingingImpl: aRecord==null");
           record = (WBIRecord)aRecord;
           data = record.getDataObject();
	}
public DataObject getDataObject() {
	System.out.println("DataBingingImpl: getDataObject");
	if(record==null) System.out.println("DataBingingImpl: record==null");
	return data;
	}
public void setDataObject(DataObject dataObject) {
	this.data = dataObject;
	record.setDataObject(data);
	System.out.println("DataBingingImpl: setDataObject");
	if(dataObject==null) System.out.println("DataBingingImpl: setDataObject==null");
	}
	

7) cdlFTP.jar  /j2ee/ftpClient.jsp
               /j2ee/ftpDownload.jsp

这两个JSP文件是运行outbound场景时用户进行相关操作的用户界面。这两个文件的细节在这里不予讨论,你可以直接使用cdlFTP.jar中所带的两个文件。

下面仅列出一些重要代码,显示如何在JSP中调用SCA服务。


ServiceManager serviceManager = ServiceManager.INSTANCE; 
Service service = (Service)serviceManager.locateService("FTP");
DataObject ftpBG = DataFactory.INSTANCE.create("http://cdlFTP.Operation.data", "FTPOperationBG");
ftpBG.setString("verb", "Create");
DataObject ftpBO = DataFactory.INSTANCE.create("http://cdlFTP.Operation.data", "FTPOperation");
String filename = file.getFileName();
ftpBO.setString("ip",ipv);
ftpBO.setString("port",portv);
ftpBO.setString("dir",dirv);
ftpBO.setString("user",userv);
ftpBO.setString("passwd",passwdv);
ftpBO.setString("filename",filename);
ftpBO.setString("localdir",localdir);
ftpBG.setDataObject("FTPOperation",ftpBO);
service.invoke("OperationFTP", ftpBG);
System.out.println("FTP Operation Success");

2.4在WebSphere应用服务器中部署WebSphere资源适配器

开发完成以后,你必须先将resource adapter部署到WebSphere应用服务器(WAS)中。

1) 如果你是自己在WID中开发的source adapter,右键点击FTP Adapter工程在弹出菜单中选择export。在弹出的窗口中选择RAR file,然后输入你要导出的rar文件的文件名,例如J2CA_ftp.rar。

2) 用ZIP工具打开导出的rar文件,并手工将CWYBS_AdapterFoundation.jar(Adapter Foundation Classes)文件加到这个rar文件的根目录下。或者,将CWYBS_AdapterFoundation.jar(Adapter Foundation Classes)文件拷贝到<WID root Dir>\runtimes\bi_v6\lib\目录下。

3) 启动WAS。启动过程参见安装验证章节。

4) 打开WAS控制台并登录。WAS控制台参见安装验证章节。

1) 在WAS控制台的左侧树型结构选择Security -> Global Security,然后在右侧的面板中选择Authentication->JAAS Configuration->J2C Authentication Data。


图九:WAS控制台-J2C认证
图九:WAS控制台-J2C认证

6) 在表格的上方点击New。


图十:WAS控制台-J2C认证列表
图十:WAS控制台-J2C认证列表

7)在Alias栏中输入cdlFTP,在user ID和Password中输入在开发adapter时定义好的用户名和密码。


图十一:WAS控制台-建立新的J2C认证
图十一:WAS控制台-建立新的J2C认证

8) 每次在WAS管理控制台更改某个配置后都会在网页的上面部分出现一个如图所示的框,你必须点击Save才能保存所进行的更改。

注意:在随后的步骤中,任何对配置进行的更改都必须点击Save才能保存。


图十二:WAS控制台-保存配置
图十二:WAS控制台-保存配置

9) 在WAS控制台左边的树型结构选择Resources -> Resource Adapters ,然后点击右边的Install RAR。


图十三:WAS控制台-安装资源适配器
图十三:WAS控制台-安装资源适配器

10) 指定待安装的rar文件的路径,点击Next。在General Properties栏中,Name项输入IBM cdl FTP Adapter,点击OK。在随后的步骤中都接受默认的设置。最后不要忘记保存所进行的设置。


图十四:WAS控制台-资源适配器属性
图十四:WAS控制台-资源适配器属性

11) 导航到Resource adapters->IBM cdl FTP Adapter->J2C connection factories,点击new。


图十五:WAS控制台-资源适配器连接工厂
图十五:WAS控制台-资源适配器连接工厂

12) 在Name项中输入cdlFTP,在JNDI项中输入cdlFTPJNDI。


图十六:WAS控制台-新建资源适配器连接工厂
图十六:WAS控制台-新建资源适配器连接工厂

13) 导航到Resource adapters->IBM cdl FTP Adapter->J2C connection factories->cdlFTP->custom properties,这里你可以点击属性名称然后更改它的默认值。local_dir和local_filename现在还没用到。


图十七:WAS控制台-资源适配器连接工厂属性
图十七:WAS控制台-资源适配器连接工厂属性

到此WebSphere资源适配器就部署成功了。最后记得保存所做的更改。

2.5 在WebSphere应用服务器中部署Outbound应用

开始之前,你先必须确定FTP Adapter被成功部署在WAS中。

1) 先确认在上面提到的所有文件都被正确加到cdlFTP.jar文件中。

2) 你需要根据你的设置更新cdlTP.jar中的ftpClient.jsp文件的第14行:


<% String localdir = "D:/JCA_ftp_Adapter/upload/"; %>

这个目录用于保存上传的临时文件,将它设置为某个你机器上存在的目录。

3) 打开一个命令行窗口,并定位到cdlFTP.jar 和J2CA_ftp.rar所在的目录。输入如下命令:


<WID root Dir>\runtimes\bi_v6\bin\serviceDeploy cdlFTP.jar -classpath J2CA_ftp.rar -keep

如果你在当前目录中看到cdlFTPApp.ear文件,则这一步操作成功。否则你需要根据错误输出信息修改cdlFTP.jar中的源文件。

4) 启动WAS和WAS管理控制台。请确认FTP适配器已经部署到WAS上。在WAS管理控制台的左侧树型结构,选择"Applications->Enterprise Applications",然后点击右边面板的Install按钮。


图十八:WAS控制台-企业应用程序
图十八:WAS控制台-企业应用程序

5) 点击"浏览"指定cdlFTPApp.ear文件所在的目录,点击next。在随后的配置过程中都使用默认值。


图十九:WAS控制台-新建企业应用程序
图十九:WAS控制台-新建企业应用程序

6) 如果看到如下的信息说明你已经部署成功。点击Save to Master Configuration保存所有的修改。如果部署失败,请根据log及trace文件的错误信息修改你的源文件。


图二十:WAS控制台-保存企业应用程序
图二十:WAS控制台-保存企业应用程序

7) 导航至"Enterprise Applications->cdlFTPApp"。点击WAS管理控制台右侧的"Map resource references to resources"链接。


图二十一:WAS控制台-映射资源适配器
图二十一:WAS控制台-映射资源适配器

8) 在出现的面板中,你需要设置WebSphere资源适配器的JNDI名称及认证所调用的方法。


图二十二:WAS控制台-企业应用程序配置
图二十二:WAS控制台-企业应用程序配置

(8-A) 首先置cdlFTPEJB左边的复选框为选中状态,然后选择cdlFTPJNDI为JNDI名称,点击Apply。

(8-B) 首先置cdlFTPEJB左边的复选框为选中状态,然后选择Use default method - widNode/cdlFTP为认证的方法,点击Apply。

点击ok并保存所做的修改。

9) 在你的WAS主机中,定位到如下的目录:


<WID root Dir>\prof\installedApps\widCell\cdlFTPApp.ear\cdlFTPWeb.war\

(9-A) 你需要手工将ftpClient.jsp 和 ftpDownload.jsp文件复制到当前的目录。

(9-B) 在当前新建一个名为download的文件夹。此文件夹用于保存下载的文件的临时文件。

(9-C) 将开源库Upload Bean Lib的库文件复制到以下目录中:


<WID_root_Dir>\prof\installedApps\widCell\cdlFTPApp.ear\cdlFTPWeb.war\lib\WEB-INF\

这是程序运行必需的一个开源库,您需要从以下地址下载:
http://www.javazoom.net/jzservlets/uploadbean/uploadbean.html
以下四个包是必需的:
fileupload.jar/struts.jar/uploadbean.jar/cos.jar

10) 导航至 "Enterprise Applications"。将cdlFTPApp置为选中状态,然后点击start。


图二十三:WAS控制台-启动企业应用程序
图二十三:WAS控制台-启动企业应用程序

11) 如果cdlFTPApp后面的status变成一个绿色的箭头,说明应用启动成功。


图二十四:WAS控制台-企业应用程序启动成功
图二十四:WAS控制台-企业应用程序启动成功

2.6运行Outbound场景

开始之前,你必须确认FTP Adapter及outbound应用被成功部署到WAS中。

1) 打开WEB浏览器,输入如下地址:
http://localhost:9080/cdlFTPWeb/ftpClient.jsp
回车后会出现如下网页。


图二十五:Outbound场景的JSP客户端
图二十五:Outbound场景的JSP客户端

2) 你可以点击Refresh File List按钮来刷新FTP服务器上的文件列表。如果你点击了列表中的某个文件夹,如[Inbound],则将进入相应的目录。


图二十六:JSP客户端-进入下一级目录
图二十六:JSP客户端-进入下一级目录

3)点击某个文件的名称会弹出一个提示你是否保存该文件的对话框。如果选择Save,这个文件会从FTP服务器下载到你本地的机器上。


图二十七:JSP客户端-下载文件
图二十七:JSP客户端-下载文件

4) 点击Browse…指定要上传的文件,如果上传的文件比较大(大于1Mb)需要比较长的上传时间。


图二十八:JSP客户端-上传文件
图二十八:JSP客户端-上传文件

5) 文件成功上传以后,你可以在页面下面的列表中找到它。同时在页面的上面部分会出现上传的文件的一些基本信息。


图二十九:JSP客户端-上传文件成功
图二十九:JSP客户端-上传文件成功

祝贺你已经成功运行了outbound场景。 注意:如果使用的是WAS集成测试环境,你可以在<WID Directory>\prof\logs\server1\目录中找到WAS的log和trace文件。在运行outbound场景时,你可以通过这些文件看到系统输出的信息。



关于作者

金千里目前在IBM中国软件开发中心任软件工程师,主要工作是WebSphere商业整合中资源适配器的开发。他毕业于中国科学院,对数据整合和数据挖掘技术有浓厚的兴趣。




对本文的评价

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

建议?







回页首


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