{******************************************************************************}
{                        UNIFIED INTERBASE (UIB)                               }
{                                                                              }
{ Project JEDI Code Library (JCL)                                              }
{                                                                              }
{ The contents of this file are subject to the Mozilla Public License Version  }
{ 1.0 (the "License"); you may not use this file except in compliance with the }
{ License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ }
{                                                                              }
{ Software distributed under the License is distributed on an "AS IS" basis,   }
{ WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for }
{ the specific language governing rights and limitations under the License.    }
{                                                                              }
{ The Original Code is JvUIBLib.pas.                                           }
{                                                                              }
{ The Initial Developer of the Original Code is documented in the accompanying }
{ help file JCL.chm. Portions created by these individuals are Copyright (C)   }
{ 2000 of these individuals.                                                   }
{                                                                              }
{ ORB Server                                                                   }
{                                                                              }
{ Unit owner:    Henri Gourvest                                                }
{ Last modified: Mar 16, 2003                                                  }
{                                                                              }
{******************************************************************************}

unit JvUIBSrv;

{$I jvcl.inc}
{$I jvuib.inc}

interface

uses
  {$IFDEF USEJVCL}
  {$IFDEF UNITVERSIONING}
  JclUnitVersioning,
  {$ENDIF UNITVERSIONING}
  {$ENDIF USEJVCL}
  Classes, IdTCPServer, SysUtils,
  JvUIBObj, JvUIBConst;

type

  TJvUIBPeerThread = class;
  TJvUIBObjectFactory = class;

  TJvUIBServer = class(TObject)
  private
    FTCPServer: TidTCPServer;
    FFactoryList: TJvUIBObjectFactory;
    FLock: TMultiReadExclusiveWriteSynchronizer;
    function GetActive: Boolean;
    procedure SetActive(const Value: Boolean);
    function GetDefaultPort: Integer;
    procedure SetDefaultPort(const Value: Integer);
    procedure AddObjectFactory(Factory: TJvUIBObjectFactory);
    procedure RemoveObjectFactory(Factory: TJvUIBObjectFactory);
    procedure FreeFactories;
    function GetFactoryFromClassID(const ClassID: TGUID): TJvUIBObjectFactory;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Start;
    procedure Stop;
  published
    property Active: Boolean read GetActive write SetActive default False;
    property DefaultPort: Integer read GetDefaultPort write SetDefaultPort;
  end;

  TJvUIBObjectFactory = class(TObject)
  private
    FNext: TJvUIBObjectFactory;
    FClassID: TGUID;
    FNetClass: TJvUIBStubClass;
   public
    constructor Create(Server: TJvUIBServer; NetClass: TJvUIBStubClass;
      const ClassID: TGUID);
    function CreateInstance(Connection: TIdTCPServerConnection): TJvUIBStub;
    destructor Destroy; override;
    property ClassID: TGUID read FClassID;
    property JvUIBClass: TJvUIBStubClass read FNetClass;
  end;

  TJvUIBPeerThread = class(TIdPeerThread)
  protected
    procedure Run; override;
    procedure CleanUp; override;
    procedure GetClassObject;
    procedure InvokeMethod;
  end;

var
  JvUIBServer: TJvUIBServer = nil;

{$IFDEF USEJVCL}
{$IFDEF UNITVERSIONING}
const
  UnitVersioning: TUnitVersionInfo = (
    RCSfile: '$URL: https://jvcl.svn.sourceforge.net/svnroot/jvcl/tags/JVCL3_32/run/JvUIBSrv.pas $';
    Revision: '$Revision: 10612 $';
    Date: '$Date: 2006-05-19 21:04:09 +0200 (ven., 19 mai 2006) $';
    LogPath: 'JVCL\run'
  );
{$ENDIF UNITVERSIONING}
{$ENDIF USEJVCL}

implementation

uses
  IdThread, IdException, IdStackConsts, IdTCPConnection;

//=== { TJvUIBServer } =======================================================

constructor TJvUIBServer.Create;
begin
  inherited Create;
  FLock := TMultiReadExclusiveWriteSynchronizer.Create;
  FTCPServer := TidTCPServer.Create(nil);
  FTCPServer.ThreadClass := TJvUIBPeerThread;
end;

destructor TJvUIBServer.Destroy;
begin
  FTCPServer.Free;
  FreeFactories;
  FLock.Free;
  inherited Destroy;
end;

function TJvUIBServer.GetActive: Boolean;
begin
  Result := FTCPServer.Active;
end;

function TJvUIBServer.GetDefaultPort: Integer;
begin
  Result := FTCPServer.DefaultPort;
end;

procedure TJvUIBServer.SetActive(const Value: Boolean);
begin
  FTCPServer.Active := Value;
end;

procedure TJvUIBServer.SetDefaultPort(const Value: Integer);
begin
  FTCPServer.DefaultPort := Value;
end;

procedure TJvUIBServer.Start;
begin
  Active := True;
end;

procedure TJvUIBServer.Stop;
begin
  Active := False;
end;

procedure TJvUIBServer.AddObjectFactory(Factory: TJvUIBObjectFactory);
begin
  FLock.BeginWrite;
  try
    Factory.FNext := FFactoryList;
    FFactoryList := Factory;
  finally
    FLock.EndWrite;
  end;
