Delphi Direct Oracle Access,DOA 4.0.7.1,TOracleEvent.Stop挂起,如何阻止它?

时间:2015-04-08 12:37:27

标签: oracle delphi

后续行动(2014年4月9日):用DOA 4.1.1替换DOA 4.0.7.1后,我无法重复此问题。我的问题已被弃用!但是,我仍然有兴趣收到任何在生产软件中实现了TOracleEvent的人的反馈。


原始问题:
如何使用DOA 4.0.7在Delphi 6中可靠地实现DBMS_ALERT的TOracleEvent?

如何在不破坏DOA源代码的情况下解决TOracleEvent.Stop方法锁定问题?

如果可能的话,我更喜欢修复或解决方法,而不需要修改原始的AllRoundAutomations DOA源代码。

我正在使用传统的Delphi 6和AllRoundAutomations Direct Oracle Access DOA 4.0.7。我已经点击了一个show stop,DOA TOracleEvent实例无法停止和释放,因此应用程序的进程无法自行终止。

我对DOA文档的理解是我应该调用myOracleEvent.Stop()来清理TOracleEvent实例。但是,如果我执行Stop()方法,我的应用程序将“挂起”该方法调用。如果我没有调用Stop(),我的应用程序将关闭,但它的进程将无限期地保持活着,直到我用taskmanager或其他方式杀死它。

TOracleEvent可以管理两种类型的信号:dbms_alert和管道。我正在使用dbms_alert信号来启用多个接收器。

在AllRoundAutomations论坛上搜索此问题时发现其他一些人遇到过此问题,但我没有找到任何答案。

我不认为这是Oracle服务器端问题,因为Application的TOracleEvent进程导致了阻塞。也就是说,TOracleEvent实例不是“等待”任何东西,它只是挂起。如果我杀掉“挂起”的Oracle会话服务器端,它就不会被释放。当然,问题可能是我的程序没有正确配置TOracleEvent属性或正确清理它以进行处理。

以下是用于测试用例状态机的Delphi-6代码,它隔离了DOA.TOracleEvent.Stop()方法冻结的问题。如果修复了TOracleEvent.Stop(),那么状态机应该显示类似于下面代码后面显示的结果。如果没有修复,Stop()方法将在调用时冻结。如果不修复(黑客攻击)DOA源代码,我无法使任何小型测试应用程序可靠地工作。

implementation
{$R *.dfm}

const
  ALERT_NAME__STACKOVERFLOW = 'STACKOVERFLOW';

/// This is the TOracleEvent.OnEvent handler that listens for DBMS_ALERT Signals.
procedure TForm1.OracleEvent1Event(Sender: TOracleEvent; const ObjectName: String; const Info: Variant);
var
  ii: integer;
begin
  Memo1.Lines.Add('*> Oracle Event Received!');
  Memo1.Lines.Add('   DBMS_ALERT.SIGNAL name = ' + ObjectName);
  if VarIsArray(Info) then begin
    for ii := 0 to VarArrayHighBound(Info, 1) do begin
      Memo1.Lines.Add('     Message= ' + Info[ii]);
    end;
  end;
  Memo1.Refresh();
end;

