watermint.org

Takayuki Okazaki’s blog

円高だということもあって思い切ってFlickrExport for Apertureを買ったのはいいものの、品質が低すぎて正直返品したい。良くても100枚一度にアップロードできない。ネットワークが途中で切断されたりすると必ずクラッシュする。ひどいときにはたった5枚すらアップロードする前にクラッシュ。しかも、単にpluginがクラッシュするだけではなくてApertureを巻き込んでクラッシュするのでたちが悪い。Nikon D90を買ってから急激に写真の枚数が増えてきたので、安定したアップローダがやっぱりほしい。jUploadrが一番安定しているから1000枚単位の大量アップロードをするときにはまだこれを使っているけど、いくつか不満な点が。
R0024191
その1。サムネイルを作る時間が長すぎるし、CPUを使いすぎる。jUploadrに写真をDrag & Dropすると写真のサムネイルを作って、その後、Photosetに入れたりするための操作ができるんですが、まずこのサムネイルを作るのにとても時間がかかる。1000枚単位の追加をするとCPUは20分ぐらい100%フル稼働でほかのことができない。そもそも、Apertureで整理した後なので、サムネイルを確認して一枚ずつ設定を変えることもないからサムネイル作成処理は僕にとって不要。ディレクトリ単位でアップロードの設定ができれば十分。個別にいじりたいときでもファイル名だけわかればとりあえずApertureで確認できるし、そのときに個別にサムネイルを作ってほしい。
R0024208
その2。Photosetsへの写真追加に失敗することがある。今まで試した、FlickrExport for Aperture、1001、jUploadr、Flickr Uploadr 2.x/3.xのいずれでも、あらかじめ設定しておいたとおりに写真がPhotosetsに追加されずに全部の処理が完了されましたと表示されてしまうことがあって、結局あとからもう一度手動でOrganizerを使って整理するのが必須でした。1000枚単位の写真をWeb画面で整理し出すと大変なので、このあたりの処理が確実に実行されるか、再試行について詳しく設定できるようにしてほしい。
R0024236
その3。未現像のRAW画像について設定ができない。これは仕組み上しょうがないんだけど、Apertureなどのような現像ソフトを使っている場合、RAW画像はAperture上で管理されていて、JPEGに書き出したファイルについてようやくjUploadrがファイルとして認識して、アップロードのタスクとして追加される。RAW現像にかかる時間もなかなか侮れなくて1000枚単位だと20分ぐらいはかかるし、いちいちRAW現像処理が終わったことを目視確認してからjUploadrにかけるなんてやってると、面倒くさい。なので、できればApertureなどであらかじめ指定したディレクトリにRAW現像するようにして、現像が終わったJPEGファイルからディレクトリに対して設定されたメタデータを元に随時アップロードするようにしてほしい。またアップロードができたJPEGファイルが完了後に自動削除されるオプションがあるとよりうれしい。
_DSC3935-D
そんなこんなで前々から作ろうと思っていたFlickrへのバッチアップローダを作り始めました。Flickr APIは認証周りの実装が面倒くさいので敬遠していました。どうせ作るなら安定して使えるJavaで実装したいのだけど、JavaのFlickr APIライブラリは古かったりアップロードAPIが実装されていなかったりするのも今まで着手していなかった理由の一つ。でも今回は、今実家にいてすることがないので、がっつりと時間を使って実装に集中することができ、とりあえず認証まわりとアップロードまでのライブラリが作りあがりました。次は、UIとアップロード順序の管理をするあたりを作ります。アプリケーションの名前はj20000という名前にしました。

id:Yoshioriさんの作ってるやつの別実装。id:taichitaichiさんのとも別の視点で作ってみました。ポイントは次のような感じです。

  • 一定時間しか保持しない、というよりは一定時間経過したエントリを隠す。
  • タイムアウトしたエントリの自動削除機構までは含まない(Mapの使われ方によって削除のタイミングはニーズが読めない)。ただし、明示的な削除方法は提供する。
  • 見えなくなる時間がわりと正確。ScheduledExecutorServiceを使って、正確なタイミングで削除されることを信頼しない。

