本文主要是介绍jCO--http://www.cnblogs.com/zfswff/p/5671148.html,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
SAP接口编程 之 JCo3.0系列(01):JCoDestination
JCo3.0是Java语言与ABAP语言双向通讯的中间件。与之前1.0/2.0相比,是重新设计的产品。API和架构设计与NCo3.0比较类似,前面也说过,NCo3.0的设计参考了JCo3.0。从本篇开始,系统介绍JCo3.0编程的技术要点。
JCo3.0安装
从https://service.sap.com/connectors 可以下载JCo3.0,注意下载的时候根据操作系统和JVM版本(32位还是64)选择不同的版本。安装就是解压,将文件解压到目标文件夹。以Windows系统为例,主要的文件包括:
sapjco3.dll
sapjco3.jar
SAP强烈推荐将这两个文件放在同一文件夹下。测试安装是否成功,可以在命令窗口下,进入安装文件夹,运行下面的命令:
java -jar sapjco3.jar
如果安装成功,应该显示如下界面:
JCoDestination
JCoDestination代表后台SAP系统,程序员不用关心与SAP的连接,jco3.0运行时环境负责管理连接和释放连接。我们先以一个简单的例子看看jco3.0 JCoDestination类的一些要点。
我使用的编程环境是Eclipse,环境准备如下:
- 新建一个Java项目,项目名为JCo3Demo。
- 将sapjco3.jar加入到项目的build path中。注意前面所说的sapjco3.jar和sapjco3.dll要放在同一个文件夹下。
- 在Eclipse Java项目文件夹下,新建一个文本文件,文件名命名为ECC.jocdestination, 文件的内容如下(SAP系统的连接参数的设置):
#SAP Logon parameters!
#Tue Dec 08 16:41:30 CST 2015
jco.client.lang=EN
jco.client.client=001
jco.client.passwd=xxxxxx
jco.client.user=STONE
jco.client.sysnr=00
jco.client.ashost=192.168.65.100
对照SAP GUI,不难理解:
环境准备好了,先来一段最简单的代码:
package jco3.demo1;import java.util.Properties;
import org.junit.Test;
import com.sap.conn.jco.JCoDestination; import com.sap.conn.jco.JCoDestinationManager; import com.sap.conn.jco.JCoException; public class JCoDestinationDemo { public JCoDestination getDestination() throws JCoException { /** * Get instance of JCoDestination from file: ECC.jcodestination * which should be located in the installation folder of project */ JCoDestination dest = JCoDestinationManager.getDestination("ECC"); return dest; } @Test public void pingDestination() throws JCoException { JCoDestination dest = this.getDestination(); dest.ping(); } }
代码说明:
-
getDestination()
方法中,JCoDestinationManager.getDestination("ECC")
从ECC.jcodestination文件中获取连接参数,创建JCoDestination
对象的实例。
这里有一个重要的约定,JCoDestinationManager.getDestination("ECC")
方法,会从Eclipse Java项目的根目录,查找ECC.jcodestination文件(文件路径和扩展名不能改变)是否存在,如果存在,从文件的内容中获取连接参数。这是DestinationDataProvider
接口的一个默认实现,在开发和测试的时候还是很方便的,但如果在真实项目中使用,安全性和灵活性就不够。后面会介绍解决方法。 -
pingDestination()
方法调用JcoDestination
对象的ping()方法测试SAP系统的连接。 -
@Test: 使用junit进行测试
配置文件的生成
刚才我们手工编辑了ECC.jcodestination文件,对于这个配置文件,因为很多连接参数来自于DestinationDataProvider
接口,如果想通过代码来创建配置文件,可以使用如下代码:
package jco3.demo2;import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.IOException; import java.util.Properties; import org.junit.Test; import com.sap.conn.jco.ext.DestinationDataProvider; public class DestinationFile { private Properties setProperties() { // logon parameters and other properties Properties connProps = new Properties(); connProps.setProperty(DestinationDataProvider.JCO_ASHOST, "192.168.65.100"); connProps.setProperty(DestinationDataProvider.JCO_SYSNR, "00"); connProps.setProperty(DestinationDataProvider.JCO_USER, "STONE"); connProps.setProperty(DestinationDataProvider.JCO_PASSWD, "xxxxxx"); connProps.setProperty(DestinationDataProvider.JCO_CLIENT, "001"); connProps.setProperty(DestinationDataProvider.JCO_LANG, "EN"); return connProps; } private void doCreateFile(String fName, String suffix, Properties props) throws IOException { /** * Write contents of properties into a text file * which was named [fName+suffix.jcodestination] */ File cfg = new File(fName+"."+suffix); if (!cfg.exists()){ // file not exists // Create file output stream, not using append mode FileOutputStream fOutputStream = new FileOutputStream(cfg, false); // store the properties in file output stream // and also add comments props.store(fOutputStream, "SAP logon parameters:"); fOutputStream.close(); }else{ throw new RuntimeException("File alreay exists."); } } @Test public void createConfigFile() throws IOException { Properties props = this.setProperties(); String fileName = "SAP_AS"; // sap application server // jcodestination suffix is required by JCoDestinationManager this.doCreateFile(fileName, "jcodestination", props); } }
代码说明:
setProperties()
方法属性参照DestinationDataProvider
类的常量设置Properties的实例。doCreateFile()
方法根据需求的文件名,扩展名在Eclipse项目的根文件夹下,创建一个文本文件,文件的内容就是Properties实例的内容。createConfigFile()
方法,调用上面的两个方法,创建配置文件。
更改配置文件名的路径和扩展名
我们看到,默认情况下,SAP对配置文件的路径和扩展名都不能改变,如果我们想把文件放在任意位置,扩展名也使用其他的扩展名,有没有办法?答案是有,方法是实现DestinationDataProvider
接口,并改写(override)getDestinationProperties()
方法,然后通过Environment.registerDestinationDataProvider()
方法进行注册。
OK, 一起来看看代码,代码分为三个部分:
- 第一部分: 创建
FileDestinationDataProviderImp
类,实现DestinationDataProvider
接口- 第二部分: 创建
FileDestinationDataProvider
类,注册FileDestinationDataProviderImp
的实例,并提供getDestination()
方法供调用- 第三部分:调用FileDestinationDataProvider类的getDestination()方法
第一部分:DestinationDataProvider
接口的实现:
package jco3.demo2;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; import java.util.Properties; import com.sap.conn.jco.ext.DestinationDataEventListener; import com.sap.conn.jco.ext.DestinationDataProvider; public class FileDestinationDataProviderImp implements DestinationDataProvider { private File dir; private String destName; // destination name private String suffix; public void setDestinationFile(File dir, String destName, String suffix) { this.dir = dir; this.destName = destName; this.suffix = suffix; } private Properties loadProperties(File dir, String destName, String suffix) throws IOException { Properties props = null; // create a file with name: fullName in destDirectory File destFile = new File(dir, destName+"."+suffix); if (destFile.exists()){ FileInputStream fInputStream = new FileInputStream(destFile); props = new Properties(); props.load(fInputStream); fInputStream.close(); }else{ throw new RuntimeException("Destination file does not exist."); } return props; } @Override public Properties getDestinationProperties(String destName) { Properties props = null; try { props = this.loadProperties(this.dir, this.destName, this.suffix); } catch (IOException e) { e.printStackTrace(); } return props; } @Override public void setDestinationDataEventListener(DestinationDataEventListener listener) { throw new UnsupportedOperationException(); } @Override public boolean supportsEvents() { return false; } }
第二部分: 创建FileDestinationDataProvider
类,注册FileDestinationDataProviderImp
的实例,并且提供getDestination()
方法。
package jco3.demo2;import java.io.File;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException; import com.sap.conn.jco.ext.Environment; public class FileDestinationDataProvider { public static JCoDestination getDestination() throws JCoException { File directory = new File("."); // current directory; String fileName = "SAP_AS"; String suffix = "txt"; FileDestinationDataProviderImp destDataProvider = new FileDestinationDataProviderImp(); destDataProvider.setDestinationFile(directory, fileName, suffix); Environment.registerDestinationDataProvider(destDataProvider); JCoDestination dest = JCoDestinationManager.getDestination(fileName); return dest; } }
我们看到,getDestination
方法中,文件的路径,文件的扩展名,都是我们自己定义的。文件名作为JCoDestinationManager.getDestination
方法的destination name。从这里也可可以看到,JCoDestinationManager.getDestination
方法从哪里查找连接参数,是依赖于Environment注册的DestinationDataProvider实现。
第三部分:测试代码FileDestinationDataProvider
的getDestination
方法:
package jco3.demo2;import org.junit.Test;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoException; public class TestFileDestinationProvider { @Test public void pingSAPDestination() throws JCoException { JCoDestination dest = FileDestinationDataProvider.getDestination(); dest.ping(); } }
DestinationDataProvider另一种实现
记得nco3.0可以将登陆参数写在代码中吗,如果我们也想将连接参数直接写在代码中,怎么做呢?刚才说过了,关键就是实现DestinationDataProvider接口,并改写getDestinationProperties()方法。不多说,上代码。
第一部分:DestinationDataProvider接口实现
package jco3.demo3;import java.util.HashMap;
import java.util.Map;
import java.util.Properties; import com.sap.conn.jco.ext.DestinationDataEventListener; import com.sap.conn.jco.ext.DestinationDataProvider; public class DestinationDataProviderImp implements DestinationDataProvider { /** * DestinationDataProvider is an interface * We define DestinationDataProviderImp class to implements this interface * so that we can define the logon parameters more flexibly * not just in xxx.jcodestionation file. * * The key point is that we override getDestinationProperties() method * Afterwards, instance of DestinationDataProvider should be registered * using Environment.registerDestinationDataProvider() method to take effect */ @SuppressWarnings("rawtypes") private Map provider = new HashMap(); @SuppressWarnings("unchecked") public void addDestinationProperties(String destName, Properties props) { provider.put(destName, props); } @Override public Properties getDestinationProperties(String destName) { if (destName == null){ throw new NullPointerException("Destinantion name is empty."); } if (provider.size() == 0){ throw new IllegalStateException("Data provider is empty."); } return (Properties) provider.get(destName); } @Override public void setDestinationDataEventListener(DestinationDataEventListener listener) { throw new UnsupportedOperationException(); } @Override public boolean supportsEvents() { return false; } }
第二部分:创建DestinationProivder
类,提供getDestination()
方法,注册DestinationDataProviderImp
类的实例:
package jco3.demo3;import java.util.Properties;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager; import com.sap.conn.jco.JCoException; import com.sap.conn.jco.ext.DestinationDataProvider; import com.sap.conn.jco.ext.Environment; public class DestinationProvider { private static Properties setProperties() { // logon parameters and other properties Properties connProps = new Properties(); connProps.setProperty(DestinationDataProvider.JCO_ASHOST, "192.168.65.100"); connProps.setProperty(DestinationDataProvider.JCO_SYSNR, "00"); connProps.setProperty(DestinationDataProvider.JCO_USER, "STONE"); connProps.setProperty(DestinationDataProvider.JCO_PASSWD, "xxxxxx"); connProps.setProperty(DestinationDataProvider.JCO_CLIENT, "001"); connProps.setProperty(DestinationDataProvider.JCO_LANG, "EN"); return connProps; } public static JCoDestination getDestination() throws JCoException { String destName = "SAP_AS"; Properties props = setProperties(); DestinationDataProviderImp destDataProvider = new DestinationDataProviderImp(); destDataProvider.addDestinationProperties(destName, props); Environment.registerDestinationDataProvider(destDataProvider); JCoDestination dest = JCoDestinationManager.getDestination(destName); return dest; } }
第三部分:测试DestinationProvider
的getDestination()
方法:
package jco3.demo3;import org.junit.Test;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoException; public class TestDestionProvider { @Test public void pingSAPDestination() throws JCoException { JCoDestination dest = DestinationProvider.getDestination(); dest.ping(); } }
JCo3.0调用SAP函数的过程
大致可以总结为以下步骤:
- 连接至SAP系统
- 创建JcoFunction接口的实例(这个实例代表SAP系统中相关函数)
- 设置importing参数
- 调用函数
- 从exporting参数或者table参数获取数据
代码:
package jco3.demo4;import org.junit.Test;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager; import com.sap.conn.jco.JCoException; import com.sap.conn.jco.JCoField; import com.sap.conn.jco.JCoFunction; import com.sap.conn.jco.JCoRepository; import com.sap.conn.jco.JCoStructure; public class RFC { public void getCompanyCodeDetail(String cocd) throws JCoException { // JCoDestination instance represents the backend SAP system JCoDestination dest = JCoDestinationManager.getDestination("ECC"); // JCoFunction instance is the FM in SAP we will use JCoRepository repository = dest.getRepository(); JCoFunction fm = repository.getFunction("BAPI_COMPANYCODE_GETDETAIL"); if (fm == null){ throw new RuntimeException("Function does not exists in SAP system."); } // set import parameter(s) fm.getImportParameterList().setValue("COMPANYCODEID", cocd); // call function fm.execute(dest); // get company code detail from exporting parameter 'COMPANYCODE_DETAIL' JCoStructure cocdDetail = fm.getExportParameterList() .getStructure("COMPANYCODE_DETAIL"); this.printStructure(cocdDetail); } private void printStructure(JCoStructure jcoStru) { for(JCoField field : jcoStru){ System.out.println(String.format("%s\\t%s", field.getName(), field.getString())); } } @Test public void test() throws JCoException { this.getCompanyCodeDetail("Z900"); } }
JCoFunction接口说明
JCoFunction
是一个接口,代表SAP系统的函数
JCoFunction
包含importing参数,exporting参数,changing参数,table参数。分别使用getImportParameterList
方法,getExportParameterList
方法,getChangingParameterList
方法和getTableParameterList
获得。这些方法的返回值都是JCoParameter
类型
JCoFunction.execute
方法实际执行函数
如何创建JCoFunction对象
上面的代码是第一种创建JCoFunction
实例的方法:
JCoRepository repository = dest.getRepository();
JCoFunction fm = dest.getRepository().getFunction("BAPI_COMPANYCODE_GETDETAIL");
如果我们不关心JCoRepository
,也可以这样写:
JCoFunction fm = dest.getRepository().getFunction("BAPI_COMPANYCODE_GETDETAIL");
第三种方法是使用JCoFunctionTemplate.getFunction
方法,JCoFunctionTemplate也是一个接口,代表SAP函数的meta-data。
JCoFunctionTemplate fmTemplate = dest.getRepository().getFunctionTemplate("BAPI_COMPANYCODE_GETDETAIL");
JCoFunction fm = fmTemplate.getFunction();
JCoStructure接口
BAPI_COMPANY_CODE_GETDETAIL函数的COMPANYCODE_DETAIL参数是一个结构,刚才我们看到遍历结构所有字段的方式:
private void printStructure(JCoStructure jcoStru) { for(JCoField field : jcoStru){ System.out.println(String.format("%s\\t%s", field.getName(), field.getString())); } }
因为JCoStructure实现了Iterable接口,所以可以采取上面的办法进行迭代。另外一种方法进行遍历:
private void printStructure2(JCoStructure jcoStructure) { for (int i = 0; i < jcoStructure.getMetaData().getFieldCount(); i++){ System.out.println(String.format("%s\\t%s", jcoStructure.getMetaData().getName(i), jcoStructure.getString(i))); } }
BAPI_COMPANYCODE_GETDETAIL
是一个适合演示的函数,没有import paramter参数,调用后COMPANYCODE_GETDETAIL
表参数返回SAP系统中所有公司代码的清单。只包括公司代码ID和公司代码名称两个字段。
JCo中,与表参数相关的两个接口是JCoTable
和JCoRecordMetaDta
, JCoTable
就是RFM中tabl参数,而JCoRecordMetaDta
是JCoTable
或JCoStructure
的元数据。
在.net环境中,我喜欢将IRfcTable转换成DataTable,但Java没有类似的数据结构,所以决定直接在方法中传递JCoTable算了。但为了方便显示,可以考虑使用一个通用代码进行输出:
package jco3.utils;import com.sap.conn.jco.JCoField;
import com.sap.conn.jco.JCoRecordMetaData;
import com.sap.conn.jco.JCoTable;public class JCoUtils
{public static void printJCoTable(JCoTable jcoTable) { // header // JCoRecordMeataData is the meta data of either a structure or a table. // Each element describes a field of the structure or table. JCoRecordMetaData tableMeta = jcoTable.getRecordMetaData(); for(int i = 0; i < tableMeta.getFieldCount(); i++){ System.out.print(String.format("%s\t", tableMeta.getName(i))); } System.out.println(); // new line // line items for(int i = 0; i < jcoTable.getNumRows(); i++){ // Sets the row pointer to the specified position(beginning from zero) jcoTable.setRow(i); // Each line is of type JCoStructure for(JCoField fld : jcoTable){ System.out.print(String.format("%s\t", fld.getValue())); } System.out.println(); } } }
要点说明:
对JCoTable,输出表头和行项目。表头通过获取JCoTable
的meta-data,然后使用meta-data的getName
()方法。
JCoRecordMetaData tableMeta = jcoTable.getRecordMetaData();
for(int i = 0; i < tableMeta.getFieldCount(); i++){System.out.print(String.format("%s\t", tableMeta.getName(i))); }
JCoTable每一行都是一个JCoStructure
,可以通过setRow()
设置指针的位置,然后再遍历各个field:
for(int i = 0; i < jcoTable.getNumRows(); i++){// Sets the row pointer to the specified position(beginning from zero) jcoTable.setRow(i); // Each line is of type JCoStructure for(JCoField fld : jcoTable){ System.out.print(String.format("%s\t", fld.getValue())); } System.out.println(); }
完成输出之后,接下来就是RFM调用:
package jco3.demo5;import org.junit.Test;
import com.sap.conn.jco.*;
import jco3.utils.JCoUtils; public class JCoTableDemo { public JCoTable getCocdList() throws JCoException { /** * Get company code list in SAP * using BAPI BAPI_COMPANYCODE_GETLIST. * * Since JCoTable is rather flexible, we simply use * this interface as return value */ JCoDestination dest = JCoDestinationManager.getDestination("ECC"); JCoFunction fm = dest.getRepository().getFunction("BAPI_COMPANYCODE_GETLIST"); fm.execute(dest); JCoTable companies = fm.getTableParameterList().getTable("COMPANYCODE_LIST"); return companies; } @Test public void printCompanies() throws JCoException { JCoTable companies = this.getCocdList(); JCoUtils.printJCoTable(companies); } }
Table参数作为import parameter
table作为输入参数,主要解决填充table的问题,基本模式如下:
someTable.appendRow();
someTable.setValue("FLDNAME", someValue);
以RFC_READ_TABLE为例,读取SAP USR04表。
package jco3.demo5;import org.junit.Test;
import com.sap.conn.jco.*;
import jco3.utils.JCoUtils; public class JCoTableAsImport { public JCoTable readTable() throws JCoException { /** * Shows how to process JCoTable (as importing) */ JCoDestination dest = JCoDestinationManager.getDestination("ECC"); JCoFunction fm = dest.getRepository().getFunction("RFC_READ_TABLE"); // table we want to query is USR04 // which is user authorization table in SAP fm.getImportParameterList().setValue("QUERY_TABLE", "USR04"); // output data will be delimited by comma fm.getImportParameterList().setValue("DELIMITER", ","); // processing table parameters JCoTable options = fm.getTableParameterList().getTable("OPTIONS"); // modification date >= 2012.01.01 and <= 2015.12.31 options.appendRow(); options.setValue("TEXT", "MODDA GE '20120101' "); options.appendRow(); options.setValue("TEXT", "AND MODDA LE '20151231' "); // We only care about fields of [user id] and [modification date] String[] outputFields = new String[] {"BNAME", "MODDA"}; JCoTable fields = fm.getTableParameterList().getTable("FIELDS"); int count = outputFields.length; fields.appendRows(count); for (int i = 0; i < count; i++){ fields.setRow(i); fields.setValue("FIELDNAME", outputFields[i]); } fm.execute(dest); JCoTable data = fm.getTableParameterList().getTable("DATA"); return data; } @Test public void printUsers() throws JCoException { JCoTable users = this.readTable(); JCoUtils.printJCoTable(users); } }
在代码中我们使用了两种方法来插入table的行项目,第一种方法:
JCoTable options = fm.getTableParameterList().getTable("OPTIONS");
// modification date >= 2012.01.01 and <= 2015.12.31 options.appendRow(); options.setValue("TEXT", "MODDA GE '20120101' "); options.appendRow(); options.setValue("TEXT", "AND MODDA LE '20151231' ");
第二种方法:
String[] outputFields = new String[] {"BNAME", "MODDA"};
JCoTable fields = fm.getTableParameterList().getTable("FIELDS"); int count = outputFields.length; fields.appendRows(count); for (int i = 0; i < count; i++){ fields.setRow(i); fields.setValue("FIELDNAME", outputFields[i]); }
JCoTable重要方法总结
id="iframe_0.6286778007291249" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://upload-images.jianshu.io/upload_images/1765749-980063a33463da2f.gif?imageMogr2/auto-orient/strip&_=5671148%22%20style=%22border:none;max-width:999px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.6286778007291249',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" style="margin: 0px; padding: 0px; border: none; width: 629px; height: 472px;">
这篇关于jCO--http://www.cnblogs.com/zfswff/p/5671148.html的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!