カテゴリー「java」の43件の記事

2015/10/24

GWT2.5.1 on Super Dev Mode を今さらながら



GWT2.5はDevModeからSuperDevModeへの過渡期のもの(当時SuperDevModeはまだdraft的な扱いだったはず)なので、起動方法がちょっとこなれていません。
起動できるようになるまでにつまづきまくってしまったので、手順を記録しておきます (なお、聞くところによると2.6でもまた別のひっかかりポイントがあるとかないとか…)。

使用するソースコードはこちらです:
github.com/yukihane/hello-mvp4g/ (tag: tag/blog/20151024/super-dev-mode)
(別の用途で使用するために作成したリポジトリなので、名前は気にしないでください…)

初期コードのgreetingメソッド引数をString型から別のSerializableな独自型に変更したものになります。
ローカルに展開したらmainディレクトリに移動してください。

以下の手順で実行すればSuper Dev Modeで操作できるように成ります。
  1. (前述の通り)カレントディレクトリをmainに移します
  2. 次のコマンドを実行し、コードサーバを起動します。
    mvn clean process-classes gwt:run-codeserver
  3. コードサーバが起動したら、指示される通りブラウザで http://localhost:9876/ を開きます。
  4. これまた指示される通り、2つのブックマークレットをブックマークバーに登録しておきます。
  5. モジュール名(今回はMainModule)のリンクが表示されますのでそのページヘ飛びます。
  6. .gwt.rpcという拡張子のファイルリンクがあると思いますので、リンク先を保存します。今回は1ファイルのみです。
  7. 保存したファイルを target/main-0.0.1-SNAPSHOT/MainModule/ へ移動させます。
  8. 次のコマンドを実行し、webサーバーを起動します。
    mvn gwt:run
  9. アラートダイアログでコンパイルを促されると思いますので、先ほど登録したブックマークレット"Dev Mode On"を押しコンパイルを実行します。
これで正しく動作すると思います。コードを修正したら"Dev Mode On"を押して都度インクリメンタルコンパイルを行う必要があるのがDevModeと異なるところですね。

少し補足をしておきます。
コードサーバ起動前に明示的にmvn process-classesを実行しなければならないのはver.2.5特有のバグだそうで、2.6(or 2.6.1?)で修正されたそうです。
[INFO]       [ERROR] Errors in 'file:/home/yuki/programs/hello-mvp4g/main/src/main/java/com/github/yukihane/hello_mvp4g/client/MainModule.java'
[INFO]          [ERROR] Line 37: No source code is available for type com.github.yukihane.hello_mvp4g.client.GreetingServiceAsync; did you forget to inherit a required module?
[INFO]          [ERROR] Line 39: No source code is available for type com.github.yukihane.hello_mvp4g.client.Messages; did you forget to inherit a required module?
[INFO]       [ERROR] Unable to find type 'com.github.yukihane.hello_mvp4g.client.MainModule'
[INFO]          [ERROR] Hint: Previous compiler errors may have made this type unavailable
[INFO]          [ERROR] Hint: Check the inheritance chain from your module; it may not be inheriting a required module or a module may not be adding its source path entries properly
[INFO] [ERROR] Compiler returned false
また、gwt.rpcファイルを自身でダウンロードしdeploy無ければならないのも、後続のバージョンでは不要になっているらしいですので2.5特有の手順ということになります。
これを行わない場合、次のような例外が発生(Jettyのログに出力されます)し、GWT-RPCが行えません。
00:00:56.715 [WARN] greetServlet: An IncompatibleRemoteServiceException was thrown while processing this call.
com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException: Type 'com.github.yukihane.hello_mvp4g.shared.Information' was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' and did not have a custom field serializer. For security purposes, this type will not be deserialized.
    at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:323)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:206)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.handler.RequestLogHandler.handle(RequestLogHandler.java:49)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:324)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:647)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)
