<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
    <channel>
        <title>タムタムの日記</title>
        <link>http://mt.orz.at/</link>
        <description></description>
        <language>ja</language>
        <copyright>Copyright 2012</copyright>
        <lastBuildDate>Sun, 05 Feb 2012 23:42:13 +0900</lastBuildDate>
        <generator>http://www.sixapart.com/movabletype/</generator>
        <docs>http://www.rssboard.org/rss-specification</docs>
        
        <item>
            <title>HiveでJSON形式のログを構造化する</title>
            <description><![CDATA[<p>
    fluentの勉強会に参加したら、まわりがモヒカンばっかりで消毒されそうになったタムタムです。
</p>
<p>
    先日のMixi Engineers' Seminar #3でMixiがJSONデータをHiveで集計しているよーという発表をしていて、Fluentのtail + apacheもjsonとしてデータが飛んでいるとの事で、相性は悪くないよねーというわけでサンプルを作ってみました。（スライドを見ていた感じではこんな感じだったはず）
</p>
<p>
    まずサンプルデータを用意します。<br />
    適当に100件ほど作りました。 (nameが名前でexam{1|2|3}は何かのテストの点数です）
</p>
<pre>
{"name" : "Irma", "exam1": 58, "exam2": 73, "exam3":85}
{"name" : "Dorothy", "exam1": 50, "exam2": 39, "exam3":11}
{"name" : "Echo", "exam1": 95, "exam2": 47, "exam3":96}
{"name" : "Ina", "exam1": 50, "exam2": 68, "exam3":38}
{"name" : "Kay", "exam1": 15, "exam2": 11, "exam3":46}
....
</pre>
<p>
    まずはデータをそのまま1つの列に入れます。 今回はサンプルなのでPARTITIONは切っていません。
</p>
<pre>
create table raw_data(line string);
load data local inpath '/mnt/iscsi/test1/exam_data1.txt' overwrite into table raw_data;
</pre>
<p>
    このJSONデータをテーブル化するクエリは以下のように書けます。
</p>
<pre>
select
  get_json_object(raw_data.line, '$.name'),
  get_json_object(raw_data.line, '$.exam1'),
  get_json_object(raw_data.line, '$.exam2'),
  get_json_object(raw_data.line, '$.exam3')
from raw_data;
</pre>
<p>
    もしくは、json_tupleとlateral viewを使います。 (Hiveのマニュアルには、上記のようなクエリを書くなら下記のようなクエリを書きましょうねと書いてあります。)
</p>
<pre>
SELECT t1.*
FROM raw_data LATERAL VIEW json_tuple(raw_data.line, 'name', 'exam1', 'exam2', 'exam3') t1 as nm, exam1, exam2, exam3;
</pre>
<p>
    json_tupleはstring型として返ってくるので、これを整数型にします。SELECT句でCastしているだけです。
</p>
<pre>
SELECT t1.name, cast(t1.exam1 as int), cast(t1.exam2 as int), cast(t1.exam3 as int)
FROM raw_data LATERAL VIEW json_tuple(raw_data.line, 'name', 'exam1', 'exam2', 'exam3') t1 as name, exam1, exam2, exam3;
</pre>
<p>
    これをView化します。
</p>
<pre>
DROP VIEW IF EXISTS exam_table;
CREATE VIEW IF NOT EXISTS exam_table (
  name,
  exam1,
  exam2,
  exam3
)
AS
SELECT t1.name, cast(t1.exam1 as int), cast(t1.exam2 as int), cast(t1.exam3 as int)
FROM raw_data LATERAL VIEW json_tuple(raw_data.line, 'name', 'exam1', 'exam2', 'exam3') t1 as name, exam1, exam2, exam3;
</pre>
<p>
    簡単な集計をしてみます。
</p>
<pre>
SELECT
  count(*),
  avg(exam1),
  avg(exam2),
  avg(exam3)
FROM
  exam_table;
</pre>
<pre>
100     50.72   52.71   50.43
</pre>
<p>
    集計できました。めでたし、めでたし。<br />
    <font size="1">ちなみにHive0.5までは(私は0.6から使い始めたのでわからんですが・・)Where句を書くとクエリがコンパイルできないらしくて、そのときは、「<span style="FONT-SIZE: 8pt">set hive.optimize.ppd=false;</span>」と設定するみたいです。<br />
    <a href="https://issues.apache.org/jira/browse/HIVE-1056">https://issues.apache.org/jira/browse/HIVE-1056</a></font>
</p>
<p>
    ちなみにJSON用のSerdeもあります。<br />
    <a href="http://code.google.com/p/hive-json-serde/">http://code.google.com/p/hive-json-serde/</a><br />
    ぶっちゃけ、こっち使った方がスマートに書けますｗ
</p>
<p>
    というわけで、hiveはfluentとの相性も良さそうですね。<br />
    （ただし今回は統計情報を見ていないので、上記のサンプルが効率良いのかは調べていません(=ω=)）
</p>]]></description>
            <link>http://mt.orz.at/archives/2012/02/hivejson.html</link>
            <guid>http://mt.orz.at/archives/2012/02/hivejson.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2012前期</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">開発</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Hadoop</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Hive</category>
            
            <pubDate>Sun, 05 Feb 2012 23:42:13 +0900</pubDate>
        </item>
        
        <item>
            <title>Solaris10でKyoto CabinetとKyoto Tycoonをビルドする。</title>
            <description><![CDATA[<p>
    Kyoto Tycoonが0.9.53でSolaris10でも動くようになったのでメモメモ。<br />
    0.9.52まではSolaris10ではsetsockoptのSO_RCVTIMEOとSO_SNDTIMEOが必ず失敗するのでSocketの接続が全てRejectされていました。0.9.53では設定は試みるけどエラーチェックをしなくなったので、Solaris10でも繋がるようになりました。
</p>
<p>
    ビルドをするためにGCC4.4を入れます。GCC4.6じゃないの？と言われそうですが、依存ライブラリのmpc(gcc-4.5から必要)のビルドがgcc3.4.3で出来ないので1回GCC4.4系を入れます。gcc3.4.3でもconfig.hを書き換えればいけると思いますけど。<br />
    gmpとmpfrとmpcはgcc4-deps-libへ入れるという方向で行きたいと思います。
</p>
<p>
    まず事前にgrepが/usr/xpg4/bin/grepを使うようにPATHを調整します。そうしないと、/usr/bin/grepを使ってしまいconfigure時にオプションを解釈できなくてチェックが正常に動きません。
</p>
<p>
    gmp
</p>
<pre>
 cd gmp-5.0.2
 ./configure --prefix=/usr/local/gcc4-deps-lib
 gmake &gt; /dev/null
 gmake check
 gmake install
</pre>
<p>
    mpfr(documentに書いてあるとおり、パッチも当てます)
</p>
<pre>
 cd mpfr-3.1.0
 wget http://www.mpfr.org/mpfr-3.1.0/allpatches
 /usr/bin/gpatch -N -Z -p1 &lt; allpatches

 ./configure --prefix=/usr/local/gcc4-deps-lib --with-gmp=/usr/local/gcc4-deps-lib
 gmake &gt; /dev/null
 gmake check
 gmake install
</pre>
<p>
    libgccのビルドにこけるので、以下を設定します。
</p>
<pre>
 export LD_LIBRARY_PATH=/usr/local/gcc4-deps-lib/lib:$LD_LIBRARY_PATH
</pre>
<p>
    gcc4.4.6
</p>
<pre>
 ./configure \
   --prefix=/usr/local/gcc446 \
   --with-ar=/usr/ccs/bin/ar \
   --with-as=/usr/ccs/bin/as \
   --with-ld=/usr/ccs/bin/ld \
   --enable-shared \
   --enable-languages=c,c++ \
   --with-gmp=/usr/local/gcc4-deps-lib \
   --with-mpfr=/usr/local/gcc4-deps-lib

 gmake -j 2 bootstrap
 gmake install
</pre>
<p>
    確認
</p>
<pre>
 export LD_LIBRARY_PATH=/usr/local/gcc446/lib:$LD_LIBRARY_PATH
 /usr/local/gcc446/bin/gcc -v
</pre>
<pre>
Using built-in specs.
Target: i386-pc-solaris2.10
コンフィグオプション: ./configure --prefix=/usr/local/gcc446 --with-ar=/usr/ccs/bin/ar --with-as=/usr/ccs/bin/as --with-ld=/usr/ccs/bin/ld --enable-shared --enable-languages=c,c++ --with-gmp=/usr/local/gcc4-deps-lib --with-mpfr=/usr/local/gcc4-deps-lib
スレッドモデル: posix
gcc version 4.4.6 (GCC)
</pre>
<p>
    環境変数はこんな感じで設定しております。
</p>
<pre>
LD_LIBRARY_PATH=/usr/local/gcc446/lib/amd64:/usr/local/gccc446/lib:/usr/local/gcc4-deps-lib/lib:/usr/local/lib:/usr/ccs/lib:/usr/sfw/lib:/opt/sfw/lib:/usr/lib:/lib:/etc/lib
PATH=/usr/local/gcc446/bin:/usr/local/bin:/usr/xpg4/bin:/usr/xpg6/bin:/usr/ccs/bin:/usr/sfw/bin:/opt/sfw/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin
</pre>
<p>
    次にKCを入れます。
</p>
<pre>
 ./configure CXX=/usr/local/gcc446/bin/gcc --prefix=/usr/local/kc-1.2.72
 gmake
 gmake install
</pre>
<p>
    次はKTを入れます。 とりあえずの注意点としては、-m64を付けないとconfigure時にlibkyotocabinetが認識できません。これは、libkyotocabinetが64ビットのSOであるのにconftest.cppを作ってリンクの確認をする時に-m64がつかなくて32ビットのELFに64ビットのSOをリンクしようとするからです。また、CXXに明示的にg++を指定しているのは、-m64つけた時にこれを指定しないとpthreadなどの他のライブラリを認識できなかったからです。
</p>
<pre>
./configure \
  CXXFLAGS="-m64" \
  CXX="/usr/local/gcc446/bin/g++" \
  LDFLAGS="-R/usr/local/kc-1.2.72/lib -R/usr/local/kt-0.9.53/lib" \
  --prefix=/usr/local/kt-0.9.53 \
  --with-kc=/usr/local/kc-1.2.72
gmake
gmake install
</pre>
<p>
    確認をします。ktserverを立ち上げて以下のコマンドを打ちます。
</p>
<pre>
bash-3.2# echo "hello" | /usr/local/kt-0.9.53/bin/ktutilmgr http -put http://localhost:1978/test1
bash-3.2# /usr/local/kt-0.9.53/bin/ktutilmgr http -get http://localhost:1978/test1
hello
</pre>
<p>
    動いた動いた。やったね。
</p>
<p>
    &nbsp;
</p>]]></description>
            <link>http://mt.orz.at/archives/2012/02/solaris10kyoto.html</link>
            <guid>http://mt.orz.at/archives/2012/02/solaris10kyoto.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2012前期</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Kyoto Cabinet</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Kyoto Tycoon</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Solaris10</category>
            
            <pubDate>Wed, 01 Feb 2012 01:00:48 +0900</pubDate>
        </item>
        
        <item>
            <title>JavaのsubListの罠</title>
            <description><![CDATA[<p>
    本当は食べ物の写真とか載せたいんですけどね。。<br />
    ついこの前やらかしちゃった事を備忘録としてメモメモ。
</p>
<p>
    Javaでは基本的にSerializableを実装していないと(Externalizableでもいいけど)、Objectをシリアライズできません。例えばMemcachedにそのままObjectをまるっと保存する場合は、ObjectOutputStreamを介して保存する事が多いと思います。（MsgPackとかJSONICとかを使う方が良いかもしれませんが）。
</p>
<p>
    で、この前やらかしてしまったのは、JavaのListインターフェースにあるsubListで返ってくるリストはSerializableインターフェースを実装していないListなので、キャッシュに保存できないでエラーが出まくりでした。と書くと語弊があるので正確に書くと、ArrayListとLinkedListはAbstractListを継承しており、subListの実装はAbstractListが持っています。AbstractList#subListの実装は、RandomAccessを実装しているクラスの場合はRandomAccessSubListを、実装していない場合はSubListのObjectを返します。RandomAccessSubListもSubListもSerializableは実装されていません。
</p>
<pre class="prettyprint">
public List&lt;E&gt; subList(int fromIndex, int toIndex) {
    return (this instanceof RandomAccess ?
            new RandomAccessSubList&lt;E&gt;(this, fromIndex, toIndex) :
            new SubList&lt;E&gt;(this, fromIndex, toIndex));
}
</pre>
<p>
    ちなみに、subListは要素をコピーして作っているわけでは無く、単純に元リストのラッパークラスのように振る舞っているだけなので、そこも注意なのです。subListで返ってきたリストに対して操作するとどうなるかのサンプルを書きました。
</p>
<pre class="prettyprint">
import java.util.ArrayList;
import java.util.List;

public class Test1 {
	public static void main(String[] args) throws Exception {

		// サンプルデータを作る
		ArrayList&lt;Integer&gt; srcList = new ArrayList&lt;Integer&gt;();
		for (int i = 0; i &lt; 10; i++) {
			srcList.add(i);
		}
		System.out.println("src=" + srcList);
		// サブリストを作ってみる
		List<Integer> subList = srcList.subList(5, 8);
		System.out.println("sub=" + subList);
		// サブリストを操作する
		subList.add(100);

		System.out.println("=====after=====");
		System.out.println("sub=" + subList);
		System.out.println("src=" + srcList);

		System.out.println(subList instanceof Serializable);
	}
}
</pre>
<p>実行結果</p>
<pre>
src=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
sub=[5, 6, 7]
=====after=====
sub=[5, 6, 7, 100]
src=[0, 1, 2, 3, 4, 5, 6, 7, 100, 8, 9]
false
</pre>
<p>
    &nbsp;
</p>]]></description>
            <link>http://mt.orz.at/archives/2012/01/javasublist.html</link>
            <guid>http://mt.orz.at/archives/2012/01/javasublist.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Java</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2012前期</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">開発</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Java</category>
            
            <pubDate>Mon, 30 Jan 2012 00:39:38 +0900</pubDate>
        </item>
        
        <item>
            <title>Hiveで整形されていないログを集計する方法</title>
            <description><![CDATA[<p>
    タムタムです。<a title="" href="http://www.zusaar.com/event/174001">Hadoop アドベントカレンダー</a>の12/24分 を書かせていただきます。<br />
    それと、時々ログ解析飲み会というものをやっているという噂があるのですが、わたしも混ぜてください＞＜&nbsp;
</p>
<p>
    さて、まずはじめに・・。 <strong>ログが整形されているなんて都市伝説です。</strong>
</p>
<p>
    自分が作るアプリは最初からログ設計をして整形して出力しているのですが、世の中そんなものばかりではありません。Hiveで集計するためにはある程度書式が整っていないとスマートに処理できません。<br />
    適当なスクリプトで処理するのも手ですが、もともと分散しないと処理できないほどの量なのに、それを分散環境で処理しないとか無いと思います・・。
</p>
<p>
    となると、スクリプトを書いてHadoop Streamingでログを処理すればいいよねーとなるわけです。が、用途はある程度限られてしまいますが実はHiveでも出来ます。
</p>
<p>
    例えば、以下のようなログがあるとします。<br />
    どこかで似たようなものを見たことある人も居るかもしれませんが気のせいです(=ω=)
</p>
<pre>
2011-10-14 00:00:14  INFO  - [XXX][MONS] (charaId:123456, tmpId:56789) start attack monster(MonsterAAA) at (85.9,199.2,-590.6)
2011-10-14 00:00:14  INFO  - [XXX][MONS] (charaId:123456, tmpId:56789) start attack monster(MonsterAAA) at (85.9,199.2,-590.6)
2011-10-14 00:00:14  INFO  - [XXX][MONS] (charaId:123456, tmpId:56789) start attack monster(MonsterAAA) at (85.9,199.2,-590.6)
2011-10-14 00:00:18  INFO  - [XXX][ITEM] (charaId:123456, tmpId:56789) item can add *******
2011-10-14 00:00:18  INFO  - [XXX][ITEM] (charaId:123456, tmpId:56789) item add ******
2011-10-14 00:00:18  INFO  - [XXX][XXXX] *************
2011-10-14 00:00:18  INFO  - [XXX][MONS] (charaId:123456, tmpId:56789) kill monster(MonsterAAA) at (85.9,199.2,-590.6)
</pre>
<p>
    さて、このファイルにはモンスターの討伐記録のログがあります。&nbsp;一番下のログです。<br />
    このログファイルからモンスター討伐数を集計したい場合、一番下の行には、誰が(charaId)いつ(時間)どこで(ワールド、エリア、XYZ座標)なにをした(MonsterAAAを倒した) が記録されており、まずはこの形式のログだけを抽出してテーブルに入れたいわけです。 （ワールドとエリアはログファイルの単位なので記録はされていないです。）
</p>
<p>
    まずは元のgzログをtmp2テーブルに入れます。ログファイルはHDFS上にあります。<br />
    パーティションを適当に切ります。idは適当なIDを入れて作業がかぶらないようにする用です。OracleのPLAN_TABLEみたいなイメージです。別に無くてもいいです(=ω=)
</p>
<pre class="prettyprint">
create table tmp2(line string)
  partitioned by (id string, world string, area string);

load data inpath '/test/log/neko/2011-10-14-dungeon01.txt.gz'
overwrite into table tmp2
partition(id='aaa111', world='neko', area='dungeon01');
</pre>
<p>
    次にMap用のスクリプトを書きます。 (map2.pl)
</p>
<pre class="prettyprint">
#!/usr/bin/perl

use warnings;
use strict;

while (&lt;&gt;) {
  my ($world, $area, $line) = split("\t", $_);
  if ($line =~ /^((\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})).*\(charaId:(\d+).*tmpId:(\d+)\).*kill monster\s*\((\w+)\)\s*at\s*\((.*),(.*),(.*)\)/) {
    my @array = (
      $world,
      $area,
      $1,        # datetime
      $4,        # charaid
      $5,        # tmpId
      $6,        # monster-name
      $7, $8, $9 # x,y,z
    );
    print join("\t", @array), "\n";
  }
}
</pre>
<p>
    わざわざ配列に入れてjoinする必要なんてありませんよね。ごめんなさい。コメントを見やすくする関係でこう書きました。<br />
    ちょっと確認したい場合は、以下のようにやればスクリプトの確認はできます。
</p>
<pre class="prettyprint">
zcat hogefuga.log.gz | awk '{print "world01\tdungeon01\t"$0}' | ./map2.pl
</pre>
<pre class="prettyprint">
world01  dungeon01  2011-10-14 00:00:18    123456  56789   MonsterAAA  85.9    199.2   -590.6
</pre>
<p>
    試しにSELECTしてみます。
</p>
<pre class="prettyprint">
SELECT TRANSFORM(line)
USING '/home/hadoop/map2.pl' AS (
  dt timestamp,
  charaId bigint,
  tmpId bigint,
  monster_name string,
  x double,
  y double,
  z double
)
FROM tmp2 WHERE id = 'aaa111'
LIMIT 10;
</pre>
<pre>
2011-10-14 00:00:18     12345678        00000000        MonsterAAA     85.9    199.2   -590.6
2011-10-14 00:00:18     12345678        00000000        MonsterBBB    -41.7    184.7   -261.3
2011-10-14 00:00:28     12345678        00000000        MonsterAAA     85.9    199.2   -590.6
2011-10-14 00:00:42     78901234        11111111        MonsterCCC     85.9    199.2   -590.6
2011-10-14 00:00:53     12345678        00000000        MonsterCCC     85.9    199.2   -590.6
2011-10-14 00:01:07     12345678        00000000        MonsterBBB     63.5    200.5   -550.9
2011-10-14 00:01:11     78901234        11111111        MonsterCCC     85.9    199.2   -590.6
2011-10-14 00:01:20     12345678        00000000        MonsterCCC     85.9    199.2   -590.6
2011-10-14 00:01:30     12345678        00000000        MonsterBBB    -61.7    184.0   -258.4
2011-10-14 00:01:37     12345678        00000000        MonsterBBB     13.5    200.1   -570.1
</pre>
<p>
    （結果をダミーのものに書き換えるのめんどくさい・・・。でも本物出すわけにはいかないし・・。）
</p>
<p>
    集計してみます。
</p>
<pre class="prettyprint">
SELECT world, area, monster_name, count(*) as cnt FROM (
  SELECT TRANSFORM(world, area, line)
  USING '/home/hadoop/map2.pl' AS (
    world string,
    area string,
    dt timestamp,
    charaId bigint,
    tmpId bigint,
    monster_name string,
    x double,
    y double,
    z double
  )
  FROM tmp2 WHERE id = 'aaa111'
) tbl GROUP BY world, area, monster_name ORDER BY cnt desc;
</pre>
<pre>
neko    dungeon01       Monster001      3215
neko    dungeon01       Monster002      1733
neko    dungeon01       Monster003      945
neko    dungeon01       Monster004      736
neko    dungeon01       Monster005      476
neko    dungeon01       Monster006      471
neko    dungeon01       Monster007      455
neko    dungeon01       Monster008      407
neko    dungeon01       Monster009      181
neko    dungeon01       Monster010      168
neko    dungeon01       Monster011      154
neko    dungeon01       Monster012      22
neko    dungeon01       Monster013      16
neko    dungeon01       Monster014      8
neko    dungeon01       Monster015      8
</pre>
<p>
    集計できました(・ω・)ﾉ<br />
    Tmpテーブルを経由しないといけないのがアレですが、externalなのでログの複製は行われないし、中間テーブル的な整形されたテーブルの実体も持たなくて良いのです。集計前にテーブルに1回入れる場合は普通に末尾にINSERT構文を書けばできます。まぁこういう方法もありますよと言うことで。
</p>
<p>
    今回はMapしか書いてませんが、Reduceもスクリプトで書くことができます。<br />
    <a href="https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Transform">https://cwiki.apache.org/confluence/display/Hive/LanguageManual+Transform</a>
</p>
<p>
    &nbsp;
</p>]]></description>
            <link>http://mt.orz.at/archives/2011/12/hive.html</link>
            <guid>http://mt.orz.at/archives/2011/12/hive.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2011後期</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">開発</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Hadoop</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Hive</category>
            
            <pubDate>Sat, 24 Dec 2011 21:19:39 +0900</pubDate>
        </item>
        
        <item>
            <title>Hadoopアドベントカレンダー2011 17日目 - Hive 0.8 について -</title>
            <description><![CDATA[<p>
    こんにちは、タムタムです。<br />
    久しぶりに記事を書きます。 Hadoopアドベントカレンダー2011の17日目という事で書かせていただきます。
</p>
<p>
    私のような底辺エンジニアでも集合論の知識のみで簡単にデータ集計処理が書けるのがHiveの良いところだと思います。<br />
    試しに某ゲームのモンスター討伐のログを、数日分、全ワールド、Zoneエリア、モンスター単位で集計したら100秒くらいで結果が出ました。（その時はレコードが1億程度と少ないという事もありますが）<br />
    ※ちなみに私はマイニングエンジニアではありません。
</p>
<p>
    というわけでHive0.8に関する事を書きたいと思います。<br />
    Hive0.8ではBitmapインデックスやtimestamp型が追加されたというのが大きなトピックらしいです。さっそく新機能を試してみたいと思います。また細かい構文が追加されているようなので、それもおいおい試してみたいと思います。今回はtimestamp型について書きます。<br />
    ちなみに、まだリリースされていないのでソースを持ってきてビルドする必要があります。ビルドの方法は次の通りです。
</p>
<pre class="prettyprint">
svn co http://svn.apache.org/repos/asf/hive/branches/branch-0.8/ hive-0.8
cd hive-0.8
ant clean package
</pre>
<p>
    build/dist配下にパッケージが出来ていますので、build/distをHIVE_HOMEに設定してbin/hiveを実行します。
</p>
<p>
    <font color="#0000FF">※（追記）Hive 0.8は12/16にリリースされてましたｗ</font>
</p>
<p>
    今まで日付データをhiveで扱うにはstringとして保持してWHERE句にUDFで日付に戻して計算をするとか、bigintでunixtimeとして保持したりしていました。timestamp型についてまずはどんな操作ができるのか調べてみます。
</p>
<p>
    hive&gt; show functions;
</p>
<p>
    の結果を0.7と0.8で比較し、0.8で追加されたUDFを列挙しました。 (descriptionはコマンド打っても出てこなかったものがあるので書くのをやめましたｗ)
</p>
<table border="1" cellspacing="3" cellpadding="3">
    <tbody>
        <tr valign="top">
            <th>
                UDF
            </th>
            <th>
                Description
            </th>
        </tr>
        <tr valign="top">
            <td>
                ewah_bitmap
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                ewah_bitmap_and
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                ewah_bitmap_empty
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                ewah_bitmap_or
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                from_utc_timestamp
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                map_keys
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                map_values
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                named_struct
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                timestamp
            </td>
            <td></td>
        </tr>
        <tr valign="top">
            <td>
                to_utc_timestamp
            </td>
            <td></td>
        </tr>
    </tbody>
</table><br />
<p>
    また<a title="" href="https://issues.apache.org/jira/browse/HIVE-2272">JIRAのチケット</a>によると、datetimeに関する様々なUDFがtimestampをサポートしたようです。<br />
</p>
<p>
    例えば以下のような操作ができます。<br />
    サンプルデータを1件用意します。書式はyyyy-MM-dd HH:mm:ss か yyyy-MM-dd HH:mm:ss.fffffffffのみです。
</p>
<pre class="prettyprint">
2011-12-25 09:00:00
</pre>
<pre class="prettyprint">
create table ts1(t1 timestamp);
load data local inpath '/mnt/iscsi/test1/data4.txt' overwrite into table ts1;

select cast(t1 as float), cast(t1 as double) from ts1 limit 1;
1.3247712E9     1.3247712E9

select cast(t1 as bigint), cast(t1 as int), cast(t1 as boolean), cast(t1 as tinyint), cast(t1 as smallint) from ts1 limit 1;
1324771200      1324771200      true    -128    26496

select cast(t1 as string) from ts1 limit 1;
2011-12-25 09:00:00

select cast('2011-12-25 9:00:00.000000000' as timestamp) from ts1 limit1;
2011-12-25 09:00:00

select
  unix_timestamp(t1),
  year(t1),
  month(t1),
  day(t1),
  dayofmonth(t1),
  weekofyear(t1),
  hour(t1),
  minute(t1),
  second(t1),
  to_date(t1)
from ts1;
1324771200      2011    12      25      25      51      9       0       0       2011-12-25

select date_add(t1, 5), date_sub(t1, 5) from ts1 limit 1;
2011-12-30      2011-12-20

select datediff(t1, t1), datediff(t1, '2011-12-10'), datediff('2011-12-20 9:00:00', t1) from ts1 limit 1;
0       15      -5
</pre>
<p>
    新しいUDFも試してみましょう。
</p>
<pre class="prettyprint">
select from_utc_timestamp(t1, 'JST') from ts1 limit 1;
2011-12-25 18:00:00

select from_utc_timestamp(t1, 'UTC') from ts1 limit 1;
2011-12-25 09:00:00

select from_utc_timestamp(t1, 'PST') from ts1 limit 1;
2011-12-25 01:00:00

select to_utc_timestamp(t1, 'JST') from ts1 limit 1;
2011-12-25 00:00:00

select to_utc_timestamp(t1, 'UTC') from ts1 limit 1;
2011-12-25 09:00:00

select to_utc_timestamp(t1, 'PST') from ts1 limit 1;
2011-12-25 17:00:00
</pre>
<p>
    うまく動いているように見えます。<font color="#FF0000">が、以下のクエリを発行すると正しい結果が得られません。<br /></font>SELECT句に複数記述するのはNGなのかな？
</p>
<pre class="prettyprint">
select from_utc_timestamp(t1, 'JST'), from_utc_timestamp(t1, 'UTC'), from_utc_timestamp(t1, 'PST') from ts1 limit 1;
2011-12-25 10:00:00     2011-12-25 10:00:00     2011-12-25 10:00:00

select to_utc_timestamp(t1, 'JST'), to_utc_timestamp(t1, 'UTC'), to_utc_timestamp(t1, 'PST') from ts1 limit 1;
2011-12-25 08:00:00     2011-12-25 08:00:00     2011-12-25 08:00:00
</pre>
<p>
    ちなみに、2011-12-25 09:00:00はどのTimeZoneの9時なのか？という事が気になると思います。チケットを見ると、「hive.time.default.timezone」こういう設定が一瞬だけあったようですが、「Removed references to a "default" timezone. All times are treated as UTC」という事で無かったことになっています。実際は上の結果を見る限り(castとか)TimeZone.getDefault()が使われているように見えます。（すいませんソース追っていません）。なのでOracleVMの場合はVMプロパティのuser.timezoneで指定できますが、システムプロパティなので他に影響を及ぼしそうで怖いです。
</p>
<p>
    Javaのコードで確認したら似たような動作したので、たぶんDefaultのTimeZoneを使っていると思います。grepしたらUTCとかいっぱい出てきましたけどw<br />
    （ていうか、Hiveのソース読めよって話ですよね）
</p>
<pre class="prettyprint">
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("JST"));
System.out.println(sdf.parse("2011-12-25 09:00:00").getTime());

sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.parse("2011-12-25 09:00:00").getTime());
</pre>
<pre>
1324771200000
1324803600000
</pre>
<p>
    個人的には、時間に関する型をサポートするならSimpleDateFormatの書式をtableの設定で出来ると嬉しいんですけどね。
