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())));
		
	}

}

HBaseの概要理解のメモ

概要
http://lunarium.info/arc/index.php/HBase%E3%81%AE%E6%A7%8B%E9%80%A0
http://www.slideshare.net/sechiro/osc2012-spring-hphbasereport


hbaseのshell操作
http://www.ne.jp/asahi/hishidama/home/tech/apache/hbase/shell.html#h_create


macへのインストール
http://happy-camper.st/lang/java/hadoop/hadoop-on-macosx.htmlhttp://happy-camper.st/lang/java/hbase/hbase-on-macosx.html


NTTデータのhadoop報告書
http://d.hatena.ne.jp/shiumachi/20100928/1285673951

Apache HttpComponents(旧httpClient)でssl通信時のエラー

アプリケーションからhttps通信を使って他のサーバと連携することはよくあると思います。
例えばfacebookgoogleのOAuth認証のOPとやりとりする場合とか。


私は開発時はMac上で行なっているがその時点ではjava7はOpenJDKしかなかったので(現在はMac版のoracle jdk7が出てますね)、ほとんどの開発をOpenJDK上で行なっていたんですが、なぜかHttpComponentsを使ってhttpsの通信を行うと下記エラーが出る。

Exception in thread "main" java.lang.RuntimeException: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
	at Main.execute(Main.java:62)
	at Main.main(Main.java:19)
Caused by: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
	at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
	at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
	at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:397)
	at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148)
	at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:150)
	at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121)
	at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:575)
	at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425)
	at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820)
	at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754)
	at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)
	at Main.execute(Main.java:54)
	... 1 more


サンプルコードは以下

public class Main {
	public static void main(String[] args) throws Exception{
		Main m = new Main();
		m.execute(args);
	}
	public void execute(String[] args) throws Exception{
		System.out.println(System.getProperty("java.version"));

		HttpClient httpClient = new DefaultHttpClient();		
		//ちょうどmixi developer Centerにサーバ証明書についてのFAQがあったので使わしてもらう
		//http://developer.mixi.co.jp/openid/faq/
		HttpGet get = new HttpGet("https://mixi.jp/");
	
		HttpResponse res = httpClient.execute(get);
		if(res.getStatusLine().getStatusCode() >= 300) {
			System.out.println(res.getStatusLine().getStatusCode());
		}
		System.out.println(EntityUtils.toString(res.getEntity()));
	}
}

httpComponentsのバージョンは

	<dependency>
		<groupId>org.apache.httpcomponents</groupId>
		<artifactId>httpclient</artifactId>
		<version>4.1.3</version>
	</dependency>

を使っています


正直あまりsslについて知識がなかったので、とりあえずHttpClientでSSL通信(HttpClient3.0-rc3) にしたがってサーバ証明書を登録して事無きを得たのだが、どうにも気持ち悪い(いちいちこんな作業をしないとhttps通信できないのか?という気持ち悪さ)



ちなみにいちいち証明書の認証とかめんどくせぇという人は以下のように書くとオレオレ証明書などでも無視して全部受けて入れるようにできます。

		try {
	        TrustStrategy trustStrategy = new TrustStrategy() {
				@Override
				public boolean isTrusted(X509Certificate[] chain, String authType)
						throws CertificateException {
					return true;
				}
			};
	        X509HostnameVerifier hostnameVerifier = new AllowAllHostnameVerifier();
	        SSLSocketFactory sslSf = new SSLSocketFactory(trustStrategy, hostnameVerifier);
	       
	        SchemeRegistry schemeRegistry = new SchemeRegistry();
	        schemeRegistry.register(new Scheme("https", 8443, sslSf));
	        schemeRegistry.register(new Scheme("https", 443, sslSf));
	       
	        ClientConnectionManager connection = new ThreadSafeClientConnManager(schemeRegistry);
			HttpClient httpClient = new DefaultHttpClient(connection);		
		}catch(UnrecoverableKeyException uke) {
			//TODO
		}catch(KeyStoreException kse) {
			//TODO		
		}catch(KeyManagementException kme) {
			//TODO
		}catch(NoSuchAlgorithmException nsae) {
			//TODO
		}	

