Java配置SAP——Java调用SAP系统的RFC接口
Java 8一级目录二级目录三级目录一级目录二级目录三级目录
Java配置SAP——Java调用SAP系统的RFC接口
前言
名词说明
RFC:(Remote Function Call,远程功能调用)。
SAP系统:是ERP解决方案的先驱,也是全世界排名第一的ERP软件,可以为各种行业、不同规模的企业提供全面的解决方案。(只需要记住,里面存了很多各种各样的数据,我们在从sap取数据的时候,sap提供了一种rfc接口的方式)
代码结构
各个文件的作用
- CallRfc:调用方法。
- JCOProvider:它实现了DestinationDataProvider类,用于安全的自定义连接参数。
- RfcManager:rfc接口的调用管理类,会读取源文件下的.properties的参数配置,然后进行连接测试,并保持连接。
连接方式
连接方式分为直连方式和连接池方式:
直连方式:
static String ABAP_AS = "ABAP_AS_WITHOUT_POOL";
连接池方式:
static String ABAP_AS_POOLED = "ABAP_AS_WITH_POOL";
连接池方式与直接连接方式主要是以下有所区别:
JCO_PEAK_LIMIT - 同时可创建的最大活动连接数,0表示无限制,默认为JCO_POOL_CAPACITY的值;
如果小于JCO_POOL_CAPACITY的值,则自动设置为该值,在没有设置JCO_POOL_CAPACITY的情况下为0。
connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, "10");
// JCO_POOL_CAPACITY - 空闲连接数,如果为0,则没有连接池效果,默认为1
connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, "3");
sapjco3.jar包导入
部分小伙伴导入sapjco3.jar包的时候可能会出现无法找到依赖包的问题,这里就windows系统给出解决方案。
下载链接: sapjco3(没有积分的小伙伴私聊获取)
下载后,将sapjco3.dll文件放入window/system32下即可。
源码示例
CallRfc.java
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.sap.conn.jco.JCoFunction;
import com.sap.conn.jco.JCoParameterList;
import com.sap.conn.jco.JCoTable;
/**
* @Description: rfc调用接口
* @author jiangzl
* @date 2020-12-21
* @ClassName: CallRfc
*
*/
@Service
public class CallRfc
{
public List<Map<String,Object>> CallSpreadRfc(Map<String,Object> paramMap, List<Map<String, Object>> paramList) {
List<Map<String,Object>> list = new ArrayList<>();
// 获取RFC 对象
JCoFunction function = RfcManager.getFunction("RFC_NAME");
//获取参数列表
JCoParameterList importParam = function.getImportParameterList();
importParam.setValue("PARAM_1", paramMap.get("date"));
//获取表字段
JCoParameterList inTableParam = function.getTableParameterList();
if (null!=paramMap.get("params") && !StringUtil.isEmpty(paramMap.get("params").toString()))
{
JCoTable tableInD = inTableParam.getTable("PARAMS");
String[] werks = paramMap.get("params").toString().split(",");
for (int i = 0; i < werks.length; i++)
{
tableInD.appendRow();
tableInD.setValue("PARAMS",werks[i]);
}
}
// 执行RFC
RfcManager.execute(function);
// 获取RFC返回的字段值
JCoParameterList exportParam = function.getExportParameterList();
// 遍历RFC返回的表对象
JCoTable tb = function.getTableParameterList().getTable("RESULT_NAME");
for (int i = 0; i < tb.getNumRows(); i++) {
tb.setRow(i);
Map<String,Object> map = new LinkedHashMap<>();
map.put("name1", tb.getString("NAME1"));
......
list.add(map);
}
paramList.addAll(list);
return list;
}
}
JCOProvider
import java.util.HashMap;
import java.util.Properties;
import com.sap.conn.jco.ext.DataProviderException;
import com.sap.conn.jco.ext.DestinationDataEventListener;
import com.sap.conn.jco.ext.DestinationDataProvider;
/**
* @Description: jco服务提供
* @author jiangzl
* @date 2020-12-21
* @ClassName: JCOProvider
*
*/
public class JCOProvider implements DestinationDataProvider {
private HashMap<String, Properties> secureDBStorage = new HashMap<String, Properties>();
private DestinationDataEventListener eL;
@Override
public Properties getDestinationProperties(String destinationName) {
try
{
//read the destination from DB
Properties p = secureDBStorage.get(destinationName);
if(p!=null)
{
//check if all is correct, for example
if(p.isEmpty())
throw new DataProviderException(DataProviderException.Reason.INVALID_CONFIGURATION, "destination configuration is incorrect", null);
return p;
}
return null;
}
catch(RuntimeException re)
{
throw new DataProviderException(DataProviderException.Reason.INTERNAL_ERROR, re);
}
}
@Override
public void setDestinationDataEventListener(
DestinationDataEventListener eventListener) {
this.eL = eventListener;
}
@Override
public boolean supportsEvents() {
return true;
}
//implementation that saves the properties in a very secure way
public void changePropertiesForABAP_AS(String destName, Properties properties) {
synchronized(secureDBStorage)
{
if(properties==null)
{
if(secureDBStorage.remove(destName)!=null)
eL.deleted(destName);
}
else
{
secureDBStorage.put(destName, properties);
eL.updated(destName); // create or updated
}
}
}
}
RfcManager
/**
* @Description: rfc调用管理类
* @author jiangzl
* @date 2020-12-21
* @ClassName: RfcManager
*
*/
public class RfcManager
{
private static Logger logger = Logger.getLogger(RfcManager.class);
private static String ABAP_AS_POOLED = "ABAP_AS_POOL";
private static JCOProvider provider = null;
private static JCoDestination destination = null;
static {
Properties properties = loadProperties();
provider = new JCOProvider();
// catch IllegalStateException if an instance is already registered
try {
Environment.registerDestinationDataProvider(provider);
} catch (IllegalStateException e) {
logger.debug(e);
}
provider.changePropertiesForABAP_AS(ABAP_AS_POOLED, properties);
}
public static Properties loadProperties() {
RfcManager manager = new RfcManager();
Properties prop = new Properties();
try {
prop.load(manager.getClass().getResourceAsStream(
"/sap_conf.properties"));
} catch (IOException e) {
logger.debug(e);
}
return prop;
}
public static JCoDestination getDestination() throws JCoException {
if (destination == null) {
destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);
}
return destination;
}
public static JCoFunction getFunction(String functionName) {
JCoFunction function = null;
try {
function = getDestination().getRepository()
.getFunctionTemplate(functionName).getFunction();
} catch (JCoException e) {
logger.error(e);
} catch (NullPointerException e) {
logger.error(e);
}
return function;
}
public static void execute(JCoFunction function) {
logger.debug("SAP Function Name : " + function.getName());
JCoParameterList paramList = function.getImportParameterList();
if (paramList != null) {
logger.debug("Function Import Structure : " + paramList.toString());
}
try {
function.execute(getDestination());
} catch (JCoException e) {
try
{
logger.info("Destination client:"+getDestination().getClient());
logger.info("Destination serverHost:"+getDestination().getApplicationServerHost());
logger.info("Destination destinationName:"+getDestination().getDestinationName());
} catch (JCoException e1)
{
e1.printStackTrace();
}
logger.error("Destination error : " + e);
}
paramList = function.getExportParameterList();
if (paramList != null) {
logger.debug("Function Export Structure : " + paramList.toString());
}
}
public static String ping() {
String msg = null;
try {
getDestination().ping();
msg = "Destination " + ABAP_AS_POOLED + " is ok";
} catch (JCoException ex) {
//msg = StringUtil.getExceptionTrace(ex);
logger.info(ex.getMessage());
}
logger.debug(msg);
return msg;
}
public static void main(String[] args) {
RfcManager.ping();
}
}
pom文件
<dependency>
<groupId>com.github.easonjim</groupId>
<artifactId>com.sap.conn.jco.sapjco3</artifactId>
<version>3.0.11</version>
</dependency>
报错信息解决
错误描述
JAVA连接SAP时出现的错误(102) JCO_ERROR_COMMUNICATION: Initialization of repository destination ABAP_AS_WITH_POOL failed: Connect to SAP gateway failed
OCATION CPIC (TCP/IP) on local host with Unicode
ERROR partner 'ip:port' not reached
TIME Sun Apr 28 18:47:59 2019
RELEASE 721
COMPONENT NI (network interface)
VERSION 40
RC -10
MODULE nixxi.cpp
LINE 3289
DETAIL NiPConnect2: ip:port
SYSTEM CALL connect
ERRNO 10060
ERRNO TEXT WSAETIMEDOUT: Connection timed out
COUNTER 2
错误分析
对于gateway 这种错误,一般都是网关配置的错误。使用外部系统调用sap时(解决方法以java代码为例),请求需要知道目标的地址和端口。两个缺一不可。
地址
- IP地址填错,仔细检查即可;
- java配置方式出错,以私有配置的方式取代properties的方式。可看组/服务器选择连接方式的配置示例;
- 检查GROUP分组参数,以及连接方式。
注意:JCO_SAPROUTER并不是可选的,值得是默认路由端口,一般为3600,如果有特殊规定,记得在这里进行设置,或者在services中进行设置。
端口
5. 未建立 Jco Serever 监听服务时相关设置。
windous
进入 %SystemRoot%\System32\drivers\etc
1.修改 services文件,在services文件尾部 将 jco.server.gwserv:sapgw00 属性值 sapgw00 加入 SAP 端口映射
sapdp00 3200/tcp #SAP Server
sapgw00 3300/tcp #SAP Gateway
2.修改 hosts文件,在 hosts中 将 jco.server.gwhost:gmdev01 属性值 gmdev01 加入 SAP服务器IP 地址映射
10.86.95.121 gmdev01
3.具体示例
参考项目目录内的 services/hosts 文件
linux
1.执行 vi /etc/hosts
修改 hosts文件,在 hosts中 将 jco.server.gwhost:gmdev01 属性值 gmdev01 加入 SAP服务器IP 地址映射
10.86.95.121 gmdev01
2.执行 vi /etc/services
修改 services文件,在services文件尾部 将 jco.server.gwserv:sapgw00 属性值 sapgw00 加入 SAP 端口映射
sapdp00 3200/tcp #SAP Server
sapgw00 3300/tcp #SAP Gateway
组/服务器选择连接方式的配置
connectProperties.setProperty(DestinationDataProvider.JCO_MSHOST, "0.0.0.0");
connectProperties.setProperty(DestinationDataProvider.JCO_R3NAME, "XXX");
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, "888");
connectProperties.setProperty(DestinationDataProvider.JCO_USER, "XXX");
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, "XXX");
connectProperties.setProperty(DestinationDataProvider.JCO_GROUP, "RFC");
connectProperties.setProperty(DestinationDataProvider.JCO_SAPROUTER," xxxxxx ");
connectProperties.setProperty(DestinationDataProvider.JCO_LANG, "en");
createDataFile(ABAP_MS, "jcoDestination", connectProperties);
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)