システム開発で思うところ

Javaで主にシステム開発をしながら思うところをツラツラを綴る。主に自分向けのメモ。EE関連の情報が少なく自分自身がそういう情報があったら良いなぁということで他の人の参考になれば幸い

JSFで自動Redirect

JSFをちょっと便利にする仕組み

はじめに

JSFで遷移先を指定するときに、return xxx.xhtml と記述します。

リダイレクトしたい場合は

return xxx.xhtml?faces-redirect=true

と記述します。

というか、リダイレクトしないことは皆無に近しいので、必ず記述します。

毎回追記するのも面倒だし、typoした場合、実行時例外になるので 正直面倒です。

定数にして、必ず追記するように、というのも方法としてありますが、とにかく「約束事の記述」は そもそも減らしたいです。

対応方法

Interceptorを使って、アクションの戻り値(つまり遷移先のViewID)に強制的に付与します。

Annotation

@Stereotype
@Named
@RequestScoped
@Target(value = {ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@InterceptorBinding
public @interface Action {

}

Interceptor

@Priorityの値を大きく(つまり優先度を低く)しているのは、各Interceptorの実行後で一番初めに処理してほしいからです。
つまり遷移後の状態を、他のInterceptorの実行前にリダイレクトとなるように処置しておくことで 画面遷移周りの挙動の安定を図ろうという算段です。 *1

工夫したところとしては、遷移後の画面IDに単純に?faces-redirect=trueを付与するだけだと、 自画面遷移として戻り値をvoidにしたときにNullPointerExceptionになってしまうので 遷移前の画面IDを取得するようにしているところです。

@Interceptor
@Dependent
@Priority(Interceptor.Priority.APPLICATION + 100)
@Action
public class ForceRedirectInterceptor {

    @AroundInvoke
    public Object invoke(InvocationContext ic) throws Exception {
        String currentViewId = FacesContext.getCurrentInstance().getViewRoot().getViewId();

        Object resultViewId = ic.proceed();

        if (resultViewId == null) {
            resultViewId = currentViewId;
        }
        resultViewId += "?faces-redirect=true";
        return resultViewId;
    }
}

使用実装

リダイレクトの記述をせずとも@Actionがあれば自動でリダイレクトされます。

ちょっと、手を抜きますが、実際に動かすと きちんとURLがアクションにあわせて
「今 有効となっている画面のURL」になっていることが確認できると思います。

@Action
public class IndexAction {

    public String fwCaseOne() {
        return "case1/index.xhtml";
    }
}

Code

今回は提示なしです。 Conversationとの組み合わせと合わせて公開します。

さいごに

多くのWebフレームワークではリダイレクトをしないことがデフォルトで、「リダイレクトするときには、こうします」みたいな実装例が示されるように感じています。

でも 個人的に不思議なのが、Webシステムでの URLの表記や、2度押しなどのリスクを鑑みた場合に「リダイレクトをしない」ケースの方が稀な気がするのです。

少なくともWebアプリレベルで リダイレクトを する か しない か は決まると思うので、起動パラメータやConfigでON・OFFできるようにしたら良い気がするのですが、実際そんなフレームワークを知らないので需要は無いのかな?

リダイレクトするとき と しないとき の場合分けを仕組みとして持たせたい要望があれば、System Property とかで指定できるようにしようかなぁと思いますが、 自分がそのケースに遭遇する気がしないので、私は「リダイレクトは常にする」を強制するという、今回のInterceptorを標準的に適用しようと思っています。

*1:これは、今後書く予定のConversationScopedの自動endに関連するので、その時に生きてきますが、それはまた後日ということで