2009年12月22日火曜日

MapReduceをJUnitでTDDで作る(7 Mapperのリファクタリング)

前回からだいぶ経ってしまいました。
今日は冬至ですね。
かぼちゃとゆず湯が私を待っている・・・。

リファクタリングに入ります。

要リファクタリング箇所
  • 警告が色々出ている
  • mapperが固定値のハードコーディングでcontext.write()を読んでいる。
まず、警告を消していきます。
Eclipseの「問題」ビューを見てみましょう。
Mapper.Context は raw 型です。総称型 Mapper.Context への参照は、パラメーター化する必要があります。
という警告がLogAnalysisMapper.javaTestLogAnalysisMapper.javaに出ています。また、
型の安全性: メソッド write(Object, Object) は raw 型 TaskInputOutputContext に属しています。総称型 TaskInputOutputContext への参照はパラメーター化される必要があります
という警告がLogAnalysisMapper.javaTestLogAnalysisMapper.javaに出ています。

これらの原因は実は同じで、Mapper<KEYIN, KEYOUT, VALUEIN, VALUEOUT>に具体的な型を指定する必要があると言うことを言っています。

今回のMapの入力のキーはLongWritableです(これはほぼお約束)。入力の値はファイルの各行なので、Text型になります。出力のキーは、抽出した行になるので、Text型です。出力の値は、実は特に何でもいいのですが、今回はLongWritableとしましょう。

つまり、
Mapper<LongWritable, Text, Text, LongWritable>.Contextと書けばいいことになります。
実際に書いてみましょう。
Test側は、contextの初期化の所に警告が残りました。
ソースファイルはコンパイルエラーとなってしまいました。

コンパイルエラーはよくないので、早速消します。
Mapper.write()がコンパイルエラーになっています。
どうやら、mapメソッドの引数の型とMapper<LongWritable, Text, Text, LongWritable>で指定した型が一致してないようです。Object型を指定しているところをLongWritable, Textで書き換えます。

コンパイルが通りました。一方、Test側のコードですが、こちらは消えそうにないですね。
こちらは放置しましょう。

では、コンパイルが通ったので実行します。
ロジックを一切触っていないので、JUnitはGREENになるはずです。
GREENになることを確認したら、次のリファクタリングに進みます。

今回、map関数内でwriteしている文字列はもともとVALUEとして引き渡されてくるものですね。
では、引数のvalueをそのままwriteしてしまいましょう。
context.write(value, new LongWritable(1));
では、JUnitを実行してみます。

ちゃんとGREENになりました。リファクタリング成功です!!

ということで今回はこの辺で。

今回までのソースです。

TestLogAnalysisMapper.java

/*
 * TestLogAnalysisMapper.java
 */
package jp.co.littel.hadoop;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.internal.verification.Times;

/**
 * ログ分析Mapperプログラム テストプログラム
 * @author yoshitsugu
 */
public class TestLogAnalysisMapper {

    /**
     * @throws java.lang.Exception
     */
    @Before
    public void setUp() throws Exception {
    }

    /**
     * @throws java.lang.Exception
     */
    @After
    public void tearDown() throws Exception {
    }

    /**
     * 抽出するパターン
     * @throws Exception 例外発生時
     */
    @Test
    public void testMapExtract() throws Exception {
        Mapper<LongWritable, Text, Text, LongWritable>.Context context = mock(Mapper.Context.class);
        String[] lines = {
                "2009-12-14 00:00:26,340 INFO  hogehoge @ abcdefg hijklmn opqrstu"
        };
        LogAnalysisMapper mapper = new LogAnalysisMapper();
        mapper.map(null, new Text(lines[0]), context);
        verify(context, new Times(1)).write(new Text(lines[0]), new LongWritable(1));
    }
}

LogAnalysisMapper.java
/*
 * LogAnalysisMapper.java
 */
package jp.co.littel.hadoop;

import java.io.IOException;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

/**
 * ログ解析Mapperプログラム
 * @author m.yoshitsugu
 */
public class LogAnalysisMapper extends Mapper {

    /* (non-Javadoc)
     * @see org.apache.hadoop.mapreduce.Mapper#map(java.lang.Object, java.lang.Object, org.apache.hadoop.mapreduce.Mapper.Context)
     */
    @Override
    protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
        context.write(value, new LongWritable(1));
    }
}

0 件のコメント:

コメントを投稿