しかし今回はライブラリを開発しているので勝手に証明書を無視するようなコードを入れ込むわけにはいきません。


というわけで、改めてsslの仕組みを調べてjavassl通信の仕組みについてお勉強。
どうやらデフォルトでcacertsというファイルにいわゆるCAルート証明書がインストールされていることになっているようだ。
なので、httpComponentsを使ったアプリケーションのjavaランタイムのcacertsにCAルート証明書が入っていないと、サーバ証明書のクライアント認証が通せないのでエラーになるということみたいです。


実際にMac標準にインストールされているJDKのcacertsを確認するとかなりの量のルート証明書がインポートされている

$ keytool -v -list -keystore  /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/security/cacerts  | more
キーストアのパスワードを入力してください:    #←デフォルトパスワードはchangeitなので、それを入力
キーストアのタイプ: JKS
キーストア・プロバイダ: SUN

キーストアには183エントリが含まれます

別名: keychainrootca-99
作成日: 2012/03/23
エントリ・タイプ: trustedCertEntry

所有者: CN=TWCA Root Certification Authority, OU=Root CA, O=TAIWAN-CA, C=TW
発行者: CN=TWCA Root Certification Authority, OU=Root CA, O=TAIWAN-CA, C=TW
シリアル番号: 1
有効期間の開始日: Thu Aug 28 16:24:33 JST 2008終了日: Wed Jan 01 00:59:59 JST 2031
証明書のフィンガプリント:
         MD5:  AA:08:8F:F6:F9:7B:B7:F2:B1:A7:1E:9B:EA:EA:BD:79
         SHA1: CF:9E:87:6D:D3:EB:FC:42:26:97:A3:B5:A3:7A:A0:76:A9:06:23:48
         SHA256: BF:D8:8F:E1:10:1C:41:AE:3E:80:1B:F8:BE:56:35:0E:E9:BA:D1:A6:B9:BD:51:5E:DC:5C:6D:5B:87:11:AC:44
         署名アルゴリズム名: SHA1withRSA
         バージョン: 3

拡張: 

#1: ObjectId: 2.5.29.14 Criticality=false
・
・
・以下略


openjdkのほうは空っぽ

$ keytool -v -list -keystore  /Library/Java/JavaVirtualMachines/1.7.0u4.jdk/Contents/Home/jre/lib/security/cacerts 
キーストアのパスワードを入力してください:  

キーストアのタイプ: JKS
キーストア・プロバイダ: SUN

キーストアには0エントリが含まれます

これが原因でサーバ証明書の認証ができないためエラーになったみたい。
試しに同じコードでMacの標準jdkにビルドパスを変更したら問題なく通信できました。

openjdkを使う場合はCAルート証明書を自前でインポートするとかしないといけないのだろうか。
そのへんまでは調べきれてないけど、とりあえず原因がわかったのですっきりした。
必要になったらまた追記します。

javaでjavaagentとagentlibを調査してみる

http://docs.oracle.com/javase/6/docs/technotes/tools/solaris/java.html
の-agentlib, -javaagentの項目について調べてみた


あれ、なんか俺agentlibとjavaagentを勘違いしている、、、、?

-agentlib
-agentlib:hprofや-agentlib:jdwpなどプロファイラやデバッガなどを利用するネイティブライブラリを指定する
詳しくはJVMTI(Java Virtual Machine Tools Interface)を参照すること

つまりCやC++を使って、VMにアクセスしてプロファイル情報などを引き抜いたり、デバッグ用に挙動を操作したりすることができるらしい


JVMTIの日本語DOC
http://java.sun.com/javase/ja/6/docs/ja/platform/jvmti/jvmti.html#starting
JVMTIを利用する
http://www.02.246.ne.jp/~torutk/javahow2/jvmti.html

