『Seasar2 徹底入門』

Seasar2 徹底入門』を読みましたが、これは良書!
Seasar2使いの方は必携の一冊ですね。

Seasar2徹底入門 SAStruts/S2JDBC対応

Seasar2徹底入門 SAStruts/S2JDBC対応

SAStrutsの認証チェック処理をどこでやるか、HOT DeployとCOOL Deploy時の挙動の違いの制御方法など、誰もが最初は悩む問題だと思いますが、本書ではそういったハマりどころを解決するためのノウハウや拡張方法もしっかりと載っている点がポイント高いです。

リファレンスとしても十分活躍するだけの情報が載っているので、開発中は側に置いておきたい一冊です。

書籍のタイトルには『入門』とありますが、これからSeasar2を始める方はもちろん、Seasar2を普段なんとなく使っている人(自分もですが、、)にとっても、Seasar2をより深く知るための助けとなる一冊だと思いました。

データバインディング式で比較演算子を使う際のバッドノウハウ

唐突にAdobe Flexのお話。

Flexにはデータバインディングという便利な機能があるのですが、

今回、とあるボタンの有効/無効の切り替えのために、

<mx:Button label="実行" enabled="{ hogehoge.length < 5 }" />

という比較演算子を使った条件を書くと

Error: The value of attribute "enabled" must not contain the '<' character.

というコンパイルエラーが発生しました。


色々と試してみた結果、コンパイルエラーを回避する方法は以下の2つ。

1. '<'(小なり)ではなく、'>'(大なり)を使う

<mx:Button label="実行" enabled="{ 5 > hogehoge.length }" />

2. '<'を実体参照'&lt;'で書く

<mx:Button label="実行" enabled="{ hogehoge.length &lt; 5 }" />

他にも

論理演算子に使う'&'(アンパサンド)も使えないみたいです。

XML の解析規則により、MXML ファイルのデータバインディング式でアンパサンド文字 & を使用する場合は、16 進数に相当する文字 & に置き換える必要があります。例えば、ActionScript で記述された論理 OR 式で && を使用する場合は、次の例が示すように && のように記述する必要があります。

<mx:Button label="Test" enabled="{authorized &amp;&amp; cc}" /> 
データバインディング式でのアンパサンド文字の使用 - Adobe Flex 3 ヘルプ

データベースACL利用時のプラグインコントローラへのアクセス制御方法

Cake のデータベースACLについて勉強するべく、CookBookにある開発例 『http://book.cakephp.org/ja/view/641/Simple-Acl-controlled-Application』 を試してみました。

実際にやってみて、『プラグインコントローラへのアクセス制御はどうすれば?』という疑問が沸いてきたので調べてみました。

準備

上記の開発例に従ってセットアップした後に、CookBookにあるプラグイン作成のサンプルである『ピザ注文プラグイン』を作成した状態を前提として説明を行います。
http://book.cakephp.org/ja/view/115/Creating-a-Plugin

アクセス制御方法

プラグイン毎にトップレベルのACOを用意してあげることで制御ができます。

まず、acosテーブルに新たなトップレベルのACOとして、"pizza_plugins"を追加して、その子ノードとしてPizzaOrdersController関連のACOを追加します。(aros_acosテーブルに追加したACOパーミッション設定も必要)

acosテーブル
+----+-----------+-------+-------------+---------------+------+------+
| id | parent_id | model | foreign_key | alias         | lft  | rght |
+----+-----------+-------+-------------+---------------+------+------+
|  1 |      NULL |       |             | controllers   |    1 |   58 |
|  2 |         1 |       |             | Pages         |    2 |    5 |
|  3 |         2 |       |             | display       |    3 |    4 |
〜〜 省略 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
| 30 |      NULL |       |             | pizza_plugins |   59 |   64 | ← 新しいトップレベルのACO
| 31 |        30 |       |             | PizzaOrders   |   60 |   63 | ← pizza_pluginsが親ノード
| 32 |        31 |       |             | index         |   61 |   62 | ← PizzaOrdersが親ノード
+----+-----------+-------+-------------+---------------+------+------+

そしてAppController::beforeFilter()内で、「通常のコントローラへのアクセス」と「プラグインコントローラへのアクセス」を振り分け、それぞれのトップレベルのACOをAuthComponentにセットしてあげれば、正しくアクセス制御が行われます。

<?php

    function beforeFilter() {
        $this->Auth->authorize = 'actions';
        $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login');
        $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login');
        $this->Auth->loginRedirect = array('controller' => 'pages', 'action' => 'display');

        if( empty($this->plugin) ) {
            $this->Auth->actionPath = 'controllers/'; 
        } else {
            // トップレベルのACOは プラグイン名 + '_plugins'
            $this->Auth->actionPath = $this->plugin . '_plugins/' ; 
        }
    }