Caused by: com.google.gwt.user.client.rpc.SerializationException: Type 'com.github.yukihane.hello_mvp4g.shared.Information' was not assignable to 'com.google.gwt.user.client.rpc.IsSerializable' and did not have a custom field serializer. For security purposes, this type will not be deserialized.
    at com.google.gwt.user.server.rpc.impl.LegacySerializationPolicy.validateDeserialize(LegacySerializationPolicy.java:127)
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserialize(ServerSerializationStreamReader.java:651)
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.readObject(ServerSerializationStreamReader.java:567)
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader$ValueReader$8.readValue(ServerSerializationStreamReader.java:140)
    at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader.deserializeValue(ServerSerializationStreamReader.java:425)
    at com.google.gwt.user.server.rpc.RPC.decodeRequest(RPC.java:312)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall(RemoteServiceServlet.java:206)
    at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:248)
    at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:362)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.handler.RequestLogHandler.handle(RequestLogHandler.java:49)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:324)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
    at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:647)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)


参考:

2015/10/12

Eclipseのちょっと賢いスニペットプラグイン Code Recommenders

Ctrl+Alt+Spaceを押すと良い具合にコードを挿入してくれるEclipseプラグイン Code RecommendersSnipmatch機能 について説明します。(※本プラグインは他にも機能があるので、スニペットプラグインというと語弊がありますが、今回はそこしか使わないのでご了承を…)
前エントリからの流れ上、SLF4Jロガーコードを挿入する例になります。

05completion


  1. Help > Eclipse Marketplace... メニューからCode Recommendersをインストールします。Eclipseのエディションによっては最初からインストールされている場合も有ります。また、"Installed"となっていても、Snipmatchは入っていない場合もあるようですので、Installed > Change ボタンを押して Code Recommenders Snipmatch が本当にインストールされているか見てみてください。
    01marketplace
  2. Window > Show View > Other... メニューからSnippetsを選択します。2種類あると思いますが、"Code Recommenders"の方を選択してください。
    02view_select
  3. "logger"でフィルタをかけるとLOG4JとSLF4Jのスニペットがヒットします。今回はSLF4Jを使いますので"SLF4J Logger" をダブルクリックしエディタを開いてください。
    (ちなみにこのエディタ、ちょっとバギーな感じです…)
    03snippetview
  4. Dependenciesに設定されているorg.slf4j.api:org.slf4j.apiをRemoveし、保存します。
    04dependencies
あとは、.javaファイル中のフィールド宣言箇所でCtrl+Alt+Spaceを押し、"log"などの単語を入力すると候補が表示されるので選択すれば挿入されます。

上記手順中、Dependenciesの項目を削除しましたが、Mavenプロジェクトだとorg.slf4j:slf4j-apiと指定してやらなければ正しく機能しないようで、それならいっそ削除してしまっても(依存ライブラリでフィルタかけられなくても)いいだろう、という判断からです。
設定データは
[ワークスペース]/.recommenders/snipmatch/repositories/http___git_eclipse_org_gitroot_recommenders_org_eclipse_recommenders_snipmatch_snippets_git
でgit管理されていますので、間違って編集してしまった場合などでも簡単に元に戻せます。
また、同理由により設定の共有もさほど難しくないと思われます。

みんな本当にloggerをinjectionしたいなんて考えてるの?

CDIについてwebで検索していると、しばしばloggerをinjectするのが便利だと説明されているサイトに当たります。
以下に検索でヒットしたサイトの例と、そこに書かれているロガーをインジェクションすることのメリットあるいは動機を引用します。

ほとんどのサイトで、タイピングの手間が省けることがメリットであると書かれていました。
私からするとこの程度の手間の省略は、コンパイル時に解決できることを実行時まで遅延させる理由付けとしては弱すぎるように感じるのです。
世間一般的には十分受け入れられる動機なのでしょうか?
(例外からクラス名を取得するような手法を紹介されている方もいらっしゃいますが、これも同様に行うべきではないと私は考えています。)


単にタイプの手間を減らしたいだけであれば、私はIDEのスニペット機能を推奨します(次のエントリでEclipseの便利プラグインを紹介します。他のIDEでも似たような機能が有るでしょう)。
(ロギングのためだけに導入するのは首肯しかねますが、既に別の用途で導入済みであるなら)lombokの@Logアノテーションを用いてもコンパイル時に解決します。