この実装では、毎回Mapの操作に対してその時点でのスナップショットを作成します。snapshotの作成はわりと高価な作業ですが、それよりも時間に対して正確であることを目標としています。経験的に、Mapに数万エントリも入っていることはふつうの用途としてはまれなので、そこは目をつぶります。スレッドセーフとなるように気をつけましたが、テストはしていません。あ、全体的にテストはしていません。

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 指定時間以上経過したエントリを隠すマップ.
 *
 * @author takayuki
 */
public class TimeoutMap<K, V> implements Map<K, V>, Serializable {
    private final long timeout;
    private ConcurrentHashMap<K, ValueAndTime<V>> map
            = new ConcurrentHashMap<K, ValueAndTime<V>>();

    final class ValueAndTime<V> implements Serializable {
        private V value;
        private long time;

        public ValueAndTime(V value, long time) {
            this.value = value;
            this.time = time;
        }

        public long getTime() {
            return time;
        }

        public V getValue() {
            return value;
        }
    }

    final class EntrySetImpl<K, V> implements Map.Entry<K, V>, Serializable {
        private K key;
        private AtomicReference<V> value;

        public EntrySetImpl(K key, V value) {
            this.key = key;
            this.value = new AtomicReference<V>(value);
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value.get();
        }

        public V setValue(V value) {
            return this.value.getAndSet(value);
        }
    }

    /**
     * コンストラクタ.
     * @param timeout タイムアウト(ミリ秒).
     */
    public TimeoutMap(long timeout) {
        this.timeout = timeout;
    }

    private ConcurrentHashMap<K, ValueAndTime<V>> snapshot() {
        long t = System.currentTimeMillis() - timeout;
        ConcurrentHashMap<K, ValueAndTime<V>> snapshot
                = new ConcurrentHashMap<K, ValueAndTime<V>>(map);

        for (K key : snapshot.keySet()) {
            if (snapshot.get(key).getTime() > t) {
                snapshot.remove(key);
            }
        }

        return snapshot;
    }

    private Collection<V> snapshotOfValues() {
        Collection<V> values = new ArrayList<V>();
        Map<K, ValueAndTime<V>> snapshot = snapshot();

        for (K key : snapshot.keySet()) {
            values.add(snapshot.get(key).getValue());
        }
        return values;
    }

    /**
     * タイムアウトしたキーのエントリを削除.
     */
    public void gc() {
        map = snapshot();
    }

    public int size() {
        return snapshot().size();
    }

    public boolean isEmpty() {
        return snapshot().isEmpty();
    }

    public boolean containsKey(Object key) {
        return snapshot().containsKey(key);
    }

    public boolean containsValue(Object value) {
        return snapshotOfValues().contains(value);
    }

    public V get(Object key) {
        ValueAndTime<V> v = snapshot().get(key);
        return v == null ? null : v.getValue();
    }

    public V put(K key, V value) {
        ValueAndTime<V> prev = map.put(key,
                new ValueAndTime(value, System.currentTimeMillis()));
        return prev == null ? null : prev.getValue();
    }

    public V remove(Object key) {
        return map.remove(key).getValue();
    }

    public void putAll(Map<? extends K, ? extends V> map) {
        for (K key : map.keySet()) {
            put(key, map.get(key));
        }
    }

    public void clear() {
        map.clear();
    }

    public Set<K> keySet() {
        return snapshot().keySet();
    }

    public Collection<V> values() {
        return snapshotOfValues();
    }

    public Set<Entry<K, V>> entrySet() {
        Set<Entry<K, V>> entryset = new HashSet<Entry<K, V>>();
        Map<K, ValueAndTime<V>> snapshot = snapshot();

        for (K key : snapshot.keySet()) {
            entryset.add(new EntrySetImpl(key, snapshot.get(key).getValue()));
        }

        return entryset;
    }
}

追記: スレッドセーフにしたいなあと思って書いているといろいろ、ぼろが見つかりますね。gc()が実行されている間、put(Object key, V value)と、remove(Object key)の実行をブロックしないとエントリの欠落や削除漏れが出そうです。

