IBM Support

使用IBM Java Toolbox实现Java/C混合编程

Technical Blog Post


Abstract

使用IBM Java Toolbox实现Java/C混合编程

Body

某些项目的开发需要在Java中调用非Java代码(例如C/C++)编写的接口。例如我们需要使用一些系统级别的API来完成开发工作,但是这些API并不提供Java语言的接口。在这种情形下,我们通常会选择使用Java本地方法来实现Java对这类API的调用。

Java本地方法最普遍的一种实现方式是JNI(Java Native Interface)调用。通过JNIJava可以与被调用的C/C++对象之间交换数据。虽然JNI的功能非常强大,但是使用JNI的本地方法必须遵循一系列的特殊规范,例如新的数据类型和使用javah -jni命令生成的头文件等等。一般而言,使用JNI方式的工程需要更多的开发和维护成本。

在一些简单的应用场景中,我们仅仅希望通过Java调用某些service program中的函数,获取相应的处理结果。此时,采用IBM Java Toolbox中提供的Service program call方法也许是一种更为快捷的实现方式。

本文将通过一个简单的范例介绍在IBM i上开发基于service program call模式的Java/C混合编程项目的思路。

 

1)创建一个简单的C程序。

这个C程序里定义了一个函数RelayCall,该函数接受一个输入参数input,一个输出参数output。函数将对输入参数input中的字符串进行一定处理后,将结果放回输出字符串中,并根据执行的成功与否给出相应的返回值。

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

int RelayCall(char* input, char* output)

{

       if(!input || !output)        return -1;

    if(strlen(input) == 0)        return -2;

       strcpy(output, "We are in C code. Input is ");

       strcat(output, input);

       return 0;

}

2)将这段C代码编译成一个service program.

假设第一步中的代码被保存在了IFS路径/samplelib/samplec.c下。而生成的service program将放置在MYLIB这个Library下。我们通过如下两条命令来编译这个service program

CRTCMOD MODULE(MYLIB/SAMPLEC) SRCSTMF('/samplelib/samplec.c')        

CRTSRVPGM SRVPGM(MYLIB/SAMPLEC) MODULE(MYLIB/SAMPLEC) EXPORT(*ALL) SRCFILE(MYLIB/SAMPLEC)

图像

编译完成后,可以通过

WRKLIB MYLIB

检测该service program是否已经成功生成到该Library下面。

图像

图像

3)编写Java代码

首先请确保你的机器上安装了最新版本的IBM Java Toolbox产品。我们将下面的Java代码保存为SrvpgmCallSample.java文件,并同样放置在/samplelib目录下。

import com.ibm.as400.access.AS400;

import com.ibm.as400.access.AS400Bin4;

import com.ibm.as400.access.AS400Message;

import com.ibm.as400.access.AS400Text;

import com.ibm.as400.access.ProgramParameter;

import com.ibm.as400.access.ServiceProgramCall;

 

public class SrvpgmCallSample {     

       public static void main(String[] args) {

              try {

                     // 创建与本地方法相对应的参数列表。

                     ProgramParameter[] parameterList = new ProgramParameter[2];

                     // 创建AS400对象,调用的service program必须存在于这台AS400机器上。

                     AS400 system = new AS400();

                     AS400Bin4 bin4 = new AS400Bin4();

                     String input = args[0];

                     // 将输入字符串转换成Byte数组的格式,注意添加结束字符'\0'

                     byte[] inputString = new AS400Text(input.length() + 1, system).toBytes(input+'\0');

                     parameterList[0] = new ProgramParameter(ProgramParameter.PASS_BY_REFERENCE, inputString);

                     parameterList[1] = new ProgramParameter(ProgramParameter.PASS_BY_REFERENCE, new byte[1024], 1024);

                     ServiceProgramCall sPGMCall = new ServiceProgramCall(system);

                     // 设置要调用的service program的完整路径

                     sPGMCall.setProgram("/QSYS.LIB/MYLIB.LIB/SAMPLEC.SRVPGM", parameterList);

                     // 设置需要调用的函数名.

                     sPGMCall.setProcedureName("RelayCall");

                     // 设置函数返回值的类型,这里我们使用整型。

                     sPGMCall.setReturnValueFormat(ServiceProgramCall.RETURN_INTEGER);

                     System.out.println("(Java) Input field is '" + input + "'");

                     // 调用service program中的方法。

                     if (sPGMCall.run() != true) {

                            // 如果调用失败,输出错误信息

                            AS400Message[] messageList = sPGMCall.getMessageList();

                            for (int i = 0; i < messageList.length; ++i) {

                                   System.out.println(messageList[i].getText());

                            }

                     } else {

                            // 调用成功,获取并打印返回值

                            byte[] outbytes = parameterList[1].getOutputData();

                            String out = ((String) new AS400Text(1024, system).toObject(outbytes)).trim();

                            int i = bin4.toInt(sPGMCall.getReturnValue());

                            System.out.println("Returncode is " + i + "\r\nOutput is: " + out);

                     }

              } catch (Exception e) {e.printStackTrace();}

              System.out.println("(Java) Returned from the native method");

       }

}

需要注意的是,IBM Java Toolbox中的ServiceProgramCall 对象只能接受Byte数组形式的字符串参数,因而在将字符串转换为Byte数组的时候,需要手动添加结束字符'\0'

4)编译并执行该Java代码

IBM i上输入QSH进入QShell环境,通过cd /samplelib命令切换到/samplelib目录。通过javac命令编译该Java文件。编译过程需要添加jt400native.jarclasspath

javac -classpath /QIBM/ProdData/OS/OSGi/shared/com.ibm.i5os.jt400native/jt400native.jar:. SrvpgmCallSample.java

图像

如果编译成功的话,通过java命令执行该Java程序。同样需要添加jt400native.jarclasspath

java -classpath /QIBM/ProdData/OS/OSGi/shared/com.ibm.i5os.jt400native/jt400native.jar:. SrvpgmCallSample 'Hello'

(Java) Input field is 'Hello'                                                                                   

Returncode is 0                                                                                                 

Output is: We are in C code. Input is Hello                                                                     

(Java) Returned from the native method                                                                          

(Java) All done...        

图像

可以看到,程序成功执行完毕,打印出了C代码中的执行结果。

通过非常简单的几步,我们便实现了在JavaC编写的函数的调用以及两者间数据的传输。相比复杂的JNI调用,IBM Java Toolboxservice program call可以更快速的实现轻量级的Java/C混合编程开发。

 

作者: Xu Meng

[{"Business Unit":{"code":"BU058","label":"IBM Infrastructure w\/TPS"},"Product":{"code":"SWG60","label":"IBM i"},"Component":"","Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"","Edition":"","Line of Business":{"code":"LOB57","label":"Power"}}]

UID

ibm11145314