JUL特有の問題の対策として、以下のように説明されている方がいらっしゃいました。
確かにコンパイル時には解決できないのでこのような理由でCDIを用いることには一理あると思いますが、一般的にはloggerはアプリケーション固有のクラスローダでロードする、すなわち設定は個別に行えるので一般的には当てはまらないと思います。
(そして更に言うと、一般的にはJULを使わない…ですよね?)


Java EE環境でjava.util.loggingとうまく付き合う方法 | Nishigaya's Tech Blog

複数のJava EEアプリが同一サーバ上に同居することを想定した場合、もう一つ注意すべき点があります。それは、「Loggerオブジェクトは、名前毎に生成されるJVMレベルでのシングルトン・オブジェクトである」ため、同居する複数のアプリが同じ名前のロガーを取得してしまうと、互いにロガーの設定に影響しあってしまう点です。先ほど、ロガー名にはパッケージ名を使用するのが望ましいということを述べましたが、異なるアプリでも共通のフレームワークやビジネスオブジェクト、バリューオブジェクトなどは同じクラス群をライブラリとして共通に使用するのが一般的で、そうするとどうしてもこれらのクラスについては、アプリが異なっても同じロガー名を使用してしまいます。せっかく、アプリ毎に異なるログファイルを設定しても、一方のアプリのログが他方のアプリのログに混じってしまうようなことが発生します。この問題を避けるためには、同じクラスを使ってもアプリ毎に必ず異なるロガー名が使われるようにしなければなりません。Java EE 6の環境であれば、アプリ名はJNDIリソースを通じてランタイムに取得することができますので、以下のようにして、ロガー名が”<アプリ名>.<パッケージ名>”のようにすることができます。

2015/10/10

Welding GWT-RPC with CDI @ConversationScoped

日本語の説明はこちら

Summary

For conversation propagation, request-parameter must have conversation-ID(cid).
Although, it is difficult to modify GWT-RPC request-body.
So, I try setting cid in request-header.
As a result, conversation is able to be propageted.

sample code(github)

Description

Environment:
  • Java1.8.0u60
  • Wildfly10Beta2(Weld2.3.0.CR1)
  • GWT(Google Web Toolkit) 2.7.0
For long-running conversation propagation, required "cid" parameter.
CDI1.2 spec:
6.7.4. Conversation context lifecycle
The long-running conversation associated with a request may be propagated to any Servlet request via use of a request parameter named cid containing the unique identifier of the conversation.
Weld doc:
5.3.2. Conversation propagation
We can force the conversation to propagate with a non-faces request by including the unique identifier of the conversation as a request parameter. The CDI specification reserves the request parameter named cid for this use.


Weld gets cid at ConversationContextActivator
And, we able to preprocess in Servlet Filter named "CDI Conversation Filter".
CDI1.2 spec:
6.7.4. Conversation context lifecycle
The container provides a filter with the name "CDI Conversation Filter", which may be mapped in web.xml, allowing the user alter when the conversation is associated with the servlet request.
There are also described:


Using custom filter, we able to create a request, not exist in request-body but providable cid.


Source shown at the beginning explain:
On GWT client side, set cid in header with RpcRequestBuilder.
On server side, define use of custom filter before CDI Conversation Fiter adaption with web.xml. Then, provide cid in header as request parameter with HttpServletRequestWrapper.

2015/09/21

GWT-RPCでCDI @ConversationScoped を成立させる

Here is English version.

要約

Conversationを伝播させるためには、リクエストパラメータにconversation IDを設定してやる必要が有ります。
しかし、GWT-RPCではユーザがリクエストボディ部を操作することは想定されておらず、困難なようでした。
そこで、クライアントからはヘッダ部にconversation IDをセットしておき、サーバサイドでその値をリクエストパラメータとして取得できるよう前処理を行うことにしました。
(これは、CDI処理前にサーブレットフィルタを用いリクエストを改変することで実現しています。)
この結果、GWT-RPCでもCDI @ConversationScoped機能を用いることができるようになりました。

ソースコード(github)

説明

試した環境は以下のとおりです。
  • Java1.8.0u60
  • Wildfly10Beta2(Weld2.3.0.CR1)
  • GWT(Google Web Toolkit) 2.7.0
long-runningな対話スコープを伝播したい場合、リクエストパラメータにキー"cid"(conversation IDの略でしょうか)の情報を含める必要があるそうです。
CDI1.2 spec:
6.7.4. Conversation context lifecycle
The long-running conversation associated with a request may be propagated to any Servlet request via use of a request parameter named cid containing the unique identifier of the conversation.
Weld doc:
5.3.2. Conversation propagation
We can force the conversation to propagate with a non-faces request by including the unique identifier of the conversation as a request parameter. The CDI specification reserves the request parameter named cid for this use.


ソースコードを眺めたところ、リクエストパラメータからcidを取得しているのは ConversationContextActivator であることがわかり、またドキュメントの以下の記述から、この処理を行う前にユーザープログラムで前処理を挟めることがわかりました。 "CDI Conversation Filter"という名前のフィルタの前に、独自のフィルタを設定しそこで処理すれば良いわけです。
CDI1.2 spec:
6.7.4. Conversation context lifecycle
The container provides a filter with the name "CDI Conversation Filter", which may be mapped in web.xml, allowing the user alter when the conversation is associated with the servlet request.
また、Weldドキュメントの 5.3.4. CDI Conversation filter, A.2.3. CDI Conversation Filter, FAQ: Why does Weld set character encoding of an HTTP request/response body to ISO-8859-1? で言及されています。

この機能を使い、クライアントで作成するメッセージはcidリクエストパラメータを含まないが、 Weldから要求された際にはcidを渡せる、ということが可能になります。


冒頭で示したソースコードについて説明します。
GWTクライアント側では、RpcRequestBuilderを用いてヘッダにConversation IDを設定するようにしています。
サーバ側では、web.xmlでCDI Conversation Fiterより前にカスタムフィルタを適用する旨を定義し、 このフィルタの中でHttpServletRequestWrapperを利用してcidリクエストパラメータを要求された際には予めヘッダに設定していた値を返すようにしています。

2015/07/16

Android Studio 1.3RC1 で finished with non-zero exit value 1

Android Studioで(他の人が作成した)プロジェクトをインポートしてビルドしてみると、以下のようなエラーがでることがたまにあります。
Error:Execution failed for task ':app:preDexDebug'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/usr/lib/jvm/java-8-oracle/bin/java'' finished with non-zero exit value 1
どうもAndroid Studio上ではこれ以上の情報が出力されないようなので、エラーの原因がわかりません。
そういう場合はコンソールでgradleコマンドを打ってみれば詳細が出ます。
上の例の場合だと :app:preDexDebug タスクで失敗しているとでているので、プロジェクトディレクトリで
$ gradle :app:preDexDebug
と打てばエラーがわかります。
ちなみに今回の場合は
com.android.dx.cf.iface.ParseException: bad class file magic (cafebabe) or version (0034.0000)
	at com.android.dx.cf.direct.DirectClassFile.parse0(DirectClassFile.java:472)
	at com.android.dx.cf.direct.DirectClassFile.parse(DirectClassFile.java:406)
	at com.android.dx.cf.direct.DirectClassFile.parseToInterfacesIfNecessary(DirectClassFile.java:388)
	at com.android.dx.cf.direct.DirectClassFile.getMagic(DirectClassFile.java:251)
	at com.android.dx.command.dexer.Main.processClass(Main.java:704)
	at com.android.dx.command.dexer.Main.processFileBytes(Main.java:673)
	at com.android.dx.command.dexer.Main.access$300(Main.java:83)
	at com.android.dx.command.dexer.Main$1.processFileBytes(Main.java:602)
	at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:284)
	at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:166)
	at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:144)
	at com.android.dx.command.dexer.Main.processOne(Main.java:632)
	at com.android.dx.command.dexer.Main.processAllFiles(Main.java:510)
	at com.android.dx.command.dexer.Main.runMonoDex(Main.java:280)
	at com.android.dx.command.dexer.Main.run(Main.java:246)
	at com.android.dx.command.dexer.Main.main(Main.java:215)
	at com.android.dx.command.Main.main(Main.java:106)
...while parsing com/example/SomeThirdPartyClass.class
1 error; aborting
というエラー出力なんですが、これはJava8を使用してコンパイルするとclass -> dex変換ができない、って怒ってるわけですね。
Android向けではない汎用JavaライブラリなんかをインポートするとターゲットJavaバージョンが明記されていないのでよく発生します。
build.gradleに
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
なんかを追記してやればいいでしょう。

これ以外では、exit value 2と出力され、同じクラスが複数クラスパスにある、っていうエラーに遭遇することも多いかと思います。
UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Multiple dex files define L;
    at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:596)
    at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:554)
    at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:535)
    at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:171)
    at com.android.dx.merge.DexMerger.merge(DexMerger.java:189)
    at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:454)
    at com.android.dx.command.dexer.Main.runMonoDex(Main.java:303)
    at com.android.dx.command.dexer.Main.run(Main.java:246)
    at com.android.dx.command.dexer.Main.main(Main.java:215)
    at com.android.dx.command.Main.main(Main.java:106)

:app:dexDebug FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:dexDebug'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/usr/lib/jvm/java-8-oracle/bin/java'' finished with non-zero exit value 2
同一ライブラリのバージョン違いなどが指定してあるとこのエラーになりますね。

2015/07/15

logmouse - yet another slf4j android binding, for testing

以前「ここが変だよ android.util.Log」 というエントリで、android.util.Log及びその仕様を引きずったslf4j-androidはちょっと使いづらいと書きました。
簡単に言うと、デバッグ目的でログを出すのに使うのだから、ログレベルを簡単に変えられるべきだがそうはなっていない、ということです。
で、できないならできるようにしちゃえ、ということで自分向けにslf4j-androidを書き換えましたので記録しておきます。
リソースファイルでログ出力レベルを決定できるようにしていますので、手軽に変更できるようになっています。
テストケースなどは一切書いていませんが、まあ、リリース版で使用するわけではないので…
名前はlogmouseです。logcatからの連想でつけてます。

インストール方法

MavenプロジェクトなのでMavenをセットアップしておいてください。
  1. yukihane/slf4j の feat/logmouse/1.7.12 ブランチをチェックアウトします(本家slf4jのv1.7.12からの派生版です)。
    $ git clone -b feat/logmouse/1.7.12 https://github.com/yukihane/slf4j.git
  2. slf4jディレクトリに入って一旦全部ビルドしてしまいます。
    $ cd slf4j && mvn clean package
  3. slf4j-androidディレクトリに入り、logmouseをmavenローカルリポジトリにインストールします。
    $ cd slf4j-android && mvn install
これでgradleプロジェクトから参照できるようになりました。

使用方法

いつものように /app/build.gradle のdependenciesに依存関係を追加してください。
compile 'com.github.yukihane.slf4j:logmouse:0.0.1-SNAPSHOT'

設定はlogmouse.propertiesに記述します。 /app/src/main/assets にファイルを置いてください。
defaultLoglevelでデフォルトのログレベルを、log.タグ名で各タグのログレベルを設定できます (この辺はSimpleLoggerに合わせました)。
ログレベル値はVERBOSE,DEBUG,INFO,WARN,ERRORのいずれかになります。VERBOSEの代わりにTRACEも使えます。
この辺はslf4jとandroid標準の兼ね合いで決めました(参考)。

補足

  • sl4jインタフェースで使えるロガーとしてはlogback-android があるようです。もしかしたらリリース版にはこれが使えるかもしれません(私はまだ触っていないので評価できません)。
  • 少し方向性は違うのですが、ロギングをテスト版/リリース版で切り替えたいという要望に対して Timber というブロダクトもあるようです。

2015/07/11

[読書]Getting Started with Hazelcast

第2版出版(されることを私が知った)記念に。 Kindle版は2015/08/05に入手できるようです。

 

 

Hazelcastとは、分散キャッシュ(Distibuted Cache)だとかインメモリデータグリッド(In-Memory Data Grid; IMDG)だとか呼ばれているカテゴリの製品です。
んじゃあ分散キャッシュ/IMDGとは何ぞやということなんですが、それについてはOracle Coherenceについての資料を読むと十分理解できるかと思います。
日本語ドキュメントも充実していますので、いきなりHazelcast関連ドキュメントから入るよりとっつきやすいかと。
今サクッとググってみた結果を以下に載せますが、他にも色々検索で得られると思います。

これらを流し読みした後、Mastering Hazelcast を読めばHazelcastがどんなものかはわかるかと思います。

IMDGプロダクトの性質は、RDBMSに近い感じでしょうか。どういうことかというと、

  • お試しで使ってみたり、自身のアプリケーションに組み込むことについてはさほど大変ではない
  • 性能を上げるための実装方法や、運用(障害対応など)にはノウハウがいる

みたいな。

 

さて本題の、読書感想ですが、読んだ当時(2014年末ごろ?)に書いていたものがありました。

この本で解説されているHazelcastのバージョンは2.6ですが、このレビューを記載した時点での最新リリースバージョンは3.4です。
変更されているAPIも多くあり、サンプルコードを動かすためのマイグレーションにも労力が必要です。

それに加えて、記載内容も基礎の基礎に留まっており薄いです。
出版当時の状況はわかりませんが、現在に至っては、公式サイトのドキュメント、及び公式サイト内にあるオンラインブック Mastering Hazelcast を読めば、この本以上の情報が得られます。

というわけで、今から第1版を購入するのはおすすめしません。

そもそも前述の通り、導入自体はさほど大変ではないので、その部分については無料で入手できる範囲の資料で事足りる気がします。

v2.6とv3.4をの差異を当時記録したものがありますので、万が一本書を読むようなら参考にしてみてください。

2015/07/10

Eclipse4.4(Luna)以降ではショートカットキー一発でラムダ式に変換できる

そういえば話題に挙がっているのを見たことがなかったので。

Eclipse4.4(ちなみに現時点での最新版は4.5(Mars))でJava8対応されましたが、ラムダ式に変換できる箇所で
Ctrl+1
のクイックアシスト機能でラムダ式に変換できます。逆変換も可能です。
Save Actionsにも設定が増えており、保存時にラムダ式に変換することも可能です。

参考:

2015/07/06

Android: 通常実行とJUnitテスト実行で共通のロギングコードを用いる

テスト対象コードにAndroidフレームワークのコードが入っていると、JUnitテスト実行時に例外が発生します。

java.lang.RuntimeException: Method e in android.util.Log not mocked. See https://sites.google.com/a/android.com/tools/tech-docs/unit-testing-support for details.
 at android.util.Log.e(Log.java)
 at yukihane.dq10don.login.LoginJsonParse.processHTML(LoginJsonParse.java:32)
 at yukihane.dq10don.login.LoginJsonParseTest.testProcessHTML(LoginJsonParseTest.java:42)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)

モック化して対処する(例外メッセージに記載されているURL先の記述通り、自動でモック化するオプションもあります)のが適切な場合もありますが、まずはじめに考えるべきことは、Androidフレームワークに依存しない処理に切り出し、それをテスト対象とする、ということでしょう。
ただ、今回はデバッグログ出力を行いたい処理(android.util.Logを使っている)であり、この対処は適切ではありません。
かといってモック化も不適切です(ログ出力が処理に影響を与えるわけではないので)。
そんなわけでloggerを使用して、Androidフレームワークを用いた実行(androidTestやエミュレータ/実機上での実行)と、JUnitテスト実行でlogging実装を差し替えることにしました。
検索した限りでは、Androidのデファクトスタンダート的なものはなさそうです。
今回はslf4j-androidを用いることにしました。
/app/build.gradle のdependencies内に以下を追記するだけです。

