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, 0, 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> 0) {
                rtn = (char*)malloc(alen + 1);
                memcpy(rtn, ba, alen);
                rtn[alen] = 0;
        }
        (*env)->ReleaseByteArrayElements(env, barr, ba, 0);
        return rtn;
}
/*C和Java的字符串类型不同需要在这里进行装换*/
int main(int argc, char **argv) {

          if(argc<7)
          {
              fprintf(stderr, "参数个数不足\n");
          return -1;
          }

        int res;
        JavaVM *jvm;
        JNIEnv *env;
        JavaVMInitArgs vm_args;
        JavaVMOption options[3];
        /*设置初始化参数*/
        options[0].optionString = "-Djava.compiler=NONE";
        options[1].optionString = "-Djava.class.path=.:../lib/jym.jar:../lib/codeutil.jar";  //这里指定了要使用的第三方Jar包
        options[2].optionString = "-verbose:NONE"; //用于跟踪运行时的信息

        /*版本号设置不能漏*/
        vm_args.version=JNI_VERSION_1_4;//jdk版本目前有1.1,1.2,1.4 只要比你的jdk的版本低就可以 我用的是jdk1.5.0的版本
        vm_args.nOptions = 3;
        vm_args.options = options;
        vm_args.ignoreUnrecognized = JNI_TRUE;
        res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
        if (res < 0 || jvm == NULL || env == NULL) {
                fprintf(stderr, "Can‘t create Java VM\n");
                return -1;
        }
        fprintf(stdout, "ok 调用JNI_CreateJavaVM创建虚拟机\n");

        /*获取实例的类定义*/
        jclass cls = (*env)->FindClass(env, "ptest/JymProduce");    //这里是jar包内JymProduce类的具体路径
        if (cls == 0) {
                fprintf(stderr, "FindClass failed\n");
                (*jvm)->DestroyJavaVM(jvm);
                fprintf(stdout, "Java VM destory.\n");
                return -1;
        }
        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 -1;
        }
        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 == 0) {
                fprintf(stderr, "GetMethodID failed\n");
                (*jvm)->DestroyJavaVM(jvm);
                fprintf(stdout, "Java VM destory.\n");
                return -1;
        }
        fprintf(stdout, "ok 获取类中的方法\n");

        //构造参数并调用对象的方法
        //发票代码
        jstring fpdm = stoJstring(env, argv[2]);

        //发票号码
        jstring fphm = stoJstring(env, argv[3]);

        //开票金额
        jlong kpje = (jlong)atoi(argv[4]);

        //开票时间,格式为YYYYMMDD
        jstring kpsj = stoJstring(env, argv[5]);

        //行业分类代码
        jstring hydm = stoJstring(env, argv[6]);

        char szJym[8];
        memset(szJym, 0, 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[1],szJym);

        /*捕捉异常*/
        if ((*env)->ExceptionOccurred(env)) {
                (*env)->ExceptionDescribe(env);
                return -1;
        }
        /*销毁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(-1);

  fputs(content, fp);
  fclose (fp);

  fflush(stdin) ;
  fflush(stdout) ;

  return 0;
}

这里是将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[101];
    memset(sCmd, 0, sizeof(sCmd));
    strcpy(sCmd, "C2JavaJym ");

    char sFile[21];
    memset(sFile, 0, 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[101];
    memset(buf, 0, sizeof(buf));
    FILE* pf = fopen(sFile, "r");
    if (pf!=NULL)
    {
        if (!feof(pf))
        {
            fgets(buf, sizeof(buf)-1, pf);
        }
        else
        {
            tuxData.setRsp("4401","获取校验码失败!");
      return false;
        }
    }
    else
    {
        tuxData.setRsp("4401","获取校验码失败!");
    return false;
    }

    StrNCpy(sJYM,buf,7);

/*通过读取文件获取校验码*/