-javaagent
java.lang.instrumentパッケージを使ってバイトコードをVM起動時に動的に書き換えたりできる。

下記さくらばさんの書き込みがそのままです
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=26578&forum=12



以下バイトコードを動的に操作 Instrumentationを参考にjavaagentについて色々動作を試してみる

eclipseのworkspaceにできたクラスファイルをjarにまとめる

$ cd ~/Documents/workspace/lick_up_jvm/target/classes
$ jar cvf sample.jar jp/co/watanabe_yusaku/instrument/InstrmentTest.class 
マニフェストが追加されました
InstrmentTest.classを追加中です(入=982)(出=528)(46%収縮されました)


いったんjarを展開してマニフェストファイルにPremain-Classを追記する

$ jar xvf sample.jar
META-INF/が作成されました
\META-INF/MANIFEST.MFが展開されました
\jp/co/watanabe_yusaku/instrument/InstrmentTest.classが展開されました

$ vim META-INF/MANIFEST.MF
↓追記
Premain-Class: jp.co.watanabe_yusaku.instrument.InstrmentTest

$ jar cvfm sample.jar META-INF/MANIFEST.MF jp/co/watanabe_yusaku/instrument/InstrmentTest.class
マニフェストが追加されました
jp/co/watanabe_yusaku/instrument/InstrmentTest.classを追加中です(入=982)(出=528)(46%収縮されました)

実行してみる

$ java -javaagent:sample.jar jp/co//watanabe_yusaku/main/HelloWorld
class java.util.HashMap$EntryIterator
class java.lang.Boolean
略
Hello, World!

ちゃんとHelloWorldクラスのmainが実行される前にInstrmentTestクラスのpremainが実行されてるようだ。


この仕組みを使えば稼働中のアプリケーションの構造(設定ファイルなどを含む)を一切変更せずに挙動を変更することができる。
以下のようなコードをjarで固めてjavaagent指定すると全てのメソッドの実行時間をミリ秒で出力することができた。

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

public class MethodTimer {
	private static ClassPool classPool;
	
	@SuppressWarnings("rawtypes")
	public static void premain(String agentArgs, Instrumentation inst) {
		classPool = ClassPool.getDefault();
		
		inst.addTransformer(new ClassFileTransformer() {
			public byte[] transform(ClassLoader loader,
									String className,
									Class<?> classBeingRedefined, 
									ProtectionDomain protectionDomain,
									byte[] classfileBuffer)
									throws IllegalClassFormatException {
				
				try {
					ByteArrayInputStream stream = new ByteArrayInputStream(classfileBuffer);
					CtClass ctClass = classPool.makeClass(stream);
					
					CtMethod[] ctMethods = ctClass.getDeclaredMethods();
					for(CtMethod ctMethod : ctMethods) {
						ctMethod.addLocalVariable("start", CtClass.longType);
						ctMethod.insertBefore("start = System.currentTimeMillis();");
						ctMethod.insertAfter("System.out.println(\"" 
						+ ctClass.getName() + "."
						+ ctMethod.getName() + "()"
						+ ":exec time = \" + (System.currentTimeMillis() - start));");
					}
					return ctClass.toBytecode();
				}catch(CannotCompileException | IOException e) {
					IllegalClassFormatException ecfe = new IllegalClassFormatException();
					ecfe.initCause(e);
					throw ecfe;
				}
			}
		});
	}
}

マニフェストファイルは

Premain-Class: MethodTimer
Boot-Class-Path: javassist.jar
Can-Redefine-Classes: true


mainクラスは

public class HelloWorld {
    public static void main(String[] args) throws Exception{
    	HelloWorld h = new HelloWorld();
    	h.execute(args);
    }
    
    public void execute(String[] args) throws Exception{
    	this.say("Hello, World0");
    	this.say("Hello, World1");
    	Thread.sleep(3000);
		this.say("Hello, World2");
		this.say("Hello, World3");
    }
    
    public void say(String str) {
    	System.out.println(str);
    }
}


実行ディレクトリにjavasisst.jarとsample.jarを配置してから実行すると

java -javaagent:sample.jar HelloWorld
sun.launcher.LauncherHelper.makePlatformString():exec time = 0
sun.launcher.LauncherHelper.getMainMethod():exec time = 0
sun.launcher.LauncherHelper.checkAndLoadMain():exec time = 6
Hello, World0
HelloWorld.say():exec time = 1
Hello, World1
HelloWorld.say():exec time = 1
Hello, World2
HelloWorld.say():exec time = 0
Hello, World3
HelloWorld.say():exec time = 0
HelloWorld.execute():exec time = 3004
HelloWorld.main():exec time = 3004

AOPを導入するまでもないけど、ちょっと本番環境で設定ファイルなどを含む環境などを一切変更しないでプロファイリングしたい時に便利かもしれないですね

自宅サーバのRAID障害が出た日

以前構築した自宅サーバのRAID環境から障害通知メールが来た。


件名:mdadm monitoring

This is an automatically generated mail message from mdadm
running on localhost.localdomain

A Fail event had been detected on md device /dev/md0.

It could be related to component device /dev/sdc1.

Faithfully yours, etc.

P.S. The /proc/mdstat file currently contains the following:

Personalities : [raid1]
md0 : active raid1 sdc1[2](F) sdb1[0]
     976759936 blocks [2/1] [U_]

unused devices: 

/var/log/messagesを確認してそれっぽいエラーが出ているのがここらへん。

ar 18 05:43:04 localhost kernel: SCSI device sdc: 1953525168 512-byte hdwr sectors (1000205 MB)
Mar 18 05:43:11 localhost kernel: sdc: Write Protect is off
Mar 18 05:43:12 localhost kernel: SCSI device sdc: drive cache: write back
Mar 18 05:44:12 localhost kernel: ata2.01: exception Emask 0x10 SAct 0x0 SErr 0x400100 action 0x6 frozen
Mar 18 05:44:12 localhost kernel: ata2: SError: { UnrecovData Handshk }
Mar 18 05:44:12 localhost kernel: ata2.01: cmd 35/00:00:3f:05:c3/00:04:09:00:00/f0 tag 0 dma 524288 out
Mar 18 05:44:12 localhost kernel:          res 40/00:cf:61:05:c3/40:03:09:00:00/f0 Emask 0x14 (ATA bus error)             169,51        34%
Mar 18 05:43:04 localhost kernel: sdc: Current [descriptor]: sense key: Medium Error
Mar 18 05:43:04 localhost kernel:     Add. Sense: Unrecovered read error - auto reallocate failed
Mar 18 05:43:04 localhost kernel:
Mar 18 05:43:04 localhost kernel: Descriptor sense data with sense descriptors (in hex):
Mar 18 05:43:04 localhost kernel:         72 03 11 04 00 00 00 0c 00 0a 80 00 00 00 00 00
Mar 18 05:43:04 localhost kernel:         09 c3 05 61
Mar 18 05:43:04 localhost kernel: end_request: I/O error, dev sdc, sector 163775841
Mar 18 05:43:04 localhost kernel: ata2: EH complete                                                                                         
Mar 18 05:43:04 localhost kernel: SCSI device sdc: 1953525168 512-byte hdwr sectors (1000205 MB)
Mar 18 05:43:11 localhost kernel: sdc: Write Protect is off
Mar 18 05:43:12 localhost kernel: SCSI device sdc: drive cache: write back
Mar 18 05:44:12 localhost kernel: ata2.01: exception Emask 0x10 SAct 0x0 SErr 0x400100 action 0x6 frozen
Mar 18 05:44:12 localhost kernel: ata2: SError: { UnrecovData Handshk }
Mar 18 05:44:12 localhost kernel: ata2.01: cmd 35/00:00:3f:05:c3/00:04:09:00:00/f0 tag 0 dma 524288 out
Mar 18 05:44:12 localhost kernel:          res 40/00:cf:61:05:c3/40:03:09:00:00/f0 Emask 0x14 (ATA bus error)
                                                                                                                          169,51        33%