dependencies {
(略)
    compile 'org.slf4j:slf4j-android:1.7.12'
    testCompile 'org.slf4j:slf4j-simple:1.7.12'
}

この設定により、通常実行時にはslf4j-androidに含まれているandroid.util.Logを用いた実装が、JUnitテスト実行時にはslf4j-simpleを用いた標準エラー出力に吐く実装が使用されます。
(注: JUnit実行時には、classpath上に複数のlogging実装があると警告されます。どちらを選ぶかを明示的に選択することはできるのだろうか…)
なお、テスト実行で用いているsimpleloggerの設定は、 /app/src/test/resources/simplelogger.properties ファイルやシステムプロパティで行えます。
詳しくはjavadocを参照してみてください。

より以前の記事一覧

other sites

  • follow us in feedly
  • github
  • stackoverflow

ソフトウェアエンジニアとして影響を受けた書籍

  • Christain Bauer: HIBERNATE イン アクション

    Christain Bauer: HIBERNATE イン アクション
    理論と実践が双方とも素晴らしい製品であるHibernate。本書はそのプロダクトを書名に冠していますが、Hibernateを使うつもりがなく、ORマッピングの解説書として読むにしても十分な良書です。Second EditionとしてJava Persistence With Hibernateという書籍も出版されていますが、残念ながら現在のところ 和訳はされていません。-インアクションは2.xの、Java Persistence-は3.1の頃のものなので、最新版とはちょっと違うところもあることに注意。 (★★★★★)

  • アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版

    アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版
    クライアント/サーバシステムを構築する上で必要となる知識が総論されてます。Web技術者も、フレームワーク部分を開発するのであれば必読。 (★★★★★)

  • Joel Spolsky∥著: ジョエル・オン・ソフトウェア

    Joel Spolsky∥著: ジョエル・オン・ソフトウェア
    前述の書籍「ソフトウエア開発プロフェッショナル」をより砕いたもの、という感じでしょうか。 前書きではプログラマでなくSE向けの本のように書かれているが、プログラマが読んでも面白い本であると思われます。 SEになった新人(あるいはそういう会社に入る/入りたての人)にとっては、これからどういったことが仕事を遂行していく上で起こりえるのか、どのように考えて行なっていけばいいのか決定する助けになると思います。 元は″Joel on Software″というブログの記事で、web上でも一部日本語で読めます。 http://japanese.joelonsoftware.com/ (★★★)

  • ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか

    ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか
    問題解決(一昔前のの流行語で言うところの『ソリューション』)能力は、システムエンジニアのスキルとして備えるべきもののうちのひとつです。しかし、これは難しい。学校で出されるテストと違い、唯一の、(問題提出者が想定している)解を求めるだけが「問題解決」では無いからです。そもそも、何が問題なのか、それは本当に問題なのか、それは本当に解決すべき問題なのか、その問題解決方法は正しいのか、などを解決しなければ、「その解は正しいのか」に辿りつくことができません。この本の最も良いところのひとつは、本があまり厚くないこと。すぐに読めるし、何回も読み返す気になるでしょう。 (★★★★★)

  • スティーブ・マコネル: ソフトウエア開発プロフェッショナル

    スティーブ・マコネル: ソフトウエア開発プロフェッショナル
    コードコンプリートで有名なスティーブマコネルの著書。新人SEに読んで欲しい。個人として業界の中でどうあるべきか、組織としてどうあるべきか、SEのプロ意識とは?SEの心構え概論、といったところでしょうか。また、業界における資格の重要性についても説かれています。この業界では資格が特に軽んじられる傾向がありますが、この傾向はどんな弊害をもたらすのか、将来的にこの業界は資格に対してどのような姿勢で臨んでいくべきなのか。日経BP社では(他の出版社もだが)最近、似たような類いのあまり面白くない書籍が乱出版されていますが、この本は別格だと思うので安心して購入して欲しいと思います。 (★★★★★)

無料ブログはココログ