リリース当時は結構さわっていましたが、今年はあまりさわっていなかったのでとりあえずインストールしました。NetBeans 6.5とプラグイン、あとProduction Suite (イラレ、フォトショ用のJavaFX書き出しプラグイン)はまた今度さわる予定です。インストールしたのはJavaFX 1.0 SDKです。コマンドラインからJavaFXアプリケーションの実行をサポートするためのコマンドとライブラリがインストールされます。
今のところJavaFX 1.0 SDKはWindowsとMac用に提供されていますが、以下Mac版の話として書いていきます。
JavaFX 1.0 SDK disk image
Mac版のバイナリはdmg形式で配布されていて、開くとパッケージインストーラが利用可能になります。今回のJavaFX 1.0 SDKはSunのアプリケーションにしては珍しく、”Macらしい”アプリケーション構成になっています。インストールすると、/System/Library/Frameworks/JavaFX.framework/ というディレクトリが作成され、Mac用JDKとほぼ同様のディレクトリ構成でインストールされ、/System/Library/Frameworks/JavaFX.framework/Versions/Current/bin/javafxなどのコマンドが /usr/bin/javafxなどにシンボリックリンクされます。
JavaFX 1.0 requires JDK 6
インストーラの情報ではJava SE 6が実行環境として必要と出ますが、Mac版ではJDK 1.5.0_13以降 (Tigerでも実行可)が入っていればよいので、うちのMacのようにCore 2 Duo (64bit CPU)ではないのでJDK 6が使えないという悲しい環境でもJava FXは動作します。
ところで、インストーラで気になったのが使用許諾契約。
Empty EULA
からっぽです。
Agree with empty EULA
どうしようもないので、同意しましょう。

先週はSunが2007年に発表して、暖めてきたネタであるJavaFXの1.0リリースが出ましたね。そのリリース日、飲み会の話の流れで何となくドメインをとってしまいました。javafx.jpです。openjfx.dev.java.net/jaもあるし、使いどころが謎ですが、使い道は飲み会ドリブンで考えていこうと思います。何かアイデアがあれば是非。

“リテラル”.equals(s)についての議論がはやりのようなので僕も便乗しておきます。個人的には

s != null && s.equals(”リテラル”)

でも

s == null || s.equals(”リテラル”)

でも、表題の

“リテラル”.equals(s)

でもどれでもいいと思っています。メンテナンスがやりにくいとか、明示的なnullに対する対処がわかりにくくなるとか、美しさとか色んなポイントで議論されてますが、個人的にはそれらの点についてどれも正論だと思いますが、重要だとは思いません。好きな方を使えばいいと思います。個人的には、だいたい“リテラル”.equals(s) を使いますが、s != null && s.equals(”リテラル”) もそこそこ使います。プロジェクトによって明確な方針が決まっていない限り、その日の気分で変わります(一番やっかい?)
この議論、最近プログラミングをしっかりやっていない身としては、二つの方言を比べて、どちらが良いか?を議論しているような雰囲気にしか見えないんです。日本で、人を招いて「お上がりください」といえば、靴を脱ぐことは暗黙の了解ですが、アメリカで靴を脱いであがってもらいたいときには「靴を脱いでお上がりください」と言わなければならないかもしれません。この違いは、文化的なコンテキストの違いで、プログラミングに話を戻せば、会社ごとの文化、プロジェクトごとの文化の違いでどこまで明示的に説明するかが変わってくるはずです。
あるプロジェクトに専任でずっとs != null && s.equals(”リテラル”)しか使わないプログラマなら、その知識だけで良い。でも、いろんなプロジェクトに参加するような立場にいるプログラマなら両方知っていて、両方ともメンテナンスできるような知識がなければいけないはず。
こう言うとスキルの差とか、教育コストの議論が出てくるかと思いますが、どちらのパターンでも正しく動作しているなら品質は同等で、メンテナンスも含めての品質を議論するのであれば、方針から外れた記法を機械的に検出して修正する業務フローまでを含めてようやく品質が語れるのではないかと思っています。


20代前半の頃はプログラミングをする際にコーディングスタイルはかなりこだわりがあったんですが、いまはほとんどありません。たぶん、この7〜8年間でたくさんのコードを読んで、色んな書き方に慣れてきたせいもあると思います。

