かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

lookupしてみる

英語がさっぱりできないのにRich Client Programming PLUGGING INTO THE NETBEANS PLATFORMを買ってしまった。

とりあえず、NetBeansのモジュールのプログラムを覗いてると良く出てくるLookupについて。
Lookupの機能自体はすごく単純でjarを1つクラスパスに追加するだけで使えるようになるらしい。

そのjarとは、NB_HOME/platform7/lib/org-openide-util.jarらしい。
(NetBeans6の場合のパスです。NetBeans5系は多分platform6とかのはず)

Javaアプリケーションプロジェクト作成

とりあえず、さくっとお試しするためにJavaプリケーションのプロジェクトを作成。
ライブラリにorg-openide-util.jarを追加する。

Mainクラスに下のようなコードを書いて実行できることを確認!

package okazuki.lookup;

import org.openide.util.Lookup;

public class Main {
    
    public static void main(String[] args) {
        Lookup lookup = Lookup.getDefault();
        System.out.println(lookup);
    }
    
}

実行結果はこんな感じ。

ProxyLookup(class=class org.openide.util.Lookup$DefLookup)->[MetaInfServicesLookup[sun.misc.Launcher$AppClassLoader@a90653], SimpleLookup[sun.misc.Launcher$AppClassLoader@a90653]]

ちゃんと何か取れとるらしい。

そもそもLookupって?

何かしらんがオブジェクトが欲しいときに探してくれるっぽい。
たとえばLookupが中に持ってるオブジェクト全部ほしいときは下のようになる。
NetBeans6と、それ以前では書き方が違うみたいなので2通りかいてます。

package okazuki.lookup;

import java.util.Collection;
import org.openide.util.Lookup;

public class Main {
    
    public static void main(String[] args) {
        Lookup lookup = Lookup.getDefault();
        
        {
            // NetBeans6
            Collection objs = lookup.lookupAll(Object.class);
            for (Object obj : objs) {
                System.out.println(obj);
            }
        }
        
        {
            //NetBeans5.x
            Lookup.Template template = new Lookup.Template(Object.class);
            Lookup.Result results = lookup.lookup(template);
            Collection objs = results.allInstances();
            for (Object obj : objs) {
                System.out.println(obj);
            }
        }
        
    }
}

実行するとこんな感じ。

sun.misc.Launcher$AppClassLoader@a90653
sun.misc.Launcher$AppClassLoader@a90653

どうやら1つしかオブジェクト持ってないらしい。
この中に俺様オブジェクトを追加するには、META-INF/services以下にファイルを置くとできるって書いてある。


その前に登録するためのクラスを定義!!
お決まりのハローワールドです

// Greeter.java
package okazuki.lookup;

public interface Greeter {
    void greet();
}

--------------------------------
// GreeterImpl.java
package okazuki.lookup;

public class GreeterImpl implements Greeter {
    public void greet() {
        System.out.println("Hello world");
    }
}

ソースフォルダ以下にMETA-INF/services/okazuki.lookup.Greeterという名前のファイルを作る。
中身は下のとおり。

okazuki.lookup.GreeterImpl

そしてメインメソッドを下のように変更する。
取得するクラスをGreeterにしている。

package okazuki.lookup;

import java.util.Collection;
import org.openide.util.Lookup;

public class Main {
    
    public static void main(String[] args) {
        Lookup lookup = Lookup.getDefault();
        
        {
            // NetBeans6
            Collection<? extends Greeter> objs = lookup.lookupAll(Greeter.class);
            for (Greeter obj : objs) {
                obj.greet();
            }
        }
        
        {
            //NetBeans5.x
            Lookup.Template<Greeter> template = new Lookup.Template<Greeter>(Greeter.class);
            Lookup.Result<Greeter> results = lookup.lookup(template);
            Collection<? extends Greeter> objs = results.allInstances();
            for (Greeter obj : objs) {
                obj.greet();
            }
        }
        
    }
}

実行するとちゃんとHello worldが出てる。

Hello world
Hello world

んじゃ差し替えてみよう

GreeterImpl以外のファイルも付け加えてみよう。
たとえば、GreeterImpl2というクラスを作ったとする。
中身は下のとおり

package okazuki.lookup;

public class GreeterImpl2 implements Greeter {
    public void greet() {
        System.out.println("こんにちは世界");
    }
}

そして、META-INF/services/okazuki.lookup.Greeterの中身にGreeterImpl2を付け加える

okazuki.lookup.GreeterImpl
okazuki.lookup.GreeterImpl2

この状態でリビルドして実行すると…

Hello world
こんにちは世界
Hello world
こんにちは世界

おぉ〜!ちゃんと出た!!!
DIコンテナあたりを使うともっと高度な事ができるけど、そんな機能イラネってときには使えるかな?

ちなみにインスタンスは何個作られるの?

GreeterImplとGreeterImpl2のコンストラクタにコードを仕込んで試してみた。
実行結果は下のようになった。

GreeterImpl::GreeterImpl
GreeterImpl2::GreeterImpl2
Hello world
こんにちは世界
Hello world
こんにちは世界

2回lookupしてるにも関わらず、1個だけインスタンスが作られてる。