Hatena::ブログ(Diary)

revamping RSSフィード

2012-04-08

[github]Github Pagesを作りました

Octopressとgithub Pagesを使ったブログ - 0xff.toBlog()


この記事を読んで、Github Pagesでblogを書いてみたくなり、作ってみました。


http://masayuki038.github.com/


以前からOctopressが気になっていたので、ちょうど良いタイミングでした。

ということで、Github Pagesの方に引っ越します。

2012-03-10

[]mvn assembly:assemblyjava.lang.IncompatibleClassChangeError

mvn assembly:assemblyで作成したファイルをjava -jarで実行すると以下の例外が出る、という事象に悩まされてました。

Exception in thread "main" java.lang.IncompatibleClassChangeError: Implementing
class
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(Unknown Source)
        at java.security.SecureClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.defineClass(Unknown Source)
        at java.net.URLClassLoader.access$000(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)
        at net.wrap_trap.utils.EntityService.<init>(EntityService.java:47)
        at net.wrap_trap.utils.BsonStore.initialize(BsonStore.java:276)
        at net.wrap_trap.utils.BsonStore.<init>(BsonStore.java:33)
        at net.wrap_trap.utils.FileStoredMap.<init>(FileStoredMap.java:26)
        at net.wrap_trap.collections.fsm.bench.Benchmark1.main(Benchmark1.java:9
)

IncompatibleClassChangeError?これまで遭遇したことがない例外だったので調べてみました。

IncompatibleClassChangeError (Java Platform SE 6)

Thrown when an incompatible class change has occurred to some class definition. The definition of some class, on which the currently executing method depends, has since changed.

クラス定義ファイル間の不整合が発生しているようですが、そんな変なクラスの変更したかな…。一旦cleanしてからリトライしましたが、事象が変わらず。

コンパイルからちょっと離れて、クラス間の定義で不整合が出るという点に着目してみたところ、2つ思い当たる事がありました。今回のモジュールorg.mongodb.bsonに直接依存しており、mongo-java-driverに間接依存していました。mongo-java-driverのjarファイルの中にorg.bsonのクラスファイルが含まれており、それが直接依存しているorg.mongodb.bsonのクラスファイルとバージョンが合わない為、この例外が出ているのではないかと。

そしてもう1つ、mvn assembly:assemblyする際に、依存モジュールをunpackするようにしていました。

<assembly>
  <id>distribution</id>
  <formats>
    <format>zip</format>
  </formats>
  <includeBaseDirectory>false</includeBaseDirectory>
  <dependencySets>
    <dependencySet>
      <unpack>true</unpack>
      <scope>runtime</scope>
      <outputDirectory>/</outputDirectory>
    </dependencySet>
  </dependencySets>
</assembly>

unpackを指定すると、assembly時に依存モジュールjarを展開してクラスファイルとしてモジュールに取り込みます。

今回の問題は、2つのモジュールにバージョンの異なるorg.bsonのクラスファイルがそれぞれ入っていており、展開される際にどちらかが上書きされてしまう為、不整合が起きていました。

依存モジュールでmongo-java-driverを使っているので、org.bsonを直接参照するのをやめ、代わりにmongo-java-driverを参照するようにしたところ、この例外は起こらなくなりました。dependencySetのunpackをtrueにする場合、この点に気をつける必要があります。

2012-03-04

[]JDK6をソースコードからビルド

jvmgcまわりの挙動を確認する為、CentOS上でJDK6のソースコードを取得してビルドしてみました。基本的な流れは以下のページを参考にしています。

「Compile Hotspot JDK from Source Code」

http://konpairu.net/blog/archives/139

ビルドするのに色々躓きましたが、その度ググって何とかなりました。躓いた箇所と対応を、備忘録として記載しておきます。

依存パッケージのインストール

# yum install alsa-lib-devel -y
# yum install cups-devel -y

Missing ./../src/share/lib/fonts/LucidaTypewriterRegular.ttf

