2010年12月23日木曜日

5.2 サービス (Services)

OSGi サービスプラットフォームでは, バンドルは, 共用サービスレジストリーから取得可能な協調的サービス群の周辺に構築される. そのような OSGi サービスは, 意味論的にはサービスインターフェースにより定義され, サービスオブジェクトとして実装される.

In the OSGi Service Platform, bundles are built around a set of cooperating services available from a shared service registry. Such an OSGi service is defined semantically by its service interface and implemented as a service object.

サービスインターフェースは, できる限り実装の詳細を伴わない形で指定されるべきである. OSGi では, 共通の必要性を考慮して, 既に多くのサービスインターフェースを定義しており, また将来にわたっても定義していく.

The service interface should be specified with as few implementation details as possible. OSGi has specified many service interfaces for common needs and will specify more in the future.

サービスオブジェクトは, バンドルにより所有され, バンドル内で実行される. この種のバンドルは, そのサービス機能をフレームワークの管理下にある他のバンドルが利用できるようにするため, サービスオブジェクトをフレームワークサービスレジストリーに登録しなければならない.

The service object is owned by, and runs within, a bundle. This bundle must register the service object with the Framework service registry so that the service's functionality is available to other bundles under control of the Framework.

サービスを所有しているバンドルとそのサービスを使用しているバンドル間の依存関係はフレームワークにより管理される. 例えば, バンドルが止まると, そのバンドルがフレームワークに登録したサービスは全て, 自動的に登録解除されなければならない.

Dependencies between the bundle owning the service and the bundles using it are managed by the Framework. For exapmle, when a bundle is stopped, all the services registered with the Framework by that bundle must be automatically unregistered.

フレームワークは, サービスとサービスオブジェクトの対応を取り, バンドルが必要とするサービスを要求するのを可能とする単純だが協力な問い合わせ機構を提供する. フレームワークはまた, 登録・変更・登録解除されたサービスに関するイベントをバンドルが受け取れるようにするためのイベント機構も提供する.

The Framework maps services to their underlying service objects, and provides a simple but powerful query mechanism that enables a bundle to request the services it needs. The Framework also provides an event mechanism so that bundles can receive events of services that are registered, modified, or unregistered.


5.2.1 サービスリファレンス (Service References)
5.2.2 サービスインターフェース (Service Interfaces)
5.2.3 サービスの登録 (Registering Services)
5.2.4 ServiceRegistration オブジェクトの早期必要性 (Early Need for ServiceRegistration Object)
5.2.5 サービスプロパティー (Service Properties)
5.2.6 永続識別子 (Persistent Identifier (PID))
5.2.7 サービスの特定 (Locating Services)
5.2.8 サービスプロパティーの取得 (Getting Service Properties)
5.2.9 サービスオブジェクトの取得 (Getting Service Objects)
5.2.10 サービスに関する情報 (Information About Services)
5.2.11 サービス例外 (Service Exceptions)
5.2.12 サービスと並行処理 (Services and Concurrency)

Dalvik インタプリター生成に関する README (dalvik/vm/mterp/README.txt)

Dalvik "mterp" README

NOTE: 再ビルドの方法はこのファイルの末尾にあります.

NOTE: Find rebuilding instructions at the bottom of this file.


=== 概要 ===

==== Overview ====

これは Dalvik インタプリターのソースコードです. 元のバージョンの中核部分は単一の C 関数として実装されていましたが, パフォーマンスを改善するためにアセンブリで書きなおしました. 今回の, および将来のアセンブリ移植を容易にし, また不具合が混入しにくくするため, プラットフォーム固有コードの開発を一度に一オペコードずつとするためのモジュール的手法を採用しました.

This is the source code for the Dalvik interpreter.  The core of the original version was implemented as a single C function, but to improve performance we rewrote it in assembly.  To make this and future assembly ports easier and less error-prone, we used a modular approach that allows development of platform-specific code one opcode at a time.

元のオール・イン・ワン関数の C バージョンは「可搬性のある」インタプリターとしてまだ存在しており, プラットフォーム固有バージョンのインタプリターを生成するのと同じソースとツールを使用して生成されます. 可搬インタプリターの一形態として, プロファイリングとデバッギングをサポートするものがあり, プラットフォーム用に最適化された実装がある場合でも, この形態は含まれます.

The original all-in-one-function C version still exists as the "portable" interpreter, and is generated using the same sources and tools that generate the platform-specific versions.  One form of the portable interpreter includes support for profiling and debugging features, and is included even if we have a platform-optimized implementation.

コンフィギュレーションそれぞれに対して, ソースコードの生成方法を制御するための config-* というファイルが存在します. ソースは out ディレクトリに書き込まれ, Android ビルドシステムがそれらのファイルを利用します.

Every configuration has a "config-*" file that controls how the sources are generated.  The sources are written into the "out" directory, where they are picked up by the Android build system.

インタプリターを詳しく知るための一番の方法は, (例えば) armv5te 内にあるような種々のコンポーネントの切れ端を見ていくことではなく, out/InterpC-portstd.c といった "out" ディレクトリに生成されたファイルを見ることです.

The best way to become familiar with the interpreter is to look at the generated files in the "out" directory, such as out/InterpC-portstd.c, rather than trying to look at the various component pieces in (say) armv5te.


==== プラットフォーム別ソース生成 ====

==== Platform-specific source generation ====

アーキテクチャー固有の config ファイルにより, 二つの生成ファイル (InterpC[arch].c と InterpAsm-[arch].S) の内容が決定されます. 目標は, 初期段階の開発とテストの期間中に C とアセンブリソースを簡単に切り替えられるようにすることであり, また, アーキテクチャ固有バージョンの機械語を使用する方法を提供することです (例えば ARMv6 で PLD 命令を使用したり, ARMv4T で CLZ 命令を避けたり, など).

The architecture-specific config files determine what goes into two generated output files (InterpC-[arch].c, InterpAsm-[arch].S).  The goal is to make it easy to swap C and assembly sources during initial development and testing, and to provide a way to use architecture-specific versions of some operations (e.g. making use of PLD instructions on ARMv6 or avoiding CLZ on ARMv4T).

インタプリターの機械語に関して, 二つの基本的な前提があります.

Two basic assumptions are made about the operation of the interpreter:

  • アセンブリバージョンでは各インストラクションに対して (例えば 64 バイトなどの) 固定サイズが使用される. 「オーバーフロー」コードは末尾に追加される.
  • C の実装のほうが望ましい場合は, アセンブリバージョンは全てのローカル状態を「仲介」構造体にパックし, それを C 関数に渡す. 状態の更新は「仲介」構造体から取り出されてリターンされる.
  • The assembly version uses fixed-size areas for each instruction (e.g. 64 bytes).  "Overflow" code is tacked on to the end.
  • When a C implementation is desired, the assembly version packs all local state into a "glue" struct, and passes that into the C function. Updates to the state are pulled out of the "glue" on return.
