使用 JCo3 库实现 Java 与 SAP RFC 函数对接的详细代码指南
Java 代码使用 JCo3 对接 SAP RFC 函数
1、环境配置
JCo3 依赖:官方下载
1.1、Windows 项目部署环境配置
1.1.1、sapjco3.dll 需要与 sapjco3.jar 在同一目录

1.1.2、设置系统环境变量
新建环境变量
变量名: JAVA_SAPJCO
变量值: D:\Program Files\sapjco3

将新建的 JAVA_SAPJCO 环境变量加入系统环境变量 Path变量集合中。
%JAVA_SAPJCO%\sapjco3.jar

打开cmd,验证是否配置成功,出现弹窗即为配置成功。
java -jar "D:\Program Files\sapjco3\sapjco3.jar"

1.1.3、项目部署环境配置
<JDK_HOME>/jre/bin 目录下<JDK_HOME>/jre/bin 目录下
1.2、Linux 项目部署环境配置
1.2.1、放置 libsapjco3.so
| 系统版本 | 说明 |
|---|---|
| Linux 32位 | 将 libsapjco3.so 文件放到 <JDK_HOME>/jre/lib/i386/server |
| Linux 64位 | 将 libsapjco3.so 文件放到 <JDK_HOME>/jre/lib/amd64 |
执行 echo $JAVA_HOME 后输出 jdk 部署目录,然后切换到表格中指定目录将 libsapjco3.so 文件拷贝进去。如果未配置 java 环境变量,就执行 which java 命令,再找到对应的目录。
1.2.2、给文件授权

