您的位置:首页 > 编程语言 > Java开发

【java学习】Jni在hadoop上的使用(下)

2012-12-07 20:29 471 查看
有了前两篇博文的铺垫,这一篇,进入“阶段三”,即在hadoop中调用.so动态库,在动态库中读取文件。

其实苦头都在前面吃了,这部分相对很顺利。

首先,在动态库.so的java wrapper中增加一个函数接口Init,用来load词典文件:

package FakeSegmentForJni;

/**
*
* This class is for verifying the jni technology. It's a fake segmenter.
* The only user is its function interface.
*
*/

public class FakeSegmentForJni {
public static native boolean Init (String file);
public static native String SegmentALine (String line);

static
{
System.loadLibrary("FakeSegmentForJni");
}
}


接下来,同《【java学习】Jni在hadoop上的使用(上)》中介绍的过程一样,用javac命令生成FakeSegmentForJni.class文件,用javah命令生成c++头文件FakeSegmentForJni_FakeSegmentForJni.h。用javah的时候,要注意路径问题。FakeSegmentForJni_FakeSegmentForJni.h看起来是这个样子的:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class FakeSegmentForJni_FakeSegmentForJni */

#ifndef _Included_FakeSegmentForJni_FakeSegmentForJni
#define _Included_FakeSegmentForJni_FakeSegmentForJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     FakeSegmentForJni_FakeSegmentForJni
* Method:    Init
* Signature: (Ljava/lang/String;)Z
*/
JNIEXPORT jboolean JNICALL Java_FakeSegmentForJni_FakeSegmentForJni_Init
(JNIEnv *, jclass, jstring);

/*
* Class:     FakeSegmentForJni_FakeSegmentForJni
* Method:    SegmentALine
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_FakeSegmentForJni_FakeSegmentForJni_SegmentALine
(JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif


其实也可以自己来写。

第二,编写FakeSegmentForJni_FakeSegmentForJni.cpp文件内容,实现相关功能,如下:

#include <jni.h>
#include <stdio.h>
#include <string.h>

#include <string>
#include <vector>
#include <fstream>
#include <iostream>

#include "FakeSegmentForJni_FakeSegmentForJni.h"

using namespace std;

// the global varable for lexicon
vector <string> WordVec;

/* * Class:     FakeSegmentForJni_FakeSegmentForJni
* * Method:    Init
* * Signature: (Ljava/lang/String;)Ljava/lang/String;
* */
JNIEXPORT jboolean JNICALL Java_FakeSegmentForJni_FakeSegmentForJni_Init
(JNIEnv *env, jclass obj, jstring line)
{
const char *pFileName = NULL;
pFileName = env->GetStringUTFChars (line, false);
if (pFileName == NULL)
return false;

ifstream in (pFileName);
if (!in)
{
cerr << "Can not open the file of " << pFileName << endl;
return false;
}
string sWord;
while (getline (in, sWord))
{
WordVec.push_back(sWord);
}
return true;
}

/* * Class:     FakeSegmentForJni_FakeSegmentForJni
* * Method:    SegmentALine
* * Signature: (Ljava/lang/String;)Ljava/lang/String;
* */

JNIEXPORT jstring JNICALL Java_FakeSegmentForJni_FakeSegmentForJni_SegmentALine
(JNIEnv *env, jclass obj, jstring line)
{
char buf[128];
buf[0] = 0;
const char *str = NULL;
str = env->GetStringUTFChars(line, false);
if (str == NULL)
return NULL;
strcpy (buf, str);
if (!WordVec.empty())
strcat (buf, WordVec.at(0).c_str());
// strcat (buf, "--copy that\n");
env->ReleaseStringUTFChars(line, str);
return env->NewStringUTF(buf);
}


功能很简单,就是在Init函数中打开一个文件,将文件中的每一行存储在全局变量WordVec中;然后,在SegmentALine函数中,将输入字符串和WordVec中的第一个元素相连接,再输出。用g++将.cpp文件编译成.so文件,命令如下:

g++ -I/System/Library/Frameworks/JavaVM.framework/Versions/A/Headers FakeSegmentForJni_FakeSegmentForJni.cpp -fPIC -shared -o libFakeSegmentForJni.so

第三步,写hadoop程序。

map函数:

public static class MapTestJni extends Mapper<Writable, Text, Text, Text> {

protected String s;
protected void setup(Context context) throws IOException, InterruptedException
{
FakeSegmentForJni.Init("Lex.txt");
s = FakeSegmentForJni.SegmentALine("jni-value");
}

protected void map(Writable key, Text value, Context context)
throws IOException, InterruptedException {

// the format of input value is:
//    mcid totaltimes item1 item2(itemkey=itemvalue)

context.write(new Text("key"), new Text(s.toString()));
}
}


在setup函数中,我们调用了FakeSegmentForJni.Init函数,来load文件Lex.txt中的内容。可以看到,相对路径就是本地当前路径。在下文分发过程中,会将Lex.txt文件分发到与jar文件相同的本地路径下。在map函数中,输出s的内容。reduce函数、控制函数和main函数与《【java学习】Jni在hadoop上的使用(中)》的一样,这里就不重复粘贴了。

第四步,在命令行中用“-files”参数将.so文件和Lex.txt文件分发到tasknode上,命令如下:

hadoop jar /xxx/TestFakeSegmentForJniHadoop.jar -files /xxx/TestJni/libFakeSegmentForJni.so,/xxx/TestJni/Lex.txt FakeSegmentForJni.TestFakeSegmentForJni /input/xxx.txt /outputJNI

多个文件用逗号间隔。

最后,检查运行结果,结果正确。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: