C++项目通过JNI使用Java第三方jar包
最近在C++项目中碰到了需要使用第三方公司开发的Java jar包的问题,最后使用了JNI来解决。
参考了网络上不少的方法介绍, 大多数介绍JNI的文章讲的的都是Java通过JNI来调C的本地代码,其实这个也可以反过来用就是C的本地代码通过创建Java虚拟机调用java方法。下面贴一下解决实例C2JavaJym.c,注释不是很多。
#include <jni.h>
#include <stdlib.h>
#include <string.h> /*C字符串转JNI字符串*/
jstring stoJstring(JNIEnv* env, const char* pat) {
jclass strClass = (*env)->FindClass(env, "Ljava/lang/String;");
jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>",
"([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
(*env)->SetByteArrayRegion(env, bytes, , strlen(pat), (jbyte*)pat);
jstring encoding = (*env)->NewStringUTF(env, "utf-8");
return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes,
encoding);
}
/*JNI字符串转C字符串*/
char* jstringTostring(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env, "java/lang/String");
jstring strencode = (*env)->NewStringUTF(env, "utf-8");
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
"(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
strencode);
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
if (alen> ) {
rtn = (char*)malloc(alen + );
memcpy(rtn, ba, alen);
rtn[alen] = ;
}
(*env)->ReleaseByteArrayElements(env, barr, ba, );
return rtn;
}
/*C和Java的字符串类型不同需要在这里进行装换*/
int main(int argc, char **argv) { if(argc<)
{
fprintf(stderr, "参数个数不足\n");
return -;
} int res;
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[];
/*设置初始化参数*/
options[].optionString = "-Djava.compiler=NONE";
options[].optionString = "-Djava.class.path=.:../lib/jym.jar:../lib/codeutil.jar"; //这里指定了要使用的第三方Jar包
options[].optionString = "-verbose:NONE"; //用于跟踪运行时的信息 /*版本号设置不能漏*/
vm_args.version=JNI_VERSION_1_4;//jdk版本目前有1.1,1.2,1.4 只要比你的jdk的版本低就可以 我用的是jdk1.5.0的版本
vm_args.nOptions = ;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res < || jvm == NULL || env == NULL) {
fprintf(stderr, "Can't create Java VM\n");
return -;
}
fprintf(stdout, "ok 调用JNI_CreateJavaVM创建虚拟机\n"); /*获取实例的类定义*/
jclass cls = (*env)->FindClass(env, "ptest/JymProduce"); //这里是jar包内JymProduce类的具体路径
if (cls == ) {
fprintf(stderr, "FindClass failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return -;
}
fprintf(stdout, "ok 返回JAVA类的CLASS对象\n"); /*创建对象实例*/
jobject obj = (*env)->AllocObject(env, cls);
if (obj == NULL) {
fprintf(stderr, "AllocObject failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return -;
}
fprintf(stdout, "ok 获取该类的实例\n"); /*获取构造函数,用于创建对象*/
/***1.1可用""作为构造函数, 1.2用"<init>"参数中不能有空格
"(Ljava/lang/String;)V"*/
jmethodID mid = (*env)->GetMethodID(env, cls, "getGertWord",
"(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
if (mid == ) {
fprintf(stderr, "GetMethodID failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return -;
}
fprintf(stdout, "ok 获取类中的方法\n"); //构造参数并调用对象的方法
//发票代码
jstring fpdm = stoJstring(env, argv[]); //发票号码
jstring fphm = stoJstring(env, argv[]); //开票金额
jlong kpje = (jlong)atoi(argv[]); //开票时间,格式为YYYYMMDD
jstring kpsj = stoJstring(env, argv[]); //行业分类代码
jstring hydm = stoJstring(env, argv[]); char szJym[];
memset(szJym, , sizeof(szJym)); jstring msg = (jstring) (*env)->CallObjectMethod(env, obj, mid, fpdm, fphm, kpje, kpsj, hydm);
strcpy(szJym,jstringTostring( env, msg));
fprintf(stdout, szJym);
fprintf(stdout, "\n");
fprintf(stdout, "ok Java返回参数\n"); PrintToFile(argv[],szJym); /*捕捉异常*/
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
return -;
}
/*销毁JAVA虚拟机*/
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
} int PrintToFile(const char* filename,const char* content)
{
FILE *fp;
if((fp=fopen(filename,"w"))==NULL)
return(-); fputs(content, fp);
fclose (fp); fflush(stdin) ;
fflush(stdout) ; return ;
}
这里是将C调用Jar包获取jym的过程生成了一个C2JavaJym的可执行程序,通过命令行来调用生成包含jym的临时文件供C++项目来读取。
编译命令 gcc -o C2JavaJym C2JavaJym.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -L${JAVA_HOME}/jre/lib/amd64/server -ljvm
char sCmd[];
memset(sCmd, , sizeof(sCmd));
strcpy(sCmd, "C2JavaJym "); char sFile[];
memset(sFile, , sizeof(sFile));
sprintf(sFile, "Jym%d.j", getpid() );
strcat(sCmd, sFile); strcat(sCmd, " ");
strcat(sCmd, sFPDM);
strcat(sCmd, " ");
strcat(sCmd, sFPHM);
strcat(sCmd, " ");
strcat(sCmd, sFPJE);
strcat(sCmd, " ");
strcat(sCmd, sDate);
strcat(sCmd, " ");
strcat(sCmd, sHYDM);
strcat(sCmd, " 1>/dev/null"); system(sCmd);
/*以上是调用生成校验码*/
char buf[];
memset(buf, , sizeof(buf));
FILE* pf = fopen(sFile, "r");
if (pf!=NULL)
{
if (!feof(pf))
{
fgets(buf, sizeof(buf)-, pf);
}
else
{
tuxData.setRsp("","获取校验码失败!");
return false;
}
}
else
{
tuxData.setRsp("","获取校验码失败!");
return false;
} StrNCpy(sJYM,buf,);
/*通过读取文件获取校验码*/
memset(sCmd, , sizeof(sCmd));
strcpy(sCmd, "rm ");
strcat(sCmd, sFile);
strcat(sCmd, " 1>/dev/null"); system(sCmd);
/*删除临时文件*/
也可以在命令行之间执行 C2JavaJym Jym2.j 235051102210 00002520 3456 20110330 04 来调用
这个方案比较土,不过还是有效的。我也试过将这个过程编译到CPP源代码中和tuxexdo服务端的pc文件中,但是都在创建虚拟机后,找不到指定的类,虚拟机的销毁也有问题,感觉是虚拟机创建的有问题。
另外还有2个方案只有构想还没有尝试过。一个是利用linux的消息机制,将Java虚拟机作为守护进程一样起在后台,C++项目往A消息队列上扔需要校验的数据,启动Java虚拟机的进程从这个A队列上获取数据,并计算出校验码,再扔到B队列上,C++项目再从这个B队列上获取算出来的校验码。这个过程可以减少Java虚拟机被频繁的创建和销毁,减少开销,但是如果并发量上来的话,等在B队列上获取校验码的C++进程比较多,怎么保证从B队列上取到的就是自己发送的内容的校验码是个问题。另一个也类似了就是利用socket来代替消息队列进行通讯。
不过实际项目中测试虚拟机的从创建到销毁的整个过程还是很快的,不像在我本机windows上那么慢,开销应该还是可以接受的。
C++项目通过JNI使用Java第三方jar包的更多相关文章
- Android.mk添加第三方jar包
最近引入第三方的jar包进工程,发现光红色的两条并不起作用,加入include $(BUILD_MULTI_PREBUILT) 才起作用,而且顺序很重要,在这里把我参考的两个例子都列出来. 以下为引用 ...
- Android.mk添加第三方jar包(转载)
转自:www.cnblogs.com/hopetribe/archive/2012/04/23/2467060.html LOCAL_PATH:= $(call my-dir)include $(CL ...
- 解决对含有第三方jar包的项目打包出现java.lang.NoClassDefFoundError问题
用eclipse普通的打包方式,对含有第三方jar包的项目进行打包.调用方法后一只出现java.lang.NoClassDefFoundError问题. 从网上搜寻,很多都是在MANIFEST.MF文 ...
- eclipse将引用了第三方jar包的java项目打成jar包
今天用java开发了一个项目,想要打成jar包,并在linux环境下运行.但是运行时引用的第三方jar包却显示classNotFind错误. 于是查了一下解决办法,在此贴出来,方便以后查阅. 用Ecl ...
- Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法
方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 “MANIFEST.MF”, 由于是打包引用了第三方jar包的Java项目,故需要自定义配置文件MANIFEST.MF,在该项目 ...
- (转载)Eclipse将引用了第三方jar包的Java项目打包成可执行jar的两种方法
转载自:http://www.cnblogs.com/lanxuezaipiao/p/3291641.html 方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 "MA ...
- Java项目导出为jar包+导出第三方jar包+使用命令行调用+传参
Java项目导出为jar包+导出第三方jar包+使用命令行调用+传参 一.打包 情况1:不需要向程序传参数,并且程序没有使用第三方jar包 Eclipse上导出jar: 然后选择一个java文件作为入 ...
- 将引用了第三方jar包的Java项目打包成jar文件的两种方法
方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 “MANIFEST.MF”, 由于是打包引用了第三方jar包的Java项目,故需要自定义配置文件MANIFEST.MF,在该项目 ...
- MyEclipse中把java项目打包——含有第三方jar包【转】
也适用于eclipse导出jar. 在将项目打包为jar包时一直出现“ClassNotDefFound”错误,百度了很多解决办法都没有解决.最终找到一个很好的解决办法. 1.打包步骤 (1)右键单击j ...
随机推荐
- 服务器返回的http状态码
状态码 响应类别 原因短语 1XX 信息性状态码(Informational) 服务器正在处理请求 2XX 成功状态码(Success) 请求已正常处理完毕 3XX 重定向状态码(Redirectio ...
- JobScheduler android任务调度处理组件(类似QuartZ)
JobScheduler是Android L(API21)新增的特性,用于定义满足某些条件下(电量,网络,时间,屏幕熄/亮 ,设备是否空闲 等)执行的任务.它的宗旨是把一些不是特别紧急的任务放到更合适 ...
- .NET跨平台实践:再谈用C#开发Linux守护进程 — 完整篇
Linux守护进程是Linux的后台服务进程,相当于Windows服务,对于为Linux开发服务程序的朋友来说,Linux守护进程相关技术是必不可少的,因为这个技术不仅仅是为了开发守护进程,还可以拓展 ...
- Python numpy 中 keepdims 的含义
keepdims主要用于保持矩阵的二维特性 import numpy as np a = np.array([[1,2],[3,4]]) # 按行相加,并且保持其二维特性 print(np.sum(a ...
- MVC3学习:利用mvc3+ajax实现全选和批量删除
本例数据库操作使用EF code first; 先利用mvc自带的模板,先生成一个list视图,然后再手动添加复选框和删除按钮 <table> <tr> @*在标题行添加一个全 ...
- RESTful SOA与DDD(领域驱动设计)
视频地址:http://www.infoq.com/presentations/RESTful-SOA-DDD 作者的一个DDD采访:http://www.informit.com/articles/ ...
- oc中的枚举
如果一个变量只有几种可能的值,比如星期有几天,一年有几个季节等.这个时候可以用枚举变量. 先定义类型再定义变量,如:enum siji{chun,xia,qiu,dong} 也可以定义匿名:enum{ ...
- [开源项目]Shell4Win,一个在Windows下执行shell命令的解释器
背景 顺利拿到心目中的理想offer之后,心里的负担一下减轻了很多,希望利用还没毕业之前这段难得的悠闲时间做一点有意义的事情.于是希望能做一个长久以来都想做的开源项目,就是题中提到的Windows下的 ...
- 复刻smartbits的国产网络测试工具minismb-网络连接数测试方法
复刻smartbits的网路性能测试工具MiniSMB,是一款专门用于测试智能路由器,网络交换机的性能和稳定性的软硬件相结合的工具.可以通过此工具测试任何ip网络设备的端口吞吐率,带宽,并发连接数和最 ...
- 页面出现Incorrect string以及数据库内容乱码
我在制作 (www.helpqy.com) 的时候遇到了页面报错Incorrect string的问题,我使用的是mysql,数据表中有很多中文内容,最后发现在安装mysql的时候需要选择defaul ...