無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

JRubyでアノテーションを処理する。

JRuby1.1ではクラスに設定されたアノテーションを直接参照する方法が用意されていない様子。includeしたJavaクラスの「java_class」関数で「Java::JavaClass」というClassのラッパが返されますが、JavaのClassが持つget_annotationとかの関数は持っていません・・・orz。

ということで、クラスから取得するのはあきらめ、別途ユーティリティを作成して、アノテーションにアクセスできるようにしてみました。

  1. (Rubyで)Moduleに関数annotation,annotationsを追加してアノテーションをとれるようにする。
  2. 関数内では、自身を引数としてJavaのユーティリティ関数を呼び出す。
  3. ユーティリティ関数は、引数でクラスを受け取ってそのアノテーションを返す。

という仕組み。関数定義さえやっておけば、以下のような感じで簡単にアノテーションにアクセスできます。

include_class '<javaClass>'
<javaClass>.annotations {|a| 
  puts a
}

実装

まず、アノテーションを返すJava側のユーティリティです。ただのラッパーですね。

package annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;

public class AnnotationUtil {

    public static <T extends Annotation> T getAnnotation(
        AnnotatedElement elm, java.lang.Class<T> annotationClass ) {
        return elm.getAnnotation( annotationClass );
    }

    public static Annotation[] getAnnotations( AnnotatedElement elm ) {
        return elm.getAnnotations();
    }
}

Ruby側の関数定義。Moduleに関数を追加しています。

require 'java'

module Module
  include_class 'annotation.AnnotationUtil'

  # クラスを指定してアノテーションを取得する。
  def annotation( annotation_type )
    #if self.method_defined?( "java_class" ) # method_defined?はなぜか動作しない・・・。
    if self.methods.include?("java_class")
      return AnnotationUtil.getAnnotation(self.java_class, annotation_type.java_class)
    else
      return nil
    end
  end

  # クラスに割り当てられたアノテーションを取得する。
  def annotations( )
    if self.methods.include?("java_class")
      return AnnotationUtil.getAnnotations(self.java_class)
    else
      return []
    end
  end
end

以上。

サンプル

次のようなアノテーションがあって、

package annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * サービスを示すアノテーション
 *
 * @version $Revision:$
 * @author  $Author:$
 */
@Target({ElementType.TYPE})
@Retention( RetentionPolicy.RUNTIME )
public @interface Service {

    /**
     * サービス名
     * @return サービス名
     */
    String serviceName();
}

「annotation.Annotated」クラスを修飾している場合、

package annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Service(serviceName="Test")
public interface Annotated {
    void method1();
    void method2();
}

こんな感じでRubyからアノテーションを参照できます。

module Test

# annotaionパッケージを取込
include_package 'annotation'

# Serviceアノテーションを取得
puts Annotated.annotation(Service)

# すべてのアノテーションを列挙
Annotated.annotations.each {|a|
  puts a.serviceName
}

end

実行結果です。※「annotation.AnnotationUtil」をJRubyのクラスパスに含めるのをお忘れ無く。

@annotation.Service(serviceName=Test)
Test

あとはこれとERBを組み合わせれば注釈処理プログラムのできあがり! .. といいたいところですが、以下の弱点があります。

とはいえ、javacやaptを使うのは面倒そうなので、さくっと作れるメリットはあるはず。いつか使ってやる!

なお、上のコードではクラスに設定されたアノテーションの取得しかサポートしていないのであしからず。メソッドやフィールドのアノテーション取得は未調査です。