Java配置SAP——Java调用SAP系统的RFC接口

前言

名词说明

RFC:(Remote Function Call,远程功能调用)。
SAP系统:是ERP解决方案的先驱,也是全世界排名第一的ERP软件,可以为各种行业、不同规模的企业提供全面的解决方案。(只需要记住,里面存了很多各种各样的数据,我们在从sap取数据的时候,sap提供了一种rfc接口的方式)

代码结构

在这里插入图片描述

各个文件的作用

  1. CallRfc:调用方法。
  2. JCOProvider:它实现了DestinationDataProvider类,用于安全的自定义连接参数。
  3. 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代码为例),请求需要知道目标的地址和端口。两个缺一不可。
地址

  1. IP地址填错,仔细检查即可;
  2. java配置方式出错,以私有配置的方式取代properties的方式。可看组/服务器选择连接方式的配置示例;
  3. 检查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);

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
调用 SAPRFC 接口,需要使用 SAP 提供的 Java Connector(SAP JCo)库。在使用 SAP JCo 调用 RFC 接口时,需要在 SAP JCo 的配置文件中设置系统 ID(System ID),以便正确地连接到 SAP 系统。 要配置 SAP JCo 的系统 ID,需要进行以下步骤: 1. 打开 SAP JCo 的配置文件 sapjco3.properties,该文件通常位于应用程序的 classpath 下。 2. 在 sapjco3.properties 文件中添加以下配置项: ``` jco.client.sysnr=<系统编号> jco.client.client=<客户端> jco.client.user=<用户名> jco.client.passwd=<密码> jco.client.lang=<语言> jco.client.ashost=<应用服务器主机名> jco.client.saprouter=<SAP 路由器> ``` 其中,<系统编号> 是 SAP 系统的编号,<客户端> 是 SAP 客户端的编号,<用户名> 和 <密码> 是 SAP 用户的登录信息,<语言> 是登录时使用的语言代码,<应用服务器主机名> 是 SAP 应用服务器的主机名,<SAP 路由器> 是可选的 SAP 路由器配置。 3. 在 Java 代码中使用 SAP JCo 的 API 调用 RFC 接口时,可以使用以下代码获取 SAP JCo 的配置信息: ``` JCoDestination destination = JCoDestinationManager.getDestination("<destination>"); ``` 其中,<destination> 是 SAP JCo 的目标名称,可以在 sapjco3.properties 文件中配置。 4. 使用 SAP JCo 的 API 调用 RFC 接口时,需要使用 SAP JCo 的函数模块名称和函数模块的输入参数,例如: ``` JCoFunction function = destination.getRepository().getFunction("<function_module>"); if (function == null) { throw new RuntimeException("<function_module> not found in SAP system."); } function.getImportParameterList().setValue("<parameter_name>", "<parameter_value>"); function.execute(destination); ``` 其中,<function_module> 是 SAP RFC 函数模块的名称,<parameter_name> 和 <parameter_value> 是函数模块的输入参数名称和值。 以上是 Java 调用 SAP RFC 接口配置系统 ID 的基本步骤。具体的配置调用方法可能会因不同的 SAP 系统RFC 接口而有所不同,需要根据实际情况进行调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值