visualvmをRemoteで利用する

JDK6からすげー便利なvisualvmというプロファイラが付属されています。
内容についてはこちらに詳しく書いてあるので端折ります。


このvisualvmをリモートで接続する場合はJMX経由で接続する方法とjstatdというデーモン経由で接続する方法があるようです。
どう違うのか気になったのですが私が調べた限りだと機能的には違いはないようです。
ただ、jstatsデーモンを起動しておけば、そのサーバ上で動いているそのjstatdを起動したユーザーと同じユーザー権限で起動しているjavaプロセスを全部表示することができるのでjstatdのほうがいいような気がします。
※いちいち全部のプロセスの起動スクリプトにJMXの設定いれるの面倒なので。



そんなわけでjstatdを起動しようとしたのですが、
http://d.hatena.ne.jp/tanamon/20091016/1255674058
を参考に

grant {
	permission java.security.AllPermission;
}

というall.policyを作って

jstatd -J-Djava.security.policy=all.policy &

って感じでjstatdは起動したのですが、jvisualvm側でリモート設定してもうんともすんともでした。



試しに

jps -l [jstatdが起動してるサーバのIP]

で確認してみると

Error communicating with remote host: Connection refused to host: 127.0.0.1; nested exception is: 
	java.net.ConnectException: Connection refused

なエラーが。



こちらに書かれている通りRMIの仕様上の問題っぽいので起動オプションを

jstatd  -J-Djava.security.policy=all.policy -J-Djava.rmi.server.hostname=[サーバのIP]

で起動してjvisualvmでリモート接続したら無事表示できました。

mac Lionでローカルでpostfixを起動してローカルのメールボックスに保存してみる

メールをローカルのpostfixに送信して自分で受信してローカルのメールボックスに保存するテストをしてみる
$vim /etc/postfix/main.cf

myhostname = yuhsaku.co.jp    #ローカルのpostfixのホスト名を適当に設定
mydestination = $myhostname, localhost.$mydomain, localhost    #myhostnameのドメインできたメールは自分が宛先とする


sudo postfix start

telnet localhost 25
をすると反応がなくなってしまうので/var/log/mail.logを見ると

	fatal: open /etc/postfix/submit.cred: No such file or directory

みたいなエラーが出ている。



http://tohae.hatenablog.com/entry/20110929/1317278539
で対応が書いてあったので、そのとおりにファイルを作成して2行を追加してからパーミッションを設定する

$ sudo touch /etc/postfix/submit.cred
$ sudo vim /etc/postfix/submit.cred
#この2行を追加
submitcred version 1
hostname|username|password
$ sudo chmod 600 /etc/postfix/submit.cred


これでとりあえず
http://linux.kororo.jp/cont/server/postfix.php
に書いてある通り動作確認をする。

$ telnet localhost 25
Trying ::1...
Connected to localhost.
Escape character is '^]'.
220 yuhsaku.co.jp ESMTP Postfix

helo localhost
250 yuhsaku.co.jp

mail from:
250 2.1.0 Ok

rcpt to:
250 2.1.5 Ok

data
354 End data with .

subject:hoge test
way way
.
250 2.0.0 Ok: queued as A6BD7198621F

quit
221 2.0.0 Bye
Connection closed by foreign host.

すると、すぐに以下のようなメッセージがコンソールに出てくるので

You have new mail in /var/mail/A11068


中身を見てみるとちゃんとメールがローカルに保存されていました。
cat /var/mail/a11068

From ore@test.co.jp  Mon Aug 27 21:43:47 2012
Return-Path: 
X-Original-To: A11068@yuhsaku.co.jp
Delivered-To: A11068@yuhsaku.co.jp
Received: from localhost (localhost [IPv6:::1])
	by yuhsaku.co.jp (Postfix) with SMTP id A6BD7198621F
	for ; Mon, 27 Aug 2012 21:43:13 +0900 (JST)
subject:hoge test
Message-Id: <20120827124332.A6BD7198621F@yuhsaku.co.jp>
Date: Mon, 27 Aug 2012 21:43:13 +0900 (JST)
From: ore@test.co.jp

way way


※postfixのデフォルトのメール保存形式はmbox形式なんですね