ERROR: Missing ./../src/share/lib/fonts/LucidaTypewriterRegular.ttf. Verify you have downloaded and overlayed on the source area all the binary files.

フォントのコピーが必要。以下のページを参考にしました。

Java SE 6(Mustang)をソースからビルドする」

http://d.hatena.ne.jp/torutk/20050825/1124985836

# cp -pir /opt/jdk1.6.0_16/jre/lib/fonts/* ../../j2se/src/share/lib/fonts/

No rule to make target `/usr/X11R6/lib/X11/config/Imake.tmpl'

make[3]: *** No rule to make target `/usr/X11R6/lib/X11/config/Imake.tmpl', needed by `xmkmf'. Stop.

X11のフォルダ構成の問題らしい。別の所にあるファイルだったのでsymlinkを貼りました。

# cd /usr/X11R6/lib/
# ls -l X11
(何も無かった)
# rm -r X11
# ln -s /usr/share/X11 X11

error: static declaration of 'sigignore' follows non-static declaration

../../../../src/solaris/hpi/native_threads/src/interrupt_md.c:115: error: static declaration of 'sigignore' follows non-static declaration

/usr/include/signal.h:377: error: previous declaration of 'sigignore' was here

ヘッダではnon-staticで宣言されているものの、実装ではstaticになっていることが原因。

以下のページを参考にしました。

「jdk1.6のコンパイル

http://www.melange.co.jp/blog/?p=779

# pushd ../../j2se/src/solaris/hpi/native_threads/src/
# cp -pi interrupt_md.c interrupt_md.org
# vi interrupt_md.c
# diff -u interrupt_md.org interrupt_md.c
--- interrupt_md.org    2012-02-27 18:26:56.000000000 +0900
+++ interrupt_md.c      2012-03-03 20:09:06.000000000 +0900
@@ -110,7 +110,7 @@
 }
 
 #ifndef HAVE_SIGIGNORE
-static int                                                     
+int                                                    
 sigignore(int sig)             
 {                              
     struct sigaction action;
# popd

No rule to make target `/usr/src/jvms/jdk16u16/control/build/linux-amd64/classes/com/sun/java/swing/plaf/windows/icons/Computer.gif'

make[5]: *** No rule to make target `/usr/src/jvms/jdk16u16/control/build/linux-amd64/classes/com/sun/java/swing/plaf/windows/icons/Computer.gif', needed by `other_files'. Stop.

Computer.gif等の画像ファイルをコピーして取り込もうとしているようです。この画像を探してみたのですが、bootstrap jdkに含まれてなかった為、コピーするタスク自体を消しました。

# pushd ../../j2se/make/javax/swing/plaf
# cp -pi FILES.gmk FILES.org
# vi FILES.gmk 
# popd

No rule to make target `/usr/src/jvms/jdk16u16/control/build/linux-amd64/lib/audio/soundbank.gm'

make[4]: *** No rule to make target `/usr/src/jvms/jdk16u16/control/build/linux-amd64/lib/audio/soundbank.gm', needed by `copy-files'. Stop.

soundbank.gmはbootstrap jdkjre/libの下にあったので、それをコピーして先に進めました。

# cp -pi /home/masayuki/install/soundbank.gm ../build/linux-amd64/lib/audio/

(諸事情によりコピー元のパスがjre/libではありませんが)

../../../src/solaris/native/sun/awt/img_util_md.h:14: error: expected specifier-qualifier-list before 'XID'

../../../src/solaris/native/sun/awt/img_util_md.h:14: error: expected specifier-qualifier-list before 'XID'

以下のページを参考にして、libXt-develをインストール

「Building OpenJDK on Ubuntu

http://d.hatena.ne.jp/dolduke/20080420

yum install libXt-devel -y

/usr/bin/ld: cannot find -lXext

/usr/bin/ld: cannot find -lXext

libXext-develをインストール

# yum install libXext-devel -y

/usr/bin/ld: cannot find -lXtst

/usr/bin/ld: cannot find -lXtst

libXtst-develをインストール

# yum install libXtst-devel -y

../../../src/solaris/native/sun/awt/awt_dnd.c:172: error: static declaration of 'xerror_code' follows non-static declaration

../../../src/solaris/native/sun/awt/awt_dnd.c:172: error: static declaration of 'xerror_code' follows non-static declaration

awt_dnd.cのxerror_codeのstaticを外しました。

# pushd ../../j2se/src/solaris/native/sun/awt/
# cp -pi awt_dnd.c awt_dnd.org
# vi awt_dnd.c
# diff -u awt_dnd.org awt_dnd.c
--- awt_dnd.org 2012-02-27 18:26:56.000000000 +0900
+++ awt_dnd.c   2012-03-03 21:24:11.000000000 +0900
@@ -169,7 +169,7 @@
     return awt_root_window;
 }
 
