- PR -

Webアプリの二重送信対応

投稿者投稿内容
sinh
ベテラン
会議室デビュー日: 2003/01/22
投稿数: 87
投稿日時: 2004-05-26 23:22
お世話になります。

現在、JavaでWebアプリケーションを作成しているのですが、
ブラウザからボタンがダブルクリックされた場合の対処で
困っています。

今回の方針でダブルクリックされた場合は
1回目のリクエストは処理をし
2回目のリクエストは無視(処理無し)をし、
ブラウザには1回目の結果を表示する
となりました。


1回目のリクエストと2回目のリクエストの区別は
画面(JSP)にhiddenでユニークIDを埋め込んでおき
それがSessionに保存されているかどうかで
判断していますが、
2回目のリクエストを無視して1回目のリクエストを処理し
結果を表示させることができていません。
どのように実装を行えば、可能となるでしょうか?

環境としては下記となります。
JDK1.4.2
Tomcat4.1.30
Struts1.1

また、今回JavaScriptは使用しないことが決定しています。

よろしくお願いします。

Dr.Doraemon
ぬし
会議室デビュー日: 2002/03/23
投稿数: 265
投稿日時: 2004-05-27 00:17
お疲れ様です。

 JSPは、マルチスレッドで動作していますので、2回目のリクエストを無視するというのは、かなり難しいのではないのでしょうか?

 通常では、JavaScriptで、変数でフラグを持っておいて処理をさせないようにするのが一般的ですが、今回はJavaScriptを利用しないとのことですから、1回目のリクエストの情報を、処理したresponseを、2回目のリクエストのレスポンスに戻す作業をしないといけないわけですからね・・・。

 スレッド周りの処理をよく考えてやれば出来るかもしれませんが、リクエストが仮に3回送られたらなどと考えるとかなり難しいのではないでしょうか?

 なお、JavaScriptを使わない方針とのことですが、これはなぜでしょうか?
携帯電話などであれば、わかるのですが、もし、パソコンでも運用であれば、JavaScriptを使っても大きな問題はないと思いますが・・・。
Anthyhime
ぬし
会議室デビュー日: 2002/09/10
投稿数: 437
投稿日時: 2004-05-27 01:01
まずリクエストを同期化させる必要があります。こちらはセッションオブジェクトをsynchronizeすればいけるでしょう(アプリケーションサーバによりますが)。
難しければDBMSを利用するなどの方法を検討してください。
次におっしゃられているようにセッションにそのユニークなIDがあるか検査します。
もしないのであれば処理を続行し処理結果をセッションに保存しJSPにリダイレクトし、JSPはセッションから処理結果を取り出し表示します。
もしあるのであれば処理を行わず即座にJSPにリダイレクトします。
これで達成可能なはずです。
sinh
ベテラン
会議室デビュー日: 2003/01/22
投稿数: 87
投稿日時: 2004-05-27 11:28
Dr.Doraemonさん:引用---------------------------------------------------
なお、JavaScriptを使わない方針とのことですが、これはなぜでしょうか?
----------------------------------------------------------------------
JavaScriptは使用しないことが決定していますと書いてしまいましたが、
お客さんからの要望ではなく、社内でできればJavaScriptは使用しないで
サーバ側で対処をしようとなりました。

詳しい理由は私も把握してないですが、その理由の1つとしては、
JavaScriptのフラグを利用した対応だとサブミットされて、画面が切り替わる
前に中止ボタンが押されると戻るか更新をしないとサブミットを
2度とできなくなるため
と聞いています。