Mar 18 05:43:04 localhost kernel: sdc: Current [descriptor]: sense key: Medium Error
Mar 18 05:43:04 localhost kernel:     Add. Sense: Unrecovered read error - auto reallocate failed
Mar 18 05:43:04 localhost kernel:
Mar 18 05:43:04 localhost kernel: Descriptor sense data with sense descriptors (in hex):
Mar 18 05:43:04 localhost kernel:         72 03 11 04 00 00 00 0c 00 0a 80 00 00 00 00 00
Mar 18 05:43:04 localhost kernel:         09 c3 05 61
Mar 18 05:43:04 localhost kernel: end_request: I/O error, dev sdc, sector 163775841
Mar 18 05:43:04 localhost kernel: ata2: EH complete                                                                                         
Mar 18 05:43:04 localhost kernel: SCSI device sdc: 1953525168 512-byte hdwr sectors (1000205 MB)
Mar 18 05:43:11 localhost kernel: sdc: Write Protect is off
Mar 18 05:43:12 localhost kernel: SCSI device sdc: drive cache: write back
Mar 18 05:44:12 localhost kernel: ata2.01: exception Emask 0x10 SAct 0x0 SErr 0x400100 action 0x6 frozen
Mar 18 05:44:12 localhost kernel: ata2: SError: { UnrecovData Handshk }
Mar 18 05:44:12 localhost kernel: ata2.01: cmd 35/00:00:3f:05:c3/00:04:09:00:00/f0 tag 0 dma 524288 out
Mar 18 05:44:12 localhost kernel:          res 40/00:cf:61:05:c3/40:03:09:00:00/f0 Emask 0x14 (ATA bus error)
Mar 18 05:44:12 localhost kernel: ata2.01: status: { DRDY }                                                               169,51        34%
Mar 18 05:44:23 localhost kernel: ata2: hard resetting link
Mar 18 05:44:24 localhost kernel: ata2: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
Mar 18 05:44:24 localhost kernel: ata2.01: revalidation failed (errno=-2)
Mar 18 05:44:24 localhost kernel: ata2.01: disabled
Mar 18 05:44:25 localhost kernel: ata2.00: failed to IDENTIFY (I/O error, err_mask=0x40)
Mar 18 05:44:25 localhost kernel: ata2.00: revalidation failed (errno=-5)
Mar 18 05:44:25 localhost kernel: ata2: failed to recover some devices, retrying in 5 secs
Mar 18 05:44:30 localhost kernel: ata2: hard resetting link                                                                                 
Mar 18 05:44:30 localhost kernel: ata2: SATA link up 3.0 Gbps (SStatus 123 SControl 300)
Mar 18 05:44:30 localhost kernel: ata2.00: configured for UDMA/133
Mar 18 05:44:30 localhost kernel: ata2: EH complete

以下らへんを参考に復旧作業中だがだめだったらディスク買い換えるか、、、、

CentOSでソフトウェアRAIDの構築
smartd での sector error 復活作業
ext3でフォーマットする方法
HDDの不良ブロックをどうにかできないか – ubuntu badblocks mke2fs e2fsck

【追記】
badblocksコマンドで/dev/sdcのディスクについて不良セクタのチェックを行ったが特に検出されなかった(1TBのディスクなので--fullオプションをつけて実行したら一週間ぐらいかかった)
とりあえずRAID1を構築しなおして/dev/md0のRAIDデバイスについてext3でフォーマットしなおしたら復旧した。
ソフトウェアRAIDの書き込み失敗とかそういう論理的な問題だったのだろうか、、、、