写点什么

使用 Java connector 消费 ABAP 系统的函数

作者:Jerry Wang
  • 2021 年 11 月 20 日
  • 本文字数:3995 字

    阅读完需:约 13 分钟

使用 Java connector 消费 ABAP 系统的函数

我的 ABAP 系统有个函数名叫 ZDIS_GET_UPSELL_MATERIALS,输入一个 customer ID 和 product ID,会输出为这对客户和 product 组合维护的一组 Upsell product ID 和描述信息。




测试如下:下面是使用 Java 消费该函数的代码:


package jco;
import java.io.File;import java.io.FileOutputStream;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.JCoFunction;import com.sap.conn.jco.JCoParameterList;import com.sap.conn.jco.JCoRepository;import com.sap.conn.jco.JCoTable;import com.sap.conn.jco.ext.DestinationDataProvider;
/** * basic examples for Java to ABAP communication * See help: https://help.sap.com/saphelp_nwpi711/helpdata/en/48/70792c872c1b5ae10000000a42189c/frameset.htm */public class StepByStepClient{ static String DESTINATION_NAME = "ABAP_AS_WITHOUT_POOL"; static public final String ABAP_DURATION = "abapLayerDuration"; static public final String UPSELL_PRODUCT = "upsellProducts"; static public final String PRODUCT_ID = "productID"; static public final String PRODUCT_TEXT = "productText"; static private Properties prepareProperty(){ Properties connectProperties = new Properties(); connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, "your abap system host name"); connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR, "00"); connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, "111"); connectProperties.setProperty(DestinationDataProvider.JCO_USER, "WANGJER"); connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, "your password"); connectProperties.setProperty(DestinationDataProvider.JCO_LANG, "en"); createDestinationDataFile(DESTINATION_NAME, connectProperties); connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, "3"); connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT, "10"); createDestinationDataFile(DESTINATION_NAME, connectProperties); return connectProperties; } static public void main(String[] arg) { createDestinationDataFile(DESTINATION_NAME, prepareProperty()); JCoDestination destination = null; try { destination = JCoDestinationManager.getDestination(DESTINATION_NAME); JCoRepository repo = destination.getRepository(); JCoFunction stfcConnection = repo.getFunction("ZDIS_GET_UPSELL_MATERIALS");
JCoParameterList imports = stfcConnection.getImportParameterList(); String customerID = "1000040"; String materialID = "11";
imports.setValue("IV_CUSTOMER_ID", customerID); imports.setValue("IV_MATERIAL_ID", materialID);
stfcConnection.execute(destination); JCoParameterList exports = stfcConnection.getExportParameterList(); // int result = exports.getInt("EV_RESULT"); int abapDuration = exports.getInt("EV_DURATION"); StringBuilder sb = new StringBuilder(); sb.append("{ \"" + ABAP_DURATION + "\": " + abapDuration + ","); sb.append("\"" + UPSELL_PRODUCT + "\":["); JCoTable codes = exports.getTable("ET_MATERIALS"); int row = codes.getNumRows(); System.out.println("Total rows: " + row); System.out.println("ABAP duration: " + abapDuration); for( int i = 0; i < row; i++){ codes.setRow(i); System.out.println(codes.getString("MATERIAL_ID") + '\t' + codes.getString("MATERIAL_TEXT")); sb.append("{\"" + PRODUCT_ID + "\":" + codes.getString("MATERIAL_ID") + "," + "\"" + PRODUCT_TEXT + "\":\"" + codes.getString("MATERIAL_TEXT") + "\""); if( i < row - 1){ sb.append("},"); } else{ sb.append("}"); } } sb.append("]}"); System.out.println("Final json: " + sb.toString()); } catch (JCoException e) { // TODO Auto-generated catch block e.printStackTrace(); } } static private void createDestinationDataFile(String destinationName, Properties connectProperties) { File destCfg = new File(destinationName+".jcoDestination"); try { FileOutputStream fos = new FileOutputStream(destCfg, false); connectProperties.store(fos, "for tests only !"); fos.close(); } catch (Exception e) { throw new RuntimeException("Unable to create the destination files", e); } }}
复制代码


为简单起见没有使用 Google 的 gson 库进行 Json 的序列化。执行结果:




ABAP

ABAP help 文档里对**LOAD-OF-PROGRAM"的关键字是这样描述的:


This event keyword defines the program constructor of an executable program, a module pool, a function group, or a subroutine pool. The program constructor is an event block whose event is raised by the ABAP-runtime environment when one of the executable programs mentioned above is loaded into the internal session.


以 Function group 为例,每当一个 function group 里的任意一个 function module 第一次被调用时,对应的 ABAP program 被加载到 internal session 里,同时 ABAP 运行时抛出 LOAD-OF-PROGRAM, 执行应用程序员编写的事件处理逻辑。


现在我有一个名为 ZTOMCAT 的 function group。其 LOAD-OF-PROGRAM 就负责弹出调试器。



我有两个 report。Report 2 的源代码:


REPORT ZJERRY_RE2.
call FUNCTION 'ZTEST_FM_1'.
复制代码


Report 1:


CALL FUNCTION 'ZTEST_FM_1'.
SUBMIT zjerry_re2 AND RETURN.
复制代码


那么我执行 report1,断点会触发一次还是两次?


答案是两次。




LOAD-OF-PROGRAM 在这种场景下的行为,ABAP help 已经说的很清楚了:


When a program is called using SUBMIT or using a transaction code, a new internal session is opened in every call and the event block is executed once in every call.


每次 program 通过 SUBMIT 或者事务码的方式调用时,会起一个新的 internal session,在此新的 session 里 LOAD-OF-PROGRAM 会触发一次。


下图也直观表明了每次调用 SUBMIT( calling programs)时会新起一个 Internal Session。


Tomcat 库文件的重复加载问题

我的 pom.xml 里定义了一个 gson 的依赖关系,ABAPer 可以把其类比成在我的 Java 代码里调用 Google 提供的 gson API。



打成 war 包之后,该库文件位于 WEB-INF/lib 文件夹下。



那么如果我有多个 Web 应用都用到了 gson, 则每个应用的 WEB-INF\lib 文件夹下面都有 gson 的 jar 文件。


问题:在运行时,Tomcat 只会将一份 gson.jar 的内容加载到内存么?


答案是不会。根据 Tomcat 的官方文档,Tomcat 会为每个 Web 应用创建一个专属的 ClassLoader 实例,每个应用的 WEB-INF\lib 下的资源,对于其他应用来说不可见,彼此隔离。



当然如果想只用一份库文件,可以把它放到目录 [tomcat-installation-directory]/common/lib 下面。更多细节参考 stackoverflow 上的讨论.

WebClient UI

我们在 WebClient UI 的开发工具里点了 Test 按钮,



会在浏览器以测试模式打开选中的 view。这背后发生了什么事?注意浏览器地址栏的 bspwd_cmp_test,这是什么东西?Jerry 倾向于把它当作是 CRM WebClient UI component 在测试模式下的启动器。



当我们以测试模式打开一个 CRM WebClient UI component 时,这个 component 的页面实际上是显示在一个 iframe 内,该 iframe 在上述提到的 bspwd_cmp_test 里实现:



点击 Test 按钮,在下图的 iframe 里会把真正被测试的 WebClient UI component 的页面嵌入进去。这个 iframe 加载时,会执行 onload 事件绑定的 JavaScript 处理函数:startBSPApplication。



这个函数为 document 动态生成 HTML 源代码:



启动器 bspwd_cmp_test 的页面 session_single_frame.htm 中立即执行的 Javascript:



运行时展开如下:


Tomcat

最初我们部署 Web 应用到 tomcat 上的做法,一般是首先用 Maven 将 Web 项目打包成 war 文件,再手动或者写脚本将 war 文件拷贝到 tomcat 对应目录下。现在我们可以在项目 pom.xml 文件里使用下面这个插件:


<plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><path>/jerry</path><port>9090</port><uriEncoding>UTF-8</uriEncoding></configuration></plugin>
复制代码


然后在命令行里执行命令:mvn tomcat7:run


这个 tomcat7-maven-plugin 插件可以作为启动器,帮我们启动 Tomcat 并且加载 war 文件,



之后我们可以直接使用 http://localhost:9090/jerry 访问 Web 应用了:



发布于: 41 分钟前阅读数: 4
用户头像

Jerry Wang

关注

个人微信公众号:汪子熙 2017.12.03 加入

SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使。

评论

发布
暂无评论
使用 Java connector 消费 ABAP 系统的函数