その他 TIPS

クライアントキャッシュ

0.8からクライアントキャッシュ機能が追加されました。クライアント内でキャッシュすることで、サーバ通信の負荷を低減することができます。

キャッシュの種類にはメモリ or ディスク及びLRU[Least Recently Used](LRUCache) or すべて保存(SimpleCache)を選択することができます。

これを利用するためには、ダウンロードしたディレクトリの「ソースディレクトリ/tests/test-cases/cache-case.scurl」が参考になります。

|| 例.
def cache = {create-simple-cache “test”, spec = {CacheSpec disk-persistent? = true}}

{if not {cache.exists? “hoge”} then
set cache[“hoge”] = 1000
else
{output cache[“hoge”]}
}

パフォーマンス改善アノテーション

@DoNotShareアノテーションを利用することで、サービスクラスの戻り値が大量データかつ同一データが少ない場合に改善する可能性がございます。

@DoNotShare
public List<LargeDto> getLargeDto(int num) {
….
}

このアノテーションは、従来バイナリデータ通信をする際に同一データが存在すると、2件目以降はリンク情報を持つだけで、実データを保持しないようになっております。これにより、高速かつコンパクトなデータ通信を実現していますが、同一データが少ない場合は、実際にアプリケーションによっては高速になる場合もあれば、遅くなる場合もございます。当機能はこれを解決する可能性のあるパラメータです。但し、どれほど高速になるかはアプリケーションやデータの種類によって変わりますので、当アノテーションをメソッドに付与して、検証をしていただく必要があります。

データ圧縮

通信時、データを圧縮することができます。これにより大幅にトラフィック量が減ることになります。これは、HTTP圧縮の仕組みを利用します。

リクエスト時、HTTPリクエストヘッダー部(Accept-Encodingにgzip)を追加します。クライアントでは、HTTPレスポンスヘッダーにContent-Encodingがgzipであれば、解凍します。

サーバで圧縮する際は、WebサーバのHTTP圧縮機能を使います。例えばTomcatで実施する際は、server.xmlを以下のように設定します。

<Connector
acceptCount=”100″
compressableMimeType=”application/octet-stream,application/x-javascript,text/html,text/xml,text/javascript,text/css,text/plain”
compression=”on”
compressionMinSize=”2048″
connectionTimeout=”20000″
disableUploadTimeout=”true”
enableLookups=”false”
maxHttpHeaderSize=”8192″
maxSpareThreads=”75″
maxThreads=”150″
minSpareThreads=”25″
noCompressionUserAgents=”gozilla,traviata”
port=”8080″
redirectPort=”8443″
/>

これで、HTTP圧縮機能を利用することができます。

また、クライアントで圧縮機能を利用するか否かは、インターセプターを利用します。インターセプターを作成し、build-http-request-headersメソッドをオーバーライドし、HTTPヘッダーに追加します。

{method protected open {build-http-request-headers
headers:#HttpRequestHeaders
}:#HttpRequestHeaders
{return
{if-non-null h = headers then
{h.set “Accept-Encoding“, “gzip“}
h
else
{HttpRequestHeaders “Accept-Encoding“, “gzip“}} 
}
}

詳しくはインターセプター利用のページを参照ください。

Google App Engine for Javaとの連携

Google App Engine for Javaを利用したクラウド上でORBを動かす記事をCodeZine上に公開しております。

web.xmlの設定

ORBではweb.xmlに4つのサーブレットと1つのフィルターを設定する必要があります。4つのサーブレットは、ダウンロードしたディレクトリにあるものをそのままお使いください。フィルター(DefaultFilter)は、以下のようになっています。

<!– DefaultFilter –>
<filter>
<filter-name>DefaultFilter</filter-name>
<filter-class>com.curlap.orb.servlet.DefaultInstanceManagementFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>DefaultFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

もしServlet2.5以上をご利用の方は、以下のようにurl-patternを4つのサーブレットのみに指定してください。(Servlet2.4では複数のurl-patternを指定できないです。)そうしますと、ORBへのリクエストのみこのフィルターを通るようになります。

<!– DefaultFilter –>
<filter>
<filter-name>DefaultFilter</filter-name>
<filter-class>com.curlap.orb.servlet.DefaultInstanceManagementFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>DefaultFilter</filter-name>
<url-pattern>/invoke-application-context</url-pattern>
<url-pattern>/new-instance</url-pattern>
<url-pattern>/invoke-http-session</url-pattern>
<url-pattern>/destroy-instance</url-pattern>

</filter-mapping>

また、コード生成の際に、サーバ側でclassファイルやjarファイルを読み込みますが、その読み込みを絞り込むことができます。これには以下のようにcontext-paramのcom.curlap.orb.generator.filterを指定してください。(カンマ区切りで指定します。)

<context-param>
<param-name>com.curlap.orb.generator.filter</param-name>
<param-value>org.apache.commons.,org.springframework.,org.seasar.,ognl.,org.aopalliance.,junit.</param-value>
</context-param>

さらにバージョン0.7からは、WEB-INF/lib配下にあるjarファイルやWEB-INF/classesをコード生成の再にロードするか否かを選択できるようになりました。これにより、不要なクラスをローディングしないようにできます。以下のようにcom.curlap.org.generator.load-filetypeにclassを設定しますと、WEB-INF/classesのみをロード、jarであればWEB-INF/libのみ、bothであれば両方をロードします。

<context-param>
<param-name>com.curlap.orb.generator.load-filetype</param-name>
<param-value>class</param-value>
</context-param>

Curlドキュメントのインストール

ダウンロードしたディレクトリのcurl/COM.CURL.ORB-V0.8-docにmanifest.mcurlがあります。これをCurlIDEのメニュー→ヘルプ→ドキュメントインストールで取り込むことができます。

