Bootstrapping the Registry |
アプリケーションのモジュール・デプロイ記述子に定義されているコンフィグ・ポイントやサービスにアクセスする事が可能となる前に、レジストリが必要となります:ここでは、どのようにレジストリを組み立てるかについて記述していきます。
ここでキーとなるクラスは、RegistryBuilderです。これはに、モジュール・デプロイ記述子の場所確認と解析を行うコード、及び、バインディングデータからレジストリを組み立てる為のコードが含まれています。記述子は全てクラスパスの中から探し出されます:アプリケーションのJAR 群にパッケージ化された記述子と共に、HiveMind 自身の記述子をも含んでいます
どのように全て成り立つか調べてみます。プロジェクトのレイアウトは以下のとおりです。
サービス・インタフェースと実装
まず始めのステップで、サービス・インタフェースを定義します:
package hivemind.examples; public interface Adder { public int add(int arg0, int arg1); }
次に、このサービス用の実装が必要になります:
package hivemind.examples.impl; import hivemind.examples.Adder; public class AdderImpl implements Adder { public int add(int arg0, int arg1) { return arg0 + arg1; } }
この例では、さらに3つのインタフェースとそれに対応する実装が追加されています:Subtracter・Multiplier・Dividerそして最後に全部を纏めるCalculatorです:
package hivemind.examples; public interface Calculator extends Adder, Subtracter, Multiplier, Divider { }
このCalculatorの実装は結合の為の配線工事が必要です:他の4つのサービス(Adder・, Substracter・Multiplier・Divider)が差し込まれます:
package hivemind.examples.impl; import hivemind.examples.Adder; import hivemind.examples.Calculator; import hivemind.examples.Divider; import hivemind.examples.Multiplier; import hivemind.examples.Subtracter; public class CalculatorImpl implements Calculator { private Adder _adder; private Subtracter _subtracter; private Multiplier _multiplier; private Divider _divider; public void setAdder(Adder adder) { _adder = adder; } public void setDivider(Divider divider) { _divider = divider; } public void setMultiplier(Multiplier multiplier) { _multiplier = multiplier; } public void setSubtracter(Subtracter subtracter) { _subtracter = subtracter; } public int add(int arg0, int arg1) { return _adder.add(arg0, arg1); } public int subtract(int arg0, int arg1) { return _subtracter.subtract(arg0, arg1); } public int multiply(int arg0, int arg1) { return _multiplier.multiply(arg0, arg1); } public int divide(int arg0, int arg1) { return _divider.divide(arg0, arg1); } }
モジュール・デプロイ記述子
最後に、HiveMind モジュール・デプロイ記述子であるhivemodule.xml が必要となります。
このモジュール記述子は、インタフェースと実装に関して各々のサービスを作成します。
<?xml version="1.0"?> <module id="hivemind.examples" version="1.0.0"> <service-point id="Adder" interface="hivemind.examples.Adder"> <create-instance class="hivemind.examples.impl.AdderImpl"/> <interceptor service-id="hivemind.LoggingInterceptor"/> </service-point> <service-point id="Subtracter" interface="hivemind.examples.impl.SubtracterImpl"> <create-instance class="hivemind.examples.impl.AdderImpl"/> <interceptor service-id="hivemind.LoggingInterceptor"/> </service-point> <service-point id="Multiplier" interface="hivemind.examples.Multipler"> <create-instance class="hivemind.examples.impl.MultiplierImpl"/> <interceptor service-id="hivemind.LoggingInterceptor"/> </service-point> <service-point id="Calculator" interface="hivemind.examples.Calculator"> <invoke-factory> <!-- Autowires service properties based on interface! --> <constuct class="hivemind.examples.impl.CalculatorImpl"/> </invoke-factory> <interceptor service-id="hivemind.LoggingInterceptor"/> </service-point> </module>
ここで、module id hivemind.examples がパッケージの名前と一致するよう選びましたが、絶対的に必須であるわけではありません。
興味深い点は、hivemind.BuilderFactory をCalculatorサービスのコンストラクトのために使い、他の4つのサービスとつなぎ合わせるためにも使っている、という点です。
レジストリのビルド
あらゆるサービス(あるいはコンフィグ・ポイント)にアクセスできるようになる前に、Registry がコンストラクトされなければなりません:Registry は、HiveMind によって管理されるサービスや設定のゲートウェイです。
package hivemind.examples; import org.apache.hivemind.Registry; import org.apache.hivemind.impl.RegistryBuilder; public class Main { public static void main(String[] args) { int arg0 = Integer.parseInt(args[0]); int arg1 = Integer.parseInt(args[1]); Registry registry = RegistryBuilder.constructDefaultRegistry(); Calculator c = (Calculator) registry.getService(Calculator.class); System.out.println("Inputs " + arg0 + " and " + arg1); System.out.println("Add : " + c.add(arg0, arg1)); System.out.println("Subtract: " + c.subtract(arg0, arg1)); System.out.println("Multiply: " + c.multiply(arg0, arg1)); System.out.println("Divide : " + c.divide(arg0, arg1)); } }
RegistryBuilder には、Registry をコンストラクトする為の、殆どの場面に見合った static メソッドが含まれています。
そして、レジストリを有する事になりましたので、Calculator インタフェースを Calculator の実装を探すためのキーとして使う事が出来るようになったわけです。実際のアプリケーションでは、同じインタフェースを実装する複数のサービスがある事がよくありますので、完全修辞のサービスIDを指定する事も必要となるでしょう。
Calculator サービスへのリファレンスを使う事で、漸くadd()・subtract()・multiply()・divide()メソッドを invoke する事が出来るようになりました。
サンプルのビルド
Ant を使ってサンプルをビルド・実行するのはいともたやすい事です:詳細は、build.xmlの中に全てあります:
<?xml version="1.0"?> <project name="HiveMind Adder Example" default="jar"> <property name="java.src.dir" value="src/java"/> <property name="test.src.dir" value="src/test"/> <property name="conf.dir" value="src/conf"/> <property name="descriptor.dir" value="src/descriptor"/> <property name="target.dir" value="target"/> <property name="classes.dir" value="${target.dir}/classes"/> <property name="test.classes.dir" value="${target.dir}/test-classes"/> <property name="example.jar" value="${target.dir}/hivemind-examples.jar"/> <property name="lib.dir" value="lib"/> <property name="junit.temp.dir" value="${target.dir}/junit-temp"/> <property name="junit.reports.dir" value="${target.dir}/junit-reports"/> <path id="build.class.path"> <fileset dir="${lib.dir}"> <include name="*.jar"/> </fileset> </path> <path id="test.build.class.path"> <path refid="build.class.path"/> <path location="${classes.dir}"/> </path> <path id="run.class.path"> <path refid="build.class.path"/> <pathelement location="${classes.dir}"/> <pathelement location="${descriptor.dir}"/> <pathelement location="${conf.dir}"/> </path> <path id="test.run.class.path"> <path refid="run.class.path"/> <path location="${test.classes.dir}"/> </path> <target name="clean" description="Delete all derived files."> <delete dir="${target.dir}" quiet="true"/> </target> <target name="compile" description="Compile all Java code."> <mkdir dir="${classes.dir}"/> <javac srcdir="${java.src.dir}" destdir="${classes.dir}" classpathref="build.class.path"/> </target> <target name="compile-tests" description="Compile test classes." depends="compile"> <mkdir dir="${test.classes.dir}"/> <javac srcdir="${test.src.dir}" destdir="${test.classes.dir}" classpathref="test.build.class.path"/> </target> <target name="run-tests" description="Run unit tests." depends="compile-tests"> <mkdir dir="${junit.temp.dir}"/> <mkdir dir="${junit.reports.dir}"/> <junit haltonfailure="off" failureproperty="junit-failure" tempdir="${junit.temp.dir}"> <classpath refid="test.run.class.path"/> <formatter type="xml"/> <formatter type="plain"/> <formatter type="brief" usefile="false"/> <batchtest todir="${junit.reports.dir}"> <fileset dir="${test.classes.dir}"> <include name="**/Test*.class"/> </fileset> </batchtest> </junit> <fail if="junit-failure" message="Some tests failed."/> </target> <target name="jar" description="Construct the JAR file." depends="compile,run-tests"> <jar destfile="${example.jar}"> <fileset dir="${classes.dir}"/> <fileset dir="${descriptor.dir}"/> </jar> </target> <target name="run" depends="compile" description="Run the Adder service."> <java classname="hivemind.examples.Main" classpathref="run.class.path" fork="true"> <arg value="11"/> <arg value="23"/> </java> </target> </project>
JAR の中に HiveMind モジュール・デプロイ記述子とクラスファイルの両方をパッケージかして入れておく、というが重要なポイントです。
その他、唯一変わった点としては、src/conf をランタイムのクラスパスに追加する、という点が挙げられるでしょうか:log4j.properties という設定ファイルを追加するためです:さもないと、Log4J が設定ミスに関するコンソールのエラーメッセージを吐き出すでしょう。
サンプルの実行
bash-2.05b$ ant run Buildfile: build.xml compile: [mkdir] Created dir: C:\workspace\hivemind-example\target\classes [javac] Compiling 15 source files to C:\workspace\hivemind-example\target\classes run: [java] Inputs 11 and 23 [java] Add : 34 [java] Subtract: -12 [java] Multiply: 253 [java] Divide : 0 BUILD SUCCESSFUL Total time: 3 seconds