第一步,环境说明:
SAPJCO,SAP提供的JAVA环境的中间件类库,JAVA的程序可以通过JCO类库,同SAP系统交换数据。
client 工作方式, 中间件叫outbound ( Java call SAP ) the examples file name: StepByStepClient.java
server 工作方式,中间件叫inbound ( SAP call java ) the examples file name: StepByStepServer.java
SAP JCO 地址:http://service.sap.com/connectors
SAP JCO版本 3.0.18 , 官网例子代码文件 StepByStepServer.java, 2018.4.25
我们来调试StepByStepServer.java程序,保留其中的第4个函数,实现sap call java server.
第二步,SAP系统中的必要参数:
ECC6 EHP6以后,可以动态调整,不用重启SAP服务器就可以生效。
SAP系统中,RZ10里面参数gw/acl_mode的值要0,这样才可以允许运行SM59有注册网关服务。
第三步,SAP系统中,SE37里面定义一个空的RFC调用函数,ABAP内容如下:
*函数名称JCO_SET_TRACE
FUNCTION JCO_SET_TRACE.
*"----------------------------------------------------------------------
*"*"局部接口:
*" IMPORTING
*" VALUE(TRACE_LEVEL) TYPE SY-LISEL
*" VALUE(TRACE_PATH) TYPE SY-LISEL
*"----------------------------------------------------------------------
ENDFUNCTION.
第四步,SE38里面做一个ABAP程序来调用上面的空的RFC:
REPORT ZTEST_JCO_SET_TRACE.
DATA trace_level TYPE N.
DATA trace_path TYPE STRING.DATA msg(255) TYPE C.
trace_level = '555555555'.
trace_path = '33333333333333.'.CALL FUNCTION 'JCO_SET_TRACE'
destination 'JCO_SERVER'
EXPORTING
TRACE_LEVEL = trace_level
TRACE_PATH = trace_path
EXCEPTIONS
COMMUNICATION_FAILURE = 1
SYSTEM_FAILURE = 2 MESSAGE msg
RESOURCE_FAILURE = 3
OTHERS = 4.
IF SY-SUBRC <> 0.
write: 'ERROR: ', SY-SUBRC, msg.
ENDIF.
说明:
1、注意看这个语法,CALL FUNCTION 'JCO_SET_TRACE' destination 'JCO_SERVER'
它通过JCO_SERVER网关,调用JCO_SET_TRACE函数,然后把这个空RFC函数带的参数数据推到SAP系统外。
2、JCO_SERVER网关怎么来?SM59中自己建一个连接
3、有一个“注册的服务器程序,程序标识【】”,需要同JAVA 程序配置文件中,server相关参数
中的PROGRAM_ID一致。 这个程序名称,还可以在SAP系统的SMGW事物代码中的登陆客户端信息中看到。
第五步,JAVA程序这块的代码:
精简了官网例子代码文件 StepByStepServer.java
import java.util.Hashtable;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CountDownLatch;
import java.io.File;
import java.io.FileOutputStream;
import com.sap.conn.jco.AbapException;
import com.sap.conn.jco.JCoContext;
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.JCoFunctionTemplate;
import com.sap.conn.jco.JCoStructure;
import com.sap.conn.jco.JCoTable;
import com.sap.conn.jco.JCo;
import com.sap.conn.jco.JCoCustomRepository;
import com.sap.conn.jco.JCoListMetaData;
import com.sap.conn.jco.JCoMetaData;
import com.sap.conn.jco.ext.DestinationDataProvider;
import com.sap.conn.jco.ext.ServerDataProvider;
import com.sap.conn.jco.server.DefaultServerHandlerFactory;
import com.sap.conn.jco.server.JCoServer;
import com.sap.conn.jco.server.JCoServerContext;
import com.sap.conn.jco.server.JCoServerContextInfo;
import com.sap.conn.jco.server.JCoServerErrorListener;
import com.sap.conn.jco.server.JCoServerExceptionListener;
import com.sap.conn.jco.server.JCoServerFactory;
import com.sap.conn.jco.server.JCoServerFunctionHandler;
import com.sap.conn.jco.server.JCoServerState;
import com.sap.conn.jco.server.JCoServerStateChangedListener;
import com.sap.conn.jco.server.JCoServerTIDHandler;
public class StepByStepServer
{
static String SERVER_NAME1 = "SERVER";
static String DESTINATION_NAME1 = "ABAP_AS_WITHOUT_POOL";
static String DESTINATION_NAME2 = "ABAP_AS_WITH_POOL";
static
{
Properties connectProperties = new Properties();
connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST,"10.0.3.51");
connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR,"00");
connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT,"100");
connectProperties.setProperty(DestinationDataProvider.JCO_USER,"60489");
connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD,"********");
connectProperties.setProperty(DestinationDataProvider.JCO_LANG,"ZH");
createDataFile(DESTINATION_NAME1, "jcoDestination",connectProperties);
connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, "3");
connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, "10");
createDataFile(DESTINATION_NAME2, "jcoDestination",connectProperties);
Properties servertProperties = new Properties();
servertProperties.setProperty(ServerDataProvider.JCO_GWHOST,"10.0.3.51");
servertProperties.setProperty(ServerDataProvider.JCO_GWSERV,"3300");
servertProperties.setProperty(ServerDataProvider.JCO_PROGID,"JCO_SERVER");
servertProperties.setProperty(ServerDataProvider.JCO_REP_DEST,"ABAP_AS_WITH_POOL");
servertProperties.setProperty(ServerDataProvider.JCO_CONNECTION_COUNT, "2");
createDataFile(SERVER_NAME1, "jcoServer",servertProperties);
}
static void createDataFile(String name, String suffix, Properties properties)
{
File cfg = new File(name+"."+suffix);
if(!cfg.exists())
{
try
{
FileOutputStream fos = new FileOutputStream(cfg,false);
properties.store(fos, "for tests only !");
fos.close();
}
catch (Exception e)
{
throw new RuntimeException("Unable to create thedestination file " + cfg.getName(), e);
}
}
}
/**
* The following server example demonstrates how to develop a function module available only on Java side. At first
* we create the respective function meta data, because the function is not available in ABAP DDIC. Then the function
* meta data is stored in a custom repository which is registered with the server instance. Naturally we also
* need the implementation of the function - see the class SetTraceHandler.
*
* Last but not least, the following ABAP report invokes the function module SetTraceHandler.
REPORT ZTEST_JCO_SET_TRACE.
DATA trace_level TYPE N.
DATA trace_path TYPE STRING.
DATA msg(255) TYPE C.
trace_level = '5'.
trace_path = '.'.
CALL FUNCTION 'JCO_SET_TRACE' destination 'JCO_SERVER'
EXPORTING
TRACE_LEVEL = trace_level
TRACE_PATH = trace_path
EXCEPTIONS
COMMUNICATION_FAILURE = 1
SYSTEM_FAILURE = 2 MESSAGE msg
RESOURCE_FAILURE = 3
OTHERS = 4
.
IF SY-SUBRC <> 0.
write: 'ERROR: ', SY-SUBRC, msg.
ENDIF.
*/
static class MyFunction implements JCoServerFunctionHandler
{
public void handleRequest(JCoServerContext serverCtx, JCoFunction function)
{
int level = function.getImportParameterList().getInt("TRACE_LEVEL");
String path = function.getImportParameterList().getString("TRACE_PATH");
System.out.println("A:" + level + " B:"+ path);
// JCo.setTrace(level, path);
}
}
public static void main(String[] a)
{
JCoListMetaData impList = JCo.createListMetaData("IMPORTS");
impList.add("TRACE_LEVEL", JCoMetaData.TYPE_NUM, 1, 2, 0, null, null, JCoListMetaData.IMPORT_PARAMETER, null, null);
impList.add("TRACE_PATH", JCoMetaData.TYPE_STRING, 8, 8, 0, null, null, JCoListMetaData.IMPORT_PARAMETER, null, null);
impList.lock();
JCoFunctionTemplate fT = JCo.createFunctionTemplate("JCO_SET_TRACE", impList, null, null, null, null);
JCoCustomRepository cR = JCo.createCustomRepository("MyCustomRepository");
cR.addFunctionTemplateToCache(fT); //Repository add FunctionTemplate
JCoServer server ;
try
{
server = JCoServerFactory.getServer(SERVER_NAME1); //create server
}
catch(JCoException ex)
{
throw new RuntimeException("Unable to create the server " + SERVER_NAME1 + " because of " + ex.getMessage(), ex);
}
String repDest = server.getRepositoryDestination();
System.out.println("repDest:"+ repDest);
if(repDest!=null)
{
try
{
cR.setDestination(JCoDestinationManager.getDestination(repDest)); //Repository setDestination
}
catch (JCoException e)
{
}
}
server.setRepository(cR); //server add Repository
JCoServerFunctionHandler myf = new MyFunction();
DefaultServerHandlerFactory.FunctionHandlerFactory factory = new DefaultServerHandlerFactory.FunctionHandlerFactory();
factory.registerHandler(fT.getName(), myf);
server.setCallHandlerFactory(factory);
server.start();
System.out.println("The program can be stoped using <ctrl>+<c>");
}
}
程序说明:
1、连接SAP服务器的参数配置,有client和server。
client程序运行:用于java程序同SAP服务器按SAP的协议建立的长SOCKET连接。
server程序:等待SAP对java程序的调用,一旦SAP的JCO_SET_TRACE发起调用,数据就会在JAVA对应的程序中接收。
可以看到,JCO_SET_TRACE函数发了2个最简单的参数变量数据过来。
2、JAVA SAPJCO类库,Repository对象,function对象,IMPORTS对象,TABLE对象等等,同ABAP RFC是对应的。
第六步,我们从SAP传2个变量数据到JAVA:
上面我们先打开我们的java server程序.
执行SAP端的ABAP程序,发起调用。
可以看到,SAP的2个变量5555,3333333333333 传到了JAVA程序中,并显示了出来。
(为啥只显示一个5,ABAP用的string, JAVA是用的int,还没来得及改)
真实场景中传结构(一行数据),表(多行数据),接收相应参数即可。
第七步,监控:
事务代码SMGW,可以看到JAVA和SAP建立的长连接。
一旦长连接建立,SAP系统中还可以在SM59中测试连接延迟。