staticメソッド及びフィールドの扱い

Curl ORBのコード生成ツールでは、Javaで定義したstaticなメソッド及びフィールドは、Curlのクラスプロシージャクラス変数として生成されます。

例えば、Javaのサービスクラスは以下のように生成されます。(クラスプロシージャはdefine-procとして、クラス変数は、letとして生成。)

Java

public class HogeImpl {
public static String str;

public static int getBarBar() {
return 210;
}
}

Curl

{import * from COM.CURL.ORB}

{define-class public HogeImpl {inherits ApplicationContextClient}

let public str:#String

{constructor public {default}
{construct-super.ApplicationContextClient “hoge1”}
}

{define-proc public {get-bar-bar}:int
{return {ApplicationContextClient.invoke-static “hoge1”, “getBarBar”}}
}
}

これは以下のようにアクセスできます。
・クラスプロシージャ : クラス名.メソッド名
・クラス変数 : クラス名.フィールド名

{do
|| クラス変数
{output HogeImpl.str}

|| クラスプロシージャ
{output HogeImple.get-bar-bar}}
}

Curl ORBのコード生成ツールを利用して、非同期通信のメソッドを生成することができます。(通常は同期通信を行うメソッドがコード生成ツールから生成されます。)

非同期通信用メソッドの生成

コード生成ツールのクラス編集画面のMethods欄に”async?“というチェックボックスがあります。これをチェックしますと非同期通信用のメソッドとしてコードが生成されます。

orb-async.jpg

以下が生成されたコードです。

{import * from COM.CURL.ORB}

{define-class public Hello {inherits ApplicationContextClient}

{constructor public {default}
{construct-super.ApplicationContextClient “hello”}

{method public {say-hello v0:String, …:EventHandler}:AsyncWorker
{return {self.async-invoke “sayHello”, arguments = {FastArray v0}, {splice …}}}
}

}

非同期での通信のため、処理を記述する際は結果を待つことなく次の処理に進んでしまいます。(例えば、以下の例ですとsay-helloメソッドを実行した後、終了結果を待たずに{output “xxx”}という行が実行されます。)そこで終了した際の処理を記述するため、生成されたコードの引数”…:EventHandler”に終了時のイベントハンドラーをセットします。この際、AsyncCallbackEvent (COM.CURL.ORB.SERVLETパッケージ内)というイベントを使用します。これによりsay-helloが終了しますと{on e:AsyncCallbackEvent do ….}の中の処理が実行されます。

{import * from COM.CURL.ORB}

{do
def hello = {Hello}
{hello.say-hello 
“this is test”,
{on e:AsyncCallbackEvent do
{if-non-null ex = e.exception then
|| Exceptionが発生した際の処理を記述
{dump ex}
else
|| 成功した際の処理を記述
|| 結果はAsyncCallbackEventのobjプロパティで取得できます。
def result = e.obj asa #String
}
}
}
{output “xxx”}
}

AsyncCallbackEventexceptionプロパティとobjプロパティを持っております。
処理中にExceptionが発生した場合は、このexception:#Exceptionをチェックすることができます。

戻り値を受け取る際は、obj:anyプロパティを参照しますと結果が受け取れます。(通常は上記コードのようにキャストすることとなります。

ヘッダー情報の送信

Curl ORBは、サーバへデータを送信する際、クライアントから任意の付加情報を送信可能です。また、アプリケーション全体に共通なヘッダ情報(グローバルヘッダー)とインスタンスの生成やメソッド実行毎に付与できるヘッダ情報(ローカルヘッダー)が利用可能となっています。

使用するAPI

set-global-headerプロシージャ 
get-global-headerプロシージャ 
clear-global-headerプロシージャ 
HttpSessionClientのコンストラクタ及び各メソッドのrequest-header:HashTableキーワード引数
ApplicationContextClientの各メソッドのrequest-header:HashTableキーワード引数
(COM.CURLAP.ORBパッケージ)

以下にサンプルコードを掲載しておきます。

|| グローバルヘッダーに値をセット
{set-global-header “global-k”, “global-v”}

|| ローカルヘッダーとして値をセット
def orb = {HttpSessionORBClient “tests1.Foo”, request-header = {HashTable “test-header-k”, “test-header-v”}}

※当ヘッダー送信機能はインターセプターを利用することで、実現可能です。(今後インターセプターを推奨としていく予定です。)

サービスクラスをクライアントでシンプルなコンテナ管理

サービスクラスをクライアントでコンテナ管理することで、サービスクラスをシングルトンとして扱うことができます。これには、set-service-to-containerプロシージャを利用して登録し、get-service-from-containerでオブジェクトを取得できます。コンテナ上では、シングルトンとして扱われ、無駄なオブジェクトを生成する必要がなくなります。

また、この際、サービスクラスごとにサーバURLが異なる場合に、サーバURLもserver-urlというキーワード引数を用いて登録しておくことができます。

|| サービスクラスの登録
||   引数は、1.任意の名称(String)、2.クラス(ClassType)、3.server-url = サーバURL(URL)

{set-service-to-container “ServiceA”, Foo}
{set-service-to-container “ServiceB”, Hoge, server-url = {url “http://nothing-url.com“}}

|| サービスの取り出し
||  引数は登録時に指定した任意の名称(String)

def foo = {get-service-from-container “ServiceA”}
def hoge = {get-service-from-container “ServiceB”}

注意)同一の名称で登録することはできません。
注意)サービスクラス(DI)のみ管理できます。 ※サービスクラス(HttpSession)は管理できません。
注意)シングルトンとして扱いたくない場合は、利用しないでください。