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/09/19

登録していたアプリがGoogle play store(Android Market)から削除された

marketに登録していたアプリ(github)が以下の理由で削除されていました。

Google では定期的な審査の結果、このアプリが他のサービスまたはプロダクトを妨害したり他のサービスまたはプロダクトに不正にアクセスしたりしていると判断しました。(デベロッパー販売 / 配布契約の第 4.4 項への違反)

 

この件によって最も強く確信したことは、流通経路が強く制限されているプラットフォームでの開発は可能な限り回避すべきだ、ということです。

(本アプリはさほど思い入れがあるわけでは無い(開発にさほど時間を費やしていない)のですが、それでも)著作物による自身の表現を制限されたのは非常に不愉快です。
私の著作物が本当に他者のサービス/プロダクトを妨害しているのであれば、それを律する根拠としてはサービス提供者との契約や法律が既に存在するわけで、プラットフォームプロバイダとの契約は余計なものです。
(※ あくまで今の私の状況では余計だ、というだけです。プラットフォームの普及率を利用して収益を増加させよう、といった状況等では妥当な対価である場合もあるでしょう。)

 

幸いにしてAndroidは自由にアプリをインストールすることができるのですが、他に、今私が開発しているもので配布に当たり余計な制約が課せられるものは…と考えると、Chrome Packaged Appsが該当しました。Chrome Packaged Appsはいわゆる野良配布ができないのです。
最近しばしば名前を聞くElectronというのは互換性がありそうなんですが、移行先としてどうなんでしょうね、また調べてみねば…というかいつも通りもうこれもJavaで良いかな…
C#の手習いにWindows Store Appもちょっと考えていたのですが、これも無しですね、やるなら普通にデスクトップアプリを作成します。

 

ところで、アプリの配布を停止させらたことに異議申し立てを行ったら開発者アカウントごと停止させらた、なんて話もあるみたいなので、私のような状況に陥った際に何かアクションを起こそうと考えた場合、慎重に行動したほうが良さそうです。

2015/07/23

Android: Nexus5(等)ではintent-filterのlabelに設定した文字列がランチャにアプリ名として表示されていない

ラウンチャーに表示される名前は、<intent-filter> の android:label に設定すれば良い、 という記事をいくつか見かけ、

そのとおりに実装したのですがなぜかうまくいかず、<activity> に設定したlabelが使用されてしまい悩んでいたのですが、どうもAPI21以降の不具合のようです。 既存のアプリに影響がある(後方互換性が崩れている)のがやっかいですね。

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/14

(Android界隈における)MVPのM

あらかじめ書いておくと、実装する上で本質的な話ではないです。

 

もともとMVPパターンのモデルっていうのは、MVCパターンが言うところのモデルと同じ、つまり やはりお前らのMVCは間違っている

 

モデルとはアプリケーションの本質そのもの

とか、あるいはThe Model-View-Presenter (MVP) Pattern   

model (both the business logic and the application data)

 

だと思うんですよ。一方で、Android界隈でMVPのモデルっていうと、 Architecting Android…The clean way? | Fernando Cejas にある図(クリックで拡大)

 

clean_architecture_android

 

にあるようなPresentation Layerに属すもの、要はMVVMパターンで言うところのViewModel (ただしデータバインディング機構は必須ではない)か、あるいはその元ネタデータの入れ物のことを指してることが多いんじゃないかと。    

 

ただ、冒頭に書いたことの繰り返しになりますが、これは別に本質的な話じゃなくて、単に呼び方が違うだけなのかな、と。   
他人が書いた文章を読むときにはどっちの意味でModelという単語を使っているのか意識しておきましょう、という程度です。



[追記]
本エントリは、人によってModelと呼んでる対象が違うので注意しましょう、ということが言いたかったものなので蛇足気味なのですが、たまたま今日こちらのスライドを見つけまして、かなり分かりやすかったので紹介しておきます。

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

Android Studioへのlombok導入手順

現在の安定最新版である1.2.2ではエラーが出たので無理でした。
1.3betaにアップグレードして導入しています。本日リリースされた1.3RC1でも今のところ問題ありません。
具体的な手順はこちらのREADME.mdをどうぞ。

関連:

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

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

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

参考:

AndroidプロジェクトにActiveAndroidを導入する

