[[PageOutline]]
{{{
#!html
 hadoop 程式開發 (eclipse plugin) 
}}}
 = 零. 環境配置 =
 == 0.1 環境說明 == 
 * ubuntu 8.10
 * sun-java-6
   * [http://www.java.com/zh_TW/download/linux_manual.jsp?locale=zh_TW&host=www.java.com:80 java 下載處]
   * [https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=jdk-6u10-docs-oth-JPR@CDS-CDS_Developer JavaDoc ]
 * eclipse 3.3.2
   * eclipse 各版本下載點 [http://archive.eclipse.org/eclipse/downloads/]
 * hadoop 0.18.3
   * hadoop 各版本下載點 [http://ftp.twaren.net/Unix/Web/apache/hadoop/core/]
 == 0.2 目錄說明 == 
 * 使用者:hadoop
 * 使用者家目錄: /home/hadooper
 * 專案目錄 : /home/hadooper/workspace
 * hadoop目錄: /opt/hadoop
 = 一、安裝 =
安裝的部份沒必要都一模一樣,僅提供參考,反正只要安裝好java , hadoop , eclipse,並清楚自己的路徑就可以了
 == 1.1. 安裝java ==
 
首先安裝java 基本套件
 
{{{
$ sudo apt-get install java-common sun-java6-bin sun-java6-jdk sun-java6-jre
}}}
 == 1.1.1. 安裝sun-java6-doc ==
 
 1 將javadoc (jdk-6u10-docs.zip) 下載下來放在 /tmp/ 下 
    * 教學環境內,已經存在於 /home/hadooper/tools/ ,將其複製到 /tmp
{{{
$ cp /home/hadooper/tools/jdk-*-docs.zip /tmp/
}}}
    * 或是到官方網站將javadoc (jdk-6u10-docs.zip) 下載下來放到 /tmp
 [https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_Developer-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=jdk-6u10-docs-oth-JPR@CDS-CDS_Developer 下載點]
[[Image(wiki:waue/2009/0617:1-1.png)]]
 
 2 執行
 
{{{
$ sudo apt-get install sun-java6-doc
$ sudo ln -sf /usr/share/doc/sun-java6-jdk/html /usr/lib/jvm/java-6-sun/docs
}}}
 == 1.2. ssh 安裝設定 ==
 [http://trac.nchc.org.tw/cloud/wiki/Hadoop_Lab1 詳見實作一]
 == 1.3. 安裝hadoop ==
 [http://trac.nchc.org.tw/cloud/wiki/Hadoop_Lab1 詳見實作一]
 == 1.4. 安裝eclipse ==
 * 取得檔案 eclipse 3.3.2  (假設已經下載於/home/hadooper/tools/ 內),執行下面指令:
 
{{{
$ cd ~/tools/
$ tar -zxvf eclipse-SDK-3.3.2-linux-gtk.tar.gz
$ sudo mv eclipse /opt
$ sudo ln -sf /opt/eclipse/eclipse /usr/local/bin/
}}}
 = 二、 建立專案 = 
 == 2.1 安裝hadoop 的 eclipse plugin ==
 * 匯入hadoop eclipse plugin 
 
{{{
$ cd /opt/hadoop
$ sudo cp /opt/hadoop/contrib/eclipse-plugin/hadoop-0.18.3-eclipse-plugin.jar /opt/eclipse/plugins
}}}
補充: 可斟酌參考eclipse.ini內容(非必要)
{{{
$ sudo cat /opt/eclipse/eclipse.ini
}}}
{{{
#!sh
-showsplash
org.eclipse.platform
-vmargs
-Xms40m
-Xmx256m
}}}
== 2.2 開啟eclipse ==
 * 打開eclipse 
 
{{{
$ eclipse &
}}}
一開始會出現問你要將工作目錄放在哪裡:在這我們用預設值
[[Image(wiki:waue/2009/0617:2-1.png)]] 
-------
'''PS: 之後的說明則是在eclipse 上的介面操作'''
-------
== 2.3 選擇視野 ==
|| window -> || open pers.. -> || other.. -> || map/reduce|| 
[[Image(wiki:waue/2009/0617:win-open-other.png)]] 
-------
設定要用 Map/Reduce 的視野
[[Image(wiki:waue/2009/0617:2-2.png)]] 
---------
使用 Map/Reduce 的視野後的介面呈現
[[Image(wiki:waue/2009/0617:2-3.png)]] 
--------
== 2.4 建立專案 ==
 || file ->  || new ->  || project ->  || Map/Reduce ->  || Map/Reduce Project -> ||  next || 
[[Image(wiki:waue/2009/0617:file-new-project.png)]]
--------
建立mapreduce專案(1)
[[Image(wiki:waue/2009/0617:2-4.png)]] 
-----------
建立mapreduce專案的(2)
{{{
#!sh
project name-> 輸入 : icas (隨意)
use default hadoop -> Configur Hadoop install... -> 輸入: "/opt/hadoop" -> ok
Finish
}}}
[[Image(wiki:waue/2009/0617:2-4-2.png)]]
--------------
== 2.5 設定專案 ==
由於剛剛建立了icas這個專案,因此eclipse已經建立了新的專案,出現在左邊視窗,右鍵點選該資料夾,並選properties
--------------
 Step1. 右鍵點選project的properties做細部設定
[[Image(wiki:waue/2009/0617:2-5.png)]]
----------
 Step2. 進入專案的細部設定頁
hadoop的javadoc的設定(1)
[[Image(wiki:waue/2009/0617:2-5-1.png)]]
 * java Build Path -> Libraries -> hadoop0.18.3-ant.jar
 * java Build Path -> Libraries -> hadoop0.18.3-core.jar 
 * java Build Path -> Libraries ->  hadoop0.18.3-tools.jar
   * 以 hadoop0.18.3-core.jar 的設定內容如下,其他依此類推
   
{{{
#!sh
source ...-> 輸入:/opt/hadoop/src/core
javadoc ...-> 輸入:file:/opt/hadoop/docs/api/
}}}
------------
 Step3. hadoop的javadoc的設定完後(2)
[[Image(wiki:waue/2009/0617:2-5-2.png)]]
------------
 Step4. java本身的javadoc的設定(3)
 
 * javadoc location -> 輸入:file:/usr/lib/jvm/java-6-sun/docs/api/
 
[[Image(wiki:waue/2009/0617:2-5-3.png)]]
-----
設定完後回到eclipse 主視窗
== 2.6 連接hadoop server ==
--------
 Step1. 視窗右下角黃色大象圖示"Map/Reduce Locations tag" -> 點選齒輪右邊的藍色大象圖示:
[[Image(wiki:waue/2009/0617:2-6.png)]]
-------------
 Step2. 進行eclipse 與 hadoop 間的設定(2)
[[Image(wiki:waue/2009/0617:2-6-1.png)]]
{{{
#!sh
Location Name -> 輸入:hadoop  (隨意)
Map/Reduce Master 
-> Host-> 輸入:localhost 
-> Port-> 輸入:9001 
DFS Master 
-> Host-> 輸入:9000
Finish
}}}
----------------
設定完後,可以看到下方多了一隻藍色大象,左方展開資料夾也可以秀出在hdfs內的檔案結構
[[Image(wiki:waue/2009/0617:2-6-2.png)]]
-------------
 = 三、 撰寫範例程式 =
 * 之前在eclipse上已經開了個專案icas,因此這個目錄在:
   * /home/hadooper/workspace/icas
 * 在這個目錄內有兩個資料夾: 
   * src : 用來裝程式原始碼 
   * bin : 用來裝編譯後的class檔
 * 如此一來原始碼和編譯檔就不會混在一起,對之後產生jar檔會很有幫助
 * 在這我們編輯一個範例程式 : WordCount 
 == 3.1 mapper.java ==
 
 1. new
 
 || File ->  || new ->  || mapper || 
[[Image(wiki:waue/2009/0617:file-new-mapper.png)]]
-----------
 2. create
 
[[Image(wiki:waue/2009/0617:3-1.png)]]
{{{
#!sh
source folder-> 輸入: icas/src
Package : Sample
Name -> : mapper
}}}
----------
 3. modify
 
{{{
#!java
package Sample;
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reporter;
public class mapper extends MapReduceBase implements Mapper {
    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();
    public void map(LongWritable key, Text value, OutputCollector output, Reporter reporter) throws IOException {
      String line = value.toString();
      StringTokenizer tokenizer = new StringTokenizer(line);
      while (tokenizer.hasMoreTokens()) {
        word.set(tokenizer.nextToken());
        output.collect(word, one);
      }
    }
  }
}}}
建立mapper.java後,貼入程式碼
[[Image(wiki:waue/2009/0617:3-2.png)]]
------------
== 3.2 reducer.java ==
 1. new
 * File -> new -> reducer
[[Image(wiki:waue/2009/0617:file-new-reducer.png)]]
-------
 2. create
[[Image(wiki:waue/2009/0617:3-3.png)]]
{{{
#!sh
source folder-> 輸入: icas/src
Package : Sample
Name -> : reducer
}}}
-----------
 3. modify 
 
{{{
#!java
package Sample;
import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
public class reducer extends MapReduceBase implements Reducer {
    public void reduce(Text key, Iterator values, OutputCollector output, Reporter reporter) throws IOException {
      int sum = 0;
      while (values.hasNext()) {
        sum += values.next().get();
      }
      output.collect(key, new IntWritable(sum));
    }
  }
}}}
 * File -> new -> Map/Reduce Driver
[[Image(wiki:waue/2009/0617:file-new-mr-driver.png)]]
----------
== 3.3 WordCount.java (main function) ==
 1. new
建立WordCount.java,此檔用來驅動mapper 與 reducer,因此選擇 Map/Reduce Driver
[[Image(wiki:waue/2009/0617:3-4.png)]]
------------
 2. create
{{{
#!sh
source folder-> 輸入: icas/src
Package : Sample
Name -> : WordCount.java
}}}
-------
 3. modify
{{{
#!java
package Sample;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;
public class WordCount {
   public static void main(String[] args) throws Exception {
     JobConf conf = new JobConf(WordCount.class);
     conf.setJobName("wordcount");
     conf.setOutputKeyClass(Text.class);
     conf.setOutputValueClass(IntWritable.class);
     conf.setMapperClass(mapper.class);
     conf.setCombinerClass(reducer.class);
     conf.setReducerClass(reducer.class);
     conf.setInputFormat(TextInputFormat.class);
     conf.setOutputFormat(TextOutputFormat.class);
    FileInputFormat.setInputPaths(conf, new Path("/user/hadooper/input"));
    FileOutputFormat.setOutputPath(conf, new Path("lab5_out2"));
     JobClient.runJob(conf);
   }
}
}}}
三個檔完成後並存檔後,整個程式建立完成
[[Image(wiki:waue/2009/0617:3-5.png)]]
-------
 * 三個檔都存檔後,可以看到icas專案下的src,bin都有檔案產生,我們用指令來check
 
{{{
$ cd workspace/icas
$ ls src/Sample/
mapper.java  reducer.java  WordCount.java
$ ls bin/Sample/
mapper.class  reducer.class  WordCount.class
}}}
 = 四、測試範例程式 =
在此提供兩種方法來run我們從eclipse 上編譯出的code。 
方法一是直接在eclipse上用圖形介面操作,參閱 4.1  在eclipse上操作
方法二是產生jar檔後搭配自動編譯程式Makefile,參閱4.2
 == 4.1 法一:在eclipse上操作 ==
 * 右鍵點選專案資料夾:icas -> run as -> run on Hadoop 
   
[[Image(wiki:waue/2009/0617:run-on-hadoop.png)]]
 == 4.2 法二:jar檔搭配自動編譯程式 ==
 
 * eclipse 可以產生出jar檔 :
 
File -> Export -> java -> JAR file [[br]]
-> next -> 
--------
選擇要匯出的專案 ->
jarfile: /home/hadooper/mytest.jar -> [[br]]
next ->
--------
next ->
--------
main class: 選擇有Main的class   -> [[br]]
Finish
--------
 * 以上的步驟就可以在/home/hadooper/ 產生出你的 mytest.jar
 * 不過程式常常修改,每次都做這些動作也很累很煩,讓我們來體驗一下'''用指令比用圖形介面操作還方便'''吧
 
 === 4.2.1 產生Makefile 檔 ===
{{{
$ cd /home/hadooper/workspace/icas/
$ gedit Makefile
}}}
 * 輸入以下Makefile的內容 (注意 ":" 後面要接 "tab" 而不是 "空白")
{{{
JarFile="sample-0.1.jar"
MainFunc="Sample.WordCount"
LocalOutDir="/tmp/output"
HADOOP_BIN="/opt/hadoop/bin"
all:jar run output clean
jar:
	jar -cvf ${JarFile} -C bin/ .
run:
	${HADOOP_BIN}/hadoop jar ${JarFile} ${MainFunc} input output
clean:
	${HADOOP_BIN}/hadoop fs -rmr output
output:
	rm -rf ${LocalOutDir}
	${HADOOP_BIN}/hadoop fs -get output ${LocalOutDir}
	gedit ${LocalOutDir}/part-r-00000 & 
help:
	@echo "Usage:"
	@echo " make jar     - Build Jar File."
	@echo " make clean   - Clean up Output directory on HDFS."
	@echo " make run     - Run your MapReduce code on Hadoop."
	@echo " make output  - Download and show output file"
	@echo " make help    - Show Makefile options."
	@echo " "
	@echo "Example:"
	@echo " make jar; make run; make output; make clean"
}}}
 * 或是直接下載 [http://trac.nchc.org.tw/cloud/raw-attachment/wiki/Hadoop_Lab5/Makefile Makefile] 吧
{{{
$ cd /home/hadooper/workspace/icas/
$ wget http://trac.nchc.org.tw/cloud/raw-attachment/wiki/Hadoop_Lab5/Makefile
}}}
 === 4.2.2 執行 ===
 
 * 執行Makefile,可以到該目錄下,執行make [參數],若不知道參數為何,可以打make 或 make help
 * make 的用法說明
{{{
$ cd /home/hadooper/workspace/icas/
$ make
Usage:
 make jar     - Build Jar File.
 make clean   - Clean up Output directory on HDFS.
 make run     - Run your MapReduce code on Hadoop.
 make output  - Download and show output file
 make help    - Show Makefile options.
 
Example:
 make jar; make run; make output; make clean
}}}
 * 下面提供各種make 的參數
 
 === make jar ===
 * 1. 編譯產生jar檔
 
{{{
$ make jar
}}}
 === make run ===
 * 2. 跑我們的wordcount 於hadoop上
{{{
$ make run
}}}
 * make run基本上能正確無誤的運作到結束,因此代表我們在eclipse編譯的程式可以順利在hadoop0.18.3的平台上運行。
 * 而回到eclipse視窗,我們可以看到下方視窗run完的job會呈現出來;左方視窗也多出output資料夾,part-r-00000就是我們的結果檔
 
[[Image(wiki:waue/2009/0617:4-1.png)]]
------
 * 因為有設定完整的javadoc, 因此可以得到詳細的解說與輔助
[[Image(wiki:waue/2009/0617:4-2.png)]]
 === make output ===
 * 3. 這個指令是幫助使用者將結果檔從hdfs下載到local端,並且用gedit來開啟你的結果檔
{{{
$ make output
}}}
 === make clean ===
 * 4. 這個指令用來把hdfs上的output資料夾清除。如果你還想要在跑一次make run,請先執行make clean,否則hadoop會告訴你,output資料夾已經存在,而拒絕工作喔!
 
{{{
$ make clean
}}}
= 五、結論 =
 * 搭配eclipse ,我們可以更有效率的開發hadoop
 * hadoop 0.20 與之前的版本api以及設定都有些改變,可以看 [wiki:waue/2009/0617 hadoop 0.20 coding (eclipse )] 
= 六、練習:匯入專案 = 
 * 將 [http://trac.nchc.org.tw/cloud/raw-attachment/wiki/Hadoop_Lab5/hadoop_sample_codes.zip nchc-sample] 給匯入到eclipse 內開發吧!