首先创建一个Java类,这里的示例是一个链接HBase的Java小程序:
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
public class Connect {
public Connect(){
super();
}
public static Configuration conf =null;
static {
conf = HBaseConfiguration.create();
}
/**
* 创建一张表
*/
public static void createTable() throws Exception {
String tableName="sensor";
String[] familys={"ID", "state"};
HBaseAdmin admin = new HBaseAdmin(conf);
if (admin.tableExists(tableName)) {
System.out.println("table already exists!");
} else {
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
for(int i=0; i<familys.length; i++){
tableDesc.addFamily(new HColumnDescriptor(familys[i]));
}
admin.createTable(tableDesc);
System.out.println("create table " + tableName + " ok.");
}
}
/**
* 查找一行记录
*/
public static String getOneRecord (String tableName, String rowKey) throws IOException{
HTable table = new HTable(conf, tableName);
Get get = new Get(rowKey.getBytes());
Result rs = table.get(get);
for(KeyValue kv : rs.raw()){
System.out.print(new String(kv.getRow()) + " " );
System.out.print(new String(kv.getFamily()) + ":" );
System.out.print(new String(kv.getQualifier()) + " " );
System.out.print(kv.getTimestamp() + " " );
System.out.println(new String(kv.getValue())); return new String(kv.getValue());
}
return "0";
}
/**
* 显示所有数据
*/
public static void getAllRecord (String tableName) {
try{
HTable table = new HTable(conf, tableName);
Scan s = new Scan();
ResultScanner ss = table.getScanner(s);
for(Result r:ss){
for(KeyValue kv : r.raw()){
System.out.print(new String(kv.getRow()) + " ");
System.out.print(new String(kv.getFamily()) + ":");
System.out.print(new String(kv.getQualifier()) + " ");
System.out.print(kv.getTimestamp() + " ");
System.out.println(new String(kv.getValue()));
}
}
} catch (IOException e){
e.printStackTrace();
}
}
/**
* 插入一行记录
*/
public static void addRecord (String rowKey, String family, String value)
throws Exception{
try {
String tableName ="sensor";
String qualifier="";
HTable table = new HTable(conf, tableName);
Put put = new Put(Bytes.toBytes(rowKey));
put.add(Bytes.toBytes(family),Bytes.toBytes(qualifier),Bytes.toBytes(value));
table.put(put);
System.out.println("insert " + family+ " to table " + tableName +" ok.");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args)
{ try
{Connect.addRecord("1","sequence","629");}
catch(Exception e)
{}
}
}
创建完Java类后,就是怎么去使用C程序调用该类的问题。
首先引用<jni.h>头文件,该文件一般在JAVA_HOME/include目录下,利用该文件提供的函数使用JNI来建立java runtime environment。
然后,根据JavaVMOption和JavaVMInitArgs创建JavaVM虚拟机类型的指针,使用JNIEnv虚拟机环境类型变量指定类的加载路径。
接着,在找到加载类之后,选择调用的方法,这里调用的都是类方法,无须建立对象,库中已提供相应的接口GetStaticMethodID。
最后,结束时选择DestroyJavaVM方法删除JVM环境。
具体代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include<signal.h>
#include<sys/wait.h>
#include<sys/shm.h>
#include<sys/stat.h>
#include<mysql.h>
#include<time.h>
#include<sys/sem.h>
#include<fcntl.h>
#include<netinet/tcp.h>
#include <assert.h>
#include <jni.h>
jstring stoJstring(JNIEnv* env, const char* pat)
{
jclass strClass = (*env)->FindClass(env,"java/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);
}
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;
}
int main(int argc, char** argv)
{
JavaVMOption options[2];
JNIEnv *env;
JavaVM *jvm;
JavaVMInitArgs vm_args;
long status;
jclass cls;
jmethodID mid;
jboolean jnot;
jobject jobj;
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
//options[2].optionString = "-verbose:jni"; //用于跟踪运行时的信息
vm_args.version = JNI_VERSION_1_4; // JDK版本号
vm_args.nOptions = 2;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
while(1)
{
if((childpid=fork()) == 0)
{
status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if(status != JNI_ERR){
printf("create java jvm success\n");
cls = (*env)->FindClass(env,"Connect"); // 在这里查找java类
if(cls !=0){
printf("find java class success\n");
mid = (*env)->GetStaticMethodID(env, cls, "addRecord", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
exit(0);}
else{
printf("Find Class failed\n");
}
(*jvm)->DestroyJavaVM(jvm);
printf( "Java VM destory\n");
}
else{
printf("create java jvm fail\n");
}
}
}
return 0;
}
几个需要注意的问题:
1. 编译语句需指定头文件路径,否则会找不到。 g++ -o testjava testjava.cpp -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -L${JRE_HOME}/lib/i386/client -ljvm
2. JRE_HOME和LD_LIBRARY_PATH要设置好,编译C++程序时要使用JRE_HOME下面的libjvm.so动态库(一开始我使用网上说的使用JAVA_HOME目录下的libjvm.so,结果出现下面错误
# An unexpected error has been detected by HotSpot Virtual Machine:
#
#
#
# Java VM: Java HotSpot(TM) Server VM (1.5.0_11-b03 mixed mode)