bm-asn1 (0.24) | 2012-10-12 14:45 |
BM ASN.1の簡単な使い方を、以下のASN.1定義を例に解説します。
FrightStatusTypes DEFINITIONS AUTOMATIC TAGS ::= BEGIN FrightNumber ::= PrintableString Airport ::= ENUMERATED { tokyo(0), osaka(1), nagoya(2), fukuoka(3) } FrightInformation ::= SEQUENCE { airport Airport, scheduled UTCTime, actual UTCTime OPTIONAL } Status ::= CHOICE { onTime NULL, delay INTEGER } FrightStatus ::= [APPLICATION 0] IMPLICIT SEQUENCE { frightNo FrightNumber, departure [0] IMPLICIT FrightInformation, arrival [1] IMPLICIT FrightInformation, status [2] Status DEFAULT onTime:NULL } AllFrights ::= [APPLICATION 1] SEQUENCE OF FrightStatus END
各ASN.1型は、ASN1Typeクラスを継承したクラスとして定義します。ただし、ユーザー定義のASN.1型クラスがASN1Typeクラスを直接に継承することはなく、jp.bitmeister.asn1.type.builtinパッケージとjp.bitmeister.asn1.type.usefulパッケージに含まれているBuilt-In ASN.1型クラスを使用します。これらのASN.1型クラスには、jp.bitmeister.asn1.type.annotationパッケージに含まれているアノテーションで、ASN.1型に関連する各種の情報を付与することができます。
public class FrightNumber extends PrintableString {}
ENUMERATED型の要素は、@ASN1Enumerationを付与したpublic static finalフィールドとして定義します。
public class Airport extends ENUMERATED { @ASN1Enumeration public static final int tokyo = 0; @ASN1Enumeration public static final int osaka = 1; @ASN1Enumeration public static final int nagoya = 2; @ASN1Enumeration public static final int fukuoka = 3; }
SEQUENCE型の要素は、SEQUENCE型を継承したクラスのフィールドに@ASN1Elementアノテーションを付与して定義します。@ASN1ElementのvalueパラメータはASN.1定義上での要素の登場順です。先頭の要素を0として、以下順番に番号付けします。SEQUENCE型の要素にできるのは、アクセス指定がpublicの変更可能なインスタンスの(つまり、staticおよびfinalではない)フィールドだけです。
public class FrightInformation extends SEQUENCE { @ASN1Element(0) public Airport airport; @ASN1Element(1) public UTCTime scheduled; @ASN1Element(value = 2, optional = true) public UTCTime actual; }
CHOICE型の要素は、CHOICE型を継承したクラスのフィールドに@ASN1Alternativeアノテーションを付与して定義します。@ASN1Alternativeのvalueパラメータは、@ASN1Elementと同様にASN.1定義上での要素の登場順です。
public class Status extends CHOICE { @ASN1Alternative(0) public NULL onTime; @ASN1Alternative(1) public INTEGER delay; }
ASN.1タグは、@ASN1Tagアノテーションで付与します。構造型の要素にデフォルト値がある場合は、@ASN1ElementアノテーションのhasDefaultパラメータをtrueに設定し、フィールドをデフォルト値で初期化します。
@ASN1Tag(value = 0, tagClass = ASN1TagClass.APPLICATION, tagMode = ASN1TagMode.IMPLICIT) public class FrightStatus extends SEQUENCE { @ASN1Element(0) public FrightNumber frightNo; @ASN1Element(1) @ASN1Tag(value = 0, tagMode = ASN1TagMode.IMPLICIT) public Information departure; @ASN1Element(2) @ASN1Tag(value = 1, tagMode = ASN1TagMode.IMPLICIT) public Information arrival; @ASN1Element(value = 3, hasDefault = true) @ASN1Tag(value = 2, tagMode = ASN1TagMode.EXPLICIT) public Status status = new Status(ASN1TagClass.CONTEXT_SPECIFIC, 0, new NULL()); }
コレクション型(SET OF, SEQUENCE OF)を定義する場合は、ジェネリックの型パラメータにコレクションの要素となる型を設定します。また、コンストラクタで要素型のクラスオブジェクトを、親クラスのコンストラクタに渡します。
public static class AllFrights extends SEQUENCE_OF<FrightStatus> { public AllFrights() { super(FrightStatus.class); } }
ASN.1型クラスに引数付きのコンストラクタを定義しておくと、コード内でデータの初期化を簡単に行うことができます。引数付きのコンストラクタを定義する場合は、必ず引数無しのコンストラクタも定義します。
public class FrightNumber extends PrintableString { public FrightNumber() {} public FrightNumber(String value) { super(value); } } public class FrightStatus extends SEQUENCE { ... public FrightStatus() {} public FrightStatus(FrightNumber frightNo, Information departure, Information arrival, Status status) { this.frightNo = frightNo; this.departure = departure; this.arrival = arrival; this.status = status; } }
ASN.1モジュールは、ASN1Moduleクラスを継承したクラスとして定義します。ASN.1モジュールクラスのメンバークラスとしてASN.1型クラスを定義すると、それらのクラスはそのモジュールに含まれるASN.1型として扱われます。また、ASN.1モジュールクラスのメンバークラスではないASN.1型クラスを、アノテーションによってモジュールに含めることもできます。ASN.1型クラスは、必ずいずれかのASN.1モジュールに含めなければなりません。
モジュール宣言のタグデフォルト指定は、@ASN1ModuleTagsアノテーションで付与します。モジュールに含まれる各ASN.1型クラスは、必ずpublic staticクラスとして定義します。
@ASN1ModuleTags(ASN1TagDefault.AUTOMATIC_TAGS) public class FrightStatusTypes extends ASN1Module { public static class FrightNumber extends PrintableString { public FrightNumber() {} public FrightNumber(String value) { super(value); } } public static class Airport extends ENUMERATED { @ASN1Enumeration public static final int tokyo = 0; ...
ASN.1モジュールクラスのメンバークラス以外のASN.1型クラスをモジュールに含める場合は、@ASN1ModuleRefアノテーションを使用します。
@ASN1ModuleRef(FrightStatusTypes.class) @ASN1Tag(value = 0, tagClass = ASN1TagClass.APPLICATION, tagMode = ASN1TagMode.IMPLICIT) public class FrightStatus extends SEQUENCE { @ASN1Element(0) public FrightNumber frightNo; ...
@ASN1ModuleRefでモジュールを指定した場合、ASN.1モジュールクラス側では@ASN1DefinedTypesでそのクラスをモジュールに登録する必要があります。
@ASN1ModuleTags(ASN1DefaultTags.IMPLICIT_TAGS) @ASN1DefinedTypes(FrightStatus.class) public class FrightStatusTypes extends ASN1Module { public FrightStatusTypes() { register(FrightStatus.class); } ...
BOOLEANやINTEGERなどのプリミティブ型への値の設定は、コンストラクタまたはsetメソッドで行います。設定された値はvalue()メソッドで取り出すことができます。また、それぞれのプリミティブ型に固有のメソッドも実装されています。
BOOLEAN bool = new BOOLEAN(true); if (bool.value()) { bool.set(false); } INTEGER number = new INTEGER(100); if (number.isIntValue()) { int value = number.intValue(); }
ユーザー定義の単純型への値の設定は、setメソッドで行います。
FrightNumber frightNo = new FrightNumber(); frightNo.set("JP041");
初期化用のコンストラクタを定義すると、コードを短縮できます。
FrightNumber frightNo = new FrightNumber("JP041");
構造型への値の設定は、フィールドへのASN.1型データの代入で行います。
FrightStatus status = new FrightStatus(); status.frightNo = new FrightNumber("JP041"); status.departure = new Information(); status.departure.airport = new Airport(Airport.tokyo); ...
CHOICE型の場合も、同じくフィールドへのデータ代入で値を設定できます。値が設定されているフィールドが、そのCHOICE型データの選択値として扱われます。フィールドの2つ以上に値が設定されている場合の動作は不定です。clearメソッドを使用すると、現在の選択値をクリアすることができます。
Status status = new Status(); status.onTime = new NULL(); status.clear(); status.delay = new INTEGER(10);
各ASN.1型クラスに初期化用のコンストラクタを定義している場合は、以下の形で初期化が行えます。
FrightStatus status = new FrightStatus( new FrightNumber("JP041"), new Information( new Airport(Airport.tokyo), new UTCTime("110627073000"), new UTCTime("110627073500") ), new Information( new Airport(Airport.fukuoka), new UTCTime("110627090000"), null ), new Status( ASN1TagClass.CONTEXT_SPECIFIC, 1, new INTEGER(5)) );
現バージョンのBM ASN.1は、BER(Basic Encoding Rules)およびDER(Distinguished Encoding Rules)エンコード、XER(XML Encoding Rules)エンコードとBERおよびXERデコードに対応しています。DERエンコードの結果のbyte列も、BerDecoderでデコードすることができます。BERとDERの違いについては、こちらを参照してください。
BerEncoder、DerEncoderおよびXerEncoderのencodeメソッドはASN.1型データを引数にとり、エンコードした結果をOutputStreamに書き込みます。
ByteArrayOutputStream bo = new ByteArrayOutputStream(); DerEncoder enc = new DerEncoder(bo); enc.encode(status);
BerDecoderおよびXerDecoderは、InputStreamからバイト列を読み取ってASN.1型データにデコードします。BerDecoderのdecodeメソッドにASN.1型クラスオブジェクトを渡すと、BerDecoderはデコード結果をそのASN.1型クラスのインスタンスに設定して返します。バイト列からデコードされたASN.1タグまたはXMLタグ名が引数のASN.1型と一致していない場合は、例外が発生します。
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); BerDecoder dec = new BerDecoder(bi); FrightStatus result = dec.decode(FrightStatus.class);
引数なしのdecodeメソッドが呼ばれると、XerDecoderおよびBerDecoderはバイト列からASN.1タグまたはXMLタグ名をデコードし、デコーダーの生成時に指定されたASN.1モジュール型に登録されている型の中からデコードされたタグに一致するASN.1型クラスを探し出してデコードを行います。
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); BerDecoder dec = new BerDecoder(FrightStatusTypes.class, bi); ASN1Type result = dec.decode();
BM ASN.1の全てのASN.1型クラスはtoStringメソッドを実装しているので、System.out.println()メソッドなどにASN.1型データを渡すことで、データ内容を文字列化して表示することができます。
次のコードを実行すると、
System.out.println(result);
コンソールに以下の出力を得ることができます。
FrightStatus ::= [APPLICATION 0] IMPLICIT SEQUENCE { frightNo FrightNumber ::= PrintableString "JP041", departure [0] IMPLICIT Information ::= SEQUENCE { airport [0] IMPLICIT Airport ::= ENUMERATED tokyo(0), scheduled [1] IMPLICIT UTCTime "110626223000Z" [2011/06/27 07:30:00.0 JST], actual [2] IMPLICIT UTCTime "110626223500Z" [2011/06/27 07:35:00.0 JST] OPTIONAL }, arrival [1] IMPLICIT Information ::= SEQUENCE { airport [0] IMPLICIT Airport ::= ENUMERATED fukuoka(3), scheduled [1] IMPLICIT UTCTime "110627000000Z" [2011/06/27 09:00:00.0 JST], actual [2] IMPLICIT <No value> OPTIONAL }, status [2] Status ::= CHOICE { delay [1] IMPLICIT INTEGER (5) } }