目前主流的跨語言異構模塊通訊方案有不少種,好比:java
一、跨語言的RPC調用(Apache Thrift):它是Facebook貢獻給Apache基金會的開源項目,旨在構建跨語言平臺的通訊方案。目前它支持很是多種語言,其中固然包括C/C++和Java。Thrift內置一個語言編譯器,能夠根據Thrift的語法規範,編譯生成指定語言的RPC調用模塊,功能也是很是的強大。Thrift的語法規範裏面定義了數據類型、數據模塊結構,有點相似WebService裏面的WSDL文件。經過Thrift,咱們就能夠實現跨語言的異構模塊間的通訊。ios
二、Google Protobuf:功能上支持C/C++和Java的語言綁定,基於對象消息的序列化、反序列化技術。性能好,效率高。proto文件生成目標語言代碼,就能夠實現跨語言的模塊通訊。固然,還有一個和Google Protobuf相似的Apache開源項目Avro,也能達到這個目標。c++
三、FCGI服務方式:全稱爲FastCGI,CGI的升級版本。支持HTTP協議對其進行訪問,而CGI( Common Gateway Interface)通用網關接口,自己也只是一種接口協議,它目前有支持C/C++語言的版本庫。值得注意的是:FCGI、CGI自己要經過Web服務器進行消息轉發方式才能被調用。常見的Web服務器有:Apache、Nginx等等。具體的調用過程是:Web服務器接收到客戶端的請求以後,經過標準輸入流的方式把數據送給FCGI、CGI服務,FCGI、CGI服務計算、運行以後,把結果經過標準輸出流的方式返回給客戶端,所以,理論上只要能操做標準輸入流、標準輸出流的語言都支持FCGI、CGI服務方式實現。經過上面的分析,跨語言的調用也簡單了。即經過C/C++把主要的業務功能模塊,封裝成FCGI、CGI服務。而後Java利用HTTP協議,經過Web服務器轉發到FCGI、CGI服務上,就能知足跨語言模塊通訊的要求。算法
固然,還有其它不少種跨語言的通訊方式,在此,我就不一一舉例了。shell
因爲本人工做的須要,要將目前在用的一些關鍵業務模塊,移植到Java平臺下面。由於原來的大多數業務模塊是用C/C++語言實現的,並且涉及的邏輯比較複雜,直接遷移到Java平臺下面,開發、遷移難度不小,並且還要涉及全量的功能性覆蓋測試。對於開發、測試工做能力的要求比較高。那麼,除了上述概括的跨語言通訊方案以外,還有沒有一種便捷、靈活的方案,能無縫的銜接Java和C/C++兩種業務功能模塊呢?雖然,本人還未達到上述成熟開源跨語言通訊框架設計者,高屋建瓴般的能力。可是,能夠基於一些成熟開源框架的設計思路、以及功能模塊,徹底能夠打造一個,全新的跨語言異構模塊通訊解決方案。下面,我就重點跟你們介紹一種,基於JNA(Java Native Access)技術和C/C++異構模塊通訊的解決方案。api
本文的靈感,主要來自於Thrift、BGCC(百度開源的Java/C++跨語言通訊框架,網址爲:http://bgcc.baidu.com/)中,對於跨語言生成的模塊,能夠經過特定的語法規範進行定製編寫(固然,WebService裏面也有相似的作法,好比WSDL、IDL等等)。以及,經過一些總結概括髮現:利用JNA技術對於某些特定場景的C/C++函數模塊訪問形式,能夠抽象提取出,某種固定的代碼編寫策略,因而乎,誕生了本篇文章。數組
說到JNA,就不得不提起JNI(Java Native Interface)。JNI做爲Java平臺的組成部分,它容許Java代碼,和其它語言編寫的代碼模塊進行交互。JNI一開始就設計成爲本地語言,尤爲是爲C/C++模塊通訊所設計的。利用JNI,Java能夠和本地環境中,已經編譯的代碼模塊進行交互。首先,Java能夠經過JNI調用本地的C/C++模塊組件,另一方面,本地的C/C++代碼模塊也能夠調用Java代碼。服務器
說完了JNI,繼續說一下JNA。JNA(Java Native Access)是一個開源的Java框架,它的官方網址是:https://jna.java.net/,最先是由SUN公司主導開發的,創建在JNI基礎之上的一個開源框架。它簡化了JNI開發中不少繁瑣的步驟,能夠認爲:JNA是JNI抽象層次更高的封裝。利用JNA,咱們只要在Java的接口中,描述目標C/C++的函數模塊和數據結構,JNA就會自動實現Java接口到C/C++函數模塊和數據結構的映射關係,簡化了開發的步驟,下降了JNI開發的難度。本文中涉及的jna.jar能夠去上述官網下載使用,下載地址:https://java.net/projects/jna/downloads。數據結構
JNA是直接經過和C語言方式的API進行通訊訪問的。因此,若是你要利用JNA和C++通訊訪問的話,沒有問題,可是要把C++面向對象的模塊,再封裝出C風格的函數便可。綜上所述,利用JNA能夠直接和C/C++模塊進行交互通訊。app
最多見的狀況能夠看下面的圖例說明。
一、狀況A是最多見的狀況:C語言的函數模塊是常見數據類型集合。這裏所謂的C語言常見數據類型(原生數據類型Primitive)是指:int、float、double、char*、int*等等原生數據類型、原生數據指針類型所構成的函數模塊。
二、狀況B是另一種狀況:C的函數模塊涉及到一些數據模型結構,也就是C裏面的結構體類型struct。結構體是由純粹的C原生數據類型、原生數據指針類型構成的。本文中,因爲要和C++模塊進行通訊,因此JNA/C++訪問模塊生成器,優先考慮一種,比較通用的生成策略:它的C函數的僞代碼,表示爲:void function(struct* pInArray,struct* pOutArray);其中pInArray表示待處理的結構體數組,而pOutArray表示處理後的結構體數組。
三、狀況C是指:結構體類型struct裏面嵌套其它的結構體,組成複合數據類型的狀況。
四、狀況D是指:除了狀況A、B、C以外的其它種狀況。不過不少種狀況,均可以間接經過狀況B,進行適配轉換。因此,本文的例子,也是基於狀況B,進行開發設計的。
先來看下,JNA/C++訪問模塊生成器設計思路的腦圖:
一、支持C/C++數據對象配置可定製化
因爲是基於上述JNA訪問C/C++異構模塊策略B的狀況。所以要對訪問的模塊結構類型進行描述。對應的描述文件名爲JNA.xml,該文件內容描述以下:
<jna> <package>Java訪問模塊的包名</package> <module>模塊名稱</module> <struct> <attr> <name>模塊屬性名稱</name> <type>模塊屬性類型</type> </attr> <attr> <name>模塊屬性名稱</name> <type>模塊屬性類型</type> </attr> </struct> </jna>
其中,package節點表示對應生成Java訪問模塊的包名;module表示要訪問的模塊名稱;struct節點下面,包含若干attr節點。attr節點的name表示模塊某個屬性的名稱;attr節點的type表示模塊某個屬性的類型(目前只支持整型int/字符型string),上述JNA.xml對應的類圖結構爲:
其中,JNAModule結構體(JNA模塊屬性)是和JNA.xml配置文件的內容一一對應。另一個JNAConfig模塊(屬性配置管理)是負責解析JNA.xml模塊信息配置文件,生成對應的JNA訪問模塊代碼和C++訪問模塊代碼。
二、JNA訪問C/C++異構模塊策略可配置
目前設計的JNA訪問C/C++異構模塊策略只支持狀況B,所以在JNA.xml配置文件中,沒有體現模塊策略規則屬性,後續能夠擴展完善。
三、自動生成JNA訪問模塊代碼
對應的類模塊爲:void JNAConfig::genJNAModule(JNAModule& module)中的JNA c++ 文件頭模塊定義、JNA c++ 文件實現模塊定義部分。
四、自動生成C/C++訪問模塊代碼
對應的類模塊爲:void JNAConfig::genJNAModule(JNAModule& module)中的JNA java 模塊定義模塊定義部分。
如今給出完整的代碼實現,首先是JNA/C++訪問模塊生成器的頭文件(C++實現)JNA.h
/** * @filename:JNA.h * * Newland Co. Ltd. All rights reserved. * * @Description:JNA接口生成模塊定義 * @author tangjie * @version 1.0 * */ #ifndef __JNA_H__ #define __JNA_H__ #ifdef WIN32 #include <Windows.h> #include <direct.h> #endif #include <string> #include <deque> #include <iostream> #include <algorithm> using namespace std; #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/xml_parser.hpp> #include <boost/typeof/std/utility.hpp> #include <boost/algorithm/string.hpp> #include <boost/foreach.hpp> #include <boost/filesystem/fstream.hpp> using namespace boost; //JNA 字段名稱以及字段類型,目前類型暫支持int/string struct JNAAttr { string attrName; int attrType; enum AttrType { INT, STRING }; int getAttrType() const { return attrType; } }; //JNA 模塊屬性包含模塊名稱/包名。聚合若干JNAAttr。 struct JNAModule { string packageName; string moduleName; deque<JNAAttr> lstAttr; }; //JNA 屬性配置管理 class JNAConfig { public: JNAConfig(); bool load(JNAModule& module); void genJNAModule(JNAModule& module); private: string getConfigFilePath(); string getConfigFile(); }; #endif // (!__JNA_H__)
其次是JNA/C++訪問模塊生成器的源文件(C++實現):JNA.cpp
/** * @filename:JNA.cpp * * Newland Co. Ltd. All rights reserved. * * @Description:JNA接口生成模塊實現 * @author tangjie * @version 1.0 * */ #include "stdafx.h" #include "JNA.h" //JNA模塊實現部分 JNAConfig::JNAConfig(){} void JNAConfig::genJNAModule(JNAModule& module) { string moduleName = module.moduleName; if(moduleName.empty()) return; //JNA c++ 文件頭模塊定義 { string macro = to_upper_copy(module.moduleName); boost::filesystem::path p(".\\"+moduleName+"Processor.h"); boost::filesystem::ofstream ofs(p.string().c_str()); ofs<<"#ifndef __"<<macro<<"PROCESSOR_H__"<<endl; ofs<<"#define __"<<macro<<"PROCESSOR_H__"<<endl<<endl; ofs<<"#include <deque>\n#include <algorithm>\n#include <iterator>\nusing namespace std;"<<endl<<endl; ofs<<"typedef struct "<<moduleName<<" {"<<endl; BOOST_FOREACH(JNAAttr attr,module.lstAttr) { ofs<<((attr.attrType == JNAAttr::INT) ? " int ":" char* ")<<attr.attrName<<";"<<endl; } ofs<<"} "<<moduleName<<";"<<endl<<endl<<endl<<"#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n"; ofs<<" class "<<moduleName<<"Processor {\n public:\n "<<moduleName<<"Processor();\n void calcRule(deque<"<<moduleName<<"> input);\n static int getResultSize(){return output.size();}\n static deque<"<<moduleName<<"> getResult(){return output;}\n\n protected:\n static deque<"<<moduleName<<"> output;\n };\n\n //C Stype API for JNA invoke\n void "<<to_lower_copy(moduleName.substr(0,1))<<moduleName.substr(1)<<"Processor("<<moduleName<<"* p"<<moduleName<<","<<moduleName<<"** pp"<<moduleName<<",int num"<<moduleName<<");\n"<<endl; ofs<<" //because use malloc/free c-api otherwise lead to memory leaks\n void freeMemory("<<moduleName<<"* p"<<moduleName<<");\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // (!__"<<macro<<"PROCESSOR_H__)\n\n\n"<<endl; ofs.close(); } //JNA c++ 文件實現模塊定義 { bool existTypeString = false; deque<JNAAttr>::iterator iter = find_if(module.lstAttr.begin(),module.lstAttr.end(),boost::bind<bool>(std::equal_to<int>(),JNAAttr::STRING,boost::bind(&JNAAttr::getAttrType, _1))); if(iter != module.lstAttr.end()) { existTypeString = true; } boost::filesystem::path p(".\\"+moduleName+"Processor.cpp"); boost::filesystem::ofstream ofs(p.string().c_str()); ofs<<"#include \""<<moduleName<<"Processor.h\"\n"<<endl; ofs<<"deque<"<<moduleName<<"> "<<moduleName<<"Processor::output;\n"<<endl; ofs<<moduleName<<"Processor::"<<moduleName<<"Processor()\n{\n "<<moduleName<<"Processor::output.clear();\n}\n"<<endl; ofs<<"void "<<moduleName<<"Processor::calcRule(deque<"<<moduleName<<"> input)\n{\n if(input.empty()) return;\n\n copy(input.begin(),input.end(),std::back_inserter(output));\n}\n"<<endl; ofs<<"void "<<to_lower_copy(moduleName.substr(0,1))<<moduleName.substr(1)<<"Processor("<<moduleName<<"* p"<<moduleName<<","<<moduleName<<"** pp"<<moduleName<<",int num"<<moduleName<<")\n{\n deque<"<<moduleName<<"> list(p"<<moduleName<<",p"<<moduleName<<"+num"<<moduleName<<");\n\n "<<moduleName<<"Processor processor;\n\n processor.calcRule(list);\n\n deque<"<<moduleName<<"> result = "<<moduleName<<"Processor::getResult();\n int number = result.size();\n\n *pp"<<moduleName<<" = ("<<moduleName<<"*)malloc(sizeof("<<moduleName<<") * number);\n\n memset(*pp"<<moduleName<<", 0, sizeof("<<moduleName<<") * number);\n\n for(int i = 0;i<number;i++)\n {"<<endl; BOOST_FOREACH(JNAAttr attr,module.lstAttr) { ofs<<((attr.attrType == JNAAttr::INT) ? (" (*pp"+moduleName+")[i]."+attr.attrName+" = result[i]."+attr.attrName+";\n"+" (*pp"+moduleName+")[i]."+attr.attrName+" = result[i]."+attr.attrName+";\n"):(" int "+attr.attrName+"Len = strlen(result[i]."+attr.attrName+") + 1;\n (*pp"+moduleName+")[i]."+attr.attrName+" = (char*)malloc(sizeof(char) * "+attr.attrName+"Len);\n strcpy((*pp"+moduleName+")[i]."+attr.attrName+", result[i]."+attr.attrName+");\n"))<<endl; } ofs<<" }\n\n return;\n}\n"<<endl; if(existTypeString) { ofs<<"void freeMemory("<<moduleName<<"* p"<<moduleName<<")\n{\n deque<"<<moduleName<<"> result = "<<moduleName<<"Processor::getResult();\n"<<endl; ofs<<" for(int i = 0;i<result.size();i++)\n {"<<endl; BOOST_FOREACH(JNAAttr attr,module.lstAttr) { if(attr.attrType == JNAAttr::STRING) { ofs<<" free(result[i]."<<attr.attrName<<");"<<endl; } } ofs<<" }\n"<<endl; } else { ofs<<"void freeMemory("<<moduleName<<"* p"<<moduleName<<")\n{"<<endl; } ofs<<" free(p"<<moduleName<<");\n}\n\n"<<endl; ofs.close(); } //JNA java 模塊定義 { boost::filesystem::path p(".\\"+moduleName+"Processor.java"); boost::filesystem::ofstream ofs(p.string().c_str()); ofs<<"package "<<module.packageName<<";\n"<<endl; ofs<<"import com.sun.jna.Library;\nimport com.sun.jna.Native;\nimport com.sun.jna.Pointer;\nimport com.sun.jna.Structure;\nimport com.sun.jna.Structure.ByReference;\nimport com.sun.jna.ptr.PointerByReference;\n\nimport java.util.ArrayList;\n\n"<<endl; ofs<<"public class "<<moduleName<<"Processor {\n public interface JNALibrary extends Library {\n public static class "<<moduleName<<"Struct extends Structure {\n public static class ByReference extends "<<moduleName<<"Struct implements\n Structure.ByReference {\n }\n"<<endl; BOOST_FOREACH(JNAAttr attr,module.lstAttr) { ofs<<" public "<<(attr.attrType == JNAAttr::INT ? "int " :"String ")<<attr.attrName<<";"<<endl; } ofs<<" public "<<moduleName<<"Struct() {\n }\n\n public "<<moduleName<<"Struct(Pointer p) {\n super(p);\n }\n }\n\n public void "<<to_lower_copy(moduleName.substr(0,1))<<moduleName.substr(1)<<"Processor("<<moduleName<<"Struct.ByReference vals,\n PointerByReference valsRef, int numVals);\n\n public void freeMemory(Pointer p);\n }\n\n public static void invoke(ArrayList<JNALibrary."<<moduleName<<"Struct> list,\n ArrayList<JNALibrary."<<moduleName<<"Struct> listResult) {\n JNALibrary library;\n JNALibrary."<<moduleName<<"Struct.ByReference ref"<<moduleName<<" = null;\n\n PointerByReference refPtrVal;\n\n try {\n library = (JNALibrary) Native.loadLibrary(\n \"../src/lib"<<moduleName<<"Processor.so\", JNALibrary.class);\n ref"<<moduleName<<" = new JNALibrary."<<moduleName<<"Struct.ByReference();\n refPtrVal = new PointerByReference();\n } catch (UnsatisfiedLinkError e) {\n System.out.println(\"JNA invoke error\");\n library = (JNALibrary) Native.loadLibrary(\n \"./lib"<<moduleName<<"Processor.so\", JNALibrary.class);\n ref"<<moduleName<<" = new JNALibrary."<<moduleName<<"Struct.ByReference();\n refPtrVal = new PointerByReference();\n }\n\n JNALibrary."<<moduleName<<"Struct[] array = (JNALibrary."<<moduleName<<"Struct[]) ref"<<moduleName<<"\n .toArray(list.size());\n"<<endl; ofs<<" for (int i = 0; i < list.size(); i++) {"<<endl; BOOST_FOREACH(JNAAttr attr,module.lstAttr) { ofs<<" array[i]."<<attr.attrName<<" = list.get(i)."<<attr.attrName<<";"<<endl; } ofs<<" }\n"<<endl; ofs<<" library."<<to_lower_copy(moduleName.substr(0,1))<<moduleName.substr(1)<<"Processor(ref"<<moduleName<<", refPtrVal, array.length);\n\n Pointer pValues = refPtrVal.getValue();\n\n JNALibrary."<<moduleName<<"Struct refValues = new JNALibrary."<<moduleName<<"Struct(\n pValues);\n\n refValues.read();\n\n JNALibrary."<<moduleName<<"Struct[] arrayResult = (JNALibrary."<<moduleName<<"Struct[]) refValues\n .toArray(list.size());\n\n for (JNALibrary."<<moduleName<<"Struct element : arrayResult) {\n listResult.add(element);\n }\n\n library.freeMemory(pValues);\n }\n}\n"<<endl; ofs.close(); } } bool JNAConfig::load(JNAModule& module) { boost::property_tree::ptree pt; boost::property_tree::read_xml(getConfigFile().c_str(), pt); try { module.packageName = pt.get<string>("jna.package"); module.moduleName = pt.get<string>("jna.module"); //加載解析JNA模塊字段定義 { BOOST_AUTO(child, pt.get_child("jna.struct")); for (BOOST_AUTO(pos, child.begin()); pos != child.end(); ++pos) { BOOST_AUTO(child_paths, pos->second.get_child("")); JNAAttr attr; int i = 0; for (BOOST_AUTO(pos_paths, child_paths.begin()); pos_paths != child_paths.end(); ++pos_paths,++i) { if(i%2 == 0) { attr.attrName = pos_paths->second.data(); } else { attr.attrType = to_lower_copy(string(pos_paths->second.data())).compare("string") ? JNAAttr::INT : JNAAttr::STRING; } } module.lstAttr.push_back(attr); } } return true; } catch(const std::exception& e) { fprintf(stderr, "%s\n", e.what()); return false; } catch(...) { fprintf(stderr, "Unexpected exception.\n"); return false; } } //獲取配置文件路徑信息 string JNAConfig::getConfigFile() { return getConfigFilePath().append(".xml"); } string JNAConfig::getConfigFilePath() { string result; char path_buffer[_MAX_PATH + 1] = {0}; char drive[_MAX_DRIVE + 1] = {0}; char dir[_MAX_DIR + 1] = {0}; char fname[_MAX_FNAME + 1] = {0}; char ext[_MAX_EXT + 1] = {0}; ::GetModuleFileNameA(NULL, path_buffer, sizeof(path_buffer) - 1); _splitpath(path_buffer, drive, dir, fname, ext); result += drive; result += dir; result += fname; return result; }
經過VS2010,編譯生成目標應用程序JNA.exe,而後把定義好的JNA.xml模塊描述文件,放在JNA.exe的同一個目錄下面。好比,我要經過JNA訪問C++模塊名稱爲MobileUser(手機用戶),對應的Java訪問模塊的包名:newlandframework.jna.businessrule。模塊屬性爲:手機用戶歸屬地市homeCity、手機用戶網齡(即手機號碼使用年限)netAge,類型爲整型int;手機號碼msisdn、用戶姓名name,類型爲字符型string。綜上獲得以下的模塊描述:
<jna> <package>newlandframework.jna.businessrule</package> <module>MobileUser</module> <struct> <attr> <name>homeCity</name> <type>int</type> </attr> <attr> <name>msisdn</name> <type>string</type> </attr> <attr> <name>name</name> <type>string</type> </attr> <attr> <name>netAge</name> <type>int</type> </attr> </struct> </jna>
雙擊運行,自動生成對應的C/C++訪問模塊代碼(MobileUserProcessor.h/cpp)、JNA訪問模塊代碼(MobileUserProcessor.java),截圖以下:
生成代碼內容以下所列舉:
MobileUserProcessor.h文件內容以下:
#ifndef __MOBILEUSERPROCESSOR_H__ #define __MOBILEUSERPROCESSOR_H__ #include <deque> #include <algorithm> #include <iterator> using namespace std; typedef struct MobileUser { int homeCity; char* msisdn; char* name; int netAge; } MobileUser; #ifdef __cplusplus extern "C" { #endif class MobileUserProcessor { public: MobileUserProcessor(); void calcRule(deque<MobileUser> input); static int getResultSize(){return output.size();} static deque<MobileUser> getResult(){return output;} protected: static deque<MobileUser> output; }; //C Stype API for JNA invoke void mobileUserProcessor(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser); //because use malloc/free c-api otherwise lead to memory leaks void freeMemory(MobileUser* pMobileUser); #ifdef __cplusplus } #endif #endif // (!__MOBILEUSERPROCESSOR_H__)
MobileUserProcessor.cpp文件內容以下:
#include "MobileUserProcessor.h" deque<MobileUser> MobileUserProcessor::output; MobileUserProcessor::MobileUserProcessor() { MobileUserProcessor::output.clear(); } void MobileUserProcessor::calcRule(deque<MobileUser> input) { if(input.empty()) return; copy(input.begin(),input.end(),std::back_inserter(output)); } void mobileUserProcessor(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser) { deque<MobileUser> list(pMobileUser,pMobileUser+numMobileUser); MobileUserProcessor processor; processor.calcRule(list); deque<MobileUser> result = MobileUserProcessor::getResult(); int number = result.size(); *ppMobileUser = (MobileUser*)malloc(sizeof(MobileUser) * number); memset(*ppMobileUser, 0, sizeof(MobileUser) * number); for(int i = 0;i<number;i++) { (*ppMobileUser)[i].homeCity = result[i].homeCity; (*ppMobileUser)[i].homeCity = result[i].homeCity; int msisdnLen = strlen(result[i].msisdn) + 1; (*ppMobileUser)[i].msisdn = (char*)malloc(sizeof(char) * msisdnLen); strcpy((*ppMobileUser)[i].msisdn, result[i].msisdn); int nameLen = strlen(result[i].name) + 1; (*ppMobileUser)[i].name = (char*)malloc(sizeof(char) * nameLen); strcpy((*ppMobileUser)[i].name, result[i].name); (*ppMobileUser)[i].netAge = result[i].netAge; (*ppMobileUser)[i].netAge = result[i].netAge; } return; } void freeMemory(MobileUser* pMobileUser) { deque<MobileUser> result = MobileUserProcessor::getResult(); for(int i = 0;i<result.size();i++) { free(result[i].msisdn); free(result[i].name); } free(pMobileUser); }
MobileUserProcessor.java文件內容以下:
package newlandframework.jna.businessrule; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.Structure.ByReference; import com.sun.jna.ptr.PointerByReference; import java.util.ArrayList; public class MobileUserProcessor { public interface JNALibrary extends Library { public static class MobileUserStruct extends Structure { public static class ByReference extends MobileUserStruct implements Structure.ByReference { } public int homeCity; public String msisdn; public String name; public int netAge; public MobileUserStruct() { } public MobileUserStruct(Pointer p) { super(p); } } public void mobileUserProcessor(MobileUserStruct.ByReference vals, PointerByReference valsRef, int numVals); public void freeMemory(Pointer p); } public static void invoke(ArrayList<JNALibrary.MobileUserStruct> list, ArrayList<JNALibrary.MobileUserStruct> listResult) { JNALibrary library; JNALibrary.MobileUserStruct.ByReference refMobileUser = null; PointerByReference refPtrVal; try { library = (JNALibrary) Native.loadLibrary( "../src/libMobileUserProcessor.so", JNALibrary.class); refMobileUser = new JNALibrary.MobileUserStruct.ByReference(); refPtrVal = new PointerByReference(); } catch (UnsatisfiedLinkError e) { System.out.println("JNA invoke error"); library = (JNALibrary) Native.loadLibrary( "./libMobileUserProcessor.so", JNALibrary.class); refMobileUser = new JNALibrary.MobileUserStruct.ByReference(); refPtrVal = new PointerByReference(); } JNALibrary.MobileUserStruct[] array = (JNALibrary.MobileUserStruct[]) refMobileUser .toArray(list.size()); for (int i = 0; i < list.size(); i++) { array[i].homeCity = list.get(i).homeCity; array[i].msisdn = list.get(i).msisdn; array[i].name = list.get(i).name; array[i].netAge = list.get(i).netAge; } library.mobileUserProcessor(refMobileUser, refPtrVal, array.length); Pointer pValues = refPtrVal.getValue(); JNALibrary.MobileUserStruct refValues = new JNALibrary.MobileUserStruct( pValues); refValues.read(); JNALibrary.MobileUserStruct[] arrayResult = (JNALibrary.MobileUserStruct[]) refValues .toArray(list.size()); for (JNALibrary.MobileUserStruct element : arrayResult) { listResult.add(element); } library.freeMemory(pValues); } }
其中C函數void mobileUserProcessor(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser);中的參數描述以下:
MobileUser* pMobileUser表示待處理的結構模塊數組(一維指針);MobileUser** ppMobileUser表示處理後的結構模塊數組(二維指針);int numMobileUser表示待處理的結構模塊數組的大小。
要實現Java和C++的跨語言通訊訪問,咱們先來看下操做步驟:
如今咱們根據上面的步驟一步一步來講明:
首先,咱們完成一個簡單的功能:經過Java訪問模塊MobileUser,Java傳入待處理的MobileUser數組集合,而後C++直接簡單地,把待處理數據直接拷貝返回(固然,你能夠在生成代碼模板的基礎上,進行二次開發定製。),最後Java的客戶端打印出C++返回的集合內容。
一、編輯JNA.xml定義通訊模塊信息
配置JNA.xml模塊信息,MobileUser結構屬性內容以下:
<jna> <package>newlandframework.jna.businessrule</package> <module>MobileUser</module> <struct> <attr> <name>homeCity</name> <type>int</type> </attr> <attr> <name>msisdn</name> <type>string</type> </attr> <attr> <name>name</name> <type>string</type> </attr> <attr> <name>netAge</name> <type>int</type> </attr> </struct> </jna>
二、運行JNA.exe (JNA/C++訪問模塊生成器)
把JNA.exe和JNA.xml放在同一目錄下面,雙擊JNA.exe生成對應的JNA訪問模塊、C/C++訪問模塊。
三、根據策略生成JNA訪問模塊
即JNA.exe生成的MobileUserProcessor.java代碼模塊。
四、根據策略生成C/C++訪問模塊
即JNA.exe生成的MobileUserProcessor.h/cpp代碼模塊。
五、編譯C/C++訪問模塊並生成對應動態庫
編譯MobileUserProcessor.h/cpp代碼模塊獲得動態庫libMobileUserProcessor.so,Makefile參考以下:
GEN_SRC = $(shell ls .*.cpp) rule:MobileUserProcessor.cpp g++ -I../inc -Wall -Wextra -pedantic -g -O2 -std=c++98 -MD -MP -c -o MobileUserProcessor.o ./MobileUserProcessor.cpp; g++ -shared -o libMobileUserProcessor.so MobileUserProcessor.o;
六、編譯JNA模塊生成.class文件
編譯MobileUserProcessor.java代碼模塊,獲得.class字節碼文件,參考命令以下:javac -classpath jna.jar -d . MobileUserProcessor.java ProcessMain.java。
七、打包JNA模塊.class文件以及動態庫
I、先到https://java.net/projects/jna/downloads 的官網下載jna.jar,而後把它解壓出來。解壓命令參考:jar xvf jna.jar com/
II、編寫調用客戶端ProcessMain.java,傳入待處理的數據集合一共5個,手機用戶名稱分別爲Jim、Tom、John、Dave、William,給JNA裏面的C++模塊MobileUserProcessor.invoke。並打印C++的處理結果。如今給出調用客戶端ProcessMain.java的實現:
package newlandframework.jna.businessrule; import java.util.ArrayList; import newlandframework.jna.businessrule.MobileUserProcessor.JNALibrary; public class ProcessMain { public ProcessMain() { } public static void main(String[] args) { ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct> list = new ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct>(); ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct> listResult = new ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct>(); MobileUserProcessor.JNALibrary.MobileUserStruct userA = new MobileUserProcessor.JNALibrary.MobileUserStruct(); MobileUserProcessor.JNALibrary.MobileUserStruct userB = new MobileUserProcessor.JNALibrary.MobileUserStruct(); MobileUserProcessor.JNALibrary.MobileUserStruct userC = new MobileUserProcessor.JNALibrary.MobileUserStruct(); MobileUserProcessor.JNALibrary.MobileUserStruct userD = new MobileUserProcessor.JNALibrary.MobileUserStruct(); MobileUserProcessor.JNALibrary.MobileUserStruct userE = new MobileUserProcessor.JNALibrary.MobileUserStruct(); userA.homeCity = 591; userA.msisdn = "5911000"; userA.name = "Jim"; userA.netAge = 2; list.add(userA); userB.homeCity = 592; userB.msisdn = "5921000"; userB.name = "Tom"; userB.netAge = 4; list.add(userB); userC.homeCity = 593; userC.msisdn = "5931000"; userC.name = "John"; userC.netAge = 5; list.add(userC); userD.homeCity = 594; userD.msisdn = "5941000"; userD.name = "Dave"; userD.netAge = 5; list.add(userD); userE.homeCity = 595; userE.msisdn = "5951000"; userE.name = "William"; userE.netAge = 3; list.add(userE); MobileUserProcessor.invoke(list, listResult); for(int i = 0; i < listResult.size(); i++) { System.out.println(listResult.get(i).homeCity); System.out.println(listResult.get(i).msisdn); System.out.println(listResult.get(i).name); System.out.println(listResult.get(i).netAge); System.out.println("----------------------------------------------"); } } }
III、編寫MANIFEST.MF文件,文件內容參考以下:
Manifest-Version: 1.0 Class-Path: . Main-Class: newlandframework.jna.businessrule.ProcessMain Name: com/sun/jna/ Specification-Title: Java Native Access (JNA) Implementation-Title: com.sun.jna Implementation-Version: 3.3.0 Specification-Version: 3 Implementation-Vendor: JNA Development Team Specification-Vendor: JNA Development Team
IV、而後把C++模塊的動態庫libMobileUserProcessor.so、MobileUserProcessor.java、ProcessMain.java代碼模塊編譯出的字節碼文件、jna.jar的字節碼文件、MANIFEST.MF打包生成MobileUserProcessor.jar。命令參考以下:jar cvfm MobileUserProcessor.jar ./META-INF/MANIFEST.MF ./newlandframework/jna/businessrule/MobileUserProcessor.class ./newlandframework/jna/businessrule/ProcessMain.class com ../src/libMobileUserProcessor.so。
八、運行jar包完成跨語言通訊訪問
在終端輸入:java -jar MobileUserProcessor.jar,而後運行截圖以下:
Java程序中,送入待處理的手機用戶名稱:Jim、Tom、John、Dave、William,經過JNA訪問C++的處理模塊。正如咱們預計的同樣,正確打印出了處理結果。
如今咱們更進一步,在原來生成的JNA/C++代碼上面進行二次開發。如今業務算法要求:經過JNA調用C++的處理模塊,傳入MobileUser數組集合,計算出集合中網齡最大和最小的前N個排名的記錄,而後,把排名結果返回,Java打印出排序結果。
首先,仍是經過JNA.exe生成對應的JNA/C++代碼模塊,而後進行二次開發,根據上述的業務要求,加入處理邏輯,封裝出C風格的處理模塊:
找出手機用戶MobileUser網齡最小的前topN個數據:
void minTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN);
找出手機用戶MobileUser網齡最大的前topN個數據:
void maxTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN);
而後對應的代碼內容以下:
MobileUserProcessor.h(C++)代碼內容以下:
/** * @filename:MobileUserProcessor.h * * Newland Co. Ltd. All rights reserved. * * @Description:手機用戶網齡TOPN排名管理類定義 * @author tangjie * @version 1.0 * */ #ifndef __MOBILEUSERPROCESSOR_H__ #define __MOBILEUSERPROCESSOR_H__ #include <string> #include <iostream> #include <deque> #include <algorithm> #include <functional> #include <iterator> #include <memory.h> using namespace std; //JNA異構模塊定義 typedef struct MobileUser { int homeCity;//歸屬地市 char* msisdn;//手機號碼 char* name;//用戶名稱 int netAge;//網齡 } MobileUser; //比較網齡 namespace std { template<> struct less<MobileUser> : public binary_function<MobileUser,MobileUser,bool> { bool operator()(const MobileUser& rhs1,const MobileUser& rhs2) const { return rhs1.netAge < rhs2.netAge; } }; } #ifdef __cplusplus extern "C" { #endif //計算規則處理基類 class BusinessRuleProcessor { public: BusinessRuleProcessor(); virtual void calcRule(deque<MobileUser> input) = 0; static int getResultSize(){return output.size();} static deque<MobileUser> getResult(){return output;} protected: static deque<MobileUser> output; }; //取網齡最小的topN class MinTopNNetAgeProcessor : public BusinessRuleProcessor { public: MinTopNNetAgeProcessor(int topN); void calcRule(deque<MobileUser> input); private: int topN; }; //取網齡最大的topN class MaxTopNNetAgeProcessor : public BusinessRuleProcessor { public: MaxTopNNetAgeProcessor(int topN); void calcRule(deque<MobileUser> input); private: int topN; }; //C Stype API for JNA invoke void minTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN); void maxTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN); void findTopNByNetAge(BusinessRuleProcessor* pRuleProcessor,MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser); //because use malloc/free c-api otherwise lead to memory leaks void freeMemory(MobileUser* pMobileUser); #ifdef __cplusplus } #endif #endif // (!__MOBILEUSERPROCESSOR_H__)
MobileUserProcessor.cpp(C++)代碼內容以下:
/** * @filename:MobileUserProcessor.cpp * * Newland Co. Ltd. All rights reserved. * * @Description:手機用戶網齡TOPN排名管理類實現 * @author tangjie * @version 1.0 * */ #include "MobileUserProcessor.h" //計算規則處理基類 deque<MobileUser> BusinessRuleProcessor::output; BusinessRuleProcessor::BusinessRuleProcessor() { BusinessRuleProcessor::output.clear(); } //取網齡最小的topN MinTopNNetAgeProcessor::MinTopNNetAgeProcessor(int topN):BusinessRuleProcessor(){this->topN = topN;} void MinTopNNetAgeProcessor::calcRule(deque<MobileUser> input) { if(input.empty()) return; sort(input.begin(),input.end(),less<MobileUser>()); int interval = (this->topN >= input.size()) ? input.size() : this->topN; copy(input.begin(),input.begin()+interval,std::back_inserter(output)); } //取網齡最大的topN MaxTopNNetAgeProcessor::MaxTopNNetAgeProcessor(int topN):BusinessRuleProcessor(){this->topN = topN;} void MaxTopNNetAgeProcessor::calcRule(deque<MobileUser> input) { if(input.empty()) return; sort(input.begin(),input.end(),not2(less<MobileUser>())); int interval = (this->topN >= input.size()) ? input.size() : this->topN; copy(input.begin(),input.begin()+interval,std::back_inserter(output)); } //C Stype API for JNA invoke void findTopNByNetAge(BusinessRuleProcessor* pRuleProcessor,MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser) { deque<MobileUser> list(pMobileUser,pMobileUser+numMobileUser); pRuleProcessor->calcRule(list); deque<MobileUser> result = BusinessRuleProcessor::getResult(); int number = result.size(); *ppMobileUser = (MobileUser*)malloc(sizeof(MobileUser) * number); memset(*ppMobileUser, 0, sizeof(MobileUser) * number); for(int i = 0;i<number;i++) { (*ppMobileUser)[i].homeCity = result[i].homeCity; (*ppMobileUser)[i].netAge = result[i].netAge; int msisdnLen = strlen(result[i].msisdn)+1; (*ppMobileUser)[i].msisdn = (char*)malloc(sizeof(char) * msisdnLen); memset((*ppMobileUser)[i].msisdn, 0, sizeof(char) * msisdnLen); strcpy((*ppMobileUser)[i].msisdn, result[i].msisdn); int nameLen = strlen(result[i].name)+1; (*ppMobileUser)[i].name = (char*)malloc(sizeof(char) * nameLen); memset((*ppMobileUser)[i].name, 0, sizeof(char) * nameLen); strcpy((*ppMobileUser)[i].name, result[i].name); } return; } void minTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN) { auto_ptr<BusinessRuleProcessor> pRuleProcessor(new MinTopNNetAgeProcessor(topN)); findTopNByNetAge(pRuleProcessor.get(),pMobileUser,ppMobileUser,numMobileUser); } void maxTopNByNetAge(MobileUser* pMobileUser,MobileUser** ppMobileUser,int numMobileUser,int topN) { auto_ptr<BusinessRuleProcessor> pRuleProcessor(new MaxTopNNetAgeProcessor(topN)); findTopNByNetAge(pRuleProcessor.get(),pMobileUser,ppMobileUser,numMobileUser); } void freeMemory(MobileUser* pMobileUser) { deque<MobileUser> result = BusinessRuleProcessor::getResult(); for(int i = 0;i<result.size();i++) { free(result[i].msisdn); free(result[i].name); } free(pMobileUser); }
MobileUserProcessor.java(Java)代碼內容以下:
/** * @filename:MobileUserProcessor.java * * Newland Co. Ltd. All rights reserved. * * @Description:手機用戶網齡TOPN排名管理JNA類實現 * @author tangjie * @version 1.0 * */ package newlandframework.jna.businessrule; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.Structure.ByReference; import com.sun.jna.ptr.PointerByReference; import java.util.ArrayList; public class MobileUserProcessor { public interface JNALibrary extends Library { public static class MobileUserStruct extends Structure { public static class ByReference extends MobileUserStruct implements Structure.ByReference { } public int homeCity; public String msisdn; public String name; public int netAge; public MobileUserStruct() { } public MobileUserStruct(Pointer p) { super(p); } } //網齡最大topN public void maxTopNByNetAge(MobileUserStruct.ByReference vals, PointerByReference valsRef, int numVals, int topN); //網齡最小topN public void minTopNByNetAge(MobileUserStruct.ByReference vals, PointerByReference valsRef, int numVals, int topN); //釋放JNA內存模塊 public void freeMemory(Pointer p); } //經過JNA訪問調用C++的實現 public static void invoke(ArrayList<JNALibrary.MobileUserStruct> list, ArrayList<JNALibrary.MobileUserStruct> listResult,int topN) { JNALibrary library; JNALibrary.MobileUserStruct.ByReference refMobileUser = null; //和C++的指針的指針綁定映射,其內容爲保存處理結果的首地址 PointerByReference refPtrVal; try { library = (JNALibrary) Native.loadLibrary( "../src/libMobileUserProcessor.so", JNALibrary.class); refMobileUser = new JNALibrary.MobileUserStruct.ByReference(); refPtrVal = new PointerByReference(); } catch (UnsatisfiedLinkError e) { System.out.println("JNA invoke error"); library = (JNALibrary) Native.loadLibrary( "./libMobileUserProcessor.so", JNALibrary.class); refMobileUser = new JNALibrary.MobileUserStruct.ByReference(); refPtrVal = new PointerByReference(); } //爲了兼容JNA,用C的方式封裝了C++的邏輯模塊。傳入給C函數的數據,在Java裏面映射成數組Array JNALibrary.MobileUserStruct[] array = (JNALibrary.MobileUserStruct[]) refMobileUser .toArray(list.size()); for (int i = 0; i < list.size(); i++) { array[i].homeCity = list.get(i).homeCity; array[i].msisdn = list.get(i).msisdn; array[i].name = list.get(i).name; array[i].netAge = list.get(i).netAge; } //取網齡最大的前topN移動手機用戶 library.maxTopNByNetAge(refMobileUser, refPtrVal, array.length, topN); //取網齡最小的前topN移動手機用戶 //library.minTopNByNetAge(refMobileUser, refPtrVal, array.length, topN); //其內容爲處理結果數組的首地址 Pointer pValues = refPtrVal.getValue(); JNALibrary.MobileUserStruct refValues = new JNALibrary.MobileUserStruct( pValues); refValues.read(); JNALibrary.MobileUserStruct[] arrayResult = (JNALibrary.MobileUserStruct[]) refValues .toArray(topN); for (JNALibrary.MobileUserStruct element : arrayResult) { listResult.add(element); } //JNA釋放C++的堆內存,不然會內存泄露 library.freeMemory(pValues); } }
而後,編寫一個客戶端ProcessMain.java傳入要處理的數據,具體參考代碼以下:
/** * @filename:ProcessMain.java * * Newland Co. Ltd. All rights reserved. * * @Description:手機網齡排名JNA調用主函數 * @author tangjie * @version 1.0 * */ package newlandframework.jna.businessrule; import java.util.ArrayList; import newlandframework.jna.businessrule.MobileUserProcessor.JNALibrary; public class ProcessMain { public ProcessMain() { } public static void main(String[] args) { ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct> list = new ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct>(); ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct> listResult = new ArrayList<MobileUserProcessor.JNALibrary.MobileUserStruct>(); //構造一些測試用戶 MobileUserProcessor.JNALibrary.MobileUserStruct userA = new MobileUserProcessor.JNALibrary.MobileUserStruct(); MobileUserProcessor.JNALibrary.MobileUserStruct userB = new MobileUserProcessor.JNALibrary.MobileUserStruct(); MobileUserProcessor.JNALibrary.MobileUserStruct userC = new MobileUserProcessor.JNALibrary.MobileUserStruct(); MobileUserProcessor.JNALibrary.MobileUserStruct userD = new MobileUserProcessor.JNALibrary.MobileUserStruct(); MobileUserProcessor.JNALibrary.MobileUserStruct userE = new MobileUserProcessor.JNALibrary.MobileUserStruct(); userA.homeCity = 591; userA.msisdn = "5911000"; userA.name = "Jim"; userA.netAge = 2; list.add(userA); userB.homeCity = 592; userB.msisdn = "5921000"; userB.name = "Tom"; userB.netAge = 4; list.add(userB); userC.homeCity = 593; userC.msisdn = "5931000"; userC.name = "John"; userC.netAge = 5; list.add(userC); userD.homeCity = 594; userD.msisdn = "5941000"; userD.name = "Dave"; userD.netAge = 5; list.add(userD); userE.homeCity = 595; userE.msisdn = "5951000"; userE.name = "William"; userE.netAge = 3; list.add(userE); //取網齡最大的前3的移動手機用戶 int topN = 3; //經過JNA調用C++的邏輯處理模塊 MobileUserProcessor.invoke(list, listResult, topN); //打印處理結果 for (int i = 0; i < listResult.size(); i++) { System.out.println(listResult.get(i).homeCity); System.out.println(listResult.get(i).msisdn); System.out.println(listResult.get(i).name); System.out.println(listResult.get(i).netAge); System.out.println("----------------------------------------------"); } } }
Jim網齡2年、Tom網齡4年、John網齡5年、Dave網齡5年、William網齡3年。分別找到網齡最大的前3名用戶信息和網齡最小的前3名用戶信息。編譯運行過程參考:利用JNA.exe(JNA/C++訪問模塊生成器)生成代碼實現Java/C++通訊訪問 章節。
而後最終的運行結果以下:
處理訪問網齡最大的前3名用戶資料:
處理訪問網齡最小的前3名用戶資料:
正如咱們所預計的結果同樣,徹底正確!利用JNA咱們很快的把C++模塊和Java模塊,有機的結合在一塊兒了。
本文經過JNA技術,實現了Java和C/C++兩種語言模塊的通訊調用,可是,目前僅僅實現了特定訪問策略生成模塊的部分代碼。後續有時間,我還會繼續概括總結出,其它JNA訪問策略的通用共性代碼,使得上述框架變得更加靈活、通用。算是爲Java/C++的跨語言通訊調用,提供另一種可行的解決方案。固然,要想繼續完善上述的通訊框架,對於JNA的進一步深刻理解是必不可少的,但願有Geek精神的各位園友們,能夠在此基礎上加以改良,不斷完善,讓JNA爲我所用。更多的JNA的技術細節你們能夠參考:https://jna.java.net/ 上面的API文檔。固然,若是你具有較好的C++功底的話,理解JNA裏面的一些實現方式和功能模塊,會更加地駕輕就熟。洋洋灑灑寫了這麼多,若是面前的你,以爲本文寫得還不錯,對您的學習、工做,有借鑑意義的話,能夠點擊「推薦」一下,舉手之勞。算是對我辛苦寫博客的一種確定和鼓勵吧!
最後,謝謝你們耐心讀完整篇文章!但願個人一點兒實踐和總結,對您有幫助!