11月21日はjava-ja第十二回。ちなみに、java-jaのイベントは第N回というように採番されていますが、実際には小数点があったり、実施される順序は必ずしも数字が小さい順ではないなど、表記揺れがあるので最新のイベントがどれなのかを知るには少し工夫が要るかもしれません。さて、この日は六本木にあるGREEさんのセミナールームでのjava-ja。六本木は絶対に立体迷路だと思います。元々少し遅れそうだったんですが、六本木一丁目駅に着いてからは少なくとも30分六本木をさまよいました。その原因はKYUKAさんもかかれていますが、すくなからずとも、GREEさんの会社案内デザインに因ると思います。
map.gif
こちらがGREEさんの案内図。僕は六本木一丁目駅の出口1に出れば、あとはこの地図通りに進めばよいはずでした。
六本木一丁目駅 出口1
一方こちらは東京メトロによる六本木一丁目駅の周辺地図です。僕はこちらの一番出口と書いてある方角に出ました。もう一度GREEさんの案内図と比べていただくと分かりますが、GREEさんの地図では、ぱっと見たところ、六本木ティーキューブの出口がさも出口1かのように見えますが、実際の出口はそれを通過しない、隣にIBMの建物が見える側です。このため、行けども行けども六本木4丁目という住所が見あたらない・・・。人目をはばからずダウンジャケットを抱えて、ぴちぴちのTシャツで六本木を歩き回りました。六本木を上記の情報でしか知らない僕としては、GREEさんの案内図は次のようにあってほしいなと思います。
map.png
ちなみに迷った30分の中には出口1にたどり着くためにかかった時間も含まれていますので、大遅刻の原因がGREEさんのせいではないことを付け加えておきます(99%僕に方向感覚が無いのが原因です)

目標のビルについてからも、全くビルが開いている気配がなかったんですが、java-jaこっち表示に勇気づけられようやく会場にたどり着けました。

会場にたどり着くと、太一さんがすでに発表中で、入ったとたんに何か話を振られてしまいましたが、空気が読めず気まずい感じに(どう答えるのが正解だったんだろう・・)

続いて小野さんのプレゼン。デザインパターンの話とかすごく良かったんですが、それよりも続々と届くケータリングが気になって話に集中できません。

あと、ymsr先生のチョイスだそうですが、会場に用意されたビールにGuiness!

すばらしい!なんて思いながら小野さんのプレゼンの間に2〜3本いただきました。す、すいません。

集金の列。

続いては山浦さんのレイアウトについての発表。参考になったのはある種の割り切り方です。Swingで悪名高いクラスといえば、扱いが複雑なGridBagLayoutですが、それをHTMLのTable相当に利用することに徹することで、うまく利便性と表現のバランスが保たれています。僕の方眼紙コンポーネントでも是非参考にしたいところです。

続いては櫻庭さんのSwing Synth。いつもながら櫻庭さんのプレゼンのクオリティーの高さには感心します。

