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)调用。通过JNI,Java可以与被调用的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.jar的classpath。
javac -classpath /QIBM/ProdData/OS/OSGi/shared/com.ibm.i5os.jt400native/jt400native.jar:. SrvpgmCallSample.java
如果编译成功的话,通过java命令执行该Java程序。同样需要添加jt400native.jar的classpath。
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代码中的执行结果。
通过非常简单的几步,我们便实现了在Java对C编写的函数的调用以及两者间数据的传输。相比复杂的JNI调用,IBM Java Toolbox的service program call可以更快速的实现轻量级的Java/C混合编程开发。
作者: Xu Meng
UID
ibm11145314