?>

CakePHPのリハビリ中

次の案件はCakePHPを使えそうな感じなので、ただいまリハビリ中。
一年ぶりなので忘れていること多いなぁ。。。

9ヶ月も放置していたこのブログですが、またマイペースにCakePHPネタでも書いていこうと思います。

まずは難解なACL周りを理解せねば。

ファイルアップロードの上限サイズを超えてもindexメソッドに制御が移らないケース

ファイルアップロードの上限サイズを超えた場合について、SAStrutsのサイトでは以下のように記述されています。

アップロードされたファイルのサイズが、あらかじめ決められた上限(struts-config.xmlのcontrollerタグのmaxFileSize属性の値) を超えた場合、SizeLimitExceededExceptionがスローされます。

この例外が発生した場合は、indexメソッドに制御が移るので、 indexメソッドで、SizeLimitExceededExceptionの存在をチェックすることで、アップロードされたファイルが上限を超えたのかチェックすることができます。

Super Agile Struts - Tutorial


しかし、自分の環境では何故かindexメソッドに制御が移らず、フォームがPOSTされていないような挙動になりました。

何が違うのかチュートリアルと見比べてみた所、フォームをサブミットする際にaタグ+javascriptを使っていたのが原因のようでした。

以下はチュートリアルのアップロードのサンプルを簡素にした例。

<s:form enctype="multipart/form-data">
<input type="file" name="formFile" />

<input type="submit" name="upload" value="アップロード"/>
</s:form>

次にこちらは、上記の例を自分のソースではどのように実装していたかというもの。

<s:form enctype="multipart/form-data" action="/upload/upload/">
<input type="file" name="formFile" />

<a href="javascript:document.uploadActionForm.submit();">アップロード</a>
</s:form>

submitボタンのname属性で実行メソッドを渡す代わりに、formタグのaction属性で実行メソッドを自分で指定して、aタグのjavascriptでフォームをサブミットしていました。

なぜこんなことをしているかというと、デフォルトのボタン系タグではデザイン的に寂しい感じなので、aタグとdivタグにスタイルシートを効かせて疑似ボタンをデザインしているため。


疑似ボタンをsubmitボタンにするという対応方法はナシなので、以下の方法で代替することにしました。

aタグをクリックした時に、javascriptで非表示にしたsubmitボタンをクリックするという方法。javascript部分はjQueryを使っています。

<s:form enctype="multipart/form-data">
<input type="file" name="formFile" />

<input type="submit" name="upload" value="アップロード" style="display:none;"/>
<a href="javascript:$(':submit').click();">アップロード</a>
</s:form>

これでちゃんと上限サイズを超えた場合にindexメソッドに制御が移るようになりました。

Hot Deploy利用時に、Hot Deploy対象外クラス内でHot Deploy対象クラスを使うための正しい作法は?

「Hot Deploy」「Hot Deploy」連呼によって、今までで一番長いタイトル。。

気を取り直して本題を。

SeasarのHot Deploy機能はアプリケーションサーバーの再起動不要で修正コードがすぐに反映されるという便利な反面、ちょっとフレームワークの拡張をした時に「Hot Deployでは動くのにCool Deployでは動かない」(またはその逆)といったことにハマることが多いように思います。

S2Container本体やHot Deploy機能の理解が浅いせいもあって、自分も何回泣かされたことか。。。


今回もSAStrutsを使っていて遭遇してしまいました。

Hot Deploy対象外クラス(RequestProcessorやtaglibなど)内で、HotDeploy対象のServiceクラスやDtoクラスをgetComponentするような以下のコードを書くとClassCastExceptionが発生し、Cool Deployにすると正常に動作する、といった現象に遭遇しました。

	HogeService service = SingletonS2Container.getComponent("hogeService");
	service.findAll();

SeasarのHot Deploy機能がクラスローダを毎回差し替えていることから起因するものと推測し、「コンテナからオブジェクトとクラスを取得してリフレクションを使う」という以下の方法でやってみたところ、ClassCastExceptionは発生せずにうまく動作しました。
ちなみにClassUtil、MethodUtilもSeasarのリフレクション用Utilクラスです。

	S2Container container = SingletonS2ContainerFactory.getContainer();
	Object serviceObject = container.getComponent("hogeService");
	Class serviceClass = container.getComponentDef("hogeService").getComponentClass();
	Method method = ClassUtil.getMethod(serviceClass, "findAll", null);
	MethodUtil.invoke(method, serviceObject, null);

一応の解決は出来たのですが、すごく場当たり的な対応の気が。。

こういった問題にはSeasar的にもっと正しい作法があるように思うのですが、、、どうなんでしょうか?