Swingとは関係ありませんが (^^;; 、大人の科学 シンセサイザークロニクルでの演奏。D90のDムービーでも演奏を収録したんですが、とっさに構えたら縦位置で、うまくYouTube等にあげるようにまだ編集できていませんのでまた今度・・。縦横かえるだけでも意外と面倒なモンですね・・・。Photoshop Extended CS3ぐらいしか手元じゃ縦横変換できるソフトないし・・

僕の発表はかなりグダグダでした・・。すいません。何せまだモノがありませんからね・・・。少なくとも今年中にプロトタイプは作りたいと目論んでいるんですが。


二次会はニュージーランドのお店。Canterbury Draughtがおいてたらいいなあ、なんて思いましたがさすがにおいてませんでした。皆さんお疲れさまでした。

今月はなかなかブログ更新ができませんでした。たまっていた分、なんとか追いついていきます。時系列的には、このエントリで書く11月19日にあったJavaホット・トピックセミナーの前に、11月13日〜18日にマレーシアに行ってきたのでそのことについて書いておきたいんですが、まだ写真が整理&アップロードできていないのでそれはまた今度・・・。
さて、11月19日は定例のJavaホット・トピックセミナーでした。今回はJava Expert #3の「まるごとNetBeans」特集ということもあって、内容も盛りだくさん。ほんとは最初から聞きたかったんですが、引っ越しの準備とか、いろいろやっていたら用賀についたのは20時すぎ・・。正直なところほとんど、懇親会目当てでしたから内容もほとんど聞いていません・・・(すいません)。この日は、マレーシアから帰ってきたばっかりだったこともあって、まだSDカードに空きが無く(8GB+2GB+2GB)、カメラを持って行くのはあきらめました。ほかに16GBのSDカードが2枚あったはずでしたが、引っ越しのどさくさで行方不明に・・・。
翌日は朝から引っ越し屋さんがきて、荷物を運んでいく予定だったので、懇親会もちゃんと一次会で切り上げて、夜中に箱詰めしようとおもっていたら、この日に限ってなぜか二次会が渋谷でありました。朝までコースです。たぶん、Javaホット・トピックセミナーの懇親会で二次会はこの2年間のうちでも初じゃないかな。よりによって引っ越し前日に・・・。結局朝6時に帰って、1時間だけ寝て箱詰めを開始し、8時半頃には引っ越し屋さんがきて、少し箱詰めを手伝ってもらいながらなんとか引っ越しは完了しました(荷物運びという点で)。
エントリタイトルがJavaホット・トピックセミナーなのに、ぜんぜん関係なくてすいません・・・。

9784774136776.jpg
今週の金曜日(11月14日)発売の技術評論社からでるJava Expert #03、まるごと!NetBeansに、「GlassFishを活用したエンタープライズシステム開発」という記事で執筆しました。記事中では現在主流のバージョンGlassFish v2をメインに、最新のGlassFish v3 preludeまでをカバーしています。GlassFish v3 preludeについては先週正式リリースされましたが、執筆時期とのタイミングの問題で若干情報が古くなっています。紙面だと書ききれなかったところもたくさんあるので、引っ越しなどがある程度落ち着きましたらまたこのブログや、Glassfishユーザ・グループ・ジャパンなどでアップデートできればいいなと思っています。たぶん、Sun Tech Days 2008ではこのあたりがきちんとカバーされるでしょうから、こちらが要チェックですね。関連しそうなセッションで言うと、12月2日13:00〜13:50の寺田さんのGlassFish and the Future of Java EEというセッション。

2005年に始まったGlassFishのダウンロード数は既に年間500万に達し、コミュニティは活発に成長しております。コンパクトかつ標準仕様に忠実な Java EE アプリケーションサーバを維持しながら、現行のGlassFish v2では品質やパフォーマンスを改善し(Project Grizzly), クラスタ機能、スクリプト言語(Ajax, Ruby, Ruby on Rails)、高可用性機能、Comet、SIP相互運用等が拡張されております。オープンソースと企業ユーザから求められる豊富な機能とパフォーマンスを伴う高度な品質製品に境目はなくなりつつあります。次に予定されているGlassFish v3は次世代のアプリケーションサーバです。OSGiを用いたモジュラー設計で、Java EE 6仕様にアーキテクチャ準拠しております。Project GlassFish v3の実装ではサーバソフトウエアで重要な2つのキーワード(モジュール化と拡張性)にフォーカスしております。このセッションではGlassFish v2のクラスタ機能、Metro Webサービススタックと .NETとの相互運用性、Web層(Grizzly, Comet, jMaki…)のツールと管理機能の特徴について紹介します。さらに、より先進的な話題GlassFish v3の概要と拡張性高いサーバサイドプラットホームを実現する実装技法について紹介します。最後に Java EE 6 に予定される新機能についても触れる予定です。

50分のセッションでカバーする範囲がとても広いので、GlassFish v3 Preludeに関してあまりたくさん盛り込めなさそうですが、全体をざっと見渡すには丁度良さそうなセッションだと思います。

先週は米Sunの川口耕介さんがいらっしゃっていたので、関連するイベントにだいたい参加してきました。そういえばSECRETS of the ROCKSTAR PROGRAMMERSを持って行ってサインしてもらおうと思ったのに忘れてました。
_DSC2712
火曜日。Javaホットトピック・セミナーの特別号。
_DSC2716
MetroJerseyのお話でした。
_DSC2735
懇親会はわらわら。用賀のセミナーで笑笑といえば、一番奥のこの席がいつも定番です。毎回入店の際に2時間までと言われますが、今までのところ途中で追い出されたことはありません (^^;
_DSC2800
水曜日はアプレッソでのセミナー。会場に行くとその日発表されたばっかりの新型MacBookを持っている人が二人も!おそるべし・・。
_DSC2812
川口さんのプレゼンはsorcererについて。何かの勉強をするのにそこそこ複雑なお題が必要というのはいい言葉でした。最近やっている「方眼紙みたいなSwingコンポーネントを作る, その1, その2, その3, その4」は、ほとんどこれをきっかけです。具体的なテーマが決まったのはその後の懇親会でaqubiさんとお話ししたのがきっかけです。
_DSC2838
その後の懇親会は二日連続のわらわら。t_yanoさんがswitch文の使い方についてえらくdisられてたのが印象的でした。ただ、連日の飲み会続きで体調が悪くてあんまり記憶ありません。自業自得・・。
_DSC2857
木曜日はJJUG CCC(日本Javaユーザグループ クロスコミュニティーカンファレンス)。用事があったので午前中の基調講演や午後のセッションも聞けず、BOFから参加。エンジニアのためのキャッチコピーの作り方というテイストの違う内容でしたが、ブログなどで感想を見る限り、おおむね好評なようで安心しました。
_DSC2874
懇親会の会場につくとそこにはJJUGのかたまり(40人ぐらい)の予想を超えるjava-jaのかたまり(60人ぐらい)が・・・。java-jaおそるべし。
_DSC2898
懇親会の会場はぎゅうぎゅうな感じでした。最初に座った席の近くには日本Androidの会の方々がいらっしゃいました。Androidの会への入会を勧められ、せっかくのご縁なのでこの機会にAndroidの会へ入会しました。まだ公開されたAndroidのソースもダウンロードさえしていなければ、エミュレータさえ動かしたことありませんが・・。

_DSC2902
左からキムティさん、はっし〜〜さん、川口さん、きしださん、nekopさん。
_DSC2950
金曜日はグラジェーの飲み会。
_DSC2916
誰にTシャツを渡しているかすべて把握している寺田さんによってまだTシャツをゲットしていない方々にTシャツの授与が行われました。
_DSC2994
二次会は会場探しに困りました。さすが金曜日・・。なかなか空いてません。
_DSC3008
何とかは入れましたがぎゅうぎゅう・・。特に長身なnekopさんは窮屈そう。
_DSC3026
土曜日はcero-tさんの川口さんを囲む会。飲み会5日目ということもあって、ほとんど記憶ありません・・。
_DSC3030
二次会は中華料理店らしきところ。ドリンクの注文がうまく通りません。一つ確認に着たと思ったら、ひとつ忘れて帰るような感じです。でもあまりうるさくなくていいお店でした。
川口さんおつかれさまでした!

JBI(Java Business Integration)はJSR 208の仕様にあるとおり、単一Java VM内で動作させることを前提としています。このため、ESBを導入する必要があるような必然的に大規模なシステムでは何らかの形で分散環境のサポートが必要となります。オープンソースのJBI実装の一つであるOpen ESBの次期バージョン、Open ESB v3のJBIコアとなるProject Fujiでは先日公開されたMilestone 2よりこの分散環境サポートを取り入れ始めたようです。
overview.png
どういう風に分散環境サポートを入れてきたのか興味があったのでちょっと調べてみました。Fuji Distributed JBI Design & Architecture[wiki.open-esb.java.net] のアーキテクチャ図にあるとおり、二つのNMR(いわゆるバスの部分)をProxy BindingというBinding Componentによってつなぎ合わせるようです。シンプルでいいですね。
この図でもう一つ注目したいポイントはそれらProxy BindingはGlassFishのクラスタリング管理に使われているグループ管理フレームワークShoalを使っているところです。ShoalはP2P技術JXTAをベースに作られたグループ管理フレームワークで、動的に更新されるクラスタトポロジを扱うことができます。たとえば、ノード障害などによって非活性化したノードを動的に切り離したり、新たに追加されたノードを動的にグループに加えるなどです。
Project Fuji Milestone2での注目しておきたい拡張のもう一つはFujiランタイムの再活性化(Reactivate Runtime)です。説明によればこれはJava EEアプリケーションサーバなどで一般的になっているホットデプロイのJBI版と考えれば良いようです。Java EEアプリケーションサーバではホットデプロイのためにClassLoaderごとアプリケーションを捨てて読み込み直すのが一般的なのに対し、この再活性化機能ではきちんとLife cycleイベントに基づいて活性化するのだそうです。