</p>
<p>
    というわけで、timestamp型に関する事を書いてみました。<br />
    もしもネタが足りなければ列指向のRCFileとSequenceFileの性能の違いやRCFileをJavaののコードで作る方法あたりを書きたいと思います。
</p>
<p>
    間違いを見つけたらどんどん指摘してくれると嬉しいです(・ω・)ﾉ
</p>]]></description>
            <link>http://mt.orz.at/archives/2011/12/hadoop2011-17-h.html</link>
            <guid>http://mt.orz.at/archives/2011/12/hadoop2011-17-h.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2011後期</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">開発</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Hadoop</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">Hive</category>
            
            <pubDate>Sat, 17 Dec 2011 00:00:00 +0900</pubDate>
        </item>
        
        <item>
            <title>ATOKのカナロック解除方法</title>
            <description><![CDATA[<p>
    ATOKを使っていると突如カナロックがかかってしまう現象があります。わたしの環境ではメモリ不足になると（仮想メモリを使うようになると）発生するみたいです。
</p>
<p>
    で、これの解除方法ですが、CTRL+SHIFT+CAPS LOCKで直りました。わたしのノートだけかもしれませんが(´・ω・)
</p>
<p>
    &nbsp;
</p>]]></description>
            <link>http://mt.orz.at/archives/2010/07/atok.html</link>
            <guid>http://mt.orz.at/archives/2010/07/atok.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2010後期</category>
            
            
            <pubDate>Thu, 22 Jul 2010 02:39:53 +0900</pubDate>
        </item>
        
        <item>
            <title>zfsのキャッシュサイズ(c_max)調整</title>
            <description><![CDATA[<p>
    ZFSはデフォルトで搭載メモリの3/4までキャッシュするような実装になっています。メモリ4GB積んでいると3GBまでキャッシュするようになっているわけですね。ファイルサーバとしてしか使わないのであれば、それでも良いのですが、他にもメモリを使いたいのでちょっと調整してみます。
</p>
<p>
    まずは、現在の値を確認します。
</p>
<pre>
root@tamsvr00:~# kstat | grep c_max
        c_max                           3206670336
</pre>
<p>
    そして変更します。/etc/systemに以下の記述を追加します。
</p>
<pre>
set zfs:zfs_arc_max = 0x40000000
</pre>
<p>
    これで1GBになります。再起動必須です。
</p>
<p>
    再起動後、もっかい確認すると設定が変更されています。
</p>
<pre>
root@tamsvr00:~# kstat | grep c_max
        c_max                           1073741824
</pre>
<p>
    「zfs_arc_max」で検索をすると、それっぽい記事が（というかGoogleの一番上にZFSのTuningに関するWikiが）ヒットします。
</p>
<p>
    どこかの勉強会で、メモリ云々でソースを書き換えたとか言ってた人が居たので、この設定で変更できますよっと。（というかソース書き換えたって事はソース見ているって事だから設定値読んでいるって事も分かっているはず。という事は、これの事ではないのかな・・？）
</p>
<p>
    &nbsp;
</p>
<p>
    ちなみにこのネタは会社の人に教えて貰ったものだったのでした。
</p>]]></description>
            <link>http://mt.orz.at/archives/2010/07/zfsc-max.html</link>
            <guid>http://mt.orz.at/archives/2010/07/zfsc-max.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2010後期</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Solaris</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">zfs</category>
            
            <pubDate>Wed, 21 Jul 2010 01:17:50 +0900</pubDate>
        </item>
        
        <item>
            <title>ZFS on Solaris</title>
            <description><![CDATA[<p>
    久しぶりの更新です。<br />
    非常にバタバタしている・・というか、月300時間労働超えました(´・ω・)ｳｴｪ・・。
</p>
<p>
    クローラーを動かしたり、色々と実験をしているのでファイル置き場が無くなってその度にサーバを止めてHDDを追加していくのは大変です。というわけで、今回はそこそこ容量の大きなファイルサーバを追加しました。
</p>
<p>
    構成は以下になります。
</p>
<ul>
    <li>OpenSolaris (Solaris10はライセンスが・・・)
    </li>
    <li>AtomD510
    </li>
    <li>メモリ4GB
    </li>
    <li>HDD IDE 80GB (システム用)
    </li>
    <li>HDD SATA 2TB * 4基 (データ用)
    </li>
    <li>GigabitLAN * 1
    </li>
</ul>
<p>
    最初はUSBにインストールして使おうとしましたが、USBへの書き込みが発生するとシステム全体が止まって使い物にならなかったのでHDDにインストールする事にしました。
</p>
<p>
    DVDドライブはないので、LiveUSBでブート。それからHDDにインストール。（Liveで起動するとインストールするというアイコンがあるので、それで）。最初はLiveUSBブート → USBにインストールでしたが、↑の理由により断念。
</p>
<p>
    ZFSは非常に簡単。今回はrootで以下のコマンドを叩くだけ。
</p>
<pre>
    zpool create data raidz c4t0d0 c4t1d0 c7d0 c8d0
</pre>
<p>
    これだけです。 これだけで/dataにマウントまでしてくれます。マウントを変えたい場合は「zfs set mountpoint」で変更するだけ。NFSで共有したい場合は、
</p>
<pre>
    zfs set sharenfs=on data
</pre>
<p>
    これだけです。
</p>
<p>
    raidzはRAID5と同等です。（いやー本当はraidz2にしたいのですがHDDが足りなくて・・・）。raid5とか会社で運用しているところがあればﾌﾟｷﾞｬｰ・・ですね。
</p>
<pre>
root@tamsvr00:~# zpool list
NAME    SIZE   USED  AVAIL    CAP  HEALTH  ALTROOT
data   7.25T  1.31M  7.25T     0%  ONLINE  -
rpool    76G  6.62G  69.4G     8%  ONLINE  -
</pre>
<pre>
root@tamsvr00:~# zpool status
  pool: data
 state: ONLINE
 scrub: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        data        ONLINE       0     0     0
          raidz1-0  ONLINE       0     0     0
            c0t0d0  ONLINE       0     0     0
            c0t1d0  ONLINE       0     0     0
            c7d0    ONLINE       0     0     0
            c8d0    ONLINE       0     0     0

errors: No known data errors

  pool: rpool
 state: ONLINE
 scrub: none requested
config:

        NAME        STATE     READ WRITE CKSUM
        rpool       ONLINE       0     0     0
          c1d0s0    ONLINE       0     0     0
</pre>
<pre>
root@tamsvr00:~# df -h
Filesystem            Size  Used Avail Use% Mounted on
rpool/ROOT/opensolaris
                       71G  4.3G   67G   7% /
swap                  4.8G  324K  4.8G   1% /etc/svc/volatile
/usr/lib/libc/libc_hwcap1.so.1
                       71G  4.3G   67G   7% /lib/libc.so.1
swap                  4.8G  8.0K  4.8G   1% /tmp
swap                  4.8G   48K  4.8G   1% /var/run
data                  5.4T  927K  5.4T   1% /data
rpool/export           67G   23K   67G   1% /export
rpool/export/home      67G   23K   67G   1% /export/home
rpool/export/home/tamtam
                       67G   45M   67G   1% /export/home/tamtam
rpool                  67G   81K   67G   1% /rpool
</pre>
<p>
    こんな感じでパリティ有で5.4TBの領域が出来ました。<br />
    これを各サーバで共有するのと、WindowsからはSambaでファイル置き場として共有します。CPUがあまり気味なのでMercurialやMailサーバくらい置こうかな。
</p>]]></description>
            <link>http://mt.orz.at/archives/2010/07/zfs-on-solaris.html</link>
            <guid>http://mt.orz.at/archives/2010/07/zfs-on-solaris.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2010後期</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Solaris</category>
            
                <category domain="http://www.sixapart.com/ns/types#tag">zfs</category>
            
            <pubDate>Mon, 19 Jul 2010 23:08:55 +0900</pubDate>
        </item>
        
        <item>
            <title>レモン牛乳</title>
            <description><![CDATA[<p>
    栃木っこにはおなじみのレモン牛乳。<br />
    実は都内でも普通に売っています。
</p>
<p>
    私はこれを飲むと運動会を思い出すのです。運動会では、このレモン牛乳(200ミリリットルのやつ）とコーヒー牛乳と普通の牛乳が売店で売り出すのです。
</p>
<p>
    <img title="レモン牛乳" border="0" alt="レモン牛乳" src="http://mt.orz.at/imagetm_Image116.jpg" width="600" height="450" />
</p>
<p>
    都内では500ミリリットルのやつが189円でした。こんなに高かったっけ・・。<br />
    というか、このヨーグルトってなんですか。こんな派生製品知らないよ！
</p>
<p>
    （追記）<br />
    私がいつも購入する場所は、スーパーオオゼキの戸越銀座店です。日曜日にはいつも置いてあります。<br />
    （これ書くと私の生息範囲がわかっちゃうのと、私の購入分が減るのが嫌なんですが・・・ｗ）
</p>
<p>
    （さらに追記）<br />
    ヨーグルト食べました。<br />
    なんでもかんでも商品化すれば良いってもんじゃねーぞっ！　という感想です。<br />
    「わぁー黄色い。まるでヨーグルトの糖尿病やぁ～！」
</p>]]></description>
            <link>http://mt.orz.at/archives/2010/01/post-595.html</link>
            <guid>http://mt.orz.at/archives/2010/01/post-595.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2010前期</category>
            
            
            <pubDate>Sun, 17 Jan 2010 20:52:33 +0900</pubDate>
        </item>
        
        <item>
            <title>デザイン変更案</title>
            <description><![CDATA[<p>
    遅くなりましたが、あけましておめでとうございます。<br />
    そろそろブログのデザインを変更したいなと思いまして、ちょっとワンポイントとなるイラストを探してました。
</p>
<p>
    ポップな感じにするならこんな素材かなぁ。
</p>
<p>
    <img title="" border="0" alt="" src="http://mt.orz.at/imagesozai1.jpg" width="600" height="600" />
</p>
<p>
    繊細な感じにするならこんな素材？
</p>
<p>
    <img title="" border="0" alt="" src="http://mt.orz.at/imagesozai2.jpg" width="600" height="600" />
</p>
<p>
    うーん。迷う。<br />
    コンセプトアート的な素材集ってないかなぁ・・。もうプロの人に依頼しようかな・・・ｗ
</p>
<p>
    ちなみに、いずれの画像も用途制限無しの素材なのです。画力の無い自分には嬉しい限りです。<br />
    素材集のお値段は1980円です。
</p>
<p>
    <a title="DVD-ROM付き 萌えキャラフリーイラスト集" href="http://www.amazon.co.jp/exec/obidos/ASIN/4816348204/tamrin69-22/" target="_blank"><img style="BORDER-BOTTOM: medium none; BORDER-LEFT: medium none; BORDER-TOP: medium none; BORDER-RIGHT: medium none" alt="DVD-ROM付き 萌えキャラフリーイラスト集" src="http://ecx.images-amazon.com/images/I/51vy1wXzIoL._SL160_.jpg" /></a>
</p>]]></description>
            <link>http://mt.orz.at/archives/2010/01/post-594.html</link>
            <guid>http://mt.orz.at/archives/2010/01/post-594.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2010前期</category>
            
            
            <pubDate>Mon, 11 Jan 2010 23:50:02 +0900</pubDate>
        </item>
        
        <item>
            <title>Postgresをインストールしてみた</title>
            <description><![CDATA[<p>
    MySQLはJDBCからアクセスする時に全件取得するのでバッチ処理が書きづらいという理由でPostgreSQLをインストールしてみました。（フェッチサイズをInteger.MIN_VALUEに設定すれば1件づつの取得になりますが、この間に他のトランザクション走らせることができないので。）（あれ・・・コネクションを2本使えばいいのでは・・？）
</p>
<p>
    インストールしたバージョンは8.4.2です。
</p>
<p>
    ビルドします。
</p>
<pre>
./configure --prefix=/lunar
make
make check
make install
</pre>
<p>
    DB初期化します。
</p>
<pre>
initdb --encoding=utf-8 -U totoro --pgdata=/lunar/data/postgres
</pre>
<p>
    起動します。
</p>
<pre>
pg_ctl -w -o "-i" -D /lunar/data/postgres -l /lunar/var/postgres.log start
</pre>
<p>
    スキーマを作ります。
</p>
<pre>
createdb -E utf-8 -O totoro -W
</pre>
<p>
    &nbsp;外部から接続できるように/lunar/data/postgres/pg_hba.confに設定を追加します。 allとか本当は良くないけど(・ε・)ｷﾆｼﾅｲ!!
</p>
<pre>
host    all         all         192.168.0.0/24        md5
</pre>
<p>
    パスワードを設定しなおしました。
</p>
<pre>
ALTER USER totoro with password 'xxxxxxxx';
</pre>
<p>
    他のホストから接続してみる。
</p>
<pre>
psql -h 192.168.1.202 -U totoro postgres
</pre>
<p>
    手間はMySQLよりも1つ多いくらい。<br />
    やばい。。Postgresの設定パラメータの意味がわからない(´・ω・)<br />
    メモリ設定とかDISKのフラッシュ設定とかログ設定とか運用設定とか。
</p>
<p>
    調べるか(´・ω・)
</p>
<p>
    &nbsp;
</p>]]></description>
            <link>http://mt.orz.at/archives/2009/12/postgres.html</link>
            <guid>http://mt.orz.at/archives/2009/12/postgres.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2009後期</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">開発</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Postgres</category>
            
            <pubDate>Mon, 28 Dec 2009 14:32:01 +0900</pubDate>
        </item>
        
        <item>
            <title>ClassLoaderを使って実行時にクラスを更新する方法（リロード処理）</title>
            <description><![CDATA[<p>
    JavaのClassLoaderの話をちょっとだけ。意外にClassLoaderを知らない人が居るようです。
</p>
<p>
    ざっくりと説明をしますと、<br />
    クラスローダーにはBootstrapクラスローダーやシステムクラスローダー、コンテキストクラスローダーがあります。（あと拡張クラスローダーですかね）。そんでもって、クラスローダーは親子関係を持っています。委譲モデルと言って、あるクラスローダーにクラスをロードする依頼があると、親に依頼を委譲します。最初に発見されたクラスが実際にロードされるわけです。
</p>
<p>
    1. Bootstrap　(rt.jar)<br />
    2. 親クラスローダーA<br />
    3. 子クラスローダーB
</p>
<p>
    こんなクラスローダーがあるとしてまだロードされていないクラスXをロードしようとした場合は次のように動作します。
</p>
<ol>
    <li>B.loadClass(X)
    </li>
    <li>Aに委譲
    </li>
    <li>Bootstrapに委譲
    </li>
    <li>BoostrapにXは存在しない
    </li>
    <li>AにXは存在しない
    </li>
    <li>BにXが存在するのでロードする。
    </li>
</ol>
<p>
    ちなみにClassLoader#findClassをオーバーライドして自前の処理で上書きしてしまえば、この委譲モデルを破壊する事ができます。まぁそんな事は非推奨ですけど(´・ω・)
</p>
<p>
    JVMプロセス内においては異なるClassLoaderで読み込んだクラスは例え同じバイナリでも異なるクラスとして扱われます。ClassLoaderAによってロードされたクラスXと、ClassLoaderBによってロードされたクラスXは別物です。<br />
    ※クラスAとBが親子関係を持っていて、AがXをロードしている時にBでロードしても委譲モデルでAが既にロードしてあるXを使うので、こういう場合は同じ定義を指します。<br />
    TomcatのWebApps1とWebApps2の関係を想像すると理解しやすいと思います。
</p>
<p>
    この特性を生かすとリロード処理が実現できるわけです。
</p>
<p>
    ClassLoaderの細かい話はDeveloperWorksの記事が丁寧なのでそちらを参照した方が良いです。今回はTomcatにも実装されているリロード処理のサンプルを作ってみたいと思います。TomcatのドキュメントにTomcatのClassLoaderの戦略が書いてあるのでそれを参考にすると分かりやすいと思います。
</p>
<p>
    例えばRPCサーバやスケジューラーを考えます。これらを設計する時には大きく二つに分けることができ、RPCサーバではサーバ本体と各命令、スケジューラーでも本体と各タスクという感じです。Tomcatの場合はServletエンジンとビジネスロジックを書いたServlet。（ビジネスロジックはServletに書くものではありませんけどね。）<br />
    RPCの命令、スケジューラーのタスク、ServletコンテナのServletは抽象化する事ができ、インターフェースを切ることが多いです。（HttpServletとかね。）
</p>
<p>
    擬似コードだとこんな感じですね。
</p>
<pre>
class MyServer {
  void run() {
    ITask task = getTask();
    task.execute();
  }
}
</pre>
<p>
    このMyServerとITaskを本体側のClassLoaderに、ITaskを実装したクラスを別のClassLoaderに読み込ませます。<br />
    例えば本体はCLASSPATHを指定してシステムクラスローダーに読み込ませます。(TomcatはBoostrap用のクラスを挟んでいますけどね。）<br />
    タスクは独自のクラスローダーをインスタンス化(このオブジェクトをc1とする)して（URLClassLoaderを直接でも良い）、そのClassLoaderを使ってクラス定義を読み込ませます。C言語だとdlopenみたいなイメージです。
</p>
<p>
    異なるクラスローダーから読み込んだクラスは別のクラスになるので、リロードしたい場合はClassLoaderをもう1回インスタンス化(c2)してあげればいいのです。
</p>
<p>
    文章だけで説明してもアレなので簡単なサンプルを書いてみました。<br />
    Thread#setContextClassLoaderを使うともっと簡単に書けると思います。
</p>
<p>
    サンプルの内容は定期的にタスクを実行するだけのものです。Reload処理はJMX経由で行うようにしました。<br />
    Eclipse上で作る時はタスククラスは別プロジェクトで作るのが良いです。同じプロジェクトに入れてしまうとシステムクラスローダーによってタスクもロードされてしまうので。色々適当です。Sleepの代わりにSemaphoreを使っている理由はsleepの場合Future#cancelで割り込みが発生しなかったからです。Semaphoreだと割り込みが入ってすぐに終了できます。<br />
    tasklist.propertiesに登録したいタスクのクラス名を書く必要があります。まぁこんな事をしなくてもLunarClassLoaderに手を加えて特定のインターフェースを実装しているクラスを検索（列挙）すれば良いだけです。今回はそこまで余力がありませんでした。 タスク用のクラスパスはソースに直書きしています。自分の環境に合わせて修正してください。VMの引数に-Dhogehogeを指定してシステムプロパティとして読み込むのが一般的な方法かと思います。
</p>
<pre class="prettyprint">
package at.orz.lunar;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.management.MBeanServer;
import javax.management.ObjectName;

public class LunarBootstrap {

    public static void main(String[] args) throws Exception {

        URL[] urls = new URL[]{
            new File("C:/usr/local/eclipse3.5/workspace/lunar-subsystem/lunar-task.jar").toURI().toURL(),
            new File("C:/usr/local/eclipse3.5/workspace/lunar-subsystem/bin").toURI().toURL()
        };

        final ExecutorService service = Executors.newCachedThreadPool();
        final LunarServer lunarServer = LunarServer.newInstance(urls, Thread.currentThread().getContextClassLoader());
        final Future&lt;?&gt; future = service.submit(lunarServer);
        service.shutdown(); // 新規追加STOP

        MBeanServer mbServer = ManagementFactory.getPlatformMBeanServer();
        mbServer.registerMBean(new ServerOperationMXBean() {
            @Override
            public void stop() {
                lunarServer.stop();
                future.cancel(true);
                if (!service.isShutdown()) {
                    service.shutdown();
                }
            }
            @Override
            public void reload() {
                lunarServer.reload();
            }
        },  new ObjectName("at.orz.lunar.LunarBootstrap:name=LunarServer"));

    }

}
</pre>
<p>
    シングルインスタンスモデルにするか、プロトタイプモデルにするか迷ったのでclsMapとかでClass定義を保持しているわけです。今回はシングルインスタンスモデルにしたので実はclsMapは必要ありません。
</p>
<pre>
package at.orz.lunar;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

public class LunarClassLoader extends URLClassLoader {

    private HashMap&lt;String, Class&lt;? extends LunarTask&gt;&gt; clsMap = new HashMap&lt;String, Class&lt;? extends LunarTask&gt;&gt;();
    private HashMap&lt;String, LunarTask&gt; insMap = new HashMap&lt;String, LunarTask&gt;();

    public LunarClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
        init();
    }

    @SuppressWarnings("unchecked")
    private void init() {

        try {

            Enumeration&lt;URL&gt; en = getResources("tasklist.properties");
            while (en.hasMoreElements()) {
                URL url =  en.nextElement();

                Properties prop = new Properties();
                InputStream is = url.openStream();
                prop.load(is);
                is.close();

                for (String clsName: prop.stringPropertyNames()) {
                    try {
                        Class&lt;?&gt; c = loadClass(clsName);
                        if (LunarTask.class.isAssignableFrom(c)) {
                            Class&lt;? extends LunarTask&gt; cls = (Class&lt;? extends LunarTask&gt;) c;
                            clsMap.put(clsName, cls);
                            System.out.printf("register class:%s%n", clsName);

                            LunarTask task = cls.newInstance();
                            task.init();
                            insMap.put(clsName, task);
                            System.out.printf("init class:%s%n", clsName);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    void destroy() {
        for (LunarTask task : insMap.values()) {
            try {
                task.destroy();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        insMap.clear();
        clsMap.clear();
    }

    public LunarTask getTask(String className) {
        return insMap.get(className);
    }

    public Iterable&lt;LunarTask&gt; taskIterator() {
        return new HashMap&lt;String, LunarTask&gt;(insMap).values();
    }

}
</pre>
<pre class="prettyprint">
package at.orz.lunar;

import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class LunarServer implements Runnable, ServerOperationMXBean {

    private LunarClassLoader loader;
    private volatile boolean running;
    private LunarServer() {

    }

    @Override
    public void run() {

        ExecutorService service = Executors.newFixedThreadPool(3);

        running = true;
        while (running) {
            for (final LunarTask task : loader.taskIterator()) {
                service.submit(new Runnable() {
                    @Override
                    public void run() {
                        task.execute();
                    }
                });
            }
            try {
                Semaphore sema = new Semaphore(0);
                sema.tryAcquire(10, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        service.shutdown();

    }

    @Override
    public void reload() {
        LunarClassLoader curLoader = loader;
        LunarClassLoader newLoader = new LunarClassLoader(curLoader.getURLs(), curLoader.getParent());
        this.loader = newLoader;
        curLoader.destroy();
    }

    @Override
    public void stop() {
        running = false;
    }

    public static LunarServer newInstance(URL[] urls, ClassLoader parent) {
        LunarServer server = new LunarServer();
        server.loader = new LunarClassLoader(urls, parent);
        return server;
    }

}
</pre>
<pre class="prettyprint">
package at.orz.lunar;

public interface LunarTask {
    public void init();
    public void destroy();
    public void execute();
}
</pre>
<pre>
package at.orz.lunar;

public interface ServerOperationMXBean {
    public void stop();
    public void reload();
}
</pre>
<p>
    ここからタスクです。Eclipse上では別プロジェクトにした方が良いです。今回は2つのクラスを登録します。
</p>
<pre class="prettyprint">
package at.orz.lunar.task;

import at.orz.lunar.LunarTask;

public class SampleTask1 implements LunarTask {
    @Override
    public void destroy() {
        System.out.println("SampleTask1.destroy()");
    }
    @Override
    public void execute() {
        System.out.println("☆SampleTask1.execute()");
    }
    @Override
    public void init() {
        System.out.println("SampleTask1.init()");
    }
}
</pre>
<pre class="prettyprint">
package at.orz.lunar.task;

import at.orz.lunar.LunarTask;

public class SampleTask2 implements LunarTask {
    @Override
    public void destroy() {
        System.out.println("SampleTask2.destroy()");
    }
    @Override
    public void execute() {
        System.out.println("★SampleTask2.execute()");
    }
    @Override
    public void init() {
        System.out.println("SampleTask2.init()");
    }
}
</pre>
<p>
    クラスパスのルートにtasklist.propertiesを作成します。
</p>
<pre>
at.orz.lunar.task.SampleTask1
at.orz.lunar.task.SampleTask2
</pre>
<p>
    実行した後にSampleTask2の内容を変更（★を★★★★★に変更）した後にjconsoleからreloadを呼び出した結果が↓です。
</p>
<pre>
register class:at.orz.lunar.task.SampleTask2
SampleTask2.init()
init class:at.orz.lunar.task.SampleTask2
register class:at.orz.lunar.task.SampleTask1
SampleTask1.init()
init class:at.orz.lunar.task.SampleTask1
★SampleTask2.execute()
☆SampleTask1.execute()
★SampleTask2.execute()
☆SampleTask1.execute()
☆SampleTask1.execute()
★SampleTask2.execute()
☆SampleTask1.execute()
★SampleTask2.execute()
register class:at.orz.lunar.task.SampleTask2
SampleTask2.init()
init class:at.orz.lunar.task.SampleTask2
register class:at.orz.lunar.task.SampleTask1
SampleTask1.init()
init class:at.orz.lunar.task.SampleTask1
SampleTask1.destroy()
SampleTask2.destroy()
☆SampleTask1.execute()
★★★★★SampleTask2.execute()
☆SampleTask1.execute()
★★★★★SampleTask2.execute()
</pre>
<p>
    リロード処理の所にログ仕込むの忘れたのでリロードが発端になっているかは確認できませんが、リロード処理は呼ばれています(； ･`д･´)<br />
    そしてリ新しいクラスの内容が読み込まれているのも確認できます。
</p>
<p>
    ※途中から書くのが面倒になったのがソースからわかっちゃうと思いますが、サンプルなので気にしない(・ε・)
</p>
<p>
    ただし、このような処理はちょっとミスするとPermGenが解放されずにOutOfMemoryErrorを引き起こす原因になりやすいので気をつけましょー。（IBMのVM使えばいいじゃんとかそういう野暮な事は言わないの。）
</p>
<p>
    &nbsp;
</p>]]></description>
            <link>http://mt.orz.at/archives/2009/12/classloader.html</link>
            <guid>http://mt.orz.at/archives/2009/12/classloader.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">Java</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2009後期</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">開発</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Java</category>
            
            <pubDate>Mon, 28 Dec 2009 02:15:05 +0900</pubDate>
        </item>
        
        <item>
            <title>Solr1.4のbackupのバグ</title>
            <description><![CDATA[<p>
    Solr1.4ではとあるURLにアクセスする事でバックアップ（スナップショット）を取れるようになっています。
</p>
<p>
    <a href="http://wiki.apache.org/solr/SolrReplicaじっさいにtion">http://wiki.apache.org/solr/SolrReplication</a>
</p>
<p>
    ↑のSolrのWikiにもあるようにReplicationの機能をONにする必要がありますが、スレーブの設定をしなくても動作するのでマスタのみでもきちんと動作します。<br />
    以下のURLにアクセスするとデータディレクトリと同列に「snapshot.日時」というディレクトリを作成します。
</p>
<p>
    http://master_host:port/solr/replication?command=backup
</p>
<p>
    この時、日付はyyyyMMddHHmmssという書式が指定されているはずなのですが、実はHHがhhになっており、午後の18時に実行しても18にならずに06になってしまいます。
</p>
<p>
    <a title="" href="http://www.google.co.jp/codesearch/p?hl=ja#iQV2WgbHbLs/trunk/src/java/org/apache/solr/handler/SnapShooter.java" target="_blank">SnapShooter</a>というクラスのDATE_FMTの指定を見ると思いっきり小文字で指定されています。&nbsp;
</p>
<p>
    むむー。バグだよね。これは。
</p>
<p>
    まぁだいたいが早朝にバックアップをすると思うから気がつかないんだと思うけど。。
</p>]]></description>
            <link>http://mt.orz.at/archives/2009/12/solr14backup.html</link>
            <guid>http://mt.orz.at/archives/2009/12/solr14backup.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2009後期</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">開発</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Solr</category>
            
            <pubDate>Sat, 26 Dec 2009 20:22:40 +0900</pubDate>
        </item>
        
        <item>
            <title>Amazonの関連商品</title>
            <description><![CDATA[<p>
    AmazonにはProduct Advertising APIというサービスがあります。ECSとかAWSとか言われていたやつです。
</p>
<p>
    関連商品の取得方法にはいくつか方法があり大きく分けると、サーチ系のAPIのResponseGroupにSimilaritiesを指定するかSimilarityLookupというAPIを使うかです。
</p>
<p>
    ItemLookupのResponseGroupに指定すると5件、SimilarityLookupを使うと10件取得できます。SimilarityLookupのResponseGroupにSimilaritiesを指定するとさらにその10件の関連商品を取得できます。（まぁこの場合は殆どが被っていますが・・・。）
</p>
<p>
    ただしAmazonのホームページを見ると70件近くの関連商品が表示されています。<br />
    SimilarityLookupで複数のItemIdを指定しRandom抽出（和集合）を取得するようにしても、同じ集合が取れません(´・ω・｀)<br />
    商品Aの関連商品A1～A10のASINがあって、A-A1の和集合、A-A2の和集合.... をマージ等、色々試してみましたがやっぱり公式サイトと同じリストを再現できません。あそこのリストどうやって作っているのでしょう(´・ω・)
</p>
<p>
    煮詰まってしまったので公式サイトのHTMLから引っこ抜いてみました。
</p>
<p>
    <a href="http://www.amazon.co.jp/dp/%7BASIN">http://www.amazon.co.jp/dp/{ASIN}</a>
</p>
<p>
    このページにアクセスしますが、User-AgentをきちんとAjaxをサポートしているブラウザのやつに指定する必要があります。適当なUser-Agentつけて送ったらJavaScriptのコードが出力されませんでした。Firefox3.5のUser-Agentを使ったらJavaScbriptのコードも出力されました。
</p>
<pre class="prettyprint">
var purchaseAsinList = new Array("aaa","bbb","ccc");
</pre>
<p>
    この様なコードが出力されているのでパースするだけです。Javaなら↓のようなコードでぱぱっと。
</p>
<pre class="prettyprint">
int beginIndex = html.indexOf("var purchaseAsinList");
int endIndex = html.indexOf("\n", beginIndex);
String pscript = (html.substring(beginIndex, endIndex));

Matcher m = Pattern.compile("\"(\\w+)\"").matcher(pscript);
while (m.find()) {
    System.out.println(m.group(1));
}
</pre>
<p>
    微妙・・。きちんとAPIを叩いて処理したいです(´・ω・)<br />
    というわけで情報募集です。
</p>]]></description>
            <link>http://mt.orz.at/archives/2009/12/amazon-2.html</link>
            <guid>http://mt.orz.at/archives/2009/12/amazon-2.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2009後期</category>
            
                <category domain="http://www.sixapart.com/ns/types#category">開発</category>
            
            
                <category domain="http://www.sixapart.com/ns/types#tag">Amazon</category>
            
            <pubDate>Sat, 26 Dec 2009 14:25:50 +0900</pubDate>
        </item>
        
        <item>
            <title>こんなコードを書いていた私涙目。</title>
            <description><![CDATA[<p>こちらの記事にあったので私はこんなコードを書いていましたという返信。</p>
<p><a href="http://1978th.net/tech/promenade.cgi?id=68">ttp://1978th.net/tech/promenade.cgi?id=68</a></p>
<p>インラインアセンブラです。<br/>
x86オンリーなのかもしれませんが私の場合は動作させる環境が固定なので。</p>

<pre class="prettyprint">static __inline__ void __attribute__ ((__always_inline__))
	atomic_add(volatile int *pv1, int v2)
{
	__asm__ volatile (
		"lock; addl %1,%0"
		: "=m" (*pv1)
		: "ir" (v2), "m" (*pv1));
}
</pre>
<p>
これを、<br />
#if defined(_GCC) とかそういうマクロで判定して使ってます。<br />
速度？そんなの知りません( ﾟДﾟ)y─┛~~
</p>]]></description>
            <link>http://mt.orz.at/archives/2009/12/post-593.html</link>
            <guid>http://mt.orz.at/archives/2009/12/post-593.html</guid>
            
                <category domain="http://www.sixapart.com/ns/types#category">日記2009後期</category>
            
            
            <pubDate>Wed, 16 Dec 2009 22:20:39 +0900</pubDate>
        </item>
        
    </channel>
</rss>

