unit uROEventRepository; {----------------------------------------------------------------------------} { RemObjects SDK Library - Core Library } { } { compiler: Delphi 5 and up, Kylix 2 and up } { platform: Win32, Linux } { } { (c)opyright RemObjects Software. all rights reserved. } { } { Using this code requires a valid license of the RemObjects SDK } { which can be obtained at http://www.remobjects.com. } {----------------------------------------------------------------------------} {$I RemObjects.inc} interface uses Classes, SysUtils, SyncObjs, Contnrs, uROSessions, uROClientIntf, uROClient, uROTypes, uROClasses, uROServerIntf; type IROEventsInvoker = interface; TROEventReceiver = class; TROSessionReference = class; TROEventRepository = class; TROInvokerInvokeMethod = procedure(__EventReceiver : TROEventReceiver; const aMessage : IROMEssage; const aTarget : IInterface) of object; { IROEventRepository } IROEventRepository = interface ['{35D49EF1-F62C-4985-B092-8B09D235D81B}'] procedure AddSession(aSessionID : TGUID); procedure RemoveSession(aSessionID : TGUID); procedure StoreEventData(SourceSessionID : TGUID; Data : Binary; const ExcludeSender: Boolean; const ExcludeSessionList: Boolean; const SessionList: String); function GetEventData(SessionID : TGUID; var TargetStream : Binary) : integer; end; { IROEventsInvoker } IROEventsInvoker = interface ['{A7FF5762-8208-4A91-B6C2-5D4ECB014EE9}'] procedure Execute(const aTarget : TObject; EventData: TStream); end; TROSessionEvent = procedure(Sender : TROEventRepository ; const SessionID : TGUID) of object; { TROEventRepository } TROEventRepository = class(TROComponent, IROEventRepository) private fSessionManager: TROCustomSessionManager; fMessage: TROMessage; fOnBeforeAddSession: TROSessionEvent; fOnAfterRemoveSession: TROSessionEvent; fOnAfterAddSession: TROSessionEvent; fOnBeforeRemoveSession: TROSessionEvent; procedure SetMessage(const Value: TROMessage); protected procedure SetSessionManager(const Value: TROCustomSessionManager); virtual; procedure Notification(AComponent: TComponent; Operation: TOperation); override; { Virtuals } procedure DoStoreEventData(SourceSessionID : TGUID; Data : Binary; const ExcludeSender: Boolean; const ExcludeSessionList: Boolean; const SessionList: String); virtual; abstract; function DoGetEventData(SessionID : TGUID; var TargetStream : Binary) : integer; virtual; abstract; procedure DoAddSession(aSessionID : TGUID); overload; virtual; abstract; procedure DoAddSession(aSessionID : TGUID; aActiveEventServer: IROActiveEventServer); overload; virtual; procedure DoRemoveSession(aSessionID : TGUID); virtual; abstract; function QueryInterface(const IID: TGUID; out Obj): HResult; override; public { IROEventRepository } procedure StoreEventData(SourceSessionID : TGUID; Data : Binary; const ExcludeSender: Boolean; const ExcludeSessionList: Boolean; const SessionList: String); function GetEventData(SessionID : TGUID; var TargetStream : Binary) : integer; procedure AddSession(aSessionID : TGUID); overload; procedure AddSession(aSessionID: TGUID; aActiveEventServer: IROActiveEventServer); overload; procedure RemoveSession(aSessionID : TGUID); procedure CheckProperties; virtual; // procedure DeleteEventData(anEvent: TROEventData); virtual; published property Message : TROMessage read fMessage write SetMessage; property SessionManager : TROCustomSessionManager read fSessionManager write SetSessionManager; property OnBeforeAddSession : TROSessionEvent read fOnBeforeAddSession write fOnBeforeAddSession; property OnAfterAddSession : TROSessionEvent read fOnAfterAddSession write fOnAfterAddSession; property OnAfterRemoveSession : TROSessionEvent read fOnAfterRemoveSession write fOnAfterRemoveSession; property OnBeforeRemoveSession : TROSessionEvent read fOnBeforeRemoveSession write fOnBeforeRemoveSession; end; TROSessionRefToEventDataLink = class; IROActiveEventServerList = interface ['{C6ACE445-267E-4217-9A9C-1D357A7CB3BF}'] function GetNext: IROActiveEventServerList; procedure SetNext(aData: IROActiveEventServerList); function GetServer: IROActiveEventServer; property Server: IROActiveEventServer read GetServer; property Next: IROActiveEventServerList read GetNext write SetNext; procedure DispatchEvent(anEventDataItem : TROEventData; aSessionReference : TGUID; aSender: TObject); end; TIROActiveEventServerList = class(TInterfacedObject, IROActiveEventServerList) private fNext: IROActiveEventServerList; fServer: IROActiveEventServer; public constructor Create(aNext: IROActiveEventServerList; aItem: IROActiveEventServer); class function AddListener(aList: IROActiveEventServerList; aItem: IROActiveEventServer): IROActiveEventServerList; class function RemoveListener(aList: IROActiveEventServerList; aItem: IROActiveEventServer): IROActiveEventServerList; function GetNext: IROActiveEventServerList; function GetServer: IROActiveEventServer; procedure SetNext(aData: IROActiveEventServerList); procedure DispatchEvent(anEventDataItem : TROEventData; aSessionReference : TGUID; aSender: TObject); end; { TROSessionReference } TROSessionReference = class private fTail: TROSessionRefToEventDataLink; fHead: TROSessionRefToEventDataLink; fSessionID: TGUID; ActiveEventReference: IROActiveEventServerList; function GetHead: TROSessionRefToEventDataLink; function GetTail: TROSessionRefToEventDataLink; public constructor Create(aSessionID : TGUID); property SessionID : TGUID read fSessionID; property Head : TROSessionRefToEventDataLink read GetHead write fHead; property Tail : TROSessionRefToEventDataLink read GetTail write fTail; end; { TROSessionRefToEventDataLink } TROSessionRefToEventDataLink = class private fNext: TROSessionRefToEventDataLink; fEventData: TROEventData; fSessionReference: TROSessionReference; public constructor Create(anEventDataItem : TROEventData; aSessionReference : TROSessionReference); destructor Destroy; override; property EventData : TROEventData read fEventData; property SessionReference : TROSessionReference read fSessionReference; property Next: TROSessionRefToEventDataLink read fNext write fNext; end; { TROInMemoryEventRepository } TROInMemoryEventRepository = class(TROEventRepository, IROSessionsChangesListener) private fSessionIDs: TStringList; // fEvents : TObjectList; fCritical : TCriticalSection; FDestroyed: Boolean; function GetSessionReferenceCount: integer; function GetSessionReferences(Index: integer): TROSessionReference; procedure UnRegisterSessionsChangesListener; procedure RegisterSessionsChangesListener; protected procedure SetSessionManager(const Value: TROCustomSessionManager); override; function FindSession(aSessionID: TGUID): TROSessionReference; { Overrides } procedure DoStoreEventData(SourceSessionID : TGUID; Data : Binary; const ExcludeSender: Boolean; const ExcludeSessionList: Boolean; const SessionList: String); override; function DoGetEventData(SessionID : TGUID; var TargetStream : Binary) : integer; override; procedure DoAddSession(aSessionID: TGUID); overload; override; procedure DoAddSession(aSessionID : TGUID; aActiveEventServer: IROActiveEventServer); overload; override; procedure DoRemoveSession(aSessionID: TGUID); override; procedure RemoveSessionActiveListener(aSessionID: TGUID); procedure Loaded; override; { IROSessionsChangesListener } procedure SessionsChangedNotification(const aSessionID : TGUID; aSessionAction: TROSessionsActions; Sender: TObject); public // procedure DeleteEventData(anEvent: TROEventData); override; procedure BeforeDestruction; override; constructor Create(aOwner : TComponent); override; destructor Destroy; override; property SessionReferences[Index : integer] : TROSessionReference read GetSessionReferences; property SessionReferenceCount : integer read GetSessionReferenceCount; published end; TROOnEventExceptionEvent = procedure (Sender: TObject; anException: Exception) of object; IROEventReceiverInvokers = interface ['{C8EA6D75-562E-4FE5-B4EF-47ACD4D2ADE8}'] function Invoke_GetEventsData(out EventsData: Binary): Integer; procedure Invoke_RegisterEventClient(const EventTypes: String); procedure Invoke_UnregisterEventClient(const EventTypes: String); end; { TROEventReceiver } TROEventReceiver = class(TROChannelAwareComponent, IROObjectRetainer, IROEventReceiver, IROEventReceiverInvokers) private fTimer : TROThreadTimer; fActive: boolean; fServiceName: string; fHandlers : TStringList; fOnActivate: TNotifyEvent; fOnDeactivate: TNotifyEvent; fOnPollException: TROOnEventExceptionEvent; fRetainedObjects : TList; fInterval: Integer; fEventAwareChannel: IROActiveEventChannel; fSynchronizeInvoke: Boolean; procedure SetInterval(const Value: integer); procedure SetActive(const Value: boolean); procedure SetServiceName(const Value: string); function GetClientID: TGUID; function GetSynchronizeInvoke: Boolean; protected function GetObject: TObject; procedure FireEvents(EventsDataStream: TStream); virtual; // Proxy methods function Invoke_GetEventsData(out EventsData: Binary): Integer; procedure Invoke_RegisterEventClient(const EventTypes: String); procedure Invoke_UnregisterEventClient(const EventTypes: String); procedure OnTimerTick(Dummy: Cardinal); procedure Dispatch(aStream: TStream; aCaller: TThread); reintroduce; public constructor Create(aOwner : TComponent); override; destructor Destroy; override; procedure Activate; procedure Deactivate; procedure RegisterEventHandlers(const EventIDs : array of string; const EventHandlers : array of TObject); overload; procedure RegisterEventHandler(const EventID : string; const EventHandler : TObject); procedure UnregisterEventHandlersByObject(const EventHandler : array of TObject); procedure UnregisterEventHandlers(const EventIDs : array of string); overload; procedure UnregisterEventHandler(const EventHandler : TObject); overload; procedure UnregisterEventHandler(const EventIDs : string); overload; function EventHandlersIsRegistered(const EventIDs: array of string; const EventHandlers: array of TObject): boolean; deprecated; function AreEventHandlersRegistered(const aEventIDs: array of string; const aEventHandlers: array of TObject): boolean; function IsEventHandlerRegistered(const aEventID: string; const aEventHandler: TObject): boolean; procedure CheckProperties; { IROObjectRetainer } procedure RetainObject(const anObject : TObject); procedure ReleaseObject(const anObject : TObject); function IsRetained(const anObject : TObject) : boolean; property ClientID : TGUID read GetClientID; published property Active : boolean read fActive write SetActive default false; property Interval : integer read fInterval write SetInterval default 30000; property Message; property Channel; property OnPollException: TROOnEventExceptionEvent read fOnPollException write fOnPollException; property OnActivate : TNotifyEvent read fOnActivate write fOnActivate; property OnDeactivate : TNotifyEvent read fOnDeactivate write fOnDeactivate; property ServiceName : string read fServiceName write SetServiceName; property SynchronizeInvoke: Boolean read GetSynchronizeInvoke write fSynchronizeInvoke default true; end; { TROEventProxy } TROEventProxy = class(TInterfacedObject) private fMessage : IROMessage; function GetMessage: IROMessage; protected property __Message : IROMessage read GetMessage; public constructor Create(const aMessage : IROMessage; CloneMessage : boolean); destructor Destroy; override; end; { TROEventWriter } TROEventWriter = class(TROEventProxy, IROEventWriter) private fRepository : IROEventRepository; fExcludeSender: boolean; fExcludeSessionList: boolean; fSessionList: TStringList; function GetRepository: IROEventRepository; protected property Repository : IROEventRepository read GetRepository; { IROEventWriter } function GetSessionList: TStrings; function GetExcludeSender: boolean; function GetExcludeSessionList: boolean; procedure SetExcludeSender(const Value: boolean); procedure SetExcludeSessionList(const Value: boolean); public constructor Create(const aMessage : IROMessage; const aRepository : IROEventRepository); reintroduce; destructor Destroy; override; property ExcludeSender : boolean read GetExcludeSender write SetExcludeSender; property ExcludeSessionList : boolean read GetExcludeSessionList write SetExcludeSessionList; property SessionList : TStrings read GetSessionList; end; TROEventWriterClass = class of TROEventWriter; { TROEventInvoker } {$M+} TROEventInvoker = class(TROEventProxy, IROEventsInvoker) private fEventReceiver : TROEventReceiver; protected { IROEventsInvoker } procedure Execute(const aTarget : TObject; EventData: TStream); property __EventReceiver : TROEventReceiver read fEventReceiver; public constructor Create(anEventReceiver : TROEventReceiver; const aMessage : IROMessage; CloneMessage : boolean); reintroduce; destructor Destroy; override; end; {$M-} TROEventInvokerClass = class of TROEventInvoker; procedure RegisterEventWriterClass(const anEventIID : TGUID; anEventWriteClass : TROEventWriterClass); procedure UnregisterEventWriterClass(const anEventIID : TGUID); function FindEventWriterClass(const anEventIID : TGUID) : TROEventWriterClass; procedure RegisterEventInvokerClass(const anEventID : string; anEventWriteClass : TROEventInvokerClass); procedure UnregisterEventInvokerClass(const anEventID : string); function FindEventInvokerClass(const anEventID : string) : TROEventInvokerClass; implementation uses uRORes; var _EventInvokers, _EventWriters : TStringList; procedure RegisterEventWriterClass(const anEventIID : TGUID; anEventWriteClass : TROEventWriterClass); var id : string; begin id := GUIDToString(anEventIID); _EventWriters.AddObject(id, TObject(anEventWriteClass)); end; procedure UnregisterEventWriterClass(const anEventIID : TGUID); var idx : integer; begin idx := _EventWriters.IndexOf(GUIDToString(anEventIID)); if (idx>=0) then _EventWriters.Delete(idx); end; function FindEventWriterClass(const anEventIID : TGUID) : TROEventWriterClass; var idx : integer; id : string; begin result := NIL; id := GUIDToString(anEventIID); idx := _EventWriters.IndexOf(id); if (idx>=0) then result := TROEventWriterClass(_EventWriters.Objects[idx]); end; procedure RegisterEventInvokerClass(const anEventID : string; anEventWriteClass : TROEventInvokerClass); begin _EventInvokers.AddObject(anEventID, TObject(anEventWriteClass)); end; procedure UnregisterEventInvokerClass(const anEventID : string); var idx : integer; begin idx := _EventInvokers.IndexOf(anEventID); if (idx>=0) then _EventInvokers.Delete(idx); end; function FindEventInvokerClass(const anEventID : string) : TROEventInvokerClass; var idx : integer; begin result := NIL; idx := _EventInvokers.IndexOf(anEventID); if (idx>=0) then result := TROEventInvokerClass(_EventInvokers.Objects[idx]); end; { TROEventRepository } procedure TROEventRepository.AddSession(aSessionID: TGUID); begin CheckProperties; if Assigned(fOnBeforeAddSession) then fOnBeforeAddSession(Self, aSessionID); DoAddSession(aSessionID); if Assigned(fOnAfterAddSession) then fOnAfterAddSession(Self, aSessionID); end; procedure TROEventRepository.AddSession(aSessionID: TGUID; aActiveEventServer: IROActiveEventServer); begin CheckProperties; if Assigned(fOnBeforeAddSession) then fOnBeforeAddSession(Self, aSessionID); DoAddSession(aSessionID, aActiveEventServer); if Assigned(fOnAfterAddSession) then fOnAfterAddSession(Self, aSessionID); end; procedure TROEventRepository.RemoveSession(aSessionID: TGUID); begin CheckProperties; if Assigned(fOnBeforeRemoveSession) then fOnBeforeRemoveSession(Self, aSessionID); DoRemoveSession(aSessionID); if Assigned(fOnAfterRemoveSession) then fOnAfterRemoveSession(Self, aSessionID); end; function TROEventRepository.GetEventData(SessionID: TGUID; var TargetStream: Binary): integer; begin CheckProperties; result := DoGetEventData(SessionID, TargetStream); end; procedure TROEventRepository.Notification(AComponent: TComponent; Operation: TOperation); begin inherited; if (Operation<>opRemove) then Exit; if (AComponent=fSessionManager) then fSessionManager := NIL else if (AComponent=fMessage) then fMessage := NIL; end; function TROEventRepository.QueryInterface(const IID: TGUID; out Obj): HResult; var eventwriterclass : TROEventWriterClass; eventwriter : TROEventWriter; id : string; begin result := inherited QueryInterface(IID, Obj); if (result<>S_OK) then begin id := GUIDToString(IID); if (id='') then Exit; eventwriterclass := FindEventWriterClass(IID); if (eventwriterclass=NIL) then Exit; CheckProperties; eventwriter := eventwriterclass.Create(fMessage, Self); if eventwriter.GetInterface(IID, Obj) then result := S_OK else eventwriter.Free; end; end; procedure TROEventRepository.SetMessage(const Value: TROMessage); begin fMessage := Value; if (fMessage<>NIL) then fMessage.FreeNotification(Self); end; procedure TROEventRepository.SetSessionManager( const Value: TROCustomSessionManager); begin if (fSessionManager=Value) then Exit; fSessionManager := Value; if (fSessionManager<>NIL) then fSessionManager.FreeNotification(Self); end; procedure TROEventRepository.StoreEventData(SourceSessionID : TGUID; Data : Binary; const ExcludeSender: Boolean; const ExcludeSessionList: Boolean; const SessionList: String); begin CheckProperties; DoStoreEventData(SourceSessionID, Data, ExcludeSender, ExcludeSessionList, SessionList); end; procedure TROEventRepository.DoAddSession(aSessionID: TGUID; aActiveEventServer: IROActiveEventServer); begin DoAddSession(aSessionID); end; (*procedure TROEventRepository.DeleteEventData(anEvent: TROEventData); begin /// end;*) procedure TROEventRepository.CheckProperties; begin // Check(SessionManager = nil, Name + '.SessionManager must be assigned.'); Check(Message = nil, Name + '.Message must be assigned.'); Message.CheckProperties; end; { TROInMemoryEventRepository } constructor TROInMemoryEventRepository.Create(aOwner: TComponent); begin inherited; fCritical := TCriticalSection.Create; // fEvents := TObjectList.Create(False); // cleaned up one by one fSessionIDs := TStringList.Create; fSessionIDs.Duplicates := dupIgnore; fSessionIDs.Sorted := TRUE; end; destructor TROInMemoryEventRepository.Destroy; var i: Integer; llink: TROSessionReference; lItem, lnext: TROSessionRefToEventDataLink; begin FDestroyed:=True; fCritical.Enter; try for i := fSessionIDs.Count -1 downto 0 do begin llink := TROSessionReference(fSessionIDs.Objects[i]); if llink = nil then continue; lItem := llink.Head; llink.Free; while lItem <> nil do begin if lItem.EventData <> nil then begin if lItem.EventData.RemoveRef <= 0 then // fEvents.Remove(lItem.EventData); lItem.EventData.Free end; lnext := lItem.Next; lItem.Free; lItem := lNext; end; end; finally fCritical.Leave; end; FreeAndNIL(fSessionIDs); // FreeAndNIL(fEvents); FreeAndNIL(fCritical); inherited; end; function TROInMemoryEventRepository.DoGetEventData(SessionID: TGUID; var TargetStream: Binary): integer; var sessionreference : TROSessionReference; prevref, currref : TROSessionRefToEventDataLink; chunksize : integer; begin result := 0; if FDestroyed then Exit; fCritical.Enter; try sessionreference := FindSession(SessionID); if (sessionreference=NIL) then Exit; TargetStream.Write(result, SizeOf(result)); currref := sessionreference.Head; if (currref<>NIL) then begin repeat currref.EventData.Data.Position := 0; chunksize := currref.EventData.Data.Size; TargetStream.Write(chunksize, SizeOf(chunksize)); TargetStream.CopyFrom(currref.EventData.Data, 0); Inc(result); prevref := currref; currref := currref.Next; if (prevref.EventData.RemoveRef=0)then begin // fEvents.Remove(prevref.EventData); prevref.EventData.Free; end; prevref.Free; until (currref=NIL); sessionreference.Head := NIL; sessionreference.Tail := NIL; end; // Updates the event count TargetStream.Position := 0; TargetStream.Write(result, SizeOf(result)); TargetStream.Position := 0; finally fCritical.Leave; end; end; procedure TROInMemoryEventRepository.DoStoreEventData(SourceSessionID : TGUID; Data : Binary; const ExcludeSender: Boolean; const ExcludeSessionList: Boolean; const SessionList: String); var newlink : TROSessionRefToEventDataLink; newevent : TROEventData; sessionref : TROSessionReference; i : integer; filteredsessionslist : TStringList; isfiltered : boolean; currsessionid : string; begin if FDestroyed then Exit; filteredsessionslist := NIL; isfiltered := SessionList<>''; if isfiltered then begin filteredsessionslist := TStringList.Create; filteredsessionslist.Sorted := TRUE; filteredsessionslist.Duplicates := dupIgnore; filteredsessionslist.CommaText := SessionList; end; fCritical.Enter; try newevent := TROEventData.Create(Data); try for i := 0 to (SessionReferenceCount-1) do begin sessionref := SessionReferences[i]; currsessionid := GUIDToString(sessionref.SessionID); // Filters the sessions the message will go to if isfiltered then begin if ExcludeSessionList then begin if (filteredsessionslist.IndexOf(currsessionid)>=0) then Continue; end else begin if not (filteredsessionslist.IndexOf(currsessionid)>=0) then Continue; end; end; if IsEqualGUID(sessionref.SessionID, SourceSessionID) and ExcludeSender then Continue; if sessionref.ActiveEventReference <> nil then begin newevent.AddRef; // we don't want the event to be freed before we're done with it. sessionref.ActiveEventReference.DispatchEvent(newevent, sessionref.SessionID, Self); end else begin newlink := TROSessionRefToEventDataLink.Create(newevent, sessionref); if (sessionref.Head=NIL) then sessionref.Head := newlink; if (sessionref.Tail<>NIL) then sessionref.Tail.Next := newlink; sessionref.Tail := newlink; end; end; finally if newevent.RemoveRef < 1 then newevent.Free; end; finally fCritical.Leave; FreeAndNIL(filteredsessionslist); end; end; procedure TROInMemoryEventRepository.DoRemoveSession(aSessionID: TGUID); var idx : integer; sessionreference : TROSessionReference; prevref, currref : TROSessionRefToEventDataLink; begin if FDestroyed then Exit; fCritical.Enter; try idx := fSessionIDs.IndexOf(GUIDToString(aSessionID)); if idx>=0 then begin sessionreference := TROSessionReference(fSessionIDs.Objects[idx]); currref := sessionreference.Head; if (currref<>NIL) then begin repeat prevref := currref; currref := currref.Next; if (prevref.EventData.RemoveRef=0) then prevref.EventData.Free; prevref.Free; until (currref=NIL); sessionreference.Head := NIL; sessionreference.Tail := NIL; end; fSessionIDs.Objects[idx].Free; fSessionIDs.Delete(idx); end; finally fCritical.Leave; end; end; procedure TROInMemoryEventRepository.DoAddSession(aSessionID: TGUID); begin DoAddSession(aSessionID, nil); end; function TROInMemoryEventRepository.GetSessionReferenceCount: integer; begin result := fSessionIDs.Count; end; function TROInMemoryEventRepository.GetSessionReferences( Index: integer): TROSessionReference; begin result := TROSessionReference(fSessionIDs.Objects[Index]); end; function TROInMemoryEventRepository.FindSession( aSessionID: TGUID): TROSessionReference; var idx : integer; begin result := NIL; idx := fSessionIDs.IndexOf(GUIDToString(aSessionID)); if (idx>=0) then result := TROSessionReference(fSessionIDs.Objects[idx]); end; procedure TROInMemoryEventRepository.UnRegisterSessionsChangesListener; var Ref: IROSessionsChangesNotifier; SelfRef: IROSessionsChangesListener; begin if GetInterface(IROSessionsChangesListener, SelfRef) then begin if Assigned(fSessionManager) and fSessionManager.GetInterface(IROSessionsChangesNotifier, ref) then begin ref.UnRegisterSessionsChangesListener(SelfRef); end; end; end; procedure TROInMemoryEventRepository.RegisterSessionsChangesListener; var Ref: IROSessionsChangesNotifier; SelfRef: IROSessionsChangesListener; begin if GetInterface(IROSessionsChangesListener, SelfRef) then begin if Assigned(fSessionManager) and fSessionManager.GetInterface(IROSessionsChangesNotifier, ref) then begin ref.RegisterSessionsChangesListener(SelfRef); end; end; end; procedure TROInMemoryEventRepository.SetSessionManager( const Value: TROCustomSessionManager); begin if (csDesigning in ComponentState) or (csLoading in ComponentState) then begin inherited; Exit; end; UnRegisterSessionsChangesListener; inherited; RegisterSessionsChangesListener; end; procedure TROInMemoryEventRepository.Loaded; begin inherited; RegisterSessionsChangesListener; end; procedure TROInMemoryEventRepository.SessionsChangedNotification( const aSessionID: TGUID; aSessionAction: TROSessionsActions; Sender: TObject); var idx: Integer; lRef: IROActiveEventServer; sessionreference: TROSessionReference; begin if FDestroyed then Exit; if aSessionAction in [saDelete, saTmpSessionDelete] then RemoveSession(aSessionID); if aSessionAction = saRemoveActiveListener then begin fCritical.Enter; try idx := fSessionIDs.IndexOf(GUIDToString(aSessionID)); if idx>=0 then begin sessionreference := TROSessionReference(fSessionIDs.Objects[idx]); if Supports(Sender, IROActiveEventServer, lref) then begin sessionreference.ActiveEventReference := TIROActiveEventServerList.RemoveListener(sessionreference.ActiveEventReference, lRef); end else sessionreference.ActiveEventReference := nil; end; finally fCritical.Leave; end; end; end; procedure TROInMemoryEventRepository.BeforeDestruction; begin UnRegisterSessionsChangesListener; inherited; end; procedure TROInMemoryEventRepository.DoAddSession(aSessionID: TGUID; aActiveEventServer: IROActiveEventServer); var newsession : TROSessionReference; idx: Integer; begin if FDestroyed then Exit; fCritical.Enter; try idx := fSessionIDs.IndexOf(GUIDToString(aSessionID)); if idx < 0 then begin newsession := TROSessionReference.Create(aSessionID); if aActiveEventServer <> nil then begin newsession.ActiveEventReference := TIROActiveEventServerList.AddListener(newsession.ActiveEventReference, aActiveEventServer); aActiveEventServer.EventsRegistered(self, aSessionID); end; fSessionIDs.AddObject(GUIDToString(aSessionID), newsession); end else begin newsession := TROSessionReference(fSessionIDs.Objects[idx]); if aActiveEventServer <> nil then begin newsession.ActiveEventReference := TIROActiveEventServerList.AddListener(newsession.ActiveEventReference, aActiveEventServer); aActiveEventServer.EventsRegistered(self, aSessionID); end; end; finally fCritical.Leave; end; end; procedure TROInMemoryEventRepository.RemoveSessionActiveListener( aSessionID: TGUID); var idx : integer; sessionreference : TROSessionReference; begin if FDestroyed then Exit; fCritical.Enter; try idx := fSessionIDs.IndexOf(GUIDToString(aSessionID)); if idx>=0 then begin sessionreference := TROSessionReference(fSessionIDs.Objects[idx]); sessionreference.ActiveEventReference := nil; end; finally fCritical.Leave; end; end; (* procedure TROInMemoryEventRepository.DeleteEventData( anEvent: TROEventData); begin fCritical.Enter; try fEvents.Remove(anEvent); finally fCritical.Leave; end; end; *) { TROSessionReference } constructor TROSessionReference.Create(aSessionID: TGUID); begin inherited Create; fSessionID := aSessionID; end; function TROSessionReference.GetHead: TROSessionRefToEventDataLink; begin Result := fHead; end; function TROSessionReference.GetTail: TROSessionRefToEventDataLink; begin Result := fTail; end; { TROSessionRefToEventDataLink } constructor TROSessionRefToEventDataLink.Create( anEventDataItem: TROEventData; aSessionReference: TROSessionReference); begin inherited Create; anEventDataItem.AddRef; fEventData := anEventDataItem; fSessionReference := aSessionReference; end; destructor TROSessionRefToEventDataLink.Destroy; begin inherited; end; { TROEventReceiver } constructor TROEventReceiver.Create(aOwner: TComponent); begin inherited; fHandlers := TStringList.Create; fInterval := 30000; fSynchronizeInvoke := true; end; destructor TROEventReceiver.Destroy; begin FreeAndNIL(fHandlers); FreeAndNIL(fRetainedObjects); inherited; end; procedure TROEventReceiver.Activate; begin Active := TRUE; end; procedure TROEventReceiver.Deactivate; begin Active := FALSE; end; procedure TROEventReceiver.SetActive(const Value: boolean); begin if Value <> fActive then begin if Value and not (csDesigning in ComponentState) then CheckProperties; fActive := Value; if (Channel = nil) or not Supports(Channel, IROActiveEventChannel, fEventAwareChannel) then fEventAwareChannel := nil; if Value then begin if fEventAwareChannel <> nil then fEventAwareChannel.RegisterEventReceiver(self) else if FTimer = nil then fTimer := TROThreadTimer.Create(OnTimerTick, fInterval, false); end else begin if fEventAwareChannel <> nil then fEventAwareChannel.UnregisterEventReceiver(self) else if fTimer <> nil then begin fTimer.AsyncFree; fTimer := nil; end; end; if Value then begin if Assigned(fOnActivate) then fOnActivate(Self); end else begin if Assigned(fOnDeactivate) then fOnDeactivate(Self); end; end; end; procedure TROEventReceiver.SetInterval(const Value: integer); begin fInterval := Value; if fTimer <> nil then fTimer.Timeout := Value; end; procedure TROEventReceiver.SetServiceName(const Value: string); begin fServiceName := Trim(Value); end; procedure TROEventReceiver.Invoke_RegisterEventClient(const EventTypes: String); var __message : IROMessage; __channel : IROTransportChannel; ClientId: string; begin CheckProperties; __message := (Message as IROMessage); __channel := (Channel as IROTransportChannel); try __message.InitializeRequestMessage(__channel, 'RemObjects', ServiceName, 'RegisterEventClient'); __channel.BeforeDispatch(__message); // sets the client id for some channels ClientId := GuidToString(__message.ClientID); __message.Write('ClientID', TypeInfo(String), ClientId, []); __message.Write('EventTypes', TypeInfo(String), EventTypes, []); __message.Finalize; __channel.Dispatch(__message); finally __Message.FreeStream; end end; procedure TROEventReceiver.Invoke_UnregisterEventClient(const EventTypes: String); var __message : IROMessage; __channel : IROTransportChannel; ClientId: string; begin CheckProperties; __message := (Message as IROMessage); __channel := (Channel as IROTransportChannel); try __message.InitializeRequestMessage(__channel, 'RemObjects', ServiceName, 'UnregisterEventClient'); __channel.BeforeDispatch(__message); ClientId := GuidToString(__message.ClientID); __message.Write('ClientID', TypeInfo(String), ClientID, []); __message.Write('EventTypes', TypeInfo(String), EventTypes, []); __message.Finalize; __channel.Dispatch(__message); finally __Message.FreeStream; end end; function TROEventReceiver.Invoke_GetEventsData(out EventsData: Binary): Integer; var __message : IROMessage; __channel : IROTransportChannel; ClientId: string; begin CheckProperties; __message := (Message as IROMessage); __channel := (Channel as IROTransportChannel); try __message.InitializeRequestMessage(__channel, 'RemObjects', ServiceName, 'GetEventsData'); __channel.BeforeDispatch(__message); ClientId := GuidToString(__message.ClientID); __message.Write('ClientID', TypeInfo(String), ClientID, []); __message.Finalize; __channel.Dispatch(__message); __message.Read('Result', TypeInfo(Integer), result, []); __message.Read('EventsData', TypeInfo(Binary), EventsData, []); finally __message.FreeStream; end end; procedure TROEventReceiver.FireEvents(EventsDataStream : TStream); var tempstream : TStream; chunksize, idx, cnt : integer; messageclone : IROMessage; eventInvokerclass : TROEventInvokerClass; eventInvoker : TROEventInvoker; begin CheckProperties; tempstream := NIL; eventInvoker := NIL; messageclone := (Message as IROMessageCloneable).Clone; EventsDataStream.Position := 0; if fEventAwareChannel <> nil then begin // event aware channels send one event at a time. messageclone.ReadFromStream(EventsDataStream); eventInvokerclass := FindEventInvokerClass(messageclone.InterfaceName); eventInvoker := eventInvokerclass.Create(Self, messageclone, FALSE); try for idx := 0 to fHandlers.Count -1 do begin if SameText(fHandlers[idx], messageclone.InterfaceName) then eventInvoker.Execute(fHandlers.Objects[idx], EventsDataStream); end; finally FreeAndNIL(eventInvoker); end; end else begin EventsDataStream.Read(cnt, SizeOf(integer)); while (EventsDataStream.Position0) and (eventsdata<>NIL) then begin Dispatch(eventsdata, fTimer); end; except on e: Exception do begin if @fOnPollException <> nil then fOnPollException(self, e); end; end; finally if (eventsdata<>NIL) then eventsdata.Free; end; end; procedure TROEventReceiver.RegisterEventHandlers( const EventIDs: array of string; const EventHandlers: array of TObject); var clientid, eventID, eventtypes : string; i : integer; begin // Adds a reference to the event handler object clientid := GUIDTOString(Self.ClientID); eventtypes := ''; for i := 0 to Length(EventIDs)-1 do begin eventID := EventIDs[i]; eventtypes := eventtypes+eventID+','; end; Delete(eventtypes, Length(eventtypes), 1); // Sends the remote request if fEventAwareChannel = nil then Invoke_RegisterEventClient(eventtypes); //Moved here to avoid invalid list in case registration is incorrect. //(for example if connection to server temporary broken) for i := 0 to Length(EventIDs)-1 do begin // Adds a reference to the local event handler object eventID := EventIDs[i]; fHandlers.AddObject(eventID, EventHandlers[i]); end; end; {$WARN SYMBOL_DEPRECATED OFF} // delphi warns on the implementation of this method because it's defined as deprecated. function TROEventReceiver.EventHandlersIsRegistered(const EventIDs: array of string; const EventHandlers: array of TObject): boolean; begin result := AreEventHandlersRegistered(EventIDs, EventHandlers); end; {$WARN SYMBOL_DEPRECATED ON} function TROEventReceiver.AreEventHandlersRegistered(const aEventIDs: array of string; const aEventHandlers: array of TObject): boolean; var i : integer; begin Result := True; for i := 0 to Length(aEventIDs) - 1 do begin if not IsEventHandlerRegistered(aEventIDs[i], aEventHandlers[i]) then begin Result := False; break; end; end; end; function TROEventReceiver.IsEventHandlerRegistered(const aEventID: string; const aEventHandler: TObject): boolean; var j : integer; begin Result := False; for j := 0 to fHandlers.Count - 1 do if (fHandlers.Objects[j] = aEventHandler) and (fHandlers[j] = aEventID) then begin Result := True; break; end; end; procedure TROEventReceiver.UnregisterEventHandlers(const EventIDs: array of string); var clientid, eventID, eventtypes : string; i, idx : integer; begin clientid := GUIDTOString(Self.ClientID); eventtypes := ''; for i := 0 to Length(EventIDs)-1 do begin eventID := EventIDs[i]; // Removes the reference to the local event handler object idx := fHandlers.IndexOf(eventID); if (idx>=0) then fHandlers.Delete(idx); eventtypes := eventtypes+eventID+','; end; Delete(eventtypes, Length(eventtypes), 1); // Sends the remote request if fEventAwareChannel = nil then Invoke_UnregisterEventClient(eventtypes); end; function TROEventReceiver.GetClientID: TGUID; begin CheckProperties; result := Message.ClientID; end; function TROEventReceiver.IsRetained(const anObject: TObject): boolean; begin result := (fRetainedObjects<>NIL) and (fRetainedObjects.IndexOf(anObject)>=0) end; procedure TROEventReceiver.ReleaseObject(const anObject: TObject); var idx : integer; begin // Only creates it if necessary if (fRetainedObjects=NIL) then Exit; idx := fRetainedObjects.IndexOf(anObject); if (idx>=0) then fRetainedObjects.Delete(idx); end; procedure TROEventReceiver.RetainObject(const anObject: TObject); begin // Only creates it if necessary if (fRetainedObjects=NIL) then fRetainedObjects := TList.Create; fRetainedObjects.Add(anObject); end; type TMyThread = class(TThread) end; // in d6 a thread is private TSyncClass = class private fData: TStream; fCaller: TROEventReceiver; public property Caller: TROEventReceiver read fCaller write fCaller; property Data: TStream read fData write fData; procedure DoDispatch; end; procedure TSyncClass.DoDispatch; begin fCaller.FireEvents(Data); end; procedure TROEventReceiver.Dispatch(aStream: TStream; aCaller: TThread); var lSync: TSyncClass; begin if fSynchronizeInvoke and Assigned(aCaller) then begin lSync := TSyncClass.Create; try lSync.Caller := Self; lSync.Data := aStream; TMyThread(aCaller).Synchronize(lSync.DoDispatch); finally lSync.Free; end; end else FireEvents(aStream); end; function TROEventReceiver.GetSynchronizeInvoke: Boolean; begin Result := fSynchronizeInvoke; end; procedure TROEventReceiver.RegisterEventHandler(const EventID: string; const EventHandler: TObject); begin RegisterEventHandlers([EventID], [EventHandler]); end; procedure TROEventReceiver.UnregisterEventHandlersByObject( const EventHandler: array of TObject); var clientid, eventID, eventtypes : string; i, idx : integer; begin clientid := GUIDTOString(Self.ClientID); eventtypes := ''; for i := 0 to Length(EventHandler)-1 do begin // Removes the reference to the local event handler object idx := fHandlers.IndexOfObject(EventHandler[i]); if (idx>=0) then begin eventID := fHandlers[i]; fHandlers.Delete(idx) end else continue; eventtypes := eventtypes+eventID+','; end; Delete(eventtypes, Length(eventtypes), 1); // Sends the remote request // if fEventAwareChannel = nil then Invoke_UnregisterEventClient(eventtypes); end; procedure TROEventReceiver.UnregisterEventHandler( const EventHandler: TObject); begin UnregisterEventHandlersByObject([EventHandler]); end; procedure TROEventReceiver.UnregisterEventHandler(const EventIDs: string); begin UnregisterEventHandlers([EventIDs]); end; procedure TROEventReceiver.CheckProperties; begin Check(Channel = nil, Name + '.Channel must be assigned.'); Channel.CheckProperties; Check(Message = nil, Name + '.Message must be assigned.'); Message.CheckProperties; Check(ServiceName = '', Name + '.ServiceName must be set.'); end; function TROEventReceiver.GetObject: TObject; begin result := Self; end; { TROEventWriter } constructor TROEventWriter.Create(const aMessage : IROMessage; const aRepository : IROEventRepository); begin inherited Create(aMessage, TRUE); fSessionList := TStringList.Create; fSessionList.Sorted := TRUE; fSessionList.Duplicates := dupIgnore; fExcludeSender := TRUE; fExcludeSessionList := FALSE; // By default only includes those in the session list fRepository := aRepository; end; destructor TROEventWriter.Destroy; begin fRepository := NIL; FreeAndNIL(fSessionList); inherited; end; {function TROEventWriter.GetMessage: IROMessage; begin result := fMessage; end;} function TROEventWriter.GetExcludeSender: boolean; begin result := fExcludeSender end; function TROEventWriter.GetExcludeSessionList: boolean; begin result := fExcludeSessionList; end; function TROEventWriter.GetRepository: IROEventRepository; begin result := fRepository end; function TROEventWriter.GetSessionList: TStrings; begin result := fSessionList; end; procedure TROEventWriter.SetExcludeSender(const Value: boolean); begin fExcludeSender := Value end; procedure TROEventWriter.SetExcludeSessionList(const Value: boolean); begin fExcludeSessionList := Value end; { TROEventProxy } constructor TROEventProxy.Create(const aMessage: IROMessage; CloneMessage: boolean); begin inherited Create; if CloneMessage then begin if (aMessage=NIL) then raise EROException.Create(err_MessageNotAssigned); fMessage := (aMessage as IROMessageCloneable).Clone end else fMessage := aMessage; end; destructor TROEventProxy.Destroy; begin fMessage := NIL; inherited; end; function TROEventProxy.GetMessage: IROMessage; begin result := fMessage end; { TROEventInvoker } constructor TROEventInvoker.Create(anEventReceiver: TROEventReceiver; const aMessage: IROMessage; CloneMessage: boolean); begin inherited Create(aMessage, CloneMessage); fEventReceiver := anEventReceiver; end; destructor TROEventInvoker.Destroy; begin inherited; end; procedure TROEventInvoker.Execute(const aTarget : TObject; EventData: TStream); var mtd : TROInvokerInvokeMethod; intf : IInterface; begin __Message.ReadFromStream(EventData); mtd := nil; @mtd := MethodAddress('Invoke_'+__Message.MessageName); if (@mtd <> nil) then begin aTarget.GetInterface(IInterface, intf); mtd(__EventReceiver, __Message, intf); end; end; { TSyncClass } { TIROActiveEventServerList } class function TIROActiveEventServerList.AddListener( aList: IROActiveEventServerList; aItem: IROActiveEventServer): IROActiveEventServerList; begin result := aList; while aList <> nil do begin if aList.Server = aItem then exit; aList := aList.Next; end; Result := TIROActiveEventServerList.Create(Result, aItem); end; constructor TIROActiveEventServerList.Create( aNext: IROActiveEventServerList; aItem: IROActiveEventServer); begin inherited Create; fNext := aNext; fServer := aItem; end; procedure TIROActiveEventServerList.DispatchEvent( anEventDataItem: TROEventData; aSessionReference: TGUID; aSender: TObject); var lItem: IROActiveEventServerList; begin lItem := Self; while lItem <> nil do begin lItem.Server.DispatchEvent(anEventDataItem, aSessionReference, aSender); lItem := lItem.Next; if lItem <> nil then anEventDataItem.AddRef; end; end; function TIROActiveEventServerList.GetNext: IROActiveEventServerList; begin result := fNext; end; function TIROActiveEventServerList.GetServer: IROActiveEventServer; begin result := fServer; end; class function TIROActiveEventServerList.RemoveListener( aList: IROActiveEventServerList; aItem: IROActiveEventServer): IROActiveEventServerList; var lPrev: IROActiveEventServerList; begin lPrev := nil; result := aList; while aList <> nil do begin if aList.Server = aItem then begin if lPrev = nil then begin result := aList.Next; exit; end else begin lPrev.Next := aList.Next; end; end; aList := aList.Next; end; end; procedure TIROActiveEventServerList.SetNext( aData: IROActiveEventServerList); begin fNext := aData; end; initialization _EventInvokers := TStringList.Create; _EventInvokers.Sorted := TRUE; _EventInvokers.Duplicates := dupIgnore; _EventWriters := TStringList.Create; _EventWriters.Sorted := TRUE; _EventWriters.Duplicates := dupIgnore; finalization FreeAndNIL(_EventInvokers); FreeAndNIL(_EventWriters); end.