「arch」の値は, 共通のプログラミング特性を持つアーキテクチャーを示していて, armv5te は全ての ARMv5TE CPU に対して機能しますが, 後方互換性や前方互換性はないかもしれません. (armv5t3-eabi といった, ABI モデルも同様に指定したいと思う「かも」しれませんが, 現在は記述が冗長になるだけで値は伴っていません.

The "arch" value should indicate an architecture family with common programming characteristics, so "armv5te" would work for all ARMv5TE CPUs, but might not be backward- or forward-compatible.  (We *might* want to specify the ABI model as well, e.g. "armv5te-eabi", but currently that adds verbosity without value.)


==== config ファイル・フォーマット ====

==== Config file format ====

config ファイルは先頭から下へと解析されます. ファイル内の各行は, 空行, ('#' ではじまる) コメント, もしくはコマンドです.

The config files are parsed from top to bottom.  Each line in the file may be blank, hold a comment (line starts with '#'), or be a command.

コマンドは次の通りです.

The commands are:

  handler-size [バイト数]

    アセンブリの範囲をバイト数で指定します. ほとんどのプラットフォームに
    おいて, 2 の累乗である必要があります.

  handler-size [bytes]

    Specify the size of the assembly region, in bytes.  On most platforms
    this will need to be a power of 2.

  import [ファイル名]

    指定されたファイルの内容が全て即座に読み込まれます. 置換処理は行われ
    ません. ".c" および ".h" ファイルは C 出力ファイルにコピーされ, ".S"
    ファイルはアセンブリ出力ファイルにコピーされます.

  import [filename]

    The specified file is included immediately, in its entirety.  No
    substitutions are performed.  ".c" and ".h" files are copied to the
    C output, ".S" files are copied to the asm output.

  asm-stub [ファイル名]

    指定されたファイルが, アセンブリ「スタブ」が必要となる箇所全てに読み
    込まれます. オペコード名に対してテキスト置換が行われます.

  asm-stub [filename]

    The named file will be included whenever an assembly "stub" is needed.
    Text substitution is performed on the opcode name.

  op-start [ディレクトリ]

    オペコードリストの開始を示します. いかなる op コマンドよりも先行しな
    ければなりません. 指定されたディレクトリは, 命令ファイルを取り出す
    デフォルトの場所となります.

  op-start [directory]

    Indicates the start of the opcode list.  Must precede any "op"
    commands.  The specified directory is the default location to pull
    instruction files from.

  op [オペコード] [ディレクトリ]

    op-start と op-end の間にのみ記述することができます. 指定されたオペ
    コードのデフォルトのソースファイル位置を上書きします. オペコードの
    定義は指定されたファイルから読み込まれます. 例えば「op OP_NOP armv5te」
    により armv5te/OP_NOP.S からロードが行われます. 置換辞書 (後述) が
    適用されます.

  op [opcode] [directory]

    Can only appear after "op-start" and before "op-end".  Overrides the
    default source file location of the specified opcode.  The opcode
    definition will come from the specified file, e.g. "op OP_NOP armv5te"
    will load from "armv5te/OP_NOP.S".  A substitution dictionary will be
    applied (see below).

  op-end

    オペコードの終わりを示します. これが現れたときには, 256 のオペコード
    が全て出力されており, 固定サイズの命令ハンドラー領域に収まらなかった
    コードがあとに続きます.

  op-end

    Indicates the end of the opcode list.  All 256 opcodes are emitted
    when this is seen, followed by any code that didn't fit inside the
    fixed-size instruction handler space.


op ディレクティブの順序は重要ではありません. 生成ツールは, VM のソースから順序の情報を取り出します.

The order of "op" directives is not significant; the generation tool will extract ordering info from the VM sources.

現在ほとんどのオペコードへの参照を含む形式は, 主に op-start ディレクティブ内で使用されています. 新しく移植を行うには, まず "c" から始め, アーキテクチャー固有の op エントリーを追加して命令を記述します. 完成すれば, それが対象のアーキテクチャーに対するデフォルトとなり, プラットフォーム固有コードを消すために "c" オペコードを挿入します.

Typically the form in which most opcodes currently exist is used in the "op-start" directive.  For a new port you would start with "c", and add architecture-specific "op" entries as you write instructions. When complete it will default to the target architecture, and you insert "c" ops to stub out platform-specific code.

op コマンドに指定された [ディレクトリ] は, "c" ディレクトリの場合は次の二点に関して特別に扱われます. (1) ソースは C コードであると仮定され, 生成される C ファイルに挿入される. (2) C 実装が出力されるとき, 「仲介スタブ」がアセンブリソースファイルに出力される. (生成スクリプトは常に 256 のアセンブリ命令を出力しますが, asm-stab がある場合はラベルだけを出力します.)

For the [directory] specified in the "op" command, the "c" directory is special in two ways: (1) the sources are assumed to be C code, and will be inserted into the generated C file; (2) when a C implementation is emitted, a "glue stub" is emitted in the assembly source file. (The generator script always emits 256 assembly instructions, unless "asm-stub" was left blank, in which case it only emits some labels.)


==== 命令ファイル・フォーマット ====

==== Instruction file format ====

アセンブリ命令ファイルは単なるアセンブリソースの断片です. セグメント型やアラインメントと同時に, 開始ラベルも生成ツールにより提供されます. 想定しているアセンブラは GNU as ですが, 他のアセンブラでも機能するでしょう (生成ツールが出力する仮想オペコードの幾つかについては調整作業が必要かもしれませんが).

The assembly instruction files are simply fragments of assembly sources. The starting label will be provided by the generation tool, as will declarations for the segment type and alignment.  The expected target assembler is GNU "as", but others will work (may require fiddling with some of the pseudo-ops emitted by the generation tool).

可搬インタプリターとコードを共有するため, C ファイルでは変わったことをいろいろやっています. (将来的にはそういうものの量は減っていくものと期待しています.)

The C files do a bunch of fancy things with macros in an attempt to share code with the portable interpreter.  (This is expected to be reduced in the future.)

全てのオペコードの断片に対して, それらが出力に追加される際, 置換辞書が適用されます. 置換対象は $value や ${value} という形式です.

A substitution dictionary is applied to all opcode fragments as they are appended to the output.  Substitutions can look like "$value" or "${value}".

辞書には常に次のものが含まれます.

The dictionary always includes:

  $opcode    オペコード名. 例えば OP_NOP.
  $opnum    オペコード番号. 例えば OP_NOP に対しては 0.
  $handler_size_bytes    命令ハンドラーのバイト単位の最大サイズ
  $handler_size_bits    命令ハンドラーの log 2 表現での最大サイズ

  $opcode - opcode name, e.g. "OP_NOP"
  $opnum - opcode number, e.g. 0 for OP_NOP
  $handler_size_bytes - max size of an instruction handler, in bytes
  $handler_size_bits - max size of an instruction handler, log 2

C のソースもアセンブリのソースも, C プリプロセッサーの処理が入るので, C 形式のコメントや #define のようなプリプロセッサー・ディレクティブを使用することができます.

Both C and assembly sources will be passed through the C pre-processor, so you can take advantage of C-style comments and preprocessor directives like "#define".

生成ツールに対する操作が幾つかあります.

Some generator operations are available.

  %include "ファイル名" [置換辞書]

    armv5te/OP_NOP.S といったファイルを読み込みます. 標準の Python 構文を
    使用し, 置換辞書の値を指定することができます. 例えば, 次のコード:

      %include "arm5te/unop.S" {"result":"r1"}

    により, "$result" が "r1" に変換された上で "arm5te/unop.S" が現在の
    位置に挿入されます.

  %include "filename" [subst-dict]

    Includes the file, which should look like "armv5te/OP_NOP.S".  You can
    specify values for the substitution dictionary, using standard Python
    syntax.  For example, this:
      %include "armv5te/unop.S" {"result":"r1"}
    would insert "armv5te/unop.S" at the current file position, replacing
    occurrences of "$result" with "r1".

  %default [置換辞書]

    標準の Python 構文を使用し, 置換辞書のデフォルトの値を指定します.
    「ベース」バージョンや「ベース」バリアントを用意するときに便利です.

  %default [subst-dict]

    Specify default substitution dictionary values, using standard Python
    syntax.  Useful if you want to have a "base" version and variants.

  %break

    命令ハンドラーの中心部分 (これは hander-size バイトに収まっていなけれ
    ばならない) と (命令ハンドラーブロックの末尾に追加される) 従属コード
    の間の区切りを示します.

  %break

    Identifies the split between the main portion of the instruction
    handler (which must fit in "handler-size" bytes) and the "sister"
    code, which is appended to the end of the instruction handler block.

  %verify "メッセージ"

    テストが必要な項目に関してメモを残します. (これはいつの日かもっと
    興味深いものになるかもしれません. 現在のところは, これに対しては,
    出力が生成される前に取り除く, という処理しか行われていません.)

  %verify "message"

    Leave a note to yourself about what needs to be tested.  (This may
    turn into something more interesting someday; for now, it just gets
    stripped out before the output is generated.)

生成ツールは, 命令が handler-size を超えていても警告を表示「せず」, 起動時にサイズ超過のハンドラーが検出された場合, VM は異常終了します. 固定幅命令のアーキテクチャーでは, これに取り組むのは容易ですが, そうでない場合はバイト数を数える必要があるでしょう.

The generation tool does *not* print a warning if your instructions exceed "handler-size", but the VM will abort on startup if it detects an oversized handler.  On architectures with fixed-width instructions this is easy to work with, on others this you will need to count bytes.


==== アセンブリソースから C 定数を使用する ====

==== Using C constants from assembly sources ====

common/asm-constants.h ファイルには, 定数値, 構造体サイズ, 構造体メンバーのオフセット, に関する定義が含まれています. 書式の自由度はかなり低いのですが, これは, C (書式が検証される場所) とアセンブリ (定義が使用される場所) の双方で使用できるように単純なマクロが使われているためです.

The file "common/asm-constants.h" has some definitions for constant values, structure sizes, and struct member offsets.  The format is fairly restricted, as simple macros are used to massage it for use with both C (where it is verified) and assembly (where the definitions are used).

ファイル内の定数の同期が取られていない場合, VM はエラーメッセージを出力し, 起動時に異常終了します.

If a constant in the file becomes out of sync, the VM will log an error message and abort during startup.


==== 開発のヒント ====

==== Development tips ====

作り始めたばかりのオペコードハンドラーの断片をデバッグする必要があり, デバッグコードがハンドラーサイズの上限を超えてしまう場合, 先頭に次の汎用ヘッダーを挿入してください.

If you need to debug the initial piece of an opcode handler, and your debug code expands it beyond the handler size limit, you can insert a generic header at the top:

    b       ${opcode}_start
%break
${opcode}_start:

    b       ${opcode}_start
%break
${opcode}_start:

既に %break を使ってしまっていても, そのまま残しておいて大丈夫です. 二番目の %break は無視されます.

If you already have a %break, it's okay to leave it in place -- the second %break is ignored.


==== 再ビルド ====

==== Rebuilding ====

ソースファイルの断片を何か変更したら, out ディレクトリ内にある合成されたソースファイル群を再ビルドする必要があります. out 内のファイルが編集可能であることを確認した上で, 次のコマンドを実行してください.

If you change any of the source file fragments, you need to rebuild the combined source files in the "out" directory.  Make sure the files in "out" are editable, then:

    $ cd mterp
    $ ./rebuild.sh

    $ cd mterp
    $ ./rebuild.sh

この文書を書いている時点では, Python 2.5 が必要です. 他のバージョンの Python をインストールしてある場合, 理解不能なエラーメッセージか, 単なる一般エラーを目にすることになるでしょう.

As of this writing, this requires Python 2.5. You may see inscrutible error messages or just general failure if you have a different version of Python installed.

最終ゴールは, このような別の手順を踏むことなく, ビルドシステムに必要な出力ファイルを生成させることですが, ビルドにおいて Python を必須とする準備はまだ整っていないのです.

The ultimate goal is to have the build system generate the necessary output files without requiring this separate step, but we're not yet ready to require Python in the build.

2010年12月21日火曜日

Eclipse プラグインをエクスポートできない (解決)

Eclipse のプラグイン開発で, 「エクスポート」→「デプロイ可能なプラグインおよびフラグメント」→「次へ」→「完了」とすると, 「Problem during export」というタイトルのポップアップがあらわれ, 「Errors occurred during the export operation. The ant tasks generated log files which can be found at ...\logs.zip」というエラーメッセージが表示され, プラグインをエクスポートできなかった. logs.zip の中のログファイルを見ると, 「インポートされた [パッケージ名] は見つかりません」と書いてあった.

問題解決しようと思い, MANIFEST.MF の Import-Package のリストに当該パッケージを追加しようとするも, Eclipse に拒否されてしまった.

最終的には, エクスポートウィザードの「デプロイ可能なプラグインおよびフラグメント」というページの「オプション」タグを開き, 「ワークスペースのコンパイル済みクラス・ファイルを使用」をチェックし, MANIFEST.MF に「DynamicImport-Package: [パッケージ名]」を追加することで, 問題は解決した. 正しい解決方法なのかどうかは不明だが.

なお, 今回のケースでは, 当該パッケージは OSGi プラットフォーム側にシステムパッケージ (org.osgi.framework.system.packages) として存在するものであり, 何らかのバンドルからエクスポートされるものではない.

5.2.12 サービスと並行処理 (Services and Concurrency)

あるスレッドで公開され, 他のスレッドで取得されたサービスは, 安全に使用できなければならない. つまり, サービス登録と, サービスもしくはサービスリファレンス取得との間の happens-before 関係を, フレームワークは保証しなければならない. つまり, 登録スレッドと取得スレッドはお互いに適切に同期されなければならない.

Services published on one thread and gotten on another thread must be safe to use. That is, the Framework must guarantee that there is a happens-before relationship between the time a service in registered and the time a service or Service Reference is gotten. That is both the registering and getting threads must be properly synchronized with each other.

5.2.11 サービス例外 (Service Exceptions)

サービス例外は, フレームワークがエラーを報告したり, ユーザーコードがサービスに問題を伝えたりするのに使用することができる実行時例外である. この例外から得られる例外型により, 当該例外が投げられる原因となった問題に関する詳細な情報が提供される.

The Service Exception in a Run Time exception that can be used by the Framework to report errors or user code that needs to signal a problem with a service. An exception type available from this exception provides the detailed information about the problem that caused the exception to be thrown.

フレームワークの実装とユーザーコードは, ServiceException クラスのサブクラスを投げてもかまわない. 既定の型で指定されるものとは異なる理由でサブクラスが投げられる場合, 型には SUBCLASS を設定すべきである. 既存の型に対して追加情報を提供するサブクラスは, 元の型コードを保持しておくべきである.

Implementation of the framework or user code are allowed to throw sub classes of the ServiceException class. If a sub class is thrown for another reason than specified by one of the types, then the type should be set to SUBCLASS. Sub classes that provide additional information on existing types should keep the original type code.

5.2.10 サービスに関する情報 (Information About Services)

Bundle インターフェースには, バンドルのサービス使用状況に関する情報を取得するための二つのメソッドがある.

The Bundle interface defines these two methods for returning information pertaining to service usage of the bundles:

  • getRegisteredServices() ─ バンドルがフレームワークに登録した ServiceReference オブジェクトを返す.
  • getServicesInUse() ─ バンドルが現在使用中の ServiceReference オブジェクトを返す.


  • getRegisteredServices() - Returns the ServiceReference objects that the bundle has registered with the Framework.
  • getServicesInUse() - Returns the ServiceReference objects that the bundle is currently using.

5.2.9 サービスオブジェクトの取得 (Getting Service Objects)

フレームワークが依存関係を管理できるようにするため, 実際のサービスオブジェクトを取得するのに BundleContext オブジェクトが使用される. バンドルがサービスオブジェクトを取得すると, そのバンドルは, 登録済みのサービスオブジェクトのライフサイクルに依存することになる. この依存関係は, 当該サービスオブジェクトを取得するのに使用した BundleContext オブジェクトにより追跡される. そしてこれが, BundleContext オブジェクトを他のバンドルと共有する際に注意を払うことが重要であるということの一つの理由となっている.

The BundleContext object is used to obtain the actual service object so that the Framework can manage dependencies. If a bundle retrieves a service object, that bundle becomes dependent upon the life cycle of that registered service object. This dependency is tracked by the BundleContext objecxt used to obtain the service object, and is one reason that it is important to be careful when sharing BundleContext objects with other bundles.

メソッド BundleContext.getService(ServiceReference) は, objectClass プロパティーで定義されたインターフェース群を実装するオブジェクトを返す.

The method BundleContext.getService(ServiceReference) returns an object that implements the interfaces as defined by the objectClass property.

このメソッドには以下の特徴がある:

This method has the following characteristics:

  • 対応するサービスオブジェクトが既に登録解除されている場合は null を返す.
  • 呼び出し元が当該サービスリファレンスに関連付けられたサービスオブジェクトを取得するための ServicePermission [ServiceReference, GET] パーミッションを持っているかどうかを判定する. セキュリティーを損なうことなく ServiceReference オブジェクトを自由に取り交わすために, このパーミッション検査が必要となる.
  • この BundleContext オブジェクトにおける当該サービスオブジェクトの使用カウントを一つ増やす.
  • サービスオブジェクトが ServiceFactory インターフェースを実装していない場合, そのサービスオブジェクトが返される. そうでない場合, 当該バンドルコンテキストにおける, そのサービスオブジェクトの使用カウントが 1 であれば, そのオブジェクトを ServiceFactory オブジェクトへとキャストし, 呼び出し元バンドル用にカスタマイズされたサービスオブジェクトを生成するために getService メソッドを呼び, 生成されたオブジェクトを返す. ServiceFactory オブジェクトに関する情報については 134 ページの「サービスファクトリー」を参照のこと.

  • Returns null if the underlying service object has been unregistered.
  • Determines if the caller has ServicePermission [ServiceReference, GET], to get the service object associated with the Service Reference. This permission check is necessary so that ServiceReference objects can be passed around freely without compromising security.
  • Increments the usage count of the service object by one for this BundleContext object.
  • If the service object does not implement the ServiceFactory interface, it is returned. Otherwise, if the bundle context's usage count of the service object is one, the object is cast to a ServiceFactory object and the getService method is called to create a customized service object for the calling bundle which is then returned. Otherwise, a cached copy of this customized object is returned. See Service Factory on page 134 for more information about ServiceFactory objects.

2010年12月18日土曜日

5.2.8 サービスプロパティーの取得 (Getting Service Properties)

サービスオブジェクに関する問い合わせに応えるため, ServiceReference インターフェースは次の二つのメソッドを定義している:

To allow for interrogation of service objects, the ServiceReference interface defines these two methods:

  • getPropertyKeys() ─ 利用可能なプロパティーキー群の配列を返す.
  • getProperty(String) ─ プロパティーの値を返す.

  • getPropertyKeys() - Returns an array of the property keys that are available.
  • getProperty(String) - Returns the value of a property.

これらのメソッドは両方とも, 参照されているサービスオブジェクトがフレームワークから削除された後も, それに関する情報を提供し続けなければならない. ログサービスに ServiceReference オブジェクトが格納されたときに, この要求事項が有用となる.

Both of these methods must continue to provide information about the referenced service object, even after it has been unregistered from the Framework. This requirement can be useful when a ServiceReference object is stored with the Log Service.

5.2.7 サービスの特定 (Locating Services)

サービスオブジェクトを使用してそのメソッドを呼び出すためには, バンドルはまず ServiceReference オブジェクトを取得しなければならない. バンドルがフレームワークから ServiceReference オブジェクトを取得するための方法として, BundleContext インターフェースに二つのメソッドが定義されている:

In order to use a service object and call its methods, a bundle must first obtain a ServiceReference object. The BundleContext interface defines two methods a bundle can call to obtain ServiceReference objects from the Framework:

  • getServiceReference(String) ─ このメソッドは, String で指定されたサービスインターフェースを実装し, そのサービスインターフェース名のもとに登録されているサービスオブジェクトへの ServiceReference オブジェクトを返す. 条件を満たすサービスオブジェクトが複数存在する場合, 最も高い SERVICE_RANKING を持つサービスオブジェクトが返される. ランキングが同じ場合, 一番低い SERVICE_ID を持つサービスオブジェクト (最初に登録されたサービスオブジェクト) が返される.

  • getServiceReference(String) - This method returns a ServiceReference object to a service object that implements, and was registered under, the name of the service interface specified as String. If multiple such service objects exist, the service object with the highest SERVICE_RANKING is returned. If there is a tie in ranking, the service object with the lowest SERVICE_ID (the service object that was registered first) is returned.

  • getServiceReferences(String,String) ─ このメソッドは次の条件を満たす ServiceReference オブジェクトの配列を返す:
  1. 指定されたサービスインターフェースを実装し, そのサービスインターフェース名のもとに登録されている.
  2. 指定された検索フィルターの条件を満たす. フィルター構文については 133 ページの「フィルター」に説明がある.

  • getServiceReferences(String,String) - This method returns an array of ServiceReference objects that:
  1. Implement and were registered under the given service interface.
  2. Satisfy the search filter specified. The filter syntax is further explained in Filters on page 133.

適合するサービスオブジェクトが存在しない場合は両メソッドとも null を返さなければならない. それ以外の場合は, 呼び出し元は一つ以上の ServiceReference オブジェクトを受け取ることになる. これらのオブジェクトは, サービスオブジェクトのプロパティーを取り出したり, BundleContext オブジェクトを介して実際のサービスオブジェクトを取得するのに使用することができる.

Both methods must return null if no matching service objects are returned. Otherwise, the caller receives one or more ServiceReference objects. These objects can be used to retrieve properties of the underlying service object, or they can be used to obtain the actual service object via the BundleContext object.

両メソッドとも, 返却されるサービスリファレンスに対応するサービスオブジェクトを取得するのに必要な ServicePermission [ServiceReference, GET] パーミッションを, 呼び出し元が持っていることを要求する. 呼び出し元が必要なパーミッションを持っていない場合, これらのメソッドの返却値に当該サービスリファレンスを含めてはならない.

Both methods require that the caller has the required ServicePermission[ServiceReference, GET] to get the service object for the returned Service Reference. If the caller lacks the required permission, these methods must not include that Service Reference in the return.

5.2.6 永続識別子 (Persistent Identifier (PID))

永続識別子 (Persistent Identifier (PID)) の目的は, フレームワークの再起動を経てもサービスを識別することである. 登録時に毎回同じ実体を参照するサービスは, PID を含むサービスプロパティーを使用すべきである. PID 用のサービスプロパティーの名前は service.pid と定義されている. PID は, フレームワークを複数回起動しても永続するサービスに与える一意な識別子である. サービスが与えられたとき, 常に同じ PID が使用されるべきである. 当該バンドルが停止され, 後でまた起動されたときは, 常に同じ PID が使用されなければならない.

The purpose of a Persistent Identifier (PID) is to identify a service across Framework restarts. Services that can reference the same underlying entity every time they are registered should therefore use a service property that contains a PID. The name of the service property for PID is defined as service.pid. The PID is a unique identifier for a service that persists over multiple invocations of the Framework. For a given service, the same PID should always be used. If the bundle is stopped and later started, the same PID must always be used.

PID の書式は次のとおり:

The format of the PID should be:

  pid ::= symbolic-name     // 1.3.2 参照のこと

  pid ::= symbolic-name     // See 1.3.2

2010年12月16日木曜日

5.2.5 サービスプロパティー (Service Properties)

プロパティーは (キー, 値) の組として情報を保持する. キーは String オブジェクトでなければならず, 値は Filter オブジェクトが認識できる型 (133 ページの「フィルター」を参照のこと) とすべきである. 同一のキーに対する複数の値は, 配列 ([]) および Collection オブジェクトによりサポートされる.

Properties hold information as key/value pairs. The key must be a String object and the value should be a type recognized by Filter objects (see Filters on page 133 for a list). Multiple values for the same key are supported with arrays ([]) and Collection objects.

バンドル間に不要な依存関係を生じさせないよう, プロパティーの値は, プリミティブもしくは Java の標準の型に限定すべきである. フレームワークは, サービスプロパティーを介してバンドル間でやりとりされるオブジェクトによって生成される依存関係を検出することはできない.

The values of properties should be limited to primitive or standard Java types to prevent unwanted inter bundle dependencies. The Framework cannot detect dependencies that are created by the exchange of objects between bundles via the service properties.

プロパティーのキーは大文字小文字を区別しない. ObjectClass, OBJECTCLASS, objectclass は全て同じプロパティーキーである. フレームワークが ServiceReference.getPropertyKeys で返すキーは, 大文字小文字の別において, 最後に設定されたものと正確に一致しなければならない. Dictionary オブジェクトが, 大文字小文字の違いしかないキーを含んでいた場合, フレームワークは例外を上げなければならない.

The key of a property is not case sensitive. ObjectClass, OBJECTCLASS and objectclass all are the same property key. A Framework must return the key in ServiceReference.getPropertyKeys in exactly the same case as it was last set. When a Dictionary object that contains keys that only differ in case is passed, the Framework must raise an exception.

サービスプロパティーの目的は, サービスオブジェクトに関する情報を提供することである. プロパティーは, サービスの実際の機能に関与する形で使用されるべきではない. サービス登録後のプロパティー変更は潜在的に高価な操作となる. 例えばフレームワークは, 後からおこなう検索のスピードを上げるため, 登録処理中にプロパティーを前処理してインデックス化するかもしれない.

The service properties are intended to provide information about the service object. The properties should not be used to participate in the actual function of the service. Modifying the properties for the service registration is a potentially expensive operation. For example, a Framework may pre-process the properties into an index during registration to speed up later lookups.

Filter インターフェースは複雑なフィルタリングをサポートし, 適合するサービスオブジェクトを探し出すために使用することができる. そのため, 全てのプロパティーはフレームワークサービスレジストリー内の単一の名前空間を共有している. この結果, 衝突を避けるため, 説明的名称や公式に定義された簡潔名称を使用することが重要となる. 幾つかの OSGi 仕様でこの名前空間の一部を予約している. service. で始まる全てのプロパティーおよび objectClass は, OSGi 仕様により予約されている.

The Filter interface supports complex filtering; it can be used to find matching service objects. Therefore, all properties share a single name space in the Framework service registry. As a result, it is important to use descriptive names or formal definitions of shorter names to prevent conflicts. Several OSGi specifications reserve parts of this name space. All properties starting with the prefix service. and the property objectClass are reserved for use by OSGi specifications.

「表 5.2 標準サービスプロパティー (+ はスカラー, 配列, もしくはコレクション, を示す)」は, 事前定義されたプロパティーのリストである.

Table 5.2 Standard Service Properties (+ indicates scalar, array of, or collection of) contains a list of pre-defined properties.

[表 5.2 標準サービスプロパティー (+ はスカラー, 配列, もしくはコレクション, を示す)]

[Table 5.2 Standard Service Properties (+ indicates scalar, array of, or collection of)]

プロパティーキー = objectClass
= String[]
定数 = OBJECTCLASS
プロパティーの説明 =
objectClass プロパティーは, サービスオブジェクトがフレームワークに登録された際のインターフェース名のセットを含んでいる. フレームワークはこのプロパティーを自動的に設定しなければならない. フレームワークは, サービスオブジェクトが BundleContext.getService(ServiceReference) で取り出されたときに, そのオブジェクトが当該インターフェース名のいずれにもキャスト可能であることを保証しなければならない.

Property Key = objectClass
Type = String[]
Constant = OBJECTCLASS
Property Description =
The objectClass property contains the set of interface names under which a service object is registered with the Framework. The Framework must set this property automatically. The Framework must guarantee that when a service object is retrieved with BundleContext.getService(ServiceReference), it can be cast to any of the interface names.

プロパティーキー = service.description
= String
定数 = SERVICE_DESCRIPTION
プロパティーの説明 =
service.description プロパティーは, ドキュメントとして使用されることを意図しており, 設定は任意である. フレームワークとバンドルは, 登録されたサービスオブジェクトの概要説明を提供するためにこのプロパティーを使用することができる. ローカリゼーションはサポートされないので, 主な使用目的はデバッグ支援にある.

Property Key = service.description
Type = String
Constant = SERVICE_DESCRIPTION
Property Description =
The service.description property is intended to be used as documentation and is optional. Frameworks and bundles can use this property to provide a short description of a registered service object. The purpose is mainly for debugging because there is no support for localization.

プロパティーキー = service.id
= Long
定数 = SERVICE_ID
プロパティーの説明 =
登録されたサービスオブジェクトには, フレームワークにより一意な service.id が割り当てられる. この番号はサービスオブジェクトのプロパティーに追加される. フレームワークは, それまでに登録された全てのサービスオブジェクトに割り当てられたどの値よりも大きく, 一意な値を, 登録されたサービスオブジェクトに割り当てる.

Property Key = service.id
Type = Long
Constant = SERVICE_ID
Property Description =
Every registered service object is assigned a unique service.id by the Framework. This number is added to the service object's properties. The Framework assigns a unique value to every registered service object that is larger than values provided to all previously registered service objects.

プロパティーキー = service.pid
= String+
定数 = SERVICE_PID
プロパティーの説明 =
service.pid プロパティーは, サービスオブジェクトの永続的で一意な識別子を任意識別する. 129 ページの「永続識別子」を参照のこと.

Property Key = service.pid
Type = String+
Constant = SERVICE_PID
Property Description =
The service.pid property optionally identifies a persistent, unique identifier for the service object. See Persistent Identifier (PID) on page 129.

プロパティーキー = service.ranking
= Integer
定数 = SERVICE_RANKING
プロパティーの説明 =
サービスオブジェクトを登録するとき, バンドルは, そのサービスオブジェクトのプロパティーの一つとして, service.ranking 番号を任意で指定してもよい. 条件を満たすサービスインターフェースが複数存在するとき, フレームワークがどのサービスオブジェクトを返すかを決定するのは, 最も高い SERVICE_RANKING 番号を持つサービス (番号が等しいときはそれらの中で最も低い SERVICE_ID を持つサービス) である.

Property Key = service.ranking
Type = Integer
Constant = SERVICE_RANKING
Property Description =
When registering a service object, a bundle may optionally specify a service.ranking number as one of the service object's properties. If multiple qualifying service interfaces exist, a service with the highest SERVICE_RANKING number, or when equal to the lowest SERVICE_ID, determines which service object is returned by the Framework.

プロパティーキー = service.vendor
= String
定数 = SERVICE_VENDOR
プロパティーの説明 =
この任意指定のプロパティーは, ベンダーを示すためにサービスオブジェクトを登録するバンドルが使用することができる.

Property Key = service.vendor
Type = String
Constant = SERVICE_VENDOR
Property Description =
This optional property can be used by the bundle registering the service object to indicate the vendor.

2010年12月15日水曜日

5.2.4 ServiceRegistration オブジェクトの早期必要性 (Early Need for ServiceRegistration Object)

サービスオブジェクトの登録により, 登録されている全ての ServiceListener オブジェクトに対して通知がおこなわれる. この通知は同期である. これが意味することは, リスナーは, registerService メソッドが ServiceRegistration オブジェクトを返却する前に, サービスにアクセスでき, そのメソッドを呼ぶことができるということである. 状況によっては, そのようなコールバックの中で ServiceRegistration オブジェクトにアクセスする必要がある場合がある. しかし, 登録をおこなうバンドルはその時点で ServiceRegistration オブジェクトをまだ受け取ってない. 127 ページの 図 5.32 はこのシーケンスを示している.

The registration of a service object will cause all registered ServiceListener objects to be notified. This is a synchronous notification. This means that such a listener can get access to the service and call its methods before the registerService method has returned the ServiceRegistration object. In certain cases, access to the ServiceRegistration object is necessary in such a callback. However, the registering bundle has not yet received the ServiceRegistration object. Figure 5.32 on page 127 shows such a sequence.

[図 5.32 ServiceRegistration と登録]

[Figure 5.32 Service Registration and registration]

上記のようなケースでは, ServiceFactory オブジェクト経由で登録オブジェクトへアクセスすることができる. ServiceFactory オブジェクトが登録された場合, フレームワークは, 登録をおこなうバンドルを, ServiceFactory メソッド getService(Bundle,ServiceRegistration) で呼ばなければならない. このメソッドには, 要求された ServiceRegistration オブジェクトがパラメーターとして渡される.

In a case as described previously, access to the registration object can be obtained via a ServiceFactory object. If a ServiceFactory object is registered, the Framework must call-back the registering bundle with the ServiceFactory method getService(Bundle,ServiceRegistration). The required ServiceRegistration object is passed as a parameter to this method.

5.2.3 サービスの登録 (Registering Services)

バンドルは, フレームワークサービスレジストリーにサービスオブジェクトを登録することにより, サービスを公開する. フレームワークに登録されたサービスオブジェクトは, OSGi 環境にインストールされた他のバンドルから見えるようになる.

A bundle publishes a service by registering a service object with the Framework service registry. A service object registered with the Framework is exposed to other bundles installed in the OSGi environment.

登録されたサービスオブジェクトは全て, 一意な ServiceRegistration オブジェクトと, そのサービスを参照する一つ以上の ServiceReference オブジェクトを持つ. これらの ServiceReference オブジェクト群は, 当該サービスオブジェクトが実装するサービスインターフェース群を含む登録プロパティー群を公開する. 希望するサービスインターフェースを実装するサービスオブジェクトを取得するため, ServiceReference オブジェクトを使用することができる.

Every registered service object has a unique ServiceRegistration object, and has one or more ServiceReference objects that refer to it. These ServiceReference objects expose the registration properties of the service object, including the set of service interfaces they implement. The ServiceReference object can then be used to acquire a service object that implements the desired service interface.

フレームワークは, バンドルがサービスオブジェクトを動的に登録および登録解除することを許可している. そのため, バンドルは STARTING, ACTIVE, STOPPING の状態の間はいつでもサービスオブジェクトを登録することが可能である.

The Framework permits bundles to register and unregister service objects dynamically. Therefore, a bundle is permitted to register service objects at any time during the STARTING, ACTIVE or STOPPING states.

バンドルは, BundleContext オブジェクトの BundleContext.registerService メソッドのいずれかを呼ぶことにより, サービスオブジェクトをフレームワークに登録する.

A bundle registers a service object with the Framework by calling one of the BundleContext.registerService methods on its BundleContext object.

  • registerService(String,Object,Dictionary) - 一つのサービスインターフェースのもとに登録されるサービスオブジェクト用
  • registerService(String[],Object,Dictionary) - 複数のサービスインターフェースのもとに登録されるサービスオブジェクト用
  • registerService(String,Object,Dictionary) - For a service object registered under a single service interface.
  • registerService(String[],Object,Dictionary) - For a service object registered under multiple service interfaces.

バンドルが登録しようしているサービスのサービスインターフェースの名前は, registerService メソッドの引数として渡す. フレームワークは, 渡されたオブジェクトがサービスファクトリーでない限り, そのサービスオブジェクトが実際に, 指定されたサービスインターフェース群のインスタンスであることを確認しなければならない. 134 ページの「サービスファクトリー」を参照のこと.

The names of the service interfaces under which a bundle wants to register its service are provided as arguments to the registerService methods. The Framework must ensure that the service object actually is an instance of each specified service interfaces, unless the object is a Service Factory. See Service Factory on page 134.

この確認をおこなうため, フレームワークは, 当該バンドルもしくは共有パッケージから, 指定されたサービスインターフェース群それぞれの Class オブジェクトをロードしなければならない. 各 Class オブジェクト毎に Class.isInstance が呼ばれなければならず, サービスオブジェクトを引数とした Class オブジェクトに対して, Class.isInstance は true を返さなければならない.

To perform this check, the Framework must load the Class object for each specified service interface from either the bundle or a shared package. For each Class object, Class.isInstance must be called and return true on the Class object with the service object as the argument.

登録しようとしているサービスオブジェクトについて, サービスのプロパティー群を (キー, 値) の組の集合として含んだ Dictionary オブジェクトを用いて, より詳細に記述してもよい.

The service object being registered may be further described by a Dictionary object, which contains the properties of the service as a collection of key/value pairs.

サービスオブジェクトの登録が成功したサービスインターフェース名群は, objectClass キーでサービスオブジェクトのプロパティーに自動的に追加される. この値はフレームワークにより自動的に設定され, バンドルが提供したいかなる値も上書きされなければならない.

The service interface names under which a service object has been successfully registered are automatically added to the service object's properties under the key objecClass. This value must be set automatically by the Framework and any value provided by the bundle must be overridden.

サービスオブジェクトの登録に成功したときは, フレームワークは呼び出し元に ServiceRegistration オブジェクトを返さなければならない. サービスオブジェクトは, その ServiceRegistration オブジェクトのホルダーによってのみ登録解除することができる (unregister() メソッド参照のこと). サービスオブジェクトの登録は, 同じサービスオブジェクトが複数回登録された場合でも, 成功したときには一意の ServiceRegistration オブジェクトを生成しなければならない.

If the service object is successfully registered, the Framework must regturn ServiceRegistration object to the caller. A service object can be unregistered only by the holder of its ServiceRegistration object (see the unregister() method). Every successfull service object registration must yield a unique ServiceRegistration object even if the same service object is registered multiple times.

サービスオブジェクト登録後にサービスオブジェクトのプロパティーを確実に変更する唯一の方法は, ServiceRegistration オブジェクトを使用することである (setProperties(Dictionary) 参照のこと). サービスオブジェクト登録後にサービスオブジェクトの Dictionary オブジェクトを変更しても, サービスのプロパティーに何ら影響しないかもしれない.

Using the ServiceRegistration object is the only way to reliably change the service object's properties after it has been registered (see setProperties(Dictionary)). Modifying a service object's Dictionary object after the service object is registered may not have any effect on the service's properties.

サービスオブジェクトの登録プロセスは, パーミッション検査の対象となる. 登録しようとしているバンドルは, 指定した全てのサービスインターフェースにサービスオブジェクトを登録するための ServicePermission[{name},REGISTER] パーミッションを持たなければならない. パーミッションを持たない場合は, 当該サービスオブジェクトは登録されてはならず, SecurityException が投げられなければならない.

The process of registering a service object is subject to a permission check. The registering bundle must have ServicePermission[<name>,REGISTER] to register the service object under all the service interfaces specifed. Otherwise, the service object must not be registered, and a SecurityException must be thrown.

5.2.2 サービスインターフェース (Service Interfaces)

サービスインターフェースはサービスの public メソッド群の仕様である.

A service interface is the specification of the service's public methods.

実際には, バンドル開発者がサービスのインターフェースを実装してサービスオブジェクトを作成し, そのサービスをフレームワークサービスレジストリーに登録することになる. バンドルがあるインターフェース名のもとにサービスを登録すると, 他のバンドルは, 関連付けられたサービスをそのインターフェース名において取得することができ, そのサービスインターフェースを通じてメソッドにアクセスすることができる. フレームワークはまた, クラス名のもとにサービスオブジェクトを登録することもサポートしている. そのため, この仕様内においてサービスインターフェースとして言及されるものは, インターフェースもしくはクラスと解釈することができる.

In practice, a bundle developer creates a service object by implementing its service interface and registers the service with the Framework service registry. Once a bundle has registered a service object under an interface name, the associated service can be acquired by bundles under that interface name, and its methods can be accessed by way of its service interface. The Framework also supports registering service objects under a class name, so references to service interface in this specification can be interpreted to be an interface or class.

フレームワークにサービスオブジェクトを要求するとき, バンドルは要求するサービスオブジェクトが実装していなければならないサービスインターフェースの名前を指定することができる. その要求において, バンドルはまた検索対象を限定するためのフィルター文字列を指定してもよい.

When requesting a service object from the Framework, a bundle can specify the name of the service interface that the requested service object must implement. In the request, the bundle may also specify a filter string to narrow the search.

多くのサービスインターフェースが OSGi アライアンス等の組織により定義されて詳述されている. 標準として受け入れられたサービスインターフェースは, 多くのバンドル開発者が実装し, また, 使用することができる.

Many service interfaces are defined and specified by organizations such as the OSGi Alliance. A service interface that has been accepted as a standard can be implemented and used by any number of bundle developers.

5.2.1 サービスリファレンス (Service References)

一般に, 登録されたサービスは ServiceReference オブジェクトを通して参照される. これにより, バンドルがサービスオブジェクト自体を要求することなくサービスについて知りたいというときに, バンドル間に動的なサービス依存関係を不必要に生成するという事態を避けることができる.

In general, registered services are referenced through ServiceReference objects. This avoids creating unnecessary dynamic service dependencies between bundles when a bundle needs to know about a service but does not require the service object itself.

ServiceReference オブジェクトは, 依存関係を生成することなく保存でき, また, 他のバンドルへ受け渡すことができる. バンドルがサービスを使用したいときは, ServiceReference オブジェクトを BundleContext.getService(ServiceReference) に渡すことでサービスを取得することができる. 129 ページの「サービスの特定」を参照のこと.

A ServiceReference object can be stored and passed on to other budles without the implications of dependencies. When a bundle wishes to use the service, it can be obtained by passing the ServiceReference object to BundleContext.getService(ServiceReference). See Locating Services on page 129.

ServiceReference オブジェクトは, サービスオブジェクトのプロパティー群や他のメタ情報をカプセル化している. バンドルはこのメタ情報を問い合わせることができ, 最も適合するサービスを選択するために利用できる.

A ServiceReference object encapsulates the properties and other meta-information about the service object it represents. This meta-information can be queried by a bundle to assist in the selection of a service that best suits its needs.

バンドルがフレームワークサービスレジストリーにサービスを問い合わせたとき, フレームワークは, サービス自体ではなく, 要求されたサービス群の ServiceReference オブジェクト群を,要求元のバンドルに提供しなければならない.

When a bundle queries the Framework service registry for services, the Framework must provide the requesting bundle with the ServiceReference objects of the requested services, rather than with the services themselves.

ServiceReference オブジェクトはまた, ServiceRegistration オブジェクトからも取得することができる.

A ServiceReference object may also be obtained from a ServiceRegistration object.

ServiceReference オブジェクトはサービスオブジェクトが登録されている間のみ有効である. しかし, ServiceReference オブジェクトが存在している間は, サービスオブジェクトのプロパティー群は参照可能でなければならない.

A ServiceReference object is valid only as long as the service object is registered. However, its properties must remain available as long as the ServiceReference object exists.

2010年12月11日土曜日

Felix on Android でバンドルがスタートできない問題の回避方法

Android に Felix (http://felix.apache.org/) を載せて OSGi バンドルをインストールし、スタートさせると、"*** Class '${class}' was not found because bundle ${importer} does not import '${package}' even though bundle ${exporter} does export it. To resolve this issue, add an import for '${package}' to bundle ${importer}." というエラーが出てしまう。エラーメッセージの内容は、「バンドル ${exporter} がパッケージ ${package} をエクスポートしているが (= バンドル ${exporter} の MANIFEST.MF の Export-Package にパッケージ ${package} がリストされているが)、バンドル ${importer} がパッケージ ${package} をインポートしていない (= バンドル ${importer} の MANIFEST.MF の Import-Package にパッケージ ${package} がリストされていない) ため、クラス ${class} を見つけることができない。この問題を解決するためには、バンドル ${importer} でパッケージ ${package} をインポートしなさい (= バンドル ${importer} の MANIFEST.MF の Import-Package にパッケージ ${package} をリストしなさい)。」、というものである。

しかし、この問題は、バンドル ${importer} の MANIFEST.MF の Import-Package に ${package} をリストしても解決できない。Felix のコードに問題があるからだ。

【解決方法1】
本質的ではないが、てっとり早い問題解決方法は、バンドルを DEX 変換するときに --keep-classes オプションをつけること。

dx --dex --keep-classes --output=Android用バンドル.jar 元のバンドル.jar

【解決方法2】
--keep-classes オプションを付けずに DEX 変換された JAR ファイルの場合、JAR ファイルの中に元の *.class ファイル群が含まれていない。そのため、org.apache.felix.framework.ModuleImplfindClass(String) メソッド内の「bytes = contentPath[i].getEntryAsBytes(actual)」というコードで bytesnull に設定されてしまう。これにより、後続の「if (bytes != null)」条件文が false になり、「getDexFileClass((JarContent)content, name, this)」が呼び出されない。しかし、DEX 変換された JAR の場合、bytesnull かどうかはどうでもよいので、つまり、JAR ファイルの中に xxx.class というファイルが存在するかどうかはどうでもよいので(なぜなら代わりに classes.dex が含まれているので)、この if 文のチェックは不要だ。実際、bytesnull でも構わずに当該 if ブロックの中を実行するようにコードを変更すると、めでたく Felix on Android にインストールした OSGi バンドルをスタートさせることができる。


なお, getDexFileClass()null を返した場合, 後続のdefineClass()bytes == null のまま呼び出されてしまう. これを避けるため, defineClass() の呼び出しを囲っている if (clazz == null)if (clazz == null && bytes != null) にする必要がある. これをしないと, Android エミュレータで SIGSEGV エラーが発生してしまう. (NullPointerException が投げられない理由はよく分からない.)


Solutions for the problem where bundles won't start on "Felix on Android"

If an OSGi bundle is installed on Felix (http://felix.apache.org/) on Android and tried to be started, an error occurs which says "*** Class '${class}' was not found because bundle ${importer} does not import '${package}' even though bundle ${exporter} does export it. To resolve this issue, add an import for '${package}' to bundle ${importer}." This error message means "The bundle ${exporter} exports the package ${package} (= the package ${package} is listed on Export-Package in MANIFEST.MF of the bundle ${exporter}) but the bundle ${importer} does not import the package ${package} (= the package ${package} is not listed on Import-Package in MANIFEST.MF of the bundle ${importer}), so the class ${class} cannot be found. To resolve this issue, the bundle ${importer} should import the package ${package} (= the package ${package} should be listed on Import-Package in MANIFEST.MF of the bundle ${importer})."

However, this issue is not solved even after listing ${package} on Import-Package in MANIFEST.MF of the bundle ${importer}. It is because Felix code has a problem.

[ Solution 1 ]
Not essential, but a quick solution is to add --keep-classes option when you DEX-convert a bundle.

dx --dex --keep-classes --output=BundleForAndroid.jar OriginalBundle.jar

[ Solution 2 ]
If a JAR file is converted into DEX without --keep-classes option, original *.class files are not contained in a resultant JAR file. So, the code "bytes = contentPath[i].getEntryAsBytes(actual)" in findClass(String) of org.apache.felix.framework.ModuleImpl sets null to bytes. Because of this, the following conditional statement "if (bytes != null)" is evaluated as false and getDexFileClass((JarContent)content, name, this) is not called. However, for a DEX-converted JAR file, because it does not matter whether bytes is null or not, in other words, because it does not matter whether xxx.class exists in the JAR file or not (because classes.dex is contained alternatively), the check of the if statement is unnecessary. As a matter of fact, if the code is changed so that the content in the if block can be executed even when bytes is null, an OSGi bundle installed on Felix on Android can be happily started.


Additionally, if getDexFileClass() returns null, the following defineClass() is called with bytes == null. To avoid this, the condition of the if block enclosing the defineClass() call has to be changed from if (clazz == null) to if (clazz == null && bytes != null). Otherwise, SIGSEGV error occurs on Android emulator. (I don't know why NullPointerException is thrown instead.)

2010年12月8日水曜日

aapt の使い方


Android アセット パッケージング ツール

aapt l[ist] [-v] [-a] file.{zip,jar,apk}

ZIP 互換アーカイブの内容をリストアップする.

aapt d[ump] [--values] WHAT file.{apk} [asset [asset ...]]

badging APK 内で宣言されているアプリ用のラベルとアイコンを表示する.
permissions APK からパーミッションを表示する.
resources APK からリソーステーブルを表示する.
configurations APK 内のコンフィギュレーションを表示する.
xmltree 指定されたアセット内のコンパイル済み XML を表示する.
xmlstrings 指定されたコンパイル済みアセットの文字列を表示する.

aapt p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml]
[-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile]
[--min-sdk-version VAL] [--target-sdk-version VAL]
[--app-version VAL] [--app-version-name TEXT] [--custom-package VAL]
[--rename-manifest-package PACKAGE]
[--rename-instrumentation-target-package PACKAGE]
[--utf16] [--auto-add-overlay]
[-I base-package [-I base-package ...]]
[-A asset-source-dir] [-G class-list-file] [-P public-definitions-file]
[-S resource-sources [-S resource-sources ...]]
[-F apk-file] [-J R-file-dir]
[raw-files-dir [raw-files-dir] ...]

Android リソース群をパッケージングする. -M, -A, -S オプション
もしくは raw-files-dir により指定されたアセットとリソースを読み込む.
-J, -P, -F, -R オプションは出力ファイルを制御する.

aapt r[emove] [-v] file.{zip,jar,apk} file1 [file2 ...]

ZIP 互換アーカイブから指定されたファイルを削除する.

aapt a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]

ZIP 互換アーカイブに指定されたファイルを追加する.

aapt v[ersion]

プログラム・バージョンを表示する.


オプション:

-a
Android 固有のデータ (リソース, マニフェスト) を表示する.

-c
どのコンフィギュレーションをインクルードするか指定する.
デフォルトでは全てのコンフィギュレーションが対象となる.
パラメータの値は, コンフィギュレーション値をカンマで
区切ったリスト. ロケールは「言語」もしくは「言語-リージョン」
の組で指定する. 例:

en
port,en
port,land,en_US

特別なロケール zz_ZZ をリストに含めた場合, デフォルトの
ロケールに対して仮想ローカリゼーションがおこなわれ,
国際化処理の対象からはずれている文字列を見つけられるよう,
全ての文字列が変更される. 例:

port,land,zz_ZZ

-d
カンマ区切りのインクルードするデバイス・アセット群.

-f
既存ファイルを強制的に上書きする.

-g
画像をグレースケールにするためのピクセル許容誤差を指定する.
デフォルトは 0.

-j
インクルードするクラス群を含む jar もしくは zip ファイルを指定する.

-k
追加するファイルのジャンクパス.

-m
-J で指定されたロケーション下にパッケージディレクトリー群を作成する.

-u
既存のパッケージを更新する (新しいファイルの追加, 古いファイルの置換,
削除されたファイルの除去).

-v
冗長出力.

-x
拡張 (非アプリケーション) リソース ID を生成する.

-z
localization="suggested" とマークされたリソース属性のローカリゼーションを
要求する.

-A
raw アセットファイルの検索ディレクトリを追加する.

-G
プロガードオプションを書き込むファイル.

-F
出力 apk ファイルを指定する.

-I
インクルードセットのベースとする既存パッケージを指定する.

-J
R.java リソース定数定義の出力場所を指定する.

-M
ZIP 内における AndroidManifest.xml のフルパスを指定する.

-P
公開リソース定義を出力する場所を指定する.

-S
リソース検索ディレクトリを指定する. 複数のディレクトリが検索され,
最初に見つかったもの (左から右への検索) が優先される.

-0
圧縮せずに apk に含めるファイル群の拡張子を指定する. 空文字列が
指定された場合, どのファイルも圧縮されなくなる.

--min-sdk-version
マニフェストに android:minSdkVersion を追加する. バージョンが 7 以上の
場合, リソースのデフォルト・エンコーディングは UTF-8 になる.

--target-sdk-version
マニフェストに android:targetSdkVersion を追加する.

--values
リソースダンプ時にリソース値も含めるようにする.

--version-code
マニフェストに android:versionCode を追加する.

--version-name
マニフェストに android:versionName を追加する.

--custom-package
他のパッケージに R.java を生成する.

--auto-add-overlay
オーバーレイ内のみにあるリソースを自動的に追加する.

--rename-manifest-package
指定されたパッケージ名となるようにマニフェストを書き変える.
コード変更を必要としないよう, 相対クラス名 (例えば .Foo) は
以前のパッケージ名を持つ絶対名に変更される.

--rename-instrumentation-target-package
全てのインストラメンテーション・コンポーネントを, 指定された
パッケージを対象とするようにマニフェストを書き変える. 改名
されたパッケージに対するテスト群を修正する意図で
--rename-manifest-package と一緒に使うと便利.

--utf16
リソースのデフォルト・エンコーディングを UTF-16 にする.
デフォルト・エンコーディングが UTF-8 である 7 以上の API
レベルのときのみ意味がある.





Android Asset Packaging Tool

aapt l[ist] [-v] [-a] file.{zip,jar,apk}

List contents of Zip-compatible archive.

aapt d[ump] [--values] WHAT file.{apk} [asset [asset ...]]

badging Print the label and icon for the app declared in APK.
permissions Print the permissions from the APK.
resources Print the resource table from the APK.
configurations Print the configurations in the APK.
xmltree Print the compiled xmls in the given assets.
xmlstrings Print the strings of the given compiled xml assets.

aapt p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml]
[-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile]
[--min-sdk-version VAL] [--target-sdk-version VAL]
[--app-version VAL] [--app-version-name TEXT] [--custom-package VAL]
[--rename-manifest-package PACKAGE]
[--rename-instrumentation-target-package PACKAGE]
[--utf16] [--auto-add-overlay]
[-I base-package [-I base-package ...]]
[-A asset-source-dir] [-G class-list-file] [-P public-definitions-file]
[-S resource-sources [-S resource-sources ...]] [-F apk-file] [-J R-file-dir]
[raw-files-dir [raw-files-dir] ...]

Package the android resources. It will read assets and resources
that aresupplied with the -M -A -S or raw-files-dir arguments.
The -J -P -F and -R options control which files are output.

aapt r[emove] [-v] file.{zip,jar,apk} file1 [file2 ...]

Delete specified files from Zip-compatible archive.

aapt a[dd] [-v] file.{zip,jar,apk} file1 [file2 ...]

Add specified files to Zip-compatible archive.

aapt v[ersion]

Print program version.

Modifiers:

-a
print Android-specific data (resources, manifest) when listing

-c
specify which configurations to include. The default is all
configurations. The value of the parameter should be a comma
separated list of configuration values. Locales should be
specified as either a language or language-region pair.
Some examples:

en
port,en
port,land,en_US

If you put the special locale, zz_ZZ on the list, it will perform
pseudolocalization on the default locale, modifying all of the
strings so you can look for strings that missed the
internationalization process. For example:

port,land,zz_ZZ

-d
one or more device assets to include, separated by commas

-f
force overwrite of existing files

-g
specify a pixel tolerance to force images to grayscale, default 0

-j
specify a jar or zip file containing classes to include

-k
junk path of file(s) added

-m
make package directories under location specified by -J

-u
update existing packages (add new, replace older, remove deleted files)

-v
verbose output

-x
create extending (non-application) resource IDs

-z
require localization of resource attributes marked with
localization="suggested"

-A
additional directory in which to find raw asset files

-G
A file to output proguard options into.

-F
specify the apk file to output

-I
add an existing package to base include set

-J
specify where to output R.java resource constant definitions

-M
specify full path to AndroidManifest.xml to include in zip

-P
specify where to output public resource definitions

-S
directory in which to find resources. Multiple directories will be
scanned and the first match found (left to right) will take precedence.

-0
specifies an additional extension for which such files will not be
stored compressed in the .apk. An empty string means to not compress
any files at all.

--min-sdk-version
inserts android:minSdkVersion in to manifest. If the version is 7 or
higher, the default encoding for resources will be in UTF-8.

--target-sdk-version
inserts android:targetSdkVersion in to manifest.

--values
when used with "dump resources" also includes resource values.

--version-code
inserts android:versionCode in to manifest.

--version-name
inserts android:versionName in to manifest.

--custom-package
generates R.java into a different package.

--auto-add-overlay
Automatically add resources that are only in overlays.

--rename-manifest-package
Rewrite the manifest so that its package name is the package name
given here. Relative class names (for example .Foo) will be changed
to absolute names with the old package so that the code does not
need to change.

--rename-instrumentation-target-package
Rewrite the manifest so that all of its instrumentation components
target the given package. Useful when used in conjunction with
--rename-manifest-package to fix tests against a package that has
been renamed.

--utf16
changes default encoding for resources to UTF-16. Only useful when
API level is set to 7 or higher where the default encoding is UTF-8.