tohokuaikiのチラシの裏

技術的ネタとか。

ConfluenceのプラグインでXWork/WebWork2の仕組みを使ってプログラムを書く(3)

実際に、Actionを定義して書いてみる。

参考にするもの

参考にするのは、Confluence自体のソースコード
ソースコードは下記からダウンロードできる。*1
https://my.atlassian.com/download/source/confluence

たとえば

スケジュールされたのJob一覧を見るActionだとソースコードは以下のファイルになる。
それぞれ confluence-project/confluence-plugins/confluence-bundled-plugins/confluence-schedule-admin/ は省略している。

URL http://localhost:1990/confluence/admin/scheduledjobs/viewscheduledjobs.action
XMLファイル src/main/resources/atlassian-plugin.xml
Actionクラス src/main/java/com/atlassian/confluence/plugins/schedule/admin/action/ViewScheduledJobsAction.java
Velocityテンプレート src/main/resources/com/atlassian/confluence/plugins/schedule/admin/viewscheduledjobs.vm

管理画面の一般設定の入力画面だと、confluence-project/confluence-core内の

URL http://localhost:1990/confluence/admin/editgeneralconfig.action
XMLファイル confluence/src/etc/java/xwork.xml
Actionクラス confluence/src/java/com/atlassian/confluence/admin/actions/GeneralConfigurationAction.java
Velocityテンプレート confluence-webapp/src/main/webapp/admin/editgeneralconfig.vm

管理画面のメールサーバの設定値入力画面だと、confluence-project/confluence-core内の

URL http://localhost:1990/confluence/admin/mail/createmailserver.action?protocol=smtp
XMLファイル confluence/src/etc/java/xwork.xml
Actionクラス confluence/src/java/com/atlassian/confluence/admin/actions/mail/CreateMailServerAction.java
Velocityテンプレート confluence-webapp/src/main/webapp/admin/mail/editmailserver.vm

となっている。

Validateの最初の一歩

バリデーションは、

  1. *-validation.xmlによるもの
  2. アノテーションによるもの
  3. 自前チェック

の3つの中で、コアの部分については*-validation.xmlによるものが見られたが、自前チェックが多いようだ。

とりあえず、atlassian-plugin.xmlでの自前チェックを行ってみる。

Validateを有効にするには

atlassian-plugin.xmlの定義で、各Actionの下に、<interceptor-ref name="validatingStack" />を入れてやる。
パッケージ(package)に対してAction一括で指定したい場合は、<default-interceptor-ref name="validatingStack" />を入れてやる。

これをすることで、Actionの前に自動的にそのActionクラスのvalidateメソッドが走るようになる。

validateメソッドでやること

フォーム値をチェックして、エラーがあった場合にaddActionError("エラーメッセージ")をしてやる。

あるいは、addFieldError("フィールド名", "エラーメッセージ", new Object[]{getフィールド名()});でもError評価される。
エラーメッセージには、i18n用のPropertyで設定したラベルが使用できる。getTextとかで囲う必要なし。

Validateでエラーと判定されたら

指定されたActionメソッドにはいかないで、Actionメソッドで"input"が返されたのと同じ動作になる。

便利Validate(1) CSRFチケットチェック*2

Actionメソッドに@com.atlassian.xwork.RequireSecurityToken(true)アノテーションを入れておくとvalidateメソッド前にトークンチェックをしてくれる。トークンチェックで引っかかったらそれ以降のvalidateメソッドもActionメソッドも通過しないで"input"を返す。
なお、チケットの埋め込みは #form_xsrfToken() マクロを入れるだけでOK。

ハマりポイント

Javaビギナーの私がはまったポイント

Confluenceのプラグインは、XWork/WebWorkであって、Struts2ではない

考え方としてはほぼほぼ同じだが、違う個所もある。Struts 2入門(2)~バリデーションの仕組みを理解する(前編)~ (5/5):CodeZineのValidatorにおいては、

import com.opensymphony.xwork2.validator.annotations.*;

とXWork2を前提にしているが、Confluenceでは

import com.opensymphony.xwork.validator.*;

であり、XWorkである。アノテーションによるValidatorは使えないようだ。

スーパークラスコンストラクタ呼び出しのsuper()は1行目に書くこと

そんなことも知らないのか!って言われそう・・・。
参考:http://www.javadrive.jp/start/extends/index4.html

Actionクラスには、コンストラクタを作ってはいけない・・・execute以外のメソッドがコールできなくなる。

コンストラクタを作ってしまうと、methodとしてexecute以外のmethodがコールできない状況になった。
ActioinクラスにdoConfirmがあって、<action method="doConfirm" class="...">ってしてるのに、エラーで

java.lang.IllegalArgumentException: Method 'doConfirm()' is not defined in action 'class jp.junoe.confluence.plugins.conf2dita.sample.LivesearchAction'
at com.opensymphony.xwork.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:305)

とか言われてかなり謎であった。

struts2のインストールとか基礎知識 - ノウハウ共有サイト!

ちなみに、ActionBeanは、クライアントからアクセスするたびにインスタンス化されるので、コンストラクタには初期化のためのコードを書くことはできません。

サーブレットの際は、HTTPServletクラスの継承の可変引数を使ってやってガシガシと各種Managerを初期化してやったのでそのつもりでやってたらダメだった。いや、execute以外のメソッドがコールできなかっただけなのでそんなでもないのかもしれないけど。

atlas-cliで pi した直後はPage Not Foundって出ることがある

これはなぜかわからないのだが、プラグインインストール直後はPage Not Foundって出て、atlassian-confluence.logにも

2014-05-20 16:40:36,509 ERROR [http-1990-19] [atlassian.confluence.servlet.ConfluenceServletDispatcher] serviceAction There is no Action mapped for namespace /admin/XXXXXX and action name XXXXXXX

と出ているのだが、30秒ほど放っておくとちゃんと出たりする。

File Not Foundではなく、Javaエラーだった場合はこの限りではない。404の時だけあとでリロードすると直ってたりする。

うっかり<default-interceptor-ref>を指定しなくて「Formに値が渡らない!!」と3時間ほど悩んだこと。。

Validator周りをあれこれしているうちに
<default-interceptor-ref name="defaultStack" />
を削除してしまい、setter/getterによるフォーム値の渡しをOFFにしてしまっていた・・・・。

*1:My Atlassianのアカウントが必要

*2:AtlassianのドキュメントなどではXSRF