hadoopのソースコードをeclipseにインポートしてデバッグ起動する時のメモ(WEB Interface編)

hdfs管理画面(http://localhost:50070/dfshealth.jsp)にアクセスするとなんか文字化けしてエラーが出てる。
localhost:50070を表示しようとすると

    12/07/18 09:43:28 WARN mortbay.log: /dfshealth.jsp
    org.apache.jasper.JasperException: JSPのクラスをコンパイルできません

    JSPファイル: /dfshealth.jsp の中の20行目でエラーが発生しました
    生成されたサーブレットのエラーです:
    The method getFSImage() from the type FSNamesystem is not visible

    JSPファイル: /dfshealth.jsp の中の20行目でエラーが発生しました
    生成されたサーブレットのエラーです:
    The method getRemovedStorageDirs() from the type FSImage is not visible

明日以降調べる

hadoopのソースコードをeclipseにインポートしてデバッグ起動する時のメモ(DataNode編)

NameNode編に続き今回はDataNodeを起動する

org.apache.hadoop.hdfs.server.datanode.DataNodeのmainメソッドを起動する。
素直に起動した。
おわり


しかしログ出力の設定をしていなかったことに気づいてlog4j.propertiesを配置して、conf以下をビルドパスに加える
そしてもう一回起動してみるとエラーが出てた、、、

ERROR conf.Configuration: Failed to set setXIncludeAware(true) for parser org.apache.xerces.jaxp.DocumentBuilderFactoryImpl@114a0b74:java.lang.UnsupportedOperationException:  setXIncludeAware is not supported on this JAXP implementation or earlier: class org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
java.lang.UnsupportedOperationException:  setXIncludeAware is not supported on this JAXP implementation or earlier: class org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
	at javax.xml.parsers.DocumentBuilderFactory.setXIncludeAware(DocumentBuilderFactory.java:589)
	at org.apache.hadoop.conf.Configuration.loadResource(Configuration.java:1143)
	at org.apache.hadoop.conf.Configuration.loadResources(Configuration.java:1119)
	at org.apache.hadoop.conf.Configuration.getProps(Configuration.java:1063)
	at org.apache.hadoop.conf.Configuration.set(Configuration.java:439)
	at org.apache.hadoop.hdfs.server.namenode.NameNode.setStartupOption(NameNode.java:1250)
	at org.apache.hadoop.hdfs.server.namenode.NameNode.createNameNode(NameNode.java:1267)
	at org.apache.hadoop.hdfs.server.namenode.NameNode.main(NameNode.java:1288)


調べてみると
org.apache.hadoop.conf.Configuration:loadResources()で以下のようなコードがある

      DocumentBuilderFactory docBuilderFactory 
        = DocumentBuilderFactory.newInstance();
      //ignore all comments inside the xml file
      docBuilderFactory.setIgnoringComments(true);

      //allow includes in the xml file
      docBuilderFactory.setNamespaceAware(true);
      try {
          docBuilderFactory.setXIncludeAware(true);
      } catch (UnsupportedOperationException e) {
        LOG.error("Failed to set setXIncludeAware(true) for parser "
                + docBuilderFactory
                + ":" + e,
                e);
      }

このsetXIncludeAware(true)のメソッドでDocumentBuilderFactoryの実装ライブラリがXML Inclusionsをサポートしているかチェックしている。
hadoop-1.0.3のivyに設定されているxercesはバージョンが1.4.4なので、その実装だとsetXIncludeAware(true)はExcepitonを返すようになっていた。

とりあえず新しいxercesのバージョンではXML Inclusionsをサポートしているようなのでバージョンを上げて対応する


ivy.xmlを修正

    <dependency org="xerces"
      name="xerces"
      rev="${xerces.version}"
      conf="jdiff->default">
    </dependency>

    <dependency org="xerces"
      name="xercesImpl"
      rev="${xerces.version}"
      conf="jdiff->default">
    </dependency>


libraries.propertiesのファイルを修正してバージョンを上げる

    xerces.version=1.4.4
    ↓
    xerces.version=2.10.0

これで解決

hadoopのソースコードをeclipseにインポートしてデバッグ起動する時のメモ(NameNode編)

環境は
Mac OS10.7.4
Eclipse Version: Indigo Service Release 2

eclispe ivy pluginのインストール

http://www.atmarkit.co.jp/fjava/rensai4/devtool22/devtool22_1.html
を参考にivyのpluginをeclipseにインストール

hadoopのsvnリポジトリからcheckoutする

http://svn.apache.org/repos/asf/hadoop/common/trunk
直下をcheckoutする
※このsvnディレクトリをrootディレクトリとした階層プロジェクトになっている。(pom.xmlのparentタグを利用している)

依存ライブラリの解決

Ivyによるライブラリダウンロード

svnからのcheckoutしたプロジェクトだとivy.xmlとかが見つからなかったので、tar.gzでソースコードダウンロードしてeclipseにインポートする方法にする。
http://shuyo.wordpress.com/2011/03/08/hadoop-development-environment-with-eclipse/
を参考に、新規javaプロジェクトをhadoop-1.0.3という名前で作成してhadoop-1.0.3.tar.gzをインポートする。


上記サイトでは依存ライブラリは手動でlibに突っ込めみたいなこと書いてあるけど、ivyで解決できるっぽい。

  • hadoop-1.0.3/hadoop-1.0.3/ivy.xmlを右クリック
  • add ivy Libraryを選択
  • 出てきた設定画面でsettingsタブでProperty filesに$hadoop-1.0.3/hadoop-1.0.3/ivy/libraries.propertiesを追加

で、Finishで実行すると依存ライブラリを全部ダウンロードしてくれた


ビルド設定

とりあえずビルドパスを通してみる
こんな感じで通してみた。

hdfsを動かしたいのでcoreモジュールとhdfsモジュールを通した

coreモジュールのビルド設定

org.apache.hadoop.fs.kfsのコンパイルエラーに対処する

coreプロジェクトのorg.apache.hadoop.fs.kfsあたりでコンパイルエラーが出ているので調べてみると
どうやらKosmos File SystemというFSを利用する際のライブラリが足りてないみたいだ。
ivy.xmlを見てみると

    <!-- This is not in the repository
  <dependency org="org.kosmix"
    name=""
    rev="${kfs.version}"
    conf="kfs->default"/>-->

となっていたので、ivyでは取ってこれてないみたいなので、直接ダウンロードしてくる必要がある。
http://code.google.com/p/kosmosfs/
からtar.gzを落としてきて新規に作成したプロジェクトにインポート。
kfs-0.5/src/javaにビルドパスを通してkfs-0.5のビルドはOK。
hadoop-1.0.3側のプロジェクト参照を追加

これでorg.apache.hadoop.fs.kfsあたりのコンパイルエラーは解消


org.apache.hadoop.security.SecurityUtilのコンパイルエラーに対処する

import sun.net.dns.ResolverConfiguration;
import sun.net.util.IPAddressUtil;
が解決できないコンパイルエラーで、以下のような文言が出ている
Access restriction: The type ResolverConfiguration is not accessible due to restriction on required library /Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/jre/lib/rt.jar

http://blog.bitmeister.jp/?p=1486
を見つけたので、「ほほーこんなんあるのかー」と思いつつ、sun/net/**をAccessibleにして登録したらコンパイルエラーが消えた。
色いろあるんもんだ。

hdfsモジュールのビルド設定

org.apache.hadoop.hdfs.server.namenode.JspHelperもコンパイルエラー

import javax.servlet.jsp.JspWriter;が解決できないエラーになっていたので、tomcat/lib以下を全部クラスパスに通して解決

NameNode起動

src/core/core-default.xmlに以下の値をセットする

	    <property>
	      <name>fs.default.name</name>
	      <value>hdfs://localhost:9000</value>
	    </property>
	    <property>
	      <name>hadoop.tmp.dir</name>
	      <value>/Users/${user.name}/development/hadoop-data</value>
	      <description>A base for other temporary directories.</description>
	    </property>


org.apache.hadoop.hdfs.server.namenode.NameNodeにメインメソッドがあるので起動する

NameNodeデータディレクトリをフォーマットする

そのまま起動すると
java.io.IOException: NameNode is not formatted.
が出るのでNameNodeのデータファイルを格納するディレクトリのフォーマットが必要

org.apache.hadoop.hdfs.server.namenode.NameNodeの起動オプションに-formatをつけるとNameNode.main()からNameNode.format()が呼ばれて
フォーマット処理がはしる

データディレクトリのフォーマットに関係しそうなクラスはこんな感じ
NameNode.java
|

FSNamesystem implements FSConstants, FSNamesystemMBean
|

FSDirectory implements FSConstants, Closeable
|

FSImage
|

Storage

java.io.IOException: webapps not found in CLASSPATHが出る問題

NameNodeのコンストラクタで初期化処理をする際にhttpServerを起動する処理中に
org.apache.hadoop.http.HttpServer:getWebAppsPath()
の中で
URL url = getClass().getClassLoader().getResource("webapps");
でIOExceptionが出ている。



hadoop-1.0.3/src/webappsをそのままソースディレクトリとして指定するとwebappsのなかのディレクトリが展開されてしまっていて、webappsのリソースが取得できない。
調べても回避方法がよくわからないので
hadoop-1.0.3/src/webapps/の下にもう一つwebappsディレクトリを作って、webapps以下を全部webapps/webapps/以下に移動させた。
再ビルドするとhadoop-1.0.3/bin以下にちゃんとwebappsディレクトリが作成されていることを確認




再度NameNodeのmainメソッドから実行してみるとNameNodeの起動に成功しました

BloomFilterを使ってみる

http://dev.ariel-networks.com/column/tech/boom_filter/
http://www.perl.com/pub/2004/04/08/bloom_filters.html

hadoopにBloomFilterの実装があったのでそのクラスを使ってみる。
その対象のデータに対して検索したい場合の検索インデックスとしてDBなどに保存するために、BloomFilterを使って生成したbit列をシリアライズする方法も試した。
bit列をもとに同じBloomFilterを復元することもできた。
なんかに使えそう。

/**
 * 
 */
package jp.co.cyberagent.partner.util;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import junit.framework.Assert;

import org.apache.hadoop.util.bloom.BloomFilter;
import org.apache.hadoop.util.bloom.Key;
import org.apache.hadoop.util.hash.Hash;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author watanabe_yusaku
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration()
public class BloomFilterTest {

	@Configuration
	static class Config{
	}
	
	@Test
	public void test() {
		//Filterテスト
		BloomFilter b = new BloomFilter(128, 128, Hash.MURMUR_HASH);
		b.add(new Key(new String("yuhsaku").getBytes()));
		b.add(new Key(new String("hiroki").getBytes()));
		b.add(new Key(new String("yumi").getBytes()));
		b.add(new Key(new String("kunitaka").getBytes()));
		b.add(new Key(new String("mitsuko").getBytes()));
		b.add(new Key(new String("tsuyoshi").getBytes()));
		
		Assert.assertTrue(b.membershipTest(new Key("yuhsaku".getBytes())));
		Assert.assertFalse(b.membershipTest(new Key("hoge".getBytes())));
		
		
		//Filterのbit列をシリアライズしてから再度復元してちゃんとFilterできるか確認
		BloomFilter rebuildBloomFilter = new BloomFilter(128, 128, Hash.MURMUR_HASH);
		
		ByteArrayOutputStream bytearrayOs = new ByteArrayOutputStream();
		DataOutputStream s = new DataOutputStream(bytearrayOs);
		byte[] bytes;
		try {
			b.write(s);
			System.out.println("size:" + s.size());
			bytes = new byte[s.size()];
			bytes = bytearrayOs.toByteArray();
			
			rebuildBloomFilter.readFields(new DataInputStream(new ByteArrayInputStream(bytes)));
		}catch(IOException ioe) {
			Assert.fail();
		}
		
		Assert.assertTrue(b.membershipTest(new Key("yuhsaku".getBytes())));
		Assert.assertFalse(b.membershipTest(new Key("hoge".getBytes())));
		
	}

}

logbackの覚書

毎回忘れてしまうので。

slf4j(Simple Logging Facade for Java) - Commons Logging の後継。
Logback - Log4j の後継。
logback-core.jar logback本体
logback-classic.jar logback用のブリッジ

jcl-over-slf4j - Commons Logging APIからSLF4Jに変換するライブラリ


http://www.slf4j.org/legacy.html