procedure TForm1.btnRunStateMachineClick(Sender: TObject);
begin
  // Button clicked to clear the display and start the state machine timer.
  AppState := 0;
  Memo1.Lines.Clear();
  Timer1.Enabled := true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  // Time Interval State Machine that steps through the test sequence. 

  // Stop the Timer for Failsafe
  Timer1.Enabled := false;

  if (AppState = 0) then begin
      try
        Memo1.Lines.Add('==========================');
        Memo1.Lines.Add('Connecting OracleSession2 to DB ...');
        // Check for, Close, and Destroy previous Sessions
        if (Assigned(OracleSession2) and OracleSession2.Connected) then begin
          // Close the Connection
          OracleSession2.LogOff();
          OracleSession2.Connected := false;
          OracleSession2.Free();
          OracleSession2 := nil;
        end;
        if (not Assigned(OracleSession2)) then begin
          OracleSession2 := TOracleSession.Create(self);
        end;
        // Configure Session #2 for general purpose routines
        OracleSession2.LogonDatabase := 'STACKOVERFLOW_DB';
        OracleSession2.LogonPassword := 'drowssap';
        OracleSession2.LogonUserName := 'answer';
        OracleSession2.Pooling := spInternal;
        OracleSession2.ThreadSafe := true;
        OracleSession2.Connected := true;
        Memo1.Lines[Memo1.Lines.Count - 1] := 'DB OracleSession2 Connected!';
        Inc(AppState);
      except
        on ex0: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex0.Message);
        end;
      end;
    end

  else if (AppState = 1) then begin
      try
        Memo1.Lines.Add('Connecting OracleSession1 to DB ...');
        if (Assigned(OracleSession1) and OracleSession1.Connected) then begin
          OracleSession1.LogOff();
          OracleSession1.Connected := false;
          OracleSession1.Free();
          OracleSession1 := nil;
        end;
        if (not Assigned(OracleSession1)) then begin
          OracleSession1 := TOracleSession.Create(self);
        end;

        // Configure Session #1 for the OracleEvent Handler
        OracleSession1.LogonDatabase := 'STACKOVERFLOW_DB';
        OracleSession1.LogonPassword := 'drowssap';
        OracleSession1.LogonUserName := 'answer';
        OracleSession1.Pooling := spInternal;
        OracleSession1.ThreadSafe := true;
        OracleSession1.Connected := true;
        Memo1.Lines[Memo1.Lines.Count - 1] := 'DB OracleSession1 Connected!';
        Inc(AppState);
      except
        on ex1: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex1.Message);
        end;
      end;
    end

  else if (AppState = 2) then begin
      try
        Memo1.Lines.Add('Configuring the Oracle Event Handler ...');
        if (not OracleEvent1.Started) then begin
          if (OracleEvent1.Session = nil) then begin
            OracleEvent1.Session := OracleSession1;
          end;
          OracleEvent1.KeepConnection := false;
          OracleEvent1.Synchronized := true;
          OracleEvent1.TimeOut := 1;
          OracleEvent1.ObjectNames := ALERT_NAME__STACKOVERFLOW;
          OracleEvent1.Start();
          Memo1.Lines[Memo1.Lines.Count -1] := 'Oracle Event Handler Started!';
        end;
        Inc(AppState);
      except
        on ex2: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex2.Message);
        end;
      end;
    end

  else if ((AppState >= 3) and (AppState <= 7)) then begin
      try
        Memo1.Lines.Add('Sending a DBMS_ALERT Signal to StackOverflow ...');
        try
          OracleSession1.DBMS_Alert.Signal(ALERT_NAME__STACKOVERFLOW, 'Hello StackOverflow! ' + FormatDateTime('HH:nn:ss', now));
          OracleSession1.Commit();
        except
          on ex: Exception do begin
            Memo1.Lines.Add('*> Oracle Event Signal ERRORT!');
            Memo1.Lines.Add(ex.Message);
          end;
        end;
        Inc(AppState);
      except
        on ex3: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex3.Message);
        end;
      end;
    end

  else if (AppState = 8) then begin
      try
        Memo1.Lines.Add('Disconnecting OracleSession2 from DB ...');
        if (Assigned(OracleSession2) and OracleSession2.Connected) then begin
          OracleSession2.LogOff();
          OracleSession2.Connected := false;
          OracleSession2.Free();
          OracleSession2 := nil;
        end;
        Memo1.Lines[Memo1.Lines.Count -1] := 'DB OracleSession2 Disconnected!';
        Inc(AppState);
      except
        on ex4: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex4.Message);
        end;
      end;
    end

  else if (AppState = 9) then begin
      try
        Memo1.Lines.Add('Stopping OracleEvent handler.');
        Memo1.Lines.Add(' * THIS IS WHERE THE HANGUP PROBLEM IS CALLED!');
        OracleEvent1.Stop();  // <<<< Freezes here if TOracleEvent.Stop() is not fixed!
        ////
        Memo1.Lines.Add('OracleEvent Handler Stopped OK!');
        Memo1.Lines.Add(' * If we got here, either there were no events...');
        Memo1.Lines.Add('   or the Bug is Fixed B-)');
        Inc(AppState);
      except
        on ex5: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex5.Message);
        end;
      end;
    end

  else if (AppState = 10) then begin
      try
        Memo1.Lines.Add('Disconnecting OracleSession1 from DB ...');
        if (Assigned(OracleSession1) and OracleSession1.Connected) then begin
          OracleSession1.LogOff();
          OracleSession1.Connected := false;
          OracleSession1.Free();
          OracleSession1 := nil;
        end;
        Memo1.Lines[Memo1.Lines.Count -1] := 'DB OracleSession1 Disconnected!';
        Inc(AppState);
      except
        on ex6: Exception do begin
          AppState := -1;
          Memo1.Lines.Add('!! ERROR !! ' + ex6.Message);
        end;
      end;
    end

  else begin
    AppState := 0;
  end;

  Memo1.Refresh();
  Timer1.Enabled := (AppState >= 0);