如上图所示,文件所属用户应该是 10143,并且缺少执行权限,因此在当前目录下执行以下命令更换文件的所属用户
chown 10143 libsapjco3.so
chgrp 10143 libsapjco3.so
再执行以下命令赋予执行权限
chmod 755 libsapjco3.so
1.3、部署异常问题
1.3.1、 Can’t load IA 64-bit .dll on a AMD 64-bit platform
项目编译及运行,根据自己的操作系统版本选择对应的 sapjco3 包。32位和64位不兼容
1.3.2、java.lang.UnsatisfiedLinkError: no sapjco3 in java.library.path
是因为没有找到 sapjco3.dll 这个库的路径,安装了JDK的环境中,这个库默认的位置不是在 system32 下,而是在 JDK/JRE/BIN 下面
1.4、开发环境配置
1.4.1、将 sapjco3.jar 加入本地 maven 库
mvn install:install-file -DgroupId=com.sap.jco3 -DartifactId=sapjco -Dversion=3.0.16 -Dpackaging=jar -Dfile="D:\\Program Files\\sapjco3\\sapjco3.jar"
然后用以下方法在 pom 添加依赖:
<dependency>
<groupId>com.sap.jco3</groupId>
<artifactId>sapjco</artifactId>
<version>3.0.16</version>
</dependency>
2、Java 代码
2.1、公共代码
2.1.1、键值对参数 KeyValueParam
/**
* 键值对参数
*
* @author wxhnyfy
*/
public class KeyValueParam {
private String name;
private String value;
public KeyValueParam(String name, String value) {
this.name = name;
this.value = value;
}
public KeyValueParam() {
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
}
2.1.2、Structure 参数 ObjectParam
/**
* Structure结构体参数
*
* @author wxhnyfy
*/
public class ObjectParam {
private String name;
private Map<String, String> value;
public ObjectParam(String name, Map<String, String> value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, String> getValue() {
return value;
}
public void setValue(Map<String, String> value) {
this.value = value;
}
}
2.1.3、Table 类型参数 ArrayListParam
/**
* Table表类型参数
*
* @author wxhnyfy
*/
public class ArrayListParam {
private String name;
private List<Map<String, String>> value;
public ArrayListParam(String name, List<Map<String, String>> value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Map<String, String>> getValue() {
return value;
}
public void setValue(List<Map<String, String>> value) {
this.value = value;
}
}
2.1.4、请求体 Request
/**
* 请求体
*
* @author chenwc
*/
public class Request {
//RFC函数名
private String functionName;
//键值对参数
private List<KeyValueParam> keyValueParams;
//structure结构体参数
private List<ObjectParam> objectParams;
//table表参数
private List<ArrayListParam> arrayListParams;
public String getFunctionName() {
return functionName;
}
public void setFunctionName(String functionName) {
this.functionName = functionName;
}
public List<KeyValueParam> getKeyValueParams() {
return keyValueParams;
}
public void setKeyValueParams(List<KeyValueParam> keyValueParams) {
this.keyValueParams = keyValueParams;
}
public List<ObjectParam> getObjectParams() {
return objectParams;
}
public void setObjectParams(List<ObjectParam> objectParams) {
this.objectParams = objectParams;
}
public List<ArrayListParam> getArrayListParams() {
return arrayListParams;
}
public void setArrayListParams(List<ArrayListParam> arrayListParams) {
this.arrayListParams = arrayListParams;
}
}
2.1.5、响应体 Response
/**
* 响应体
*
* @author wxhnyfy
*/
public class Response {
/**
* json参数返回,返回所有类型
*/
private JSONObject exportJson;
/**
* 异常信息
*/
private JSONObject exceptionJson;
public JSONObject getExportJson() {
return exportJson;
}
public void setExportJson(JSONObject exportJson) {
this.exportJson = exportJson;
}
public JSONObject getExceptionJson() {
return exceptionJson;
}
public void setExceptionJson(JSONObject exceptionJson) {
this.exceptionJson = exceptionJson;
}
}
2.1.6、SAP 连接配置 SapConfig
/**
* SAP 连接配置
*
* @author wxhnyfy
*/
public class SapConfig {
// 服务器
private String ashost;
// 系统编号
private String sysnr;
// SAP集团
private String client;
// SAP用户名
private String user;
// 密码
private String password;
// 登录语言
private String lang;
//客户端名称
private String clientname;
//路由
private String router;
public SapConfig(String ashost, String sysnr, String client, String user, String password, String lang, String clientname) {
this.ashost = ashost;
this.sysnr = sysnr;
this.client = client;
this.user = user;
this.password = password;
this.lang = lang;
this.clientname = clientname;
}
public String getRouter() {
return router;
}
public void setRouter(String router) {
this.router = router;
}
public String getAshost() {
return ashost;
}
public void setAshost(String ashost) {
this.ashost = ashost;
}
public String getSysnr() {
return sysnr;
}
public void setSysnr(String sysnr) {
this.sysnr = sysnr;
}
public String getClient() {
return client;
}
public void setClient(String client) {
this.client = client;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getLang() {
return lang;
}
public void setLang(String lang) {
this.lang = lang;
}
public String getClientname() {
return clientname;
}
public void setClientname(String clientname) {
this.clientname = clientname;
}
}
2.2、基于 JCo3 + sapjco-spring-boot-starter
2.2.1、依赖
<dependency>
<groupId>com.sap.jco3</groupId>
<artifactId>sapjco</artifactId>
<version>3.0.16</version>
</dependency>
<dependency>
<groupId>com.github.virtualcry</groupId>
<artifactId>sapjco-spring-boot-starter</artifactId>
<version>3.1.4</version>
<exclusions>
<exclusion>
<groupId>com.github.virtualcry</groupId>
<artifactId>sapjco-spring-boot-starter-autoconfigure</artifactId>
</exclusion>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</exclusion>
</exclusions>
</dependency>
2.2.2、RFC 函数执行器 RfcExecutor
import cn.gitlab.virtualcry.sapjco.beans.factory.DefaultJCoConnectionFactory;
import cn.gitlab.virtualcry.sapjco.beans.factory.JCoConnectionFactory;
import cn.gitlab.virtualcry.sapjco.beans.factory.JCoConnectionFactoryProvider;
import cn.gitlab.virtualcry.sapjco.client.JCoClient;
import cn.gitlab.virtualcry.sapjco.client.handler.FunctionRequestHandler;
import cn.gitlab.virtualcry.sapjco.config.JCoSettings;
import cn.gitlab.virtualcry.sapjco.util.ObjectUtils;
import cn.gitlab.virtualcry.sapjco.util.data.JCoDataUtils;
import java.util.HashMap;
import java.util.Map;
/**
* RFC 函数执行器
*
* @author wxhnyfy
*/
public class RfcExecutor {
/**
* 调用 sap rfc
*
* @param sapConfig sap配置
* @param sapParameterMap 传入参数
* @param rfc rfc名称
* @return 返回参数
*/
public static Map<String, Object> callRfc(SapConfig sapConfig, Map<String, Object> sapParameterMap, String rfc) {
JCoSettings jCoSettings = JCoSettings.builder()
//服务器
.ashost(sapConfig.getAshost())
//系统编号
.sysnr(sapConfig.getSysnr())
//SAP集团
.client(sapConfig.getClient())
//用户名
.user(sapConfig.getUser())
//密码
.password(sapConfig.getPassword())
//登录语言
.language(sapConfig.getLang())
.build();
JCoConnectionFactory jCoConnectionFactory = null;
JCoClient jCoClient = null;
Map<String, Object> result = new HashMap<>();
try {
// 获取连接工厂
jCoConnectionFactory = JCoConnectionFactoryProvider.getSingleton().getIfAvailable(DefaultJCoConnectionFactory::new);
// 获取客户端连接
jCoClient = jCoConnectionFactory.getOrCreateClient(sapConfig.getClientname(), jCoSettings);
//设置传参,其中importParameter只作为输入参数,tableParameter和changingParameter可输入也可输出
FunctionRequestHandler requestHandler = (importParameter, tableParameter, changingParameter) -> {
JCoDataUtils.setJCoParameterListValue(tableParameter, sapParameterMap);
JCoDataUtils.setJCoParameterListValue(importParameter, sapParameterMap);
JCoDataUtils.setJCoParameterListValue(changingParameter, sapParameterMap);
};
result = jCoClient.invokeSapFunc(rfc, requestHandler);
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭连接
try {
if (!ObjectUtils.isEmpty(jCoClient)) {
jCoClient.release();
}
if (!ObjectUtils.isEmpty(jCoConnectionFactory)) {
jCoConnectionFactory.releaseClients();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return result;
}
}
2.2.3、测试代码
public class TestMain {
private static final Logger logger = LoggerFactory.getLogger(TestMain.class);
public static void main(String[] args) {
//sapjco-spring-boot-starter必须要注册环境,否则连不上SAP服务器
JCoDataProvider.registerInEnvironment();
SapConfig config = new SapConfig("服务器IP",
"系统编号", "SAP集团号", "用户名", "密码",
"登录语言", "客户端名称");
//入参
Map<String, Object> sapParameterMap = new HashMap<>();
sapParameterMap.put("key1", "value1");
sapParameterMap.put("key2", "value2");
sapParameterMap.put("key3", "value3");
sapParameterMap.put("key4", "value4");
sapParameterMap.put("key5", "value5");
Map<String, Object> result = RfcExecutor.callRfc(config, sapParameterMap, "RFC函数名");
Object returnValue = result.get("returnValue");
byte[] bytes = (byte[]) returnValue;
logger.info("bytes:{}", bytes.length);
logger.info("returnValue: {}", new String(bytes));
List<Map<String, String>> ET_RETURN = (List<Map<String, String>>) result.get("ET_RETURN");
logger.info("result:{}", JSON.toJSONString(result));
}
}
2.3、基于 JCo3
2.3.1、依赖
<dependency>
<groupId>com.sap.jco3</groupId>
<artifactId>sapjco</artifactId>
<version>3.0.16</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.2</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.12.5</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
2.3.2、创建连接文件
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.ext.DestinationDataProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Properties;
/**
* 创建连接文件
*
* @author wxhnyfy
*/
public class CreateDestination {
public static final String DESTINATION_NAME1 = "SAP客户端";
public static final String ABAP_AS_WITH_POOL = "SAP客户端(连接池)";
static {
Properties connectProperties = new Properties();
connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, "服务器IP");
connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, "SAP系统编号");
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, "集团编号");
connectProperties.setProperty(DestinationDataProvider.JCO_USER, "用户名");
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, "密码");
connectProperties.setProperty(DestinationDataProvider.JCO_LANG, "登录语言");
connectProperties.setProperty(DestinationDataProvider.JCO_SAPROUTER, "路由");
createDataFile(DESTINATION_NAME1, "jcoDestination", connectProperties);
// 连接池属性
connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, "3");
connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, "10");
createDataFile(ABAP_AS_WITH_POOL, "jcoDestination", connectProperties);
}
/**
* 获取连接
*
* @return JCoDestination
*/
public static JCoDestination getDestination() {
JCoDestination destination = null;
try {
destination = JCoDestinationManager.getDestination(DESTINATION_NAME1);
} catch (Exception e) {
e.printStackTrace();
}
return destination;
}
/**
* 获取连接(使用连接池)
*
* @return JCoDestination
*/
public static JCoDestination getDestinationUsingPool() {
JCoDestination destination = null;
try {
destination = JCoDestinationManager.getDestination(ABAP_AS_WITH_POOL);
destination.ping();
} catch (Exception e) {
e.printStackTrace();
}
return destination;
}
/**
* 创建连接文件
*
* @param name 文件名
* @param suffix 后缀
* @param properties 属性
*/
static void createDataFile(String name, String suffix, Properties properties) {
File cfg = new File(name + "." + suffix);
cfg.deleteOnExit();
if (!cfg.exists()) {
try {
FileOutputStream fos = new FileOutputStream(cfg, false);
properties.store(fos, "for tests only !");
fos.close();
} catch (Exception e) {
throw new RuntimeException("无法创建 destination 文件 " + cfg.getName(), e);
}
}
}
}
2.3.3、RFC 函数执行器 RfcExecutor
import com.sap.conn.jco.*;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
/**
* RFC 函数执行器
*
* @author wxhnyfy
*/
public class RfcExecutor {
private static final Logger logger = LoggerFactory.getLogger(RfcExecutor.class);
/**
* 线程安全的 SimpleDateFormat
*/
private static final ThreadLocal<DateFormat> THREAD_LOCAL = new ThreadLocal<>();
/**
* 获取 DateFormat
*
* @param pattern 格式化
* @return DateFormat
*/
public static DateFormat getDateFormat(String pattern) {
DateFormat df = THREAD_LOCAL.get();
if (df == null) {
df = new SimpleDateFormat(pattern);
}
THREAD_LOCAL.set(df);
return df;
}
/**
* 无状态执行,执行一次RFC函数连接一次
*
* @param request 请求
* @param destination 目标
* @return 响应
*/
public static Response statelessExecute(Request request, JCoDestination destination) {
logger.info("开始连接...");
Response response = new Response();
try {
// 调用SAP的sapRequestContent.getFuntionName()方法
JCoFunction function = destination.getRepository().getFunction(request.getFunctionName());
if (null == function) {
logger.error("SAP没有名称为:{} 的RFC函数!", request.getFunctionName());
return response;
}
logger.info("连接成功!");
response = exec(request, function, destination);
} catch (JCoException e) {
logger.info("SAP调用RFC函数失败, 错误信息:" + e.getMessage());
e.printStackTrace();
}
return response;
}
/**
* 有状态执行,连接后不断开连接执行多个RFC函数
*
* @param requestList 请求(有顺序的列表)
* @param destination 目标
* @return 响应
*/
public static Map<String, Response> statefulExecute(LinkedList<Request> requestList, JCoDestination destination) {
logger.info("开始连接...");
Map<String, Response> responseMap = new HashMap<>();
try {
JCoContext.begin(destination);
for (int kk = 0; kk < requestList.size(); kk++) {
Request request = requestList.get(kk);
logger.info("开始执行第{}个RFC函数:{}", kk + 1, request.getFunctionName());
Response response = new Response();
JCoFunction function = destination.getRepository().getFunction(request.getFunctionName());
if (null == function) {
logger.error("SAP没有名称为:{} 的RFC函数!", request.getFunctionName());
responseMap.put(request.getFunctionName(), response);
continue;
}
logger.info("连接成功!");
response = exec(request, function, destination);
responseMap.put(request.getFunctionName(), response);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
JCoContext.end(destination);
} catch (JCoException e) {
e.printStackTrace();
}
}
return responseMap;
}
/**
* 执行RFC函数
*
* @param request 请求
* @param function RFC函数
* @param destination 目标
* @return 响应
*/
private synchronized static Response exec(Request request, JCoFunction function, JCoDestination destination) {
Response response = new Response();
try {
//输入参数
if (null != function.getImportParameterList()) {
setJCoParameterListValue(function.getImportParameterList(), request);
}
if (null != function.getTableParameterList()) {
setJCoParameterListValue(function.getTableParameterList(), request);
}
if (null != function.getChangingParameterList()) {
setJCoParameterListValue(function.getChangingParameterList(), request);
}
function.execute(destination);
//输出参数
JSONObject exportJson = new JSONObject();
if (null != function.getExportParameterList()) {
getJCoParameterListValue(function.getExportParameterList(), exportJson);
}
if (null != function.getTableParameterList()) {
getJCoParameterListValue(function.getTableParameterList(), exportJson);
}
response.setExportJson(exportJson);
JSONObject exceptionJson = new JSONObject();
if (null != function.getExceptionList()) {
List<AbapException> exceptionList = Arrays.stream(function.getExceptionList()).collect(Collectors.toList());
exceptionList.forEach(exception -> exceptionJson.put(exception.getKey(), exception.getMessage()));
}
response.setExceptionJson(exceptionJson);
} catch (JCoException e) {
e.printStackTrace();
}
return response;
}
/**
* 设置JCoParameterList的值
*
* @param parameterList 参数列表
* @param request 参数
*/
public static void setJCoParameterListValue(JCoParameterList parameterList, Request request) {
if (parameterList == null || request == null) {
return;
}
JCoFieldIterator fieldIterator = parameterList.getFieldIterator();
while (fieldIterator.hasNextField()) {
JCoField field = fieldIterator.nextField();
if (field.getType() == JCoMetaData.TYPE_TABLE) {
setJCoFieldValue(field, request.getArrayListParams());
} else if (field.getType() == JCoMetaData.TYPE_STRUCTURE) {
setJCoFieldValue(field, request.getObjectParams());
} else {
setJCoFieldValue(field, request.getKeyValueParams());
}
}
}
/**
* 设置JCoField的值
*
* @param field JCoField
* @param parameter 参数
*/
public static void setJCoFieldValue(JCoField field, Object parameter) {
List<Object> list = parameter instanceof List ? (List) parameter : new ArrayList<>();
for (Object obj : list) {
if (obj instanceof KeyValueParam) {
KeyValueParam keyValueParam = (KeyValueParam) obj;
if (field.getName().equals(keyValueParam.getName())) {
field.setValue(keyValueParam.getValue());
}
} else if (obj instanceof ObjectParam) {
ObjectParam objectParam = (ObjectParam) obj;
if (field.getName().equals(objectParam.getName())) {
JCoStructure inStructure = field.getStructure();
Map<String, String> value = objectParam.getValue();
for (Map.Entry<String, String> entry : value.entrySet()) {
inStructure.setValue(entry.getKey(), entry.getValue());
}
}
} else if (obj instanceof ArrayListParam) {
ArrayListParam arrayListParam = (ArrayListParam) obj;
if (field.getName().equals(arrayListParam.getName())) {
JCoTable inTable = field.getTable();
List<Map<String, String>> value = arrayListParam.getValue();
for (Map<String, String> map : value) {
inTable.appendRow();
for (Map.Entry<String, String> entry : map.entrySet()) {
logger.info("key:{}, value:{}", entry.getKey(), entry.getValue());
inTable.setValue(entry.getKey(), entry.getValue());
}
}
}
}
}
}
/**
* 获取JCoParameterList的值
*
* @param parameterList 参数列表
* @param exportJson 导出的JSON
*/
public static void getJCoParameterListValue(JCoParameterList parameterList, JSONObject exportJson) {
if (null == parameterList) {
return;
}
//遍历参数
JCoFieldIterator fieldIterator = parameterList.getFieldIterator();
while (fieldIterator.hasNextField()) {
//获取参数
JCoField field = fieldIterator.nextField();
//判断是否为Table
if (field.isTable()) {
JCoTable jCoTable = field.getTable();
JSONArray tempTable = new JSONArray();
//遍历Table
for (int i = 0; i < jCoTable.getNumRows(); i++) {
JSONObject tempRow = new JSONObject();
jCoTable.setRow(i);
JCoRecordFieldIterator iterator = jCoTable.getRecordFieldIterator();
//遍历Table行
while (iterator.hasNextField()) {
JCoField field1 = iterator.nextField();
tempRow.put(field1.getName(), getJCoFieldValue(field1));
}
tempTable.add(tempRow);
}
exportJson.put(field.getName(), tempTable);
}
//判断是否为Structure
else if (field.isStructure()) {
JCoStructure jCoStructure = field.getStructure();
JSONObject tempStruc = new JSONObject();
//遍历Structure
JCoRecordFieldIterator iterator = jCoStructure.getRecordFieldIterator();
while (iterator.hasNextField()) {
JCoField field1 = iterator.nextField();
tempStruc.put(field1.getName(), getJCoFieldValue(field1));
}
exportJson.put(field.getName(), tempStruc);
}
//普通参数
else {
exportJson.put(field.getName(), getJCoFieldValue(field));
}
}
}
/**
* 获取JCoField的值
*
* @param field JCoField
* @return Object
*/
public static Object getJCoFieldValue(JCoField field) {
Object value;
switch (field.getType()) {
case JCoMetaData.TYPE_INT1:
case JCoMetaData.TYPE_INT2:
case JCoMetaData.TYPE_INT:
value = field.getInt();
break;
case JCoMetaData.TYPE_BCD:
case JCoMetaData.TYPE_DECF16:
case JCoMetaData.TYPE_DECF34:
value = field.getBigDecimal();
break;
case JCoMetaData.TYPE_DATE:
value = field.getDate();
if (null != value) {
//日期类型的值需要转换成yyyy-MM-dd格式
value = getDateFormat("yyyy-MM-dd").format((Date) value);
}
break;
case JCoMetaData.TYPE_TIME:
value = field.getTime();
if (null != value) {
//时间类型的值需要转换成HH:mm:ss格式
value = getDateFormat("HH:mm:ss").format((Date) value);
}
break;
case JCoMetaData.TYPE_FLOAT:
value = field.getFloat();
break;
case JCoMetaData.TYPE_BYTE:
case JCoMetaData.TYPE_XSTRING:
value = field.getByteArray();
if (null != value && ((byte[]) value).length > 0) {
//获取到的是字节数组,需要转换成16进制字符串
value = new String(Hex.encodeHex((byte[]) value)).toUpperCase();
}
break;
case JCoMetaData.TYPE_CHAR:
case JCoMetaData.TYPE_NUM:
case JCoMetaData.TYPE_STRING:
default:
value = field.getString();
break;
}
return value;
}
}
2.3.4、测试代码
2.3.4.1、无状态连接测试
import com.sap.conn.jco.JCoDestination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
/**
* 无状态连接测试
*
* @author wxhnyfy
*/
public class StatelessTest {
private static final Logger logger = LoggerFactory.getLogger(StatelessTest.class);
public static void main(String[] args) {
JCoDestination destination = CreateDestination.getDestination();
Request request = new Request();
KeyValueParam kp1 = new KeyValueParam("key1", "value1");
KeyValueParam kp2 = new KeyValueParam("key2", "value2");
List<KeyValueParam> params = new ArrayList<>();
params.add(kp1);
params.add(kp2);
request.setFunctionName("function1");
request.setKeyValueParams(params);
Response response = RfcExecutor.statelessExecute(request, destination);
logger.info("返回结果:{}", response.getExportJson());
}
}
2.3.4.2、有状态连接测试
import com.sap.conn.jco.JCoDestination;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* 有状态连接测试
*
* @author wxhnyfy
*/
public class StatefulTest {
private static final Logger logger = LoggerFactory.getLogger(StatefulTest.class);
public static void main(String[] args) {
JCoDestination destination = CreateDestination.getDestination();
LinkedList<Request> requestList = new LinkedList<>();
Request request = new Request();
KeyValueParam kp1 = new KeyValueParam("key1", "value1");
KeyValueParam kp2 = new KeyValueParam("key2", "value2");
List<KeyValueParam> params = new ArrayList<>();
params.add(kp1);
params.add(kp2);
request.setFunctionName("function1");
request.setKeyValueParams(params);
Request request2 = new Request();
KeyValueParam kp3 = new KeyValueParam("key3", "value3");
KeyValueParam kp4 = new KeyValueParam("key4", "value4");
KeyValueParam kp5 = new KeyValueParam("key5", "value5");
KeyValueParam kp6 = new KeyValueParam("key6", "value6");
KeyValueParam kp7 = new KeyValueParam("key7", "value7");
List<KeyValueParam> params2 = new ArrayList<>();
params2.add(kp3);
params2.add(kp4);
params2.add(kp5);
params2.add(kp6);
params2.add(kp7);
request2.setFunctionName("function2");
request2.setKeyValueParams(params2);
requestList.add(request);
requestList.add(request2);
Map<String, Response> responseList = RfcExecutor.statefulExecute(requestList, destination);
logger.info("function1 返回结果:{}", responseList.get("function1").getExportJson());
logger.info("function2 返回结果:{}", responseList.get("function2").getExportJson());
}
}
2.3.5、将 RFC 函数的入参/出参导出为 Excel 文件
2.3.5.1、POI 实体类 SheetData
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* POI 实体类
*
* @author wxhnyfy
*/
public class SheetData {
/**
* 表头
*/
private List<String> header = new ArrayList<>();
/**
* 表格数据
*/
private List<Map<String, Object>> sheetDataList = new ArrayList<>();
public SheetData() {
}
public SheetData(List<String> header, List<Map<String, Object>> sheetDataList) {
this.header = header;
this.sheetDataList = sheetDataList;
}
public List<String> getHeader() {
return header;
}
public void setHeader(List<String> header) {
this.header = header;
}
public List<Map<String, Object>> getSheetDataList() {
return sheetDataList;
}
public void setSheetDataList(List<Map<String, Object>> sheetDataList) {
this.sheetDataList = sheetDataList;
}
}
2.3.5.2、POI 写文件
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* 写文件
*
* @author wxhnyfy
*/
public class WriteExcelFile {
private static final Logger log = LoggerFactory.getLogger(WriteExcelFile.class);
/**
* 写入Excel文件
*
* @param excelFile 待写入文件路径
* @param sheetName sheet页签名称
* @param sheetData 待写入数据
*/
public static void writeFile(File excelFile, String sheetName, SheetData sheetData) {
long start = System.currentTimeMillis();
try {
FileUtils.forceMkdirParent(excelFile);
FileUtils.touch(excelFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
log.info("写入文件:{}", excelFile.getAbsolutePath());
if (!excelFile.getName().toLowerCase().endsWith("xlsx")) {
log.info("只支持 .xlsx 的文件类型写入!");
return;
}
// 创建一个工作簿 03
// HSSFWorkbook workbook = new HSSFWorkbook();
// 创建一个工作簿 07
XSSFWorkbook workbook = new XSSFWorkbook();
// 创建一个工作表
XSSFSheet sheet = workbook.createSheet(sheetName);
List<String> header = sheetData.getHeader();
// 创建第一行
XSSFRow row1 = sheet.createRow(0);
for (int i = 0; i < header.size(); i++) {
// 创建一个单元格
XSSFCell cell = row1.createCell(i);
CellStyle cellStyle = workbook.createCellStyle();
Font font = workbook.createFont();
//设置字体
font.setFontName("宋体");
//设置字号
font.setFontHeightInPoints((short) 13);
cellStyle.setFont(font);
//文本自动换行
cellStyle.setWrapText(true);
//左右居中
cellStyle.setAlignment(HorizontalAlignment.CENTER);
//上下居中
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
cell.setCellStyle(cellStyle);
cell.setCellValue(header.get(i));
}
log.info("写入标题行完成");
// 调整列宽
for (int colNum = 0; colNum < header.size(); colNum++) {
//设置第几列列宽自适应
// sheet.autoSizeColumn(colNum, true);
// int width = sheet.getColumnWidth(colNum);
// // 设置最小列宽
// sheet.setColumnWidth(colNum, Math.max(width, 10 * 256));
//固定列宽
sheet.setColumnWidth(colNum, 20 * 256);
}
log.info("设置列宽完成");
// 设置居中显示
CellStyle style = workbook.createCellStyle();
// 设置水平居中
style.setAlignment(HorizontalAlignment.LEFT);
// 设置垂直居中
style.setVerticalAlignment(VerticalAlignment.CENTER);
List<Map<String, Object>> sheetDataList = sheetData.getSheetDataList();
for (int i = 0; i < sheetDataList.size(); i++) {
// 创建第二行
XSSFRow row2 = sheet.createRow(i + 1);
Map<String, Object> rowDataMap = sheetDataList.get(i);
for (int j = 0; j < header.size(); j++) {
// 创建一个单元格
XSSFCell cell = row2.createCell(j);
if (StringUtils.isNotEmpty(header.get(j))
&& "序号".equals(header.get(j))
&& rowDataMap.get(header.get(j)).toString().contains("参数")) {
// 设置单元格合并
CellRangeAddress region = new CellRangeAddress(
//开始行
i + 1,
//结束行
i + 1,
//开始列
0,
//结束列
header.size() - 1);
sheet.addMergedRegion(region);
cell.setCellStyle(style);
}
if (null == rowDataMap.get(header.get(j))) {
cell.setCellValue("");
} else {
cell.setCellValue(rowDataMap.get(header.get(j)).toString());
}
}
}
log.info("写入数据行完成");
//第一行开启筛选
sheet.setAutoFilter(new CellRangeAddress(0, 0, 0, header.size() - 1));
log.info("开启标题行筛选完成");
/*
四个参数分别代表:
cellNum:表示要冻结的列数;
rowNum:表示要冻结的行数;
firstCellNum:表示被固定列右边第一列的列号;
firstRollNum :表示被固定行下边第一列的行号;
注意: 后2个参数均从0开始计算列号和行号,且firstCellNum>=cellNum &&firstRollNum >=cellNum
sheet.createFreezePane(1,0,1,0);//就是固定了首列,列号的显示为:A,BCDEF...
sheet.createFreezePane(1,0,3,0);//固定了首列,列号的显示为:A,DEF...
//注意:BC列不是被隐藏,而是默认显示列为A,DEF,若想要看BC列,只需移动滚轮即可.行号同理
*/
//这里是只冻结第一行
sheet.createFreezePane(0, 1, 0, 1);
log.info("冻结标题行完成");
FileOutputStream fileOutputStream = null;
try {
// 创建文件
fileOutputStream = new FileOutputStream(excelFile);
// 写入文件
workbook.write(fileOutputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != fileOutputStream) {
fileOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
log.info("写入文件完成,总耗时:{} 秒", (end - start) / 1000.0);
}
}
2.3.5.3、将 RFC 函数的入参/出参导出为 Excel 文件
import com.sap.conn.jco.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
/**
* RFC执行
*
* @author chenwc
* @date 2023/11/28 11:21
*/
public class RfcExecutorExport {
private static final Logger logger = LoggerFactory.getLogger(RfcExecutorExport.class);
public static void main(String[] args) {
File file = new File("1.txt");
String path = file.getAbsolutePath().replace("1.txt", "");
path = path + "outputExcel\\";
JCoDestination destination = CreateDestination.getDestination();
Request request = new Request();
request.setFunctionName("ZMDG_UPDATE_VEN_CUS_ZFIELDS");
Response response = statelessExecute(request, destination);
logger.info("response:{}", response.getExportJson());
JSONObject exportJson = response.getExportJson();
JSONObject inputJson = exportJson.getJSONObject("传入参数");
JSONObject outputJson = exportJson.getJSONObject("传出参数");
JSONObject tableJson = exportJson.getJSONObject("表格参数");
//写文件示例2
List<String> header = new ArrayList<>();
header.add("序号");
header.add("参数名称");
header.add("描述");
header.add("字段类型");
header.add("说明");
List<Map<String, Object>> writeDataMapList = new ArrayList<>();
int i = 0;
Map<String, Object> row1 = new HashMap<>();
row1.put("序号", "输入参数");
writeDataMapList.add(row1);
JSONObject kvJson = new JSONObject();
JSONObject objJson = new JSONObject();
if (null != inputJson && !inputJson.isEmpty()) {
for (String key : inputJson.keySet()) {
Object param = inputJson.get(key);
if (param instanceof JSONObject) {
kvJson.put(key, param);
}
if (param instanceof JSONArray) {
objJson.put(key, param);
}
}
}
Map<String, Object> row3334 = new HashMap<>();
row3334.put("序号", "KeyValue输入参数");
writeDataMapList.add(row3334);
if (!kvJson.isEmpty()) {
for (String key : kvJson.keySet()) {
i++;
JSONObject paramJson = kvJson.getJSONObject(key);
Map<String, Object> row = new HashMap<>();
row.put("序号", i);
row.put("参数名称", key);
row.put("描述", paramJson.getString("字段描述"));
row.put("字段类型", paramJson.getString("字段类型") + "(" + paramJson.getString("字段长度") + ")");
row.put("说明", "");
writeDataMapList.add(row);
}
}
if (!objJson.isEmpty()) {
for (String key : objJson.keySet()) {
i++;
JSONArray paramJson = objJson.getJSONArray(key);
Map<String, Object> row33 = new HashMap<>();
row33.put("序号", "对象输入参数名称:" + key);
writeDataMapList.add(row33);
int co = 0;
for (int j = 0; j < paramJson.size(); j++) {
co++;
JSONObject jsonObject = paramJson.getJSONObject(j);
Map<String, Object> row = new HashMap<>();
row.put("序号", co);
row.put("参数名称", jsonObject.getString("字段名"));
row.put("描述", jsonObject.getString("字段描述"));
row.put("字段类型", jsonObject.getString("字段类型") + "(" + jsonObject.getString("字段长度") + ")");
row.put("说明", "");
writeDataMapList.add(row);
}
}
}
Map<String, Object> row2 = new HashMap<>();
row2.put("序号", "表参数");
writeDataMapList.add(row2);
if (null != tableJson && !tableJson.isEmpty()) {
for (String key : tableJson.keySet()) {
JSONArray paramJson = tableJson.getJSONArray(key);
Map<String, Object> row333 = new HashMap<>();
row333.put("序号", "表参数名称:" + key);
writeDataMapList.add(row333);
i = 0;
for (int j = 0; j < paramJson.size(); j++) {
i++;
JSONObject jsonObject = paramJson.getJSONObject(j);
Map<String, Object> row = new HashMap<>();
row.put("序号", i);
row.put("参数名称", jsonObject.getString("字段名"));
row.put("描述", jsonObject.getString("字段描述"));
row.put("字段类型", jsonObject.getString("字段类型") + "(" + jsonObject.getString("字段长度") + ")");
row.put("说明", "");
writeDataMapList.add(row);
}
}
}
JSONObject kvOutJson = new JSONObject();
JSONObject objOutJson = new JSONObject();
if (null != outputJson && !outputJson.isEmpty()) {
for (String key : outputJson.keySet()) {
Object param = outputJson.get(key);
if (param instanceof JSONObject) {
kvOutJson.put(key, param);
}
if (param instanceof JSONArray) {
objOutJson.put(key, param);
}
}
}
Map<String, Object> row3 = new HashMap<>();
row3.put("序号", "输出KeyValue参数");
writeDataMapList.add(row3);
i = 0;
if (!kvOutJson.isEmpty()) {
for (String key : kvOutJson.keySet()) {
i++;
JSONObject paramJson = kvOutJson.getJSONObject(key);
Map<String, Object> row = new HashMap<>();
row.put("序号", i);
row.put("参数名称", key);
row.put("描述", paramJson.getString("字段描述"));
row.put("字段类型", paramJson.getString("字段类型") + "(" + paramJson.getString("字段长度") + ")");
row.put("说明", "");
writeDataMapList.add(row);
}
}
if (!objOutJson.isEmpty()) {
for (String key : objOutJson.keySet()) {
i++;
JSONArray paramJson = objOutJson.getJSONArray(key);
Map<String, Object> row33 = new HashMap<>();
row33.put("序号", "对象输出参数名称:" + key);
writeDataMapList.add(row33);
int co = 0;
for (int j = 0; j < paramJson.size(); j++) {
co++;
JSONObject jsonObject = paramJson.getJSONObject(j);
Map<String, Object> row = new HashMap<>();
row.put("序号", co);
row.put("参数名称", jsonObject.getString("字段名"));
row.put("描述", jsonObject.getString("字段描述"));
row.put("字段类型", jsonObject.getString("字段类型") + "(" + jsonObject.getString("字段长度") + ")");
row.put("说明", "");
writeDataMapList.add(row);
}
}
}
File newWriteFile = new File(path + request.getFunctionName() + ".xlsx");
WriteExcelFile.writeFile(newWriteFile, request.getFunctionName(), new SheetData(header, writeDataMapList));
}
/**
* 无状态执行,执行一次RFC函数连接一次
*
* @param request 请求
* @param destination 目的地
* @return 响应
*/
public static Response statelessExecute(Request request, JCoDestination destination) {
logger.info("开始连接...");
Response response = new Response();
try {
// 调用SAP的sapRequestContent.getFuntionName()方法
JCoFunction function = destination.getRepository().getFunction(request.getFunctionName());
if (null == function) {
logger.error("SAP没有名称为:{} 的RFC函数!", request.getFunctionName());
return response;
}
logger.info("连接成功!");
response = exec(request, function, destination);
} catch (JCoException e) {
logger.info("SAP调用RFC函数失败, 错误信息:" + e.getMessage());
e.printStackTrace();
}
return response;
}
/**
* 执行RFC函数
*
* @param request 请求
* @param function RFC函数
* @param destination 目标
* @return 响应
*/
private synchronized static Response exec(Request request, JCoFunction function, JCoDestination destination) {
Response response = new Response();
JSONObject exportJson = new JSONObject();
//输入参数
if (null != function.getImportParameterList()) {
JSONObject importJson = new JSONObject();
getJCoParameterListValue(function.getImportParameterList(), importJson);
exportJson.put("传入参数", importJson);
}
if (null != function.getTableParameterList()) {
JSONObject tableJson = new JSONObject();
getJCoParameterListValue(function.getTableParameterList(), tableJson);
exportJson.put("表格参数", tableJson);
}
if (null != function.getChangingParameterList()) {
JSONObject changingJson = new JSONObject();
getJCoParameterListValue(function.getChangingParameterList(), changingJson);
exportJson.put("传入修改参数", changingJson);
}
try {
function.execute(destination);
} catch (JCoException e) {
e.printStackTrace();
}
if (null != function.getExportParameterList()) {
JSONObject exportJson1 = new JSONObject();
getJCoParameterListValue(function.getExportParameterList(), exportJson1);
exportJson.put("传出参数", exportJson1);
}
if (null != function.getTableParameterList()) {
JSONObject exportJson1 = new JSONObject();
getJCoParameterListValue(function.getTableParameterList(), exportJson1);
exportJson.put("表格参数", exportJson1);
}
response.setExportJson(exportJson);
JSONObject exceptionJson = new JSONObject();
if (null != function.getExceptionList()) {
List<AbapException> exceptionList = Arrays.stream(function.getExceptionList()).collect(Collectors.toList());
exceptionList.forEach(exception -> exceptionJson.put(exception.getKey(), exception.getMessage()));
}
response.setExceptionJson(exceptionJson);
return response;
}
/**
* 获取JCoParameterList的值
*
* @param parameterList 参数列表
* @param exportJson 导出的JSON
*/
public static void getJCoParameterListValue(JCoParameterList parameterList, JSONObject exportJson) {
if (null == parameterList) {
return;
}
//遍历参数
JCoFieldIterator fieldIterator = parameterList.getFieldIterator();
while (fieldIterator.hasNextField()) {
//获取参数
JCoField field = fieldIterator.nextField();
//判断是否为Table
if (field.isTable()) {
JCoTable jCoTable = field.getTable();
JSONArray tempTable = new JSONArray();
JCoRecordFieldIterator iterator = jCoTable.getRecordFieldIterator();
//遍历Table
while (iterator.hasNextField()) {
JSONObject tempRow = new JSONObject();
JCoField field1 = iterator.nextField();
tempRow.put("字段名", field1.getName());
tempRow.put("字段类型", field1.getTypeAsString());
tempRow.put("字段描述", field1.getDescription());
tempRow.put("字段长度", field1.getLength());
tempTable.add(tempRow);
}
exportJson.put(field.getName(), tempTable);
}
//判断是否为Structure
else if (field.isStructure()) {
JCoStructure jCoStructure = field.getStructure();
JSONArray tempTable = new JSONArray();
//遍历Structure
JCoRecordFieldIterator iterator = jCoStructure.getRecordFieldIterator();
while (iterator.hasNextField()) {
JSONObject tempRow = new JSONObject();
JCoField field1 = iterator.nextField();
tempRow.put("字段名", field1.getName());
tempRow.put("字段类型", field1.getTypeAsString());
tempRow.put("字段描述", field1.getDescription());
tempRow.put("字段长度", field1.getLength());
tempTable.add(tempRow);
}
exportJson.put(field.getName(), tempTable);
}
//普通参数
else {
JSONObject tempField = new JSONObject();
tempField.put("字段名", field.getName());
tempField.put("字段类型", field.getTypeAsString());
tempField.put("字段描述", field.getDescription());
tempField.put("字段长度", field.getLength());
exportJson.put(field.getName(), tempField);
}
}
}
}
导出的 Excel 文件如下图所示:

2.4、基于 JCo3 多线程调用 RFC 函数
2.4.1、多步骤任务接口
/**
* 多步骤任务接口
*
* @author wxhnyfy
*/
public interface MultiStepJob {
//是否完成
boolean isFinished();
//运行下一步
public JSONObject runNextStep();
//获取名称
String getName();
//清理
public void cleanUp();
}
2.4.2、无状态多步骤作业
import com.sap.conn.jco.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 无状态多步骤作业
*
* @author wxhnyfy
*/
public class StatelessMultiStep implements MultiStepJob {
private static final Logger logger = LoggerFactory.getLogger(StatelessMultiStep.class);
//原子整数
static AtomicInteger JOB_COUNT = new AtomicInteger(0);
//增量计数器
int jobID = JOB_COUNT.addAndGet(1);
//调用数量
int calls;
//目标
JCoDestination destination;
//执行调用数量
int executedCalls = 0;
//异常
Exception ex = null;
//执行模板
Request request;
//json参数返回,返回所有类型
JSONObject exportJson;
/**
* 无状态多步骤作业
*
* @param destination 目标
* @param calls 调用数量
* @param request 执行模板
*/
public StatelessMultiStep(JCoDestination destination, int calls, Request request) {
this.calls = calls;
this.destination = destination;
this.request = request;
}
/**
* 是否完成
*
* @return 是否完成
*/
@Override
public boolean isFinished() {
return executedCalls == calls || ex != null;
}
/**
* 获取名称
*
* @return 名称
*/
@Override
public String getName() {
return "stateless Job-" + jobID;
}
/**
* 运行下一步
*/
@Override
public JSONObject runNextStep() {
try {
//获取计数器模板
JCoFunction function = destination.getRepository().getFunction(request.getFunctionName());
if (function == null) {
throw new RuntimeException("SAP 没有该【" + request.getFunctionName() + "】RFC 函数");
}
//输入参数
if (null != function.getImportParameterList()) {
if (null != request.getKeyValueParams() && !request.getKeyValueParams().isEmpty()) {
logger.info("输入参数:{}", JSON.toJSONString(request.getKeyValueParams()));
request.getKeyValueParams().forEach(item -> function.getImportParameterList().setValue(item.getName(), item.getValue()));
}
}
if (null != function.getImportParameterList()) {
if (null != request.getObjectParams() && !request.getObjectParams().isEmpty()) {
request.getObjectParams().forEach(item -> {
JCoStructure inStructure = function.getImportParameterList().getStructure(item.getName());
Map<String, String> value = item.getValue();
for (Map.Entry<String, String> entry : value.entrySet()) {
inStructure.setValue(entry.getKey(), entry.getValue());
}
});
}
if (null != request.getArrayListParams() && !request.getArrayListParams().isEmpty()) {
request.getArrayListParams().forEach(item -> {
JCoTable inTable = function.getImportParameterList().getTable(item.getName());
List<Map<String, String>> value = item.getValue();
for (Map<String, String> map : value) {
inTable.appendRow();
for (Map.Entry<String, String> entry : map.entrySet()) {
inTable.setValue(entry.getKey(), entry.getValue());
}
}
});
}
}
//执行目标
function.execute(destination);
//执行调用数量增加
executedCalls++;
if (null == exportJson) {
exportJson = new JSONObject();
}
//如果完成
if (isFinished()) {
logger.info("RFC: {} 调用完成", request.getFunctionName());
//导出参数列表
if (null != function.getExportParameterList()) {
function.getExportParameterList().forEach(export -> {
if (export.isStructure()) {
JCoStructure jCoStructure = export.getStructure();
JSONObject tempStruc = new JSONObject();
JCoRecordMetaData strucMeta = jCoStructure.getRecordMetaData();
for (int i = 0; i < strucMeta.getFieldCount(); i++) {
tempStruc.put(strucMeta.getName(i), jCoStructure.getString(strucMeta.getName(i)));
}
exportJson.put(export.getName(), tempStruc);
} else if (export.isTable()) {
JCoTable jCoTable = export.getTable();
JSONArray tempTable = new JSONArray();
for (int i = 0; i < jCoTable.getNumRows(); i++) {
JSONObject tempRow = new JSONObject();
jCoTable.setRow(i);
JCoRecordMetaData tableMeta = jCoTable.getRecordMetaData();
for (int j = 0; j < tableMeta.getFieldCount(); j++) {
tempRow.put(tableMeta.getName(j), jCoTable.getString(tableMeta.getName(j)));
}
tempTable.add(tempRow);
}
exportJson.put(export.getName(), tempTable);
} else {
exportJson.put(export.getName(), export.getString());
}
});
}
//表参数列表
if (null != function.getTableParameterList()) {
function.getTableParameterList().forEach(table -> {
JSONArray tempTable = new JSONArray();
JCoTable jCoTable = table.getTable();
for (int i = 0; i < jCoTable.getNumRows(); i++) {
JSONObject tempRow = new JSONObject();
jCoTable.setRow(i);
JCoRecordMetaData tableMeta = jCoTable.getRecordMetaData();
for (int j = 0; j < tableMeta.getFieldCount(); j++) {
tempRow.put(tableMeta.getName(j), jCoTable.getString(tableMeta.getName(j)));
}
tempTable.add(tempRow);
}
exportJson.put(table.getName(), tempTable);
});
}
//logger.info("exportJson:{}", exportJson.toJSONString());
}
} catch (JCoException | RuntimeException je) {
je.printStackTrace();
ex = je;
}
return exportJson;
}
/**
* 清理
*/
@Override
public void cleanUp() {
StringBuilder sb = new StringBuilder("任务 ").append(getName()).append(" 处理完成。 ");
if (ex != null) {
sb.append("出现异常:").append(ex);
} else {
sb.append("成功! ");
}
logger.info(sb.toString());
}
}
2.4.3、有状态多步骤作业
import com.sap.conn.jco.JCoContext;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoException;
/**
* 有状态多步骤作业
*
* @author wxhnyfy
*/
public class StatefulMultiStep extends StatelessMultiStep {
/**
* 有状态多步骤作业
*
* @param destination 目标
* @param calls 调用数量
* @param request 执行模板
*/
public StatefulMultiStep(JCoDestination destination, int calls, Request request) {
super(destination, calls, request);
}
/**
* 获取名称
*
* @return 名称
*/
@Override
public String getName() {
return "stateful Job-" + jobID;
}
/**
* 运行下一步
*/
@Override
public JSONObject runNextStep() {
if (executedCalls == 0) {
JCoContext.begin(destination);
}
return super.runNextStep();
}
/**
* 清理
*/
@Override
public void cleanUp() {
try {
JCoContext.end(destination);
} catch (JCoException je) {
ex = je;
}
super.cleanUp();
}
}
2.4.4、会话引用线程
import com.sap.conn.jco.ext.JCoSessionReference;
import com.sap.conn.jco.ext.SessionException;
import com.sap.conn.jco.ext.SessionReferenceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Hashtable;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
* 会话引用线程
*
* @author wxhnyfy
*/
public class SessionReferenceThread {
private static final Logger logger = LoggerFactory.getLogger(SessionReferenceThread.class);
/**
* 异步执行,能获取执行结果
*/
public static class GetResultCompletableFuture implements Supplier<JSONArray> {
//会话
static Hashtable<MultiStepJob, MySessionReference> sessions = new Hashtable<>();
//线程本地变量
static ThreadLocal<MySessionReference> localSessionReference = new ThreadLocal<>();
//计数器
private final CountDownLatch doneSignal;
//阻塞队列
private BlockingQueue<MultiStepJob> queue = new LinkedBlockingQueue<>();
public GetResultCompletableFuture(CountDownLatch doneSignal, BlockingQueue<MultiStepJob> queue) {
this.doneSignal = doneSignal;
this.queue = queue;
}
@Override
public JSONArray get() {
JSONArray jsonArray = new JSONArray();
try {
//无限循环
for (; ; ) {
//从队列中取出任务
MultiStepJob job = queue.poll(10, TimeUnit.SECONDS);
//队列为空,结束循环
if (job == null) {
return jsonArray;
}
MySessionReference sesRef = sessions.get(job);
if (sesRef == null) {
sesRef = new MySessionReference();
sessions.put(job, sesRef);
}
localSessionReference.set(sesRef);
logger.info("任务 " + job.getName() + " 开始。");
try {
JSONObject export = job.runNextStep();
if (export != null) {
jsonArray.add(export);
}
} catch (Throwable th) {
th.printStackTrace();
}
if (job.isFinished()) {
logger.info("任务 " + job.getName() + " 结束。");
sessions.remove(job);
job.cleanUp();
} else {
logger.info("任务 " + job.getName() + " 激活。");
queue.add(job);
}
localSessionReference.set(null);
}
} catch (InterruptedException e) {
//just leave
} finally {
doneSignal.countDown();
}
return jsonArray;
}
}
/**
* 同步执行,能获取执行结果
*/
public static class ReturnResultTaskCallable implements Callable<JSONArray> {
//会话
static Hashtable<MultiStepJob, MySessionReference> sessions = new Hashtable<>();
//线程本地变量
static ThreadLocal<MySessionReference> localSessionReference = new ThreadLocal<>();
//计数器
private final CountDownLatch doneSignal;
//阻塞队列
private BlockingQueue<MultiStepJob> queue = new LinkedBlockingQueue<>();
public ReturnResultTaskCallable(CountDownLatch doneSignal, BlockingQueue<MultiStepJob> queue) {
this.doneSignal = doneSignal;
this.queue = queue;
}
@Override
public JSONArray call() {
JSONArray jsonArray = new JSONArray();
try {
//无限循环
for (; ; ) {
//从队列中取出任务
MultiStepJob job = queue.poll(10, TimeUnit.SECONDS);
//队列为空,结束循环
if (job == null) {
return jsonArray;
}
MySessionReference sesRef = sessions.get(job);
if (sesRef == null) {
sesRef = new MySessionReference();
sessions.put(job, sesRef);
}
localSessionReference.set(sesRef);
logger.info("任务 " + job.getName() + " 开始。");
try {
JSONObject export = job.runNextStep();
if (export != null) {
jsonArray.add(export);
}
} catch (Throwable th) {
th.printStackTrace();
}
if (job.isFinished()) {
logger.info("任务 " + job.getName() + " 结束。");
sessions.remove(job);
job.cleanUp();
} else {
logger.info("任务 " + job.getName() + " 激活。");
queue.add(job);
}
localSessionReference.set(null);
}
} catch (InterruptedException e) {
//just leave
} finally {
doneSignal.countDown();
}
return jsonArray;
}
}
/**
* 同步执行,不需要获取执行结果
*/
public static class WorkerThread extends Thread {
//会话
static Hashtable<MultiStepJob, MySessionReference> sessions = new Hashtable<>();
//线程本地变量
static ThreadLocal<MySessionReference> localSessionReference = new ThreadLocal<>();
//计数器
private final CountDownLatch doneSignal;
//阻塞队列
private BlockingQueue<MultiStepJob> queue = new LinkedBlockingQueue<>();
/**
* 工作线程
*
* @param doneSignal 计数器
*/
WorkerThread(CountDownLatch doneSignal) {
this.doneSignal = doneSignal;
}
public WorkerThread(CountDownLatch doneSignal,
BlockingQueue<MultiStepJob> queue) {
this.doneSignal = doneSignal;
this.queue = queue;
}
@Override
public void run() {
try {
//无限循环
for (; ; ) {
//从队列中取出任务
MultiStepJob job = queue.poll(10, TimeUnit.SECONDS);
//队列为空,结束循环
if (job == null) {
return;
}
MySessionReference sesRef = sessions.get(job);
if (sesRef == null) {
sesRef = new MySessionReference();
sessions.put(job, sesRef);
}
localSessionReference.set(sesRef);
logger.info("任务 " + job.getName() + " 开始。");
try {
JSONObject export = job.runNextStep();
if (export != null) {
logger.info("执行结果:" + export.toJSONString());
}
} catch (Throwable th) {
th.printStackTrace();
}
if (job.isFinished()) {
logger.info("任务 " + job.getName() + " 结束。");
sessions.remove(job);
job.cleanUp();
} else {
logger.info("任务 " + job.getName() + " 激活。");
queue.add(job);
}
localSessionReference.set(null);
}
} catch (InterruptedException e) {
//just leave
} finally {
doneSignal.countDown();
}
}
}
public static class MySessionReference implements JCoSessionReference {
//原子整数
static AtomicInteger atomicInt = new AtomicInteger(0);
//ID
private final String id = "session-" + atomicInt.addAndGet(1);
/**
* 上下文已完成
*/
@Override
public void contextFinished() {
logger.debug("session " + id + " 上下文已完成");
}
/**
* 上下文已开始
*/
@Override
public void contextStarted() {
logger.debug("session " + id + " 上下文已开始");
}
/**
* 获取ID
*
* @return ID
*/
@Override
public String getID() {
return id;
}
}
public static class MySessionReferenceProvider implements SessionReferenceProvider {
/**
* 获取当前会话参考
*
* @param scopeType 作用域类型
* @return 会话参考
*/
@Override
public JCoSessionReference getCurrentSessionReference(String scopeType) {
//没有返回结果的线程
MySessionReference sesRef = WorkerThread.localSessionReference.get();
//异步执行带返回结果的线程
//MySessionReference sesRef = GetResultCompletableFuture.localSessionReference.get();
//同步执行带返回结果的线程
//MySessionReference sesRef = ReturnResultTaskCallable.localSessionReference.get();
if (sesRef != null) {
return sesRef;
}
throw new RuntimeException("未知线程:" + Thread.currentThread().getId());
}
/**
* 会话是否存活
*
* @param sessionId 会话ID
* @return 是否存活
*/
@Override
public boolean isSessionAlive(String sessionId) {
//没有返回结果的线程
Collection<MySessionReference> availableSessions = WorkerThread.sessions.values();
//异步执行带返回结果的线程
//Collection<MySessionReference> availableSessions = GetResultCompletableFuture.sessions.values();
//同步执行带返回结果的线程
//Collection<MySessionReference> availableSessions = ReturnResultTaskCallable.sessions.values();
for (MySessionReference ref : availableSessions) {
if (ref.getID().equals(sessionId)) {
return true;
}
}
return false;
}
/**
* JCoServer会话继续
*
* @param sessionID 会话ID
* @throws SessionException 会话异常
*/
@Override
public void jcoServerSessionContinued(String sessionID) throws SessionException {
logger.debug("JCoServer会话继续, sessionID: " + sessionID);
}
/**
* JCoServer会话完成
*
* @param sessionID 会话ID
*/
@Override
public void jcoServerSessionFinished(String sessionID) {
logger.debug("JCoServer会话完成, sessionID: " + sessionID);
}
/**
* JCoServer会话激活
*
* @param sessionID 会话ID
* @throws SessionException 会话异常
*/
@Override
public void jcoServerSessionPassivated(String sessionID) throws SessionException {
logger.debug("JCoServer会话激活, sessionID: " + sessionID);
}
/**
* JCoServer会话开始
*
* @return 会话参考
* @throws SessionException 会话异常
*/
@Override
public JCoSessionReference jcoServerSessionStarted() throws SessionException {
logger.debug("JCoServer会话开始");
return null;
}
}
}
2.4.5、测试代码
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.Environment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 多线程执行sap任务
*
* @author wxhnyfy
*/
public class TestMultipleThread {
private static final Logger logger = LoggerFactory.getLogger(TestMultipleThread.class);
private static final String DESTINATION_NAME1 = "SAP客户端";
/**
* 阻塞队列
*/
private static final BlockingQueue<MultiStepJob> QUEUE = new LinkedBlockingQueue<>();
static {
Properties connectProperties = new Properties();
connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, "服务器 IP");
connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, "系统编号");
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, "SAP集团");
connectProperties.setProperty(DestinationDataProvider.JCO_USER, "用户名");
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, "密码");
connectProperties.setProperty(DestinationDataProvider.JCO_LANG, "登录语言");
connectProperties.setProperty(DestinationDataProvider.JCO_SAPROUTER, "路由");
createDataFile(DESTINATION_NAME1, "jcoDestination", connectProperties);
connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, "3");
connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, "10");
}
/**
* 创建连接文件
*
* @param name 文件名
* @param suffix 后缀
* @param properties 属性
*/
static void createDataFile(String name, String suffix, Properties properties) {
File cfg = new File(name + "." + suffix);
cfg.deleteOnExit();
if (!cfg.exists()) {
try {
FileOutputStream fos = new FileOutputStream(cfg, false);
properties.store(fos, "for tests only !");
fos.close();
} catch (Exception e) {
throw new RuntimeException("无法创建 destination 文件 " + cfg.getName(), e);
}
}
}
/**
* 开 threadCount 个线程,从阻塞队列中取出任务执行
*/
public static void main(String[] args) {
//注册sessionReferenceProvider
Environment.registerSessionReferenceProvider(new SessionReferenceThread.MySessionReferenceProvider());
//线程池实例
ExecutorService executorService = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
//线程数,开启多少个线程执行阻塞队列中的任务
//一般要线程数要大于阻塞队列的长度,否则会有任务一直在队列中得不到执行
int threadCount = 5;
try {
JCoDestination destination = JCoDestinationManager.getDestination(DESTINATION_NAME1);
List<String> list = new ArrayList<>();
list.add("1000004926");
list.add("1000004927");
list.add("1000004928");
list.add("1000004929");
list.add("1000004930");
list.add("1000004931");
list.add("1000004932");
for (String num : list) {
KeyValueParam kp1 = new KeyValueParam("key1", "value1");
KeyValueParam kp2 = new KeyValueParam("key2", num);
List<KeyValueParam> params = new ArrayList<>();
params.add(kp1);
params.add(kp2);
Request request = new Request();
request.setFunctionName("function1");
request.setKeyValueParams(params);
//无状态的多步骤
QUEUE.add(new StatelessMultiStep(destination, 1, request));
//有状态的多步骤
//queue.add(new StatefulMultiStep(destination, 1, request));
}
logger.info(">>> Start");
JSONArray allThreadResult = new JSONArray();
//开线程
CountDownLatch doneSignal = new CountDownLatch(threadCount);
//这里开了10个线程,每个线程都会从阻塞队列中取任务执行,当阻塞队列中没有任务时,线程会结束
for (int i = 0; i < threadCount; i++) {
//方法1
SessionReferenceThread.WorkerThread workerThread = new SessionReferenceThread.WorkerThread(doneSignal, QUEUE);
executorService.submit(workerThread);
//方法2
//new SessionReferenceThread.WorkerThread(doneSignal, queue).start();
//方法3,异步执行
// SessionReferenceThread.GetResultCompletableFuture completableFuture = new SessionReferenceThread.GetResultCompletableFuture(doneSignal, queue);
// CompletableFuture<JSONArray> result = CompletableFuture.supplyAsync(completableFuture, executorService)
// .whenComplete((res, ex) -> {
// logger.info(">>> 当前线程:" + Thread.currentThread().getName());
// logger.info(">>> 结果:" + res);
// if (ex != null) {
// logger.error(">>> 异常:", ex);
// }
// });
//同步执行
// SessionReferenceThread.ReturnResultTaskCallable callable = new SessionReferenceThread.ReturnResultTaskCallable(doneSignal, queue);
// Future<JSONArray> future = executorService.submit(callable);
// //获取结果,阻塞线程。阻塞后会在一个线程中执行完队列中所有任务
// JSONArray result = future.get();
// allThreadResult.addAll(result);
}
//提交完任务后,关闭线程池,不再接受新的任务
executorService.shutdown();
logger.info(">>> Wait ... ");
try {
doneSignal.await();
} catch (InterruptedException ie) {
//just leave
}
// logger.info(">>> 执行结果:" + allThreadResult.toJSONString());
logger.info(">>> Done");
} catch (Exception e) {
e.printStackTrace();
}
}
}
作者:菠萝蚊鸭