システム開発で思うところスマートフォン用の表示で見るシステム開発で思うところJavaで主にシステム開発をしながら思うところをツラツラを綴る。サーバのセッションで保持しているトークンを比較する、というやり方だと思います。やり方なので検索性も良いと思います。画面単位(タブ単位)でイイ感じに情報を管理してくれるスコープがありますので、それを前提としたやり方で試してみたいと思います。「そもそもConversation自体が面倒なんですが」ということについては、以前の記事の「フォルダ単位で会話スコープを制御する」を使用する前提です。2つの組み合わせ)を指定できるようにするデフォルトは「何もしない」アノテーションによって宣言的にする仕様の補足エラー判定後の操作エラー画面への遷移だけでなく、メッセージ出力とした理由は、確認画面をそのまま流用できるようにして実装負担を減らせる仕組みを設けた方が良いだろうと思ったためです。エラーではなく「登録は完了しています」というようなメッセージにしてユーザー自身による2重登録防止にする、というやり方もあるでしょう。f:viewActionで初期表示イベントによる操作が出来るので複雑なことも可能です。具体的には、f:viewActionでアクションと紐づけをして、ケース分けから自画面とそれ以外の画面への振り分けや、ログ出力やエラー通知などをするイメージです。上述のように、複雑なロジックが必要なことは「遷移先画面の指定」で実現すると割り切って、あくまでアプリケーションの実装としてはアノテーションによる宣言のみです。このやり方で落ち着きました。2重Submit対策はドメイン知識とは別の話なので、そういうものは極力「ロジック」に紛れ込まないようにする、つまり宣言的にしておく方が最適だと考えました。デフォルトの挙動2回目の操作時に「何もしない」をデフォルトとした理由は、プロダクトによって指針が大きく異なると考えたためです。お試し実装の範疇では、固定的な実装はしない、としました。エラーメッセージを出力することをデフォルトにしたければ、@ExcuteOnceのmessageのデフォルト値でメッセージIDを指定するなり、ExecuteOnceInterceptorのメッセージ編集で定数値をしていするなりすれば良いでしょう。エラー画面IDの指定も同様の対応で対処可能です。状態保持と更新ExecuteOnceLifecycleでは、内部で状態を保持しています。これは本クラスをConversationScopedとすることで考慮しています。後発のリクエスト時にjavax.enterprise.context.BusyConversationExceptionがスローされる仕様になっています。(略)スレッドセーフの考慮は不要です。P69とあるように、EEの仕組みとして状態を安全に保持できる仕組みが設けられています。不正な登録の実行は発生しません。*2会話スコープを任意のタイミングで終了する上述の過去の記事の実装では、xhtmlのフォルダが変わった時「だけ」がConversationScopeの終了条件でした。@EndConversationというアノテーションで、任意のタイミングで終了できるようにしました。サイクリックに行えるようになります。使わないでも出来た当初、メソッドに複数のInterceptorが適用される場合、ExecuteOnceInterceptorで対象メソッドを実行しなくても、別のInterceptorによって実行されるかもしれないと考えて、実行時例外によるハンドリングを検討しました。結論としては、それは杞憂でした。InvocationContext#proceed()をcallしなければ、それ以降のInterceptorが適用されることはありませんでした。*3使用方法2重Submitのトリガーとなるアクションに@ExecuteOnceを付与。実行できなくなります。各パラメータを使用したパターンについては、実行結果で示します。デフォルト時の挙動です。このままだとユーザーは困るので、通常は遷移先画面かメッセージ出力をして、何をしたらよいのか誘導した方が良いでしょう。完了画面へ遷移します。「戻る」で前の画面に戻ります。登録ログは更新されていきますが、画面は一切変化しないことが確認できます。2回目の操作で完了画面へ遷移これが一番オーソドックスだと思います。2度目以降の入力は無視して、とにかく完了画面へ遷移します。エラーメッセージも出力しません。"complete.xhtml";}<1回目の登録までは省略します>エラーメッセージありエラーメッセージのみを指定した場合、自分画面へ遷移してメッセージを出力します。画面上に、継続して処理したい場合のルートの情報をメッセージに出力しないとユーザーは不安になるでしょう。"メッセージ出力:既に登録済みです。重複登録を抑止しました。トップページへ戻ってください。本例だと、完了画面とエラー画面の違いは少ないですが、メッセージ出力タグがあったりして少しだけ違いがあります。1画面に複数仕様が混在しないので良いかもしれません。デメリットは、画面レイアウトの変更をした際、完了画面とエラー画面の両方をメンテナンスしないといけないところでしょうか?この辺りはJSFのコンポーネントなり、テンプレートなりを使えば手間は減らせるかもしれません。"エラー画面へ遷移:既に登録済みです。重複登録を抑止しました。新たに別の入力するか、トップページへ戻ってください。メソッドに@EndConversationを付与する事で会話スコープを終了できます。通常フローの完了画面で「さらに入力」で、入力画面に戻ったら会話スコープが終了した後、新たに開始されます。制御により、処理終了時に会話スコープは終了し、index.xhtmlの初期表示時に新たに会話スコープが開始されています。シーケンスをサイクリックに行えます。"index.xhtml";}<完了画面の登録までは省略します>実装と仕組み最優先のInterceptorで処理を行うことで、後続のInterceotorによって対象メソッドがcallされないようにします。Annotationデフォルト値は、空白とします。意味としてはNullの方が良いのですが、Nullは設定できないので空白です。"";}Interceptor2回目以降の状態の場合は、通常フローの画面遷移をしないようにします。コメントを入れました。確認画面の表示内容も更新されてしまいます。?最終的な更新は抑止できていますが、ユーザーからすると「あれ?結局どっちで登録されたの?」という状況になってしまいます。同じ印象を受けるでしょう。入力画面で使用したFormと確認画面以降で使用するFormを別にして、DoubleSubmitLifecycleの状態を使って複製する、しないという制御をする、ということが案として考えられます。入力から確認への遷移アクションをimmediate=trueにするような仕組みを実装する、という案でも良いかもしれません。検討したいと思います。BusyConversationException対策BusyConversationException(ConversationScopedにおける重複処理時で発生するException)に関する処理は考慮していません。やりたいと思っています。理由はBusyConversationExceptionに限らず、例外発生時に行う画面遷移制御について、もう少し自分なりに考えたいと思っているためです。クライアントのボタン抑止JavaScriptによる操作抑止はやっていません。過度に干渉するのは好ましくないと考えたためです。やった方がユーザーには親切です。いずれにしても、クライアント側の制御はクライアント側で採用したフレームワークに則って対応すると良いと思います。Ajaxでの2重Submit対策Ajaxでの2重Submit対策はやっていません。そもそもAjaxにおける2重Submit対策は、通常のWebアプリの2重Submitとは少し状況が違うように考えています。少なくともユーザーに不利益を生み出しかねない「注文」や「登録」のようなメインとなるユースケースをAjaxでSubmitする方式を適用するのは適切ではないと考えます。入力情報はサーバー側で保持しており、クライアントから送るのは「これで良いですよ」という通知となるSubmitだけです。なんら問題はないと考えます。JSFでクライアントと密に連携をとる事例として必要性を感じたときに試してみたいと思います。沢山の参考知見がありました。そこまでしなくて実現はできました。会話スコープのためにタグを追記しているのでは?というツッコミは容易に想像できますが、これは基本的に全てのページに共通して使用するテンプレートに実装するものであって、2重Submit防止制御をするページに都度適用するものとは違うと考えています。トークンタグの実装をしなくても良い、くらいかもしれません。一般的なやり方(タグでクライアントにトークンをhiddenで保持する)であっても、基本ルールを設けてしまえば「そういうものか」ということで特に困ることは無い気もします。コード内で直接制御するという方法*4のだって別に悪い手だとは思っていません。やり方もありますよ、というところでしょうか。目的を達成できる方式が試せたので満足です。(4)はてなブログをはじめよう!vermeer-1977さんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。