AMF48

AMF Encode/Decode Library for Java

サーブレットから、漏れの無いセッションリストを取得する必要が出て来たのですが
たしかセキュリティへの配慮から、このAPIは提供されていない認識です。

そこで、自分で既存のAPIを使って作り出すしかないのかなと思い、実装してみました。

package jp.develop.tool.servlet;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

/**
 * Session list providing Listener.
 */
public class SessionListListener implements HttpSessionListener, HttpSessionActivationListener, Serializable {

	private static final boolean DEBUG = true;
	private static final long serialVersionUID = 7490899888824580726L;
	private static final String SESSION_LISTENER_KEY = "_sessionListListener";

	private static Object lock = new Object();
	private static List sessions = new ArrayList();
	private static List sessionsForOutput = Collections.unmodifiableList(sessions);

	/**
	 * Get all sessions.
	 * @return All sessions list.
	 */
	public static List getSessions() {
		return sessionsForOutput;
	}

	/**
     * Default Constructor.
     */
    public SessionListListener() {
    	log(String.format("Session List Listener is initialized."));
    }

	/**
     * @see HttpSessionListener#sessionCreated(HttpSessionEvent)
     */
    public void sessionCreated(HttpSessionEvent event) {
    	synchronized (lock) {
    		HttpSession session = event.getSession();
    		session.setAttribute(SESSION_LISTENER_KEY, this);
        	sessions.add(session);
        	log(String.format("Session Added: %s", session.getId()));
		}
    }

    /**
     * @see HttpSessionListener#sessionDestroyed(HttpSessionEvent)
     */
    public void sessionDestroyed(HttpSessionEvent event) {
    	synchronized (lock) {
    		HttpSession session = event.getSession();
    		session.removeAttribute(SESSION_LISTENER_KEY);
        	sessions.remove(session);		// XXX May not good implementation for performance.
        	log(String.format("Session Removed: %s", session.getId()));
		}
    }

    /**
     * @see HttpSessionActivationListener#sessionDidActivate(HttpSessionEvent)
     */
    public void sessionDidActivate(HttpSessionEvent event) {
    	synchronized (lock) {
    		HttpSession session = event.getSession();
        	sessions.add(session);
        	log(String.format("Session Activate: %s", session.getId()));
		}
    }

	/**
     * @see HttpSessionActivationListener#sessionWillPassivate(HttpSessionEvent)
     */
    public void sessionWillPassivate(HttpSessionEvent event) {
    	synchronized (lock) {
    		HttpSession session = event.getSession();
        	sessions.remove(session);		// XXX May not good implementation for performance.
        	log(String.format("Session Passivate: %s", session.getId()));
		}
    }

	private static void log(String message) {
    	if (DEBUG) {
    		System.out.println(message);
    	}
	}
}

問題を見つけた方、是非、教えてください~

DevQuizが終わったので、結果をまとめておこうかと…

 回答数: 5000/5000
 L: 68088/72187
 R: 77650/81749
 U: 67642/72303
 D: 77117/81778

LRUDは、計17520個余っていますが、まだまだ削れるみたいですねぇ。
(5000問のうち、2509問だけは最適解と確認済)

【やったこと】 ※ちなみに、今回は評価関数を全く書いていませんw

1. 初期状態と、ゴール状態の双方から1手づつ展開
 ・パネルの状態を表す文字列がキー、経路が値のマップを作成しながら展開
 ・前方からの展開と、後方からの展開が一致すれば、経路をつないで回答とする
 ・この方法だと、メモリー使用量がネックになって、確実に解けるのは40手どまりでした…
 ・ただ、それでも頑張ってまわせば1500問以上は解けていた気がします

2. 問題を分割(左端、上端偏)
 ・左端あるい上端から1列づつ埋めて、問題を小さくして、総当りできるサイズに削る戦略
 ・ただし、袋小路などが出来ないように、隣接ブロック数が1つ以下の部分は移動対象に追加
 ・移動の対象にならないブロックを「?」に置き換えてパネルの状態を圧縮
 ・ゴールは、「0」がどこにくるかわからないので、可能性がある分準備…
 ・1列全てだと移動できない場合、分割して移動するように処理を追加
 ・部分移動に成功した場合、移動済の部分は変更しない条件付きで経路探索を継続
 ・ここまで書けば4900問ぐらいまで解けた気がします

3. 問題を分割(上下左右端偏)
 ・下側、右側から埋めていくことを許容
 ・下側などから埋めると「0」の位置が問題になるので、予めゴールの「0」を移動させておく必要あり(後で、その移動に使った分を、経路に追加しておく)
 ・ここまで書くと4996問まで解けたと記憶しています

4. 残り4問を個別対応
 ・3問は、初期に移動するブロックを指定すれば解けました。
 ・1問は、ゴール状態を指定(=「0」のずらし方を指定)でなんとか解けました。

5. この時点て3万ぐらいLRUDが足りなかったので…^^;
 ・初期に与える移動ブロックを2行とか3行にするなど、部分移動量を増加(ロングパス狙いw)
 ・初期ブロックの与え方を変えた2PGを実行し終わった時点で、8000ぐらい余裕ができて、無事ゴール♪

#150点獲得後は、総当りのPGを改良して、最適解がいくつあるのか数えていました…
#Java7のFork/Joinをお試しに使ってみたので、性能評価したら公開したいかも…

【結論】
力技でも、満点は取れる!w

いろいろ試行錯誤の結果、なんとかスライドパズルを5000問解いて
DevQuiz (Google Developer Day 2011に参加するための問題)で、
満点の150点を達成!

いろいろ書きたいこともあるけど、提出期間の終わる、来週の月曜まで
自粛規制っと…。今は画像だけ貼っておきますw

FullMarks

GAEが、Channel APIのクライアントとして、JavaScript用のライブラリしか
提供していないのでAdobe AIR(Flex)から使うのは難しいと思っていたのですが、
試しに挑戦してみたら動きそうですねぇ…

テストコードでは、動作したので、もうちょっと構成を整理して
動くようなら公開しようかなぁ…
(北陸のFxUGで、ライトニングトークぐらいはできそうか?w)

<2011/06/05追記>
AIR上で実行しているとトークンのタイムアウト(2時間制限)時に、
onerrorイベントが発生していない感じ…
おおよそ2時間メッセージを受け続けられるとこまでは検証したのだけど…
(100分ぐらいで、新しいトークンとるか、ハートビートを送って
取得できないようなら、新規トークン取得かなぁ…)

現在VectorのEncode部分を実装しているのですが、時間がかかりそうなので
既存の問題点部分だけリリースしてしまおうと…
(影響があるのは、Vector.<Object>のDecode時のみですが…)

#VectorのEncode機能を追加すると、EncoderのI/Fを変えないといけなく…orz