GAE/Jでhot deploy

仕事でGAE/Pを使っているので、Javaでもやってみたいと手を付けてみたらなんと標準でhot deployできなかった。

jRebelというライブラリでできそうという情報を発見

  1. intelliJ Ideaにプラグインがあるのでインストールする
  2. run with jRebel 'AppEngine Dev x.x.x' で開発用サーバ起動
  3. アクティベートしてくれと出るので有償のライセンスを買うか、https://my.jrebel.com に登録してアクティベートする

これでできる。

と思ったが、どうもうまく動いてくれない。

JavaFX 勉強会 & 忘年会に行ってきた

仕事残してきたから懇親会は行けなかったけど・・・

最近やってない間に結構よさげなFWとかが出てたり開発されてるみたいで、
デザイン音痴でもそこそこよさげな見た目にできるbootstrapライクなフレームワーク
(http://watermint.org/post/69825099011/designing-ui-framework-for-javafx)とか
MVCフレームワークとか(名前忘れたからスライドがアップされたら確認しておこう)

Glassfishとの連携についての話は、自分もjnetpcap使ってパケットの可視化するツール作ったことがあったので興味深かった。
androidも標準でチャートAPIみたいなの搭載されないかな。

今はandroidばっかだけど、javafxを業務で使うことも今後ある・・・かもしれないと感じさせられたイベントでした。

android Timezoneの罠

諸事情でEurope/Moscowの現地時間を出力する必要があったのだが、

タイムゾーンにEurope/Moscowを指定しても日本との時差がどうしても5時間にならない。
書いたコードを吐き気がするほど見直した後に以下のリンクを見つけた。

https://code.google.com/p/android/issues/detail?id=21377

まさか天下のgoogleが作ってるOSのマスターデータが誤りだとは思ってなかったのでハマってしまった。

アプリ使用者にタイムゾーン修正パッチ当てろとは言えないので、
この状態でモスクワの時間を取るにはどうすればいいかを考えることにした。

とりあえず使用可能なタイムゾーンの一覧を眺めた結果、
Europe/MoscowはGMT+4なので
Asia/Tokyoの代わりにEtc/GMT+9を、Europe/Moscowの代わりにEtc/GMT+4を使おうとした。
(表記の種別統一されてたほうが5時間の差であることが分かりやすいという理由から)

が、それもまた間違いだった。

日本はグリニッジ標準時+9時間、モスクワは同+4時間となっているが、
コード中のタイムゾーン名で使うのはEtc/GMT-9とEtc/GMT-4が正解。

2番目の間違いについてはもともとそうだったので言い訳のしようもない。
いつも日本時間はJST指定でEtc/GMT系のタイムゾーンは使っていなかったのがハマった原因だった。

しかしなんでプラスとマイナス逆なんだ・・・

android4.4のWebViewでpopupを処理できなくなる

このバグだった(原因分かるまで結構時間かかった)
https://code.google.com/p/android/issues/detail?id=62413

target="_blank"やjavascriptの別ウインドウ開くやつ(関数名忘れた)をWebChromeClient#onCreateWindow()で処理
しようとするとonCreateWindowに入らずに何も起こらない(クラッシュはしていなかったのでStacktraceには出なかったのが厄介)

*Property()メソッドの定義を忘れてはならない(戒め)

public class MyBean {

  private SimpleStringProperty name;
  // これを追加
  private SimpleIntegerProperty value;

  public MyBean() {
    name = new SimpleStringProperty();
    value = new SimpleIntegerProperty();
  }

  public SimpleStringProperty nameProperty() {
    return name;
  }

  // 追加したフィールドについてvalueProperty()の定義を忘れてしまう

  public String getName() {
    return name.get();
  }

  public void setName(String name) {
    this.name.set(name);
  }

  // getter, setterは(定義しないと使えないので)定義してた

  public int getValue() {
    return this.value.get();
  }

  public void setValue(int value) {
    this.value.set(value);
  }

上記のコードは一見エラーもなく動くけど、TableViewのカラムとバインドして
複数回中身を更新してたら、
「途中までは更新されてたけど、ある時点から更新されなくなった。」

諦め気味になるまで調査と試行錯誤した結果気づいた、*Property()定義し忘れ。
定義したらあっさり期待通りに動作しました。

他にタイプミスとかあると怖い。

ログファイルの遅延生成

ログファイルが常時存在するwebアプリだと意識しないけど、
デスクトップアプリだと気まぐれに起動して使わないまま落とすことがよくあるため、
日付付きのファイルなんか作ってると空ファイルがどんどん増えてゆく。

逐次空ファイルを消すよりはログ自体を遅延生成できた方が都合がいいという訳で
実現できる方法を探していた。

しかし日本語の情報が見つけられなかったので
lazy file creationとかで探してたら以下が見つかった。

LOGBACK-202

どうやらSiftingAppenderでラップするらしい。
discriminatorは必須要素なのか略するとエラーになった。

<appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
  <discriminator>
    <key>notuse</key>
    <defaultValue>notuse</defaultValue>
  </discriminator>
  <sift>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
      <file>hoge.log</file>
      <append>false</append>
      <layout class="ch.qos.logback.classic.PatternLayout">
        <pattern>%msg%n</pattern>
      </layout>
    </appender>
  </sift>
</appender>

<logger name="org.foo.Bar" level="INFO">
  <appender-ref ref="SIFT" />
</logger>

2013/04/16
The only down side is that SiftingAppender doesn't work well with RollingFileAppender.

しかし問題もあるようで、RollingFileAppenderをラップした場合はうまく動作しないらしい