Anthyhimeさん:引用-------------------------------------------------------------
まずリクエストを同期化させる必要があります。こちらはセッションオブジェクトをsynchronizeすればいけるでしょう(アプリケーションサーバによりますが)。
-------------------------------------------------------------------------------
今のところ開発はTomcatで行っていて、もう少しすると
本番と同じサーバ(InterStage)となるのですが、
アプリサーバによって、セッションで同期化できるかどうか決まる
のは、ドキュメント類を見ればわかるのでしょうか?
かつのり
ぬし
会議室デビュー日: 2004/03/18
投稿数: 2015
お住まい・勤務地: 札幌
投稿日時: 2004-05-27 15:18
セッションオブジェクトで同期を行う場合、
Tomcat4.1.xが有名ですが、同一セッションで
違うセッションオブジェクトが返される場合があります。
その場合に同期を取ることができなくなります。

対処方法として、セッションID文字列の
intern()の戻り値に対して同期を行えば大丈夫です。
sinh
ベテラン
会議室デビュー日: 2003/01/22
投稿数: 87
投稿日時: 2004-05-28 14:36
かつのりさん:引用-------------------------------
対処方法として、セッションID文字列の
intern()の戻り値に対して同期を行えば大丈夫です。
------------------------------------------------

と書かれていたので、セッションID文字列のintern()で同期を行おうと思い
以下のようなコード記述しました。
------------------------------------------------------------------------------
1://Sessionにて同期
2:synchronized(session.toString().intern()){
3: if(ユニークなIDがあるか検査){
4: // 2回目のリクエスト処理
5: }else{
6: // 1回目のリクエスト処理
7: // 処理で作成したrequest, sessionデータをsessionに全て保存
8: session = request.getSession(false);
9: java.util.Enumeration requestEnu = request.getAttributeNames();
10: java.util.Enumeration sessionEnu = session.getAttributeNames();
11: while(sessionEnu.hasMoreElements()){
12: String name = (String) sessionEnu.nextElement();
13: session.setAttribute(name + "_s_b", session.getAttribute(name));
14: }
15: while(requestEnu.hasMoreElements()){
16: String name = (String) requestEnu.nextElement();
17: session.setAttribute(name + "_r_b", request.getAttribute(name));
18: }
19: }
20:}
-----------------------------------------------------------------------------

ですが、これを実行すると例外が発生し、内容を確認すると、
--------------------------------------------------------
1:java.util.ConcurrentModificationException
2: at java.util.HashMap$HashIterator.nextEntry(HashMap.java:782)
3: at java.util.HashMap$KeyIterator.next(HashMap.java:818)
4: at org.apache.catalina.util.Enumerator.nextElement(Enumerator.java:166)
5: at gt.sk.controller.SkRequestProcessor.processActionPerform(SkRequestProcessor.java:305)
6: at gt.sk.controller.SkRequestProcessor.process(SkRequestProcessor.java:157)
7: at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)
8: at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:525)
9: at javax.servlet.http.HttpServlet.service(HttpServlet.java:760)
10: at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
11: at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
12: at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
13: at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:260)
14: at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
15: at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
16: at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
17: at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
18: at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
19: at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
20: at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
21: at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2396)
22: at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
23: at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
24: at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:170)
25: at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
26: at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:172)
27: at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
28: at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
29: at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
30: at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
31: at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
32: at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
33: at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
34: at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:223)
35: at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:405)
36: at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:380)
37: at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:508)
38: at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:533)
39: at java.lang.Thread.run(Thread.java:534)
------------------------------------------------------------------
となっていて、このStackTraceの5行目が上のコードの12行目となります。

原因としてはSessionに同時にアクセスしているからだと
思うのですが、対処方法は存在するのでしょうか?
Anthyhime
ぬし
会議室デビュー日: 2002/09/10
投稿数: 437
投稿日時: 2004-05-28 21:22
2行目のsessionて何のオブジェクトですか?
sinh
ベテラン
会議室デビュー日: 2003/01/22
投稿数: 87
投稿日時: 2004-05-28 21:35
Anthyhimeさん引用:------------------------------
2行目のsessionて何のオブジェクトですか?
-----------------------------------------------

説明不足で申し訳ないです。
sessionはHttpSessionオブジェクトです。
なにかコードで間違えているでしょうか?

スキルアップ/キャリアアップ(JOB@IT)