-static unsigned char xerror_code = Success;
+unsigned char xerror_code = Success;
 
 static int 
 xerror_handler(Display *dpy, XErrorEvent *err) {
# popd

No rule to make target `../../../src/share/lib/cmm/sRGB.pf', needed by `/usr/src/jvms/jdk16u16/control/build/linux-amd64/lib/cmm/sRGB.pf'. Stop.

make[4]: *** No rule to make target `../../../src/share/lib/cmm/sRGB.pf', needed by `/usr/src/jvms/jdk16u16/control/build/linux-amd64/lib/cmm/sRGB.pf'. Stop.

これもjre/libの下にあったのでコピーしています。

# mkdir ../../j2se/src/share/lib/cmm
# cp -pi /home/masayuki/install/cmm/* ../../j2se/src/share/lib/cmm/

relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC

make[4]: Entering directory `/usr/src/jvms/jdk16u16/j2se/make/sun/jdbc'

/usr/bin/gcc -shared -o /usr/src/jvms/jdk16u16/control/build/linux-amd64/tmp/sun/sun.jdbc.odbc/JdbcOdbc/libodbcinst.so dummyodbc.c

/usr/bin/ld: /tmp/cccOEmCj.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC

jdbcのところで上記エラーが出ました。何故ここだけ…。CFLAGSを見てないようなのでMakefileに「-fPIC」をハードコード。

# diff -u ../../j2se/make/sun/jdbc/Makefile.org ../../j2se/make/sun/jdbc/Makefile
--- ../../j2se/make/sun/jdbc/Makefile.org       2012-02-27 18:26:50.000000000 +0900
+++ ../../j2se/make/sun/jdbc/Makefile   2012-03-03 22:11:06.000000000 +0900
@@ -85,10 +85,10 @@
 make_libs: $(TEMPDIR)/libodbcinst.so $(TEMPDIR)/libodbc.so
 
 $(TEMPDIR)/libodbcinst.so: dummyodbc.c  $(TEMPDIR)
-       $(CC) -shared -o $@ $<
+       $(CC) -fPIC -shared -o $@ $<
 
 $(TEMPDIR)/libodbc.so: dummyodbc.c $(TEMPDIR) 
-       $(CC) -shared -o $@ $<
+       $(CC) -fPIC -shared -o $@ $<
 
 clean::
        $(RM) -f $(TEMPDIR)/libodbcinst.so $(TEMPDIR)/libodbc.so

No rule to make target `/usr/src/jvms/jdk16u16/control/build/linux-amd64-fastdebug/lib/audio/soundbank.gm'

make[6]: *** No rule to make target `/usr/src/jvms/jdk16u16/control/build/linux-amd64-fastdebug/lib/audio/soundbank.gm', needed by `copy-files'. Stop.

またsoundbank.gmが。fastbugというモジュール?も作られていて、そちら側でエラーとなっています。前回と同様にコピー。

# mkdir ../build/linux-amd64-fastdebug/lib/audio
# cp -ip /home/masayuki/install/soundbank.gm ../build/linux-amd64-fastdebug/lib/audio

../../../src/solaris/native/sun/awt/awt_TopLevel.c:25:31: error: X11/Xmu/Editres.h: No such file or directory

../../../src/solaris/native/sun/awt/awt_TopLevel.c:25:31: error: X11/Xmu/Editres.h: No such file or directory

libXmu-develをインストール

# yum install libXmu-devel -y

2012-01-22

[]Mockito+PowerMockでStringのmockを作る

文字列ハッシュ衝突時のテストケースを書くことになりましたが、いつも使っているMockitoはfinal classのmockを作ることができません。

調べてみたところ、PowerMockというモジュールを使うことによって、MockitoAPIを用いてfinal classのmockを作ることができることが分かり、試してみました。

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.powermock.api.mockito.PowerMockito.*; 

@RunWith(PowerMockRunner.class)
@PrepareForTest(String.class)
public class HashCollisionTest {

    @Test
    public void testHashCodeEquivalent(){
        String foo = "foo";
        String bar = "bar";

        assertThat(foo.hashCode(), is(not(bar.hashCode())));
        
        bar = spy(bar);
        when(bar.hashCode()).thenReturn(foo.hashCode());
        
        assertThat(bar, is("bar"));
        assertThat(foo.hashCode(), is(bar.hashCode()));
    }
}

Mockitoのみを用いた場合と異なるのは以下の3点です。

  • static importの変更
    • 「org.mockito.Mockito.*」から「org.powermock.api.mockito.PowerMockito.*」に
  • @RunWith(PowerMockRunner.class)を追加
  • @PrepareForTest([mockの対象となるclass])を追加

PowerMockは既存のmockモジュールを拡張するモジュールで、EasyMockMockitoに対応しています。final classのmockを作成する為に別のmockモジュールを導入しても良いですが、既にEasyMockMockitoを使ってテストコードを書いているのであれば、PowerMockを導入してみることをお勧めします。

参考

2012-01-13

[java]new Semaphore(0)

とあるコードの中に、以下のような記述がありました。

import java.util.concurrent.Semaphore;
...
    Semaphore permit = new Semaphore(0);

これまで、ミューテックスは1つの、セマフォは任意の数のクリティカルセクションへの進入を許可するもの、と覚えていました。ですので、初期値に「0」が指定されたセマフォがどういった意味を持つのか理解できず調べてみました。その中で、意外にセマフォ自体を理解できていないことが分かり、認識の弱かった部分を纏めてみました。

所有(アカウント)という概念がない

ミューテックスの場合は獲得したタスク(スレッド)がそのミューテックスを所有しますが、セマフォには所有されるという概念がありません。以下のコードを実行し、スレッドダンプを取ってみました。

import java.util.concurrent.Semaphore;

import org.junit.Test;

public class SemaphoreTest {

    @Test
    public void trySemaphore() throws InterruptedException{
        final Semaphore permit = new Semaphore(2);
        Runnable task = new Runnable(){
            public void run() {
                try {
                    permit.acquire();
                    Thread.sleep(Long.MAX_VALUE);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally{
                    permit.release();
                }
            }
        };
        
        Thread t1 = new Thread(task, "t1");
        Thread t2 = new Thread(task, "t2");
        Thread t3 = new Thread(task, "t3");
        
        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();
    }
}

上記コードの実行中にスレッドダンプを取ってみると、以下のようになります。

Full thread dump Java HotSpot(TM) Client VM (21.0-b17 mixed mode, sharing):

"t3" prio=6 tid=0x046cfc00 nid=0xb00 waiting on condition [0x04c9f000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x24b57978> (a java.util.concurrent.Semaphore$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(Unknown Source)
	at java.util.concurrent.Semaphore.acquire(Unknown Source)
	at net.wrap_trap.test.concurrent.SemaphoreTest$1.run(SemaphoreTest.java:15)
	at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
	- None

"t2" prio=6 tid=0x046cf400 nid=0x308 waiting on condition [0x0489f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at net.wrap_trap.test.concurrent.SemaphoreTest$1.run(SemaphoreTest.java:16)
	at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
	- None

"t1" prio=6 tid=0x046bf000 nid=0x860 waiting on condition [0x04c1f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at net.wrap_trap.test.concurrent.SemaphoreTest$1.run(SemaphoreTest.java:16)
	at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
	- None
...

スレッドt1,t2はセマフォを獲得し、t3はセマフォを待っている状態ですが、「Locked ownable synchronizers:」は何れもNoneとなっています。

同じことをReentrantLockで試してみます。

Full thread dump Java HotSpot(TM) Client VM (21.0-b17 mixed mode, sharing):

"t3" prio=6 tid=0x047e6800 nid=0xc2c waiting on condition [0x04aef000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x24b57f50> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(Unknown Source)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(Unknown Source)
	at java.util.concurrent.locks.ReentrantLock.lock(Unknown Source)
	at net.wrap_trap.test.concurrent.ReentrantLockTest$1.run(ReentrantLockTest.java:15)
	at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
	- None

"t2" prio=6 tid=0x047e3400 nid=0x1204 waiting on condition [0x0466f000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x24b57f50> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at java.util.concurrent.locks.LockSupport.park(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(Unknown Source)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(Unknown Source)
	at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(Unknown Source)
	at java.util.concurrent.locks.ReentrantLock.lock(Unknown Source)
	at net.wrap_trap.test.concurrent.ReentrantLockTest$1.run(ReentrantLockTest.java:15)
	at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
	- None

"t1" prio=6 tid=0x0478ec00 nid=0x5d4 waiting on condition [0x04c0f000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at net.wrap_trap.test.concurrent.ReentrantLockTest$1.run(ReentrantLockTest.java:16)
	at java.lang.Thread.run(Unknown Source)

   Locked ownable synchronizers:
	- <0x24b57f50> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
...

スレッドt1はロックを獲得しており、「Locked ownable synchronizers:」には獲得しているロックの情報が表示されます。セマフォは獲得したスレッドには関連せず、獲得できる数を管理しているだけです。

どのスレッドからも解放できる

セマフォは特定のスレッドと関連しない為、どのスレッドからでもセマフォを解放することができます。例えば以下のようなコード。

import java.util.Random;
import java.util.concurrent.Semaphore;

import org.junit.Test;

public class ConsumerProducer {

    static class Value {
        public Object value;
    }
    
    @Test
    public void tryProvide() throws InterruptedException {
        final Semaphore s = new Semaphore(0);
        final Value shared = new Value();
        final Random rand = new Random();
        
        class Consumer implements Runnable {
            public void run() {
                while(true) {
                    try {
                        s.acquire();
                        System.out.println("get: " + shared.value);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }            
        }
        
        class Producer implements Runnable {
            public void run() {
                while(true) {
                    shared.value = rand.nextInt();
                    System.out.println("put: " + shared.value);
                    s.release();
                    try {
                        Thread.sleep(3000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }                        
        }
    
        Thread t1 = new Thread(new Consumer());
        t1.start();
        
        Thread t2 = new Thread(new Producer());
        t2.start();
        
        t1.join();
        t2.join();
    }
}

Customerのスレッドセマフォを獲得するだけ、Producerのスレッドセマフォを解放するだけです。つまり、セマフォを獲得したスレッドとは異なるスレッドセマフォを解放しています。上記コードでは「new Semaphore(0);」としていて、最初にCustomerのスレッドセマフォを獲得しようとするのですが、Producerが値を準備してセマフォを解放するまでセマフォを獲得することができません。

もちろんObject#wait、Object#notifyを使って同じようなことはできますが、synchronizedブロックを必要としないセマフォの方が書き方としてはスマートだと思います。但し、前記のとおりセマフォには所有という考え方が無い為、例えばセマフォの獲得で長い時間待たされた場合、どのスレッドセマフォを解放できていないのかスレッドダンプで確認しようとしても、セマフォを獲得しているスレッドを特定することはできません。この点は注意が必要です。