Post Reply 
 
Thread Rating:
  • 0 Votes - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
SplashScreen の利用にはお気をつけて
07-08-2014, 04:53 PM
Post: #1
SplashScreen の利用にはお気をつけて
★point
 実行時間の短いサービスでは、SplashScreen を利用するな



現在、ORBを利用したサービス呼び出しを連続で行うと、
結果の返却順番が入れ替わってしまう、という問題が起きています。

■プログラムの前提
 1.サービス名:税計算サービス
  日付と区分を引数に、対象となる税率を返却する
 2.イベントA.日付を表すDateField の ValueFinished で、税計算サービス(TaxService.calc-tax)を呼び出す
 3.イベントB.区分を表す RadioFrame の ValueFinished で、税計算サービス(TaxService.calc-tax)を呼び出す
 4.税計算サービスの実行結果を、税率フィールドに表示する

■引数と結果の組み合わせ例
  日付:2010、区分:消費税 結果:5%
  日付:2014、区分:消費税 結果:8%
  日付:2010、区分:外貨  結果:0%
  日付:2014、区分:外貨  結果:0%

■操作手順
 ①DateField の日付を2010年から、2014年に変更する
  (フォーカスアウト等はしていないのでValueFinishedは未発生)
 ②RadioFrame のラジオボタンを、消費税から、外貨に変更する
  (この段階で、DateFieldのValueFinished が発生、次に、RadioFrame のValueFinished が発生する)

■結果
 5回に1回くらいの割合で、税率フィールドに8%と表示される
 ※1:上記操作の結果、画面上は「日付:2014、区分:外貨」となっているのにもかかわらず、
    「日付:2014、区分:消費税」(ラジオボタンの変更前の状態)の結果がセットされる
 ※2:税率フィールドには、一瞬「0%」と表示されたのち、「8%」が表示される


100%発生するわけではなく、ある程度の割合で発生するので、
明確な原因は不明なのですが、サービス呼び出しの箇所にログを仕掛けたところ、
下記のような処理の順番になっていることがわかりました。

■処理順
 ①DateField の ValueFinished イベントで calc-tax が呼び出される
 ②RadioFrame の ValueFinished イベントで calc-pay-pln-dt が呼び出される
  (本来であれば、①の結果を返却してから、サービスが呼ばれるべき!)
 ③②の結果が返却される
 ④①の結果が返却される
  (順番が入れ替わることで、不正な結果となってしまう)

これは、DateField のValueFinished で呼び出されたサービスの結果が返却される前に、
RadioFrame のValueFinished イベントが割り込んでいるように見えます。

dispatch-events を記述することでこのような問題が発生する可能性がありますが、
プログラムには、dispathc-events は記述していません。

そこで怪しいと思ったのが、「SplashScreen」です。


現在、サービスの呼び出し時に、下記のようなインタセプタを設定しています。
内容としては、サービス呼び出し時(handle-before-request)に SplashScreen を表示して、
サービス実行後(handle-after-response)に、SplashScreenを閉じる、という処理になっています。

このサービス呼び出し時の処理のなかで、対象のサービスの場合だけ、SplashScreenを表示しない、
という処理を入れると、この問題は再現しなくなりました。

SplashScreen のコードはOPEN-CONTROLS にもないので、確実なことは言えませんが、
もしかすると、SplashScreen.setup の内部でdispatch-events のような処理が実行されており、
そのため、DateField とRadioButton のValueFinished イベント割り込みが発生しているのではないか、
と私は判断しています。

同様の問題が発生している環境があれば、この辺りを疑ってみるとよいかも知れません。

もし詳しい方がいらっしゃれば情報提供いただけるとありがたいです。