end;

procedure TJvUIBServer.FreeFactories;
var
  Factory, Next: TJvUIBObjectFactory;
begin
  FLock.BeginWrite;
  try
    Factory := FFactoryList;
    while Factory <> nil do
    begin
      Next := Factory.FNext;
      Factory.Free;
      Factory := Next;
    end;
  finally
    FLock.EndWrite;
  end;
end;

function TJvUIBServer.GetFactoryFromClassID(const ClassID: TGUID): TJvUIBObjectFactory;
begin
  FLock.BeginRead;
  try
    Result := FFactoryList;
    while Result <> nil do
    begin
      {$IFDEF DELPHI6_UP}
      if IsEqualGUID(Result.ClassID, ClassID) then
      {$ELSE}
      if CompareMem(@Result.ClassID, @ClassID, SizeOf(TGUID)) then
      {$ENDIF DELPHI6_UP}
        Exit;
      Result := Result.FNext;
    end;
  finally
    FLock.EndRead;
  end;
end;

procedure TJvUIBServer.RemoveObjectFactory(Factory: TJvUIBObjectFactory);
var
  F, P: TJvUIBObjectFactory;
begin
  FLock.BeginWrite;
  try
    P := nil;
    F := FFactoryList;
    while F <> nil do
    begin
      if F = Factory then
      begin
        if P <> nil then
          P.FNext := F.FNext
        else
          FFactoryList := F.FNext;
        Exit;
      end;
      P := F;
      F := F.FNext;
    end;
  finally
    FLock.EndWrite;
  end;
end;

//=== { TJvUIBObjectFactory } ================================================

constructor TJvUIBObjectFactory.Create(Server: TJvUIBServer;
  NetClass: TJvUIBStubClass; const ClassID: TGUID);
begin
  Assert(Assigned(Server));
  FNetClass := NetClass;
  FClassID := ClassID;
  JvUIBServer.AddObjectFactory(Self);
end;

function TJvUIBObjectFactory.CreateInstance(Connection: TIdTCPServerConnection): TJvUIBStub;
begin
  Result := FNetClass.Create(Connection);
  IUnknown(Result)._AddRef;
end;

destructor TJvUIBObjectFactory.Destroy;
begin
  JvUIBServer.RemoveObjectFactory(Self);
  inherited Destroy;
end;

//=== { TJvUIBPeerThread } ===================================================

procedure TJvUIBPeerThread.CleanUp;
begin
  if FData <> nil then
  begin
    IUnknown(TJvUIBStub(FData))._Release;
    FData := nil;
  end;
  inherited CleanUp;
end;

procedure TJvUIBPeerThread.GetClassObject;
var
  GUID: TGUID;
  Factory: TJvUIBObjectFactory;
  Reslt: HRESULT;
begin
  Reslt := S_OK;
  with FConnection do
  begin
    ReadBuffer(GUID, SizeOf(TGUID));
    Factory := JvUIBServer.GetFactoryFromClassID(GUID);
    if Factory <> nil then
    begin
      if FData <> nil then
      begin
        IUnknown(TJvUIBStub(FData))._Release;
        FData := nil;
      end;
      FData := Factory.CreateInstance(FConnection);
      OpenWriteBuffer;
      Connection.WriteBuffer(Reslt, SizeOf(HRESULT));
      CloseWriteBuffer;
    end
    else
    begin
      Reslt := S_FALSE;
      OpenWriteBuffer;
      Connection.WriteBuffer(Reslt, SizeOf(HRESULT));
      CloseWriteBuffer;
    end;
  end;
end;

procedure TJvUIBPeerThread.InvokeMethod;
var
  MethodID: Integer;
begin
  if FData <> nil then
  begin
    FConnection.ReadBuffer(MethodID, SizeOf(Integer));
    TJvUIBStub(FData).Invoke(MethodID);
  end
  else
  begin
    // TODO Disconnect !!!
  end;
end;

procedure TJvUIBPeerThread.Run;
var
  Command: TServerCommand;
begin
  try
    while Connection.Connected do
    begin
      FConnection.ReadBuffer(Command, SizeOf(TServerCommand));
      case Command of
        scGetClassObject:
          GetClassObject;
        scInvokeMethod:
          InvokeMethod;
      else
        // TODO Disconnect
      end;
    end;
  except
    on E: EIdSocketError do begin
      case E.LastError of
        Id_WSAECONNABORTED, Id_WSAECONNRESET:
          Connection.Disconnect;
      end;
    end;
  end;
  if not Connection.Connected then
    Stop;
end;

initialization
  {$IFDEF USEJVCL}
  {$IFDEF UNITVERSIONING}
  RegisterUnitVersion(HInstance, UnitVersioning);
  {$ENDIF UNITVERSIONING}
  {$ENDIF USEJVCL}
  JvUIBServer := TJvUIBServer.Create;

finalization
  JvUIBServer.Free;
  JvUIBServer := nil;
  {$IFDEF USEJVCL}
  {$IFDEF UNITVERSIONING}
  UnregisterUnitVersion(HInstance);
  {$ENDIF UNITVERSIONING}
  {$ENDIF USEJVCL}

end.