end;

procedure TForm1.btnStopClick(Sender: TObject);
begin
  // Stop Button Clicked to halt the State Machine
  Timer1.Enabled := false;
  Memo1.Lines.Add('State Machine Stopped.');
  Memo1.Refresh();
end;

/// Other handlers on the TOracleEvent instance
procedure TForm1.OracleEvent1Start(Sender: TOracleEvent);
begin
  Memo1.Lines.Add('*> Oracle Event START!');
end;

procedure TForm1.OracleEvent1Stop(Sender: TOracleEvent);
begin
  Memo1.Lines.Add('*> Oracle Event STOP!');
end;

procedure TForm1.OracleEvent1Error(Sender: TOracleEvent; const Error: Exception);
begin
  Memo1.Lines.Add('*> Oracle Event ERROR!');
  Memo1.Lines.Add('=> ' + Error.Message);
end;

procedure TForm1.OracleEvent1TimeOut(Sender: TOracleEvent; var Continue: Boolean);
begin
  Memo1.Lines.Add('*> Oracle Event TIMEOUT!');
end;

以下是状态机的输出:

==========================
The following are the results for successfully running 
the state machine with a DOA.TOracleEvent.Stop() "fixed".
Without fixing DOA.TOracleEvent.Stop(), the state machine will "freeze"
==========================

DB OracleSession2 Connected!
DB OracleSession1 Connected!
Configuring the Oracle Event Handler ...
Oracle Event Handler Started!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:41
*> Oracle Event TIMEOUT!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:42
*> Oracle Event TIMEOUT!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:43
*> Oracle Event TIMEOUT!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:44
*> Oracle Event TIMEOUT!
Sending a DBMS_ALERT Signal to StackOverflow ...
*> Oracle Event Received!
   DBMS_ALERT.SIGNAL name = STACKOVERFLOW
     Message= Hello StackOverflow! 21:55:45
DB OracleSession2 Disconnected!
*> Oracle Event TIMEOUT!
Stopping OracleEvent handler.
 * This is where the Hangup problem gets called!
*> Oracle Event STOP!
OracleEvent Handler Stopped OK!
 * If we got here, either there were no events...
   or the Bug is Fixed B-)
DB OracleSession1 Disconnected!
==========================

1 个答案:

答案 0 :(得分:0)

以下是DOA 4.1.1源代码中TOracleEvent.Stop例程的修订版。此例程中未更改的批量DOA代码将替换为&#39;。 。 。&#39;

此修订过程是(迄今为止)TOracleEvent.Stop()调用的功能性工作,间歇性地导致我的应用程序挂起。

procedure TOracleEvent.Stop;
var
  bLoggedOff: boolean;
begin
  . . .
  . . .
  // Skip the CriticalSection Enter/Leave toggle.
  // This is causing my application to get stuck here!
  //CriticalSection.Enter;
  //CriticalSection.Leave;
  . . .
  . . .
  // LogOff the duplicate sessions
  if (not KeepConnection) then begin
    bLoggedOff := false;
    // Keep trying the logoff until we actually get Logged Off or disconnected.
    while (not bLoggedOff) do begin
      try
        if ((InternalSession <> nil) and (InternalSession.Connected)) then begin
          InternalSession.LogOff;
        end;
        if ((StopSession <> nil) and (StopSession.Connected)) then begin
          StopSession.LogOff;
        end;
        bLoggedOff := true;
      except
        on exLogoff: Exception do begin
          // This error is typically ORA-24909: Call In Progress, Current Operation Cancelled.
          // Sink this hangover error by sleeping it off
          Sleep(1000);
        end;
      end;
    end;
  end;
end;