memset(sCmd, 0, 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上那么慢,开销应该还是可以接受的。

原文地址:https://www.cnblogs.com/zhujiabin/p/10558051.html

时间: 2024-08-08 07:24:55

C++项目通过JNI使用Java第三方jar包的相关文章

Android.mk添加第三方jar包(转载)

转自:www.cnblogs.com/hopetribe/archive/2012/04/23/2467060.html LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS) LOCAL_STATIC_JAVA_LIBRARIES := xsocket jackson-mapper logging jackson-core javatar log4j LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(

Android.mk添加第三方jar包

最近引入第三方的jar包进工程,发现光红色的两条并不起作用,加入include $(BUILD_MULTI_PREBUILT) 才起作用,而且顺序很重要,在这里把我参考的两个例子都列出来. 以下为引用例子: --------------------------------------------------------------------------------------------------------------------------------------------------

解决maven打包打不进lib下的第三方jar包问题

maven项目有时候找不到第三方jar包的依赖坐标,这时就需要在项目下建立一个文件夹,将第三方jar包放在此处,在maven打包时将文件夹下的jar包打包进去 在pom.xml中添加如下插件,并指明jar包路径 <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8<

Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法

方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 “MANIFEST.MF”, 由于是打包引用了第三方jar包的Java项目,故需要自定义配置文件MANIFEST.MF,在该项目下建立文件MANIFEST.MF,内容如下: Manifest-Version: 1.0 Class-Path: lib/commons-codec.jar lib/commons-httpclient-3.1.jar lib/commons-logging-1.1.jar lib/log4j-1.

MyEclipse中把java项目打包——含有第三方jar包【转】

在将项目打包为jar包时一直出现“ClassNotDefFound”错误,百度了很多解决办法都没有解决.最终找到一个很好的解决办法. 1.打包步骤 (1)右键单击java项目“gz10000……”,选择“Export”: (2)选择“java”——“Runable JAR file”,点击“next”: (3)选择你的main()函数的文件名,选择你的保存路径,选择“Package required…………JAR”,点击“Finished”. 2.总结 1)如果含有第三方jar包,在打包的时候应

将引用了第三方jar包的Java项目打包成jar文件的两种方法

方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 “MANIFEST.MF”, 由于是打包引用了第三方jar包的Java项目,故需要自定义配置文件MANIFEST.MF,在该项目下建立文件MANIFEST.MF,内容如下: Manifest-Version: 1.0 Class-Path: lib/commons-codec.jar lib/commons-httpclient-3.1.jar lib/commons-logging-1.1.jar lib/log4j-1.

(转载)Eclipse将引用了第三方jar包的Java项目打包成可执行jar的两种方法

转载自:http://www.cnblogs.com/lanxuezaipiao/p/3291641.html 方案一:用Eclipse自带的Export功能 步骤1:准备主清单文件 "MANIFEST.MF", 由于是打包引用了第三方jar包的Java项目,故需要自定义配置文件MANIFEST.MF,在该项目下建立文件MANIFEST.MF,内容如下: Manifest-Version: 1.0 Class-Path: lib/commons-codec.jar lib/common

Java项目导出为jar包+导出第三方jar包+使用命令行调用+传参

Java项目导出为jar包+导出第三方jar包+使用命令行调用+传参 一.打包 情况1:不需要向程序传参数,并且程序没有使用第三方jar包 Eclipse上导出jar: 然后选择一个java文件作为入库,需要带main()主函数: 情况2:向程序传参数,并且程序没有使用第三方jar包 这种情形下打包和情况1是一样的 情况3:程序使用了第三方jar包 这种情况下的打包要复杂一些,需要把第三方jar包一起导出,可以使用eclipse的插件fat jar,很简便,但是fat jar只支持eclipse

Eclipse将引用了第三方jar包的Java项目打包成jar文件

参见: http://www.cnblogs.com/lanxuezaipiao/p/3291641.html Fat Jar Eclipse Plug-In是一个可以将Eclipse Java Project的所有资源打包进一个可执行jar文件的小工具,可以方便的完成各种打包任务,我们经常会来打jar包,但是eclipse自带的打包jar似乎不太够用,Fat Jar是eclipse的一个插件,特别是Fat Jar可以打成可执行Jar包,并且在图片等其他资源.引用外包方面使用起来更方便. 安装方