Code:
||サービスコール時に、プログラムIDが指定されているかどうかを判断するインタセプタ
{define-class public InitializeServiceInterceptor {inherits ClientInterceptor}

  ||プログレスバーの表示を無視するサービスの一覧
  || 共通処理と終了(ログアウト)処理は無視する
  field exclusion-methods:{Array-of String} =
      {{Array-of String}
         "lightning-service"
      }

  ||プログレスバー処理を行う対象のメソッドかどうかを調べる
  ||アプリケーション終了時にはサブアプレットの生成ができないので、
  ||終了時のログアウトだけ、プログレスバーを表示しない
  || また、共通機能からの通信 find-mst-info、find-base-date-info についても表示しない
  {method {get-progress-method?
              methos-name:String ,
              arguments:#FastArray
          }:bool

    {if {self.exclusion-methods.find methos-name} > -1 then
        {return false}
    else
        {if {self.get-common? arguments} then
            {return false}
        }
        {return true}
    }

  }

  {constructor public {default
                          client-filter:{proc-type {AbstractClient}:bool} =
                              {fn client => true}
                      }
    {construct-super client-filter = client-filter}
  }

  ||  handle-before-request :メソッド実行前の処理を定義できます。
  {method protected open {handle-before-request
                             http-request-headers:#HttpRequestHeaders,
                             orb-request-hearders:#HashTable,
                             method:Method,
                             arguments:#FastArray
                         }:(http-request-headers:#HttpRequestHeaders, orb-request-headers:#HashTable)


    {if {self.get-progress-method? method.name, arguments} then
        {ProgressView.setup
            title = "サーバ処理",
            footer = "サーバ処理中です..."
        }
    }
    {self.print  "[START] " & method.defining-class.name & "." & method.name }

    {if-non-null args = arguments then
        {for arg in args do
            {if not {self.get-common? arguments} then
                {self.print "INPUT-DTO = " & {String {type-of arg}}}
            }
        }
    }


    def (ret-http-request-headers:#HttpRequestHeaders, ret-orb-request-headers:#HashTable) =
        {super.handle-before-request
            http-request-headers,
            orb-request-hearders,
            method ,
            arguments
        }

    {return ret-http-request-headers, ret-orb-request-headers}
  }



  ||  handle-after-response :メソッド実行後の処理を定義できます。
  {method protected open {handle-after-response
                             header:#HttpResponseHeaders,
                             map:#{HashTable-of any, any},
                             method:Method,
                             arguments:#{FastArray-of any},
                             return-value:any}:(#HttpResponseHeaders, #{HashTable-of any, any})
    {if {self.get-progress-method? method.name, arguments} then
        {ProgressView.destroy}
    }
    {self.print  "[END] " & method.defining-class.name & "." & method.name }
    {return {super.handle-after-response header, map, method, arguments,return-value }}
  }

  ||  handle-exception :例外発生時の処理を定義できます。
  {method protected open {handle-exception ||throw-advice
                             method:Method,
                             arguments:#FastArray,
                             exception:Exception
                         }:void
    ||{if {self.get-progress-method? method.name} then
    {ProgressView.destroy}
    ||}

    {self.print "[ERROR] " & method.defining-class.name & "." & method.name}
    {type-switch exception
    case rae:RemoteApplicationException do
        {self.print "[ERROR-MESSAGE] " & rae.remote-error.message-id & ":" & rae.remote-error.message}
    case rse:RemoteSystemException do
        {self.print "[ERROR-MESSAGE] " & rse.remote-error.message-id & ":" & rse.remote-error.message}
    else
        {self.print "[ERROR-MESSAGE] " & exception.message & ":" & {type-of exception}}
    }


    ||システム例外が、アプリケーション処理継続不能な処理の場合、
    ||共通的に処理する
    {type-switch exception
    case re:RemoteSystemException do
        {if-non-null re.remote-error, message-id = re.remote-error.message-id  then

            ||タイムアウト、バッチ実行中の処理判断の検討結果に合わせて変更
            {switch message-id

             case SERVER-ERROR-CODE-EXECUTING-BATCH do
                {popup-message "バッチ実行中のためサーバアクセスが拒否されました。\nアプリケーションを終了します。"}
                {self.print "バッチ実行中のためユーザー確認後、アプリケーション終了"}
                {exit}
             case  SERVER-ERROR-CODE-TIMEOUT do
                {popup-message
                    "タイムアウトしました。\nアプリケーションを終了します。"
                }
                {self.print "タイムアウトのためユーザーがアプリケーション終了を判断"}
                {exit}
            }
        }

    }

    {super.handle-exception method, arguments, exception}
  }

  ||  register-interceptor :Interceptor登録時の処理を定義できます。
  {method protected open {register-interceptor
                             client:AbstractClient
                         }:void
    {super.register-interceptor client}
  }
  || -- private --
  {method private {print str:String}:void
    {output-log-info "[SERVICE] " & str}
  }

}

||インターセプターの登録
{register-orb-interceptors
    {InitializeServiceInterceptor}
}


||プログレスビューとして表示する画面
{import * from CURL.ENGINE.BROWSER}
{def package splash-screen-applet-source:String =
    {stringify
        {curl 8.0 applet}
        {import * from CURL.ENGINE.BROWSER}


        {define-class public CustomSplashScreenSubApplet
          {inherits SplashScreenSubApplet}

          {constructor public {default}
            {construct-super}

            set self.footer-frame.font-size = 11pt
          }

          {method public open {create-main-body}:Graphic

            def vb =  {VBox
                          font-size = 11pt,
                          width=4in,  spacing = 3pt, margin = 4pt,
                          height = 0.7in,
                          border-width = 1pt,
                          border-color = "black",
                          background = "white",
                          hstretch? = true,
                          framelike-stretch? = true,
                          self.title-frame,

                          self.progress-bar-frame,

                          self.footer-frame,

                          ||透明度を設定する
                          {on AttachEvent at vb:VBox do

                              def view = vb.parent.parent asa View
                              {view.set-opacity 0.7 asa float}

                          }
                      }
            {vb.add-event-handler
                {context-popup
                    menu-pane={MenuPane
                                  {MenuAction
                                      label="閉じる",
                                      {on Action do
                                          {exit}
                                      }
                                  }
                              }
                }
            }
            {return
                vb
            }
          }
        }

        {after 0s do
            {CustomSplashScreenSubApplet}
        }
    }
}


||
{define-class public ProgressView

  ||このパラメータが true の間は、別のプログレスビューを表示したり、閉じたりしない
  ||複数のサービスをfor文で呼ぶ場合などで、その処理全体の時間中にプログレスビューを表示したい場合に利用してください。
  ||{with} マクロを使ってこのフラグを true に設定することを強くお勧めします。
  let public ignore?:bool = false

  ||プログレスバーが表示されているかどうかを保持する変数
  let private show?:bool = false


  {define-proc public {set-progress-bar-indeterminate}:void
    {if {this-class}.ignore? then
        {return}
    }
    {if ProgressView.show? then
        {SplashScreen.set-progress-bar-indeterminate}
    }
  }
  {define-proc public {set-progress-bar-value val:double}:void
    {if {this-class}.ignore? then
        {return}
    }
    {if ProgressView.show? then
        {SplashScreen.set-progress-bar-value val}
    }
  }
  {define-proc public {set-progress-bar
                          min:double = 0.0,
                          max:double = 100.0,
                          value:double = min}:void
    {if {this-class}.ignore? then
        {return}
    }
    {if ProgressView.show? then
        {SplashScreen.set-progress-bar min = min, max = max, value = value}
    }
  }

  {define-proc public {show}:void
    {if {this-class}.ignore? then
        {return}
    }
    {if not ProgressView.show? then
        {SplashScreen.show}
        set ProgressView.show? = true
    }
  }

  {define-proc public {setup
                          title:String = "お待ちください",
                          footer:String = "処理の終了を待っています ...",
                          image-url:#Url = null,
                          width:Distance = 0m,
                          height:Distance = 0m,
                          splash-applet-url:#Url =  {string-url splash-screen-applet-source},
                          delay:Time = .3s
                      }:void


    {if {this-class}.ignore? then
        {return}
    }

    {ProgressView.destroy}

    {if not ProgressView.show? then
        {try
            {SplashScreen.setup
                title = title,
                footer = footer,
                image-url = image-url,
                width = width,
                height = height,
                splash-applet-url = splash-applet-url,
                delay = delay
            }
            {SplashScreen.set-progress-bar-indeterminate}
         catch e:Exception do
        }
        set ProgressView.show? = true
    }
  }


  {define-proc public {set-footer
                          footer:String,
                          halign:any = "left",
                          font-family:String =  "MS Gothic",
                          font-size:Distance = 0m,
                          font-weight:any = null,
                          color:String = "black"
                      }:void
    {if {this-class}.ignore? then
        {return}
    }
    {SplashScreen.set-footer
        footer,
        halign = halign,
        font-family = font-family,
        font-size = font-size,
        font-weight = font-weight,
        color = color
    }
  }


  {define-proc public {destroy}:void
    {if {this-class}.ignore? then
        {return}
    }
    {try
        {SplashScreen.destroy}
    catch e:Exception do
    }
    set ProgressView.show? = false
  }

}
Find all posts by this user
Quote this message in a reply
Post Reply 

Forum Jump:


User(s) browsing this thread:
1 Guest(s)