はじめに注意事項:

  • github上の溜まっているIssueやPull request、最終コミット日時などを見てみると明らかな通り、現在開発は停滞しているようです。導入するにあたっては(性能や機能比較だけでなく)このような状況も踏まえて、他プロダクトを含め検討した方がよいでしょう。
    • 別に私だけが難癖つけているわけではなく、例えば実際に使用しているらしいクックパッド開発者も同様の主張をしています

      ActiveAndroidの開発は停滞しており、issueやpull-requestは放置されていますから、ActiveAndroidの未来は明るくなさそうです。
      将来的にローカルのデータベースを使った機能を拡充したいことを考えると、ORMについてはそろそろ刷新する必要があると感じています。

    • ActiveAndroidの作者はより新しいOllieというORMの開発も行っているそうで、これもActiveAndroidの将来性の無さを感じさせます。(ちなみにOllieの開発も盛んではない模様…)
  • 私自身はかなり初期の評価段階で採用しないことを決めたので、ここに記載した以外の問題もあるかと思います。
ここで説明する導入の手順概要:
  1. ActiveAndroidのソースをcheckoutしてビルドする
  2. MavenローカルリポジトリにビルドしたActiveAndroidをインストールする
  3. Androidプロジェクト(gradle形式プロジェクト)に依存関係を加え使用できるようにする

ActiveAndroidのソースをcheckoutしてビルド、インストールする

公式モジュールが配布されているリポジトリがどこにあるかわからないので自前でビルドします。
Mavenのバージョン3.1.1をダウンロードして使用できるようにセットアップしてください。
これより古いバージョンではビルドできません。新しいバージョン(3.2.5以降?)でも問題が発生します。(補足を後述)

$ git checkout https://github.com/pardom/ActiveAndroid.git
$ cd ActiveAndroid
$ mvn clean source:jar javadoc:jar install -Dmaven.test.skip=true -Dandroid.sdk.path=$ANDROID_SDK_HOME

特定のバージョン(API 16)のシステムイメージに依存しています。ビルドに失敗する場合はその旨のエラーが出力されると思いますので、API16のSDK Platformをインストールしてください。

自分のプロジェクトからActiveAndroidを参照する

/build.gradle の allprojects > repositories を編集し、ローカルリポジトリを参照先に加えます(参考)。


allprojects {
    repositories {
        (略)
        mavenLocal()
    }
}

次に、/app/build.propertiesに依存関係を追加します。(参考)。
もしAndroid Studioが生成する/app/build.gradle をそのまま使用しているならば、 com.android.support:support-v4 が依存関係に追加されています。ActiveAndroidが依存している com.google.android:support-v4 とこれは競合するので除外しておきます。


dependencies {
    (略)
    compile ('com.activeandroid:activeandroid:3.1-SNAPSHOT') {
        exclude module: 'support-v4'
    }
}

以上でActiveAndroidが利用できるようになります。

補足: 最新バージョンのMavenでActiveAndroidをビルドできるようにする

Maven3.3.3でビルドすると以下の例外が出て失敗します。


Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/aether/spi/connector/Transfer$State
    at org.eclipse.aether.connector.wagon.WagonRepositoryConnector$GetTask.run(WagonRepositoryConnector.java:608)
    at org.eclipse.aether.util.concurrency.RunnableErrorForwarder$1.run(RunnableErrorForwarder.java:67)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: org.eclipse.aether.spi.connector.Transfer$State
    at org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy.loadClass(SelfFirstStrategy.java:50)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.unsynchronizedLoadClass(ClassRealm.java:271)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:247)
    at org.codehaus.plexus.classworlds.realm.ClassRealm.loadClass(ClassRealm.java:239)
    ... 5 more

これは利用しているandroid-maven-pluginというMavenプラグインのバージョンが古いのが原因らしいので、新しい物を使用するように設定変更すれば最新Mavenでもビルドできるようになります。
以下のPull requestをmergeしてください。

2015/07/09

android-aptとは何か

androidプログラムでアノテーション処理プログラミングを行う際のサポートを目的としたAndroid Studio向けのGradleプラグイン。複数の実装があり(1, 2)、いずれもandroid-aptと呼ばれることがあるが、特に共通仕様があるわけではない。

初めてandroid-aptという単語を見た時は、android環境向けのaptコマンド実装か何かかと思いましたが、全然違いました。非常に紛らわしいネーミングだと思います。
Android Studioを使用しないのであれば不必要なものですし、android実行環境に特化したものというわけでもありません(例えば.dexファイルを処理する、といったようなことを行うものではない)。

«Qiitaって何なのさ

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社では(他の出版社もだが)最近、似たような類いのあまり面白くない書籍が乱出版されていますが、この本は別格だと思うので安心して購入して欲しいと思います。 (★★★★★)

無料ブログはココログ