BorlandTalk.com Forum Index BorlandTalk.com
Borland discussion newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Maintaining history of system events

 
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design
View previous topic :: View next topic  
Author Message
Ivo Bauer
Guest





PostPosted: Fri Mar 23, 2007 5:42 pm    Post subject: Maintaining history of system events Reply with quote



Hi all,

This post is somehow lengthy, but I will appreciate if you could read
it entirely. :-)


I have an application/system in which I need to maintain a history of
all events that occurs during system's lifetime. I have a class
hierarchy of all defined system events - all of them are direct or
indirect descendants of TBaseEvent class:

TBaseEvent = class
protected
function GetDescription: string; virtual; abstract;
public
property Description: string read GetDescription;
end;

TBaseEvent descendants add their own properties that are relevant only
to a specific event type/class. I use class inheritance extensively
here to group together a set of events that describe the same specific
part of a system, in order to avoid reintroducing the same properties
in a bunch of event classes.

Now, whenever the event occurs in my system, I create an instance of
TEventInstance class (pun not intended) and pass it a reference to a
specific event class to create and the instance ID. TEventInstance is
responsible for managing lifetime of FEvent instance it holds:

TEventClass = class of TBaseEvent;

TEventInstance = class
private
FEvent: TBaseEvent;
FID: TGUID;
FTimeStamp: TDateTime;
FWasRead: Boolean;
public
constructor Create(AEventClass: TEventClass; AInstanceID: TGUID);
property Event: TBaseEvent read FEvent;
property ID: TGUID read FID;
property TimeStamp: TDateTime read FTimeStamp write FTimeStamp;
property WasRead: Boolean read FWasRead write FWasRead;
end;

Once the TEventInstance instance is created, the event manager class
can set up additional properties such as TimeStamp to remember when
exactly this event occured, or WasRead property that indicates whether
or not user has seen and acknowledged the instance of this event.

I'm slowly approaching my problem: There could be two "types" of
events with regard to how they occur in the system:

1) Explicit events - they are invoked explicitely by the user, such as
changing of certain system properties, user-issued commands etc. When
such event occurs, the event manager class should set the WasRead
property of the event instance to True because if it was the user who
invoked this event, it apparently indicates that the same user also
acknowledged the occurence of this event.

2) Implicit events - they are autogenerated by the system or any of
its internal subsystems. Due to their nature, the WasRead property of
given event instance has to be set to False and the user
confirmation/acknowledgement is required.

Now, here's my problem: I'm not sure how to design the event manager
class, or more precisely, its part that is responsible for setting the
value of WasRead property of newly instantiated event instance. The
explicit/implicit attribute of each event type/class is fixed, in
other words if given event class is said to be explicit, it cannot
become implicit under any condition, and vice versa.

Should I introduce this explicit/implicit attribute directly in the
TBaseEvent class and let their descendants to determine the actual
value of this attribute? I also thought about creating of
TBaseExplicitEvent and TBaseImplicitEvent as a direct descendants of
TBaseEvent and have all concrete event classes to descend from these
two intermediate classes, but I dismissed this idea after a while
because the class inheritance should be designed around the
functionality and not around the way how the classes are used.

I ended up with the simple array of class references that is held
privately by the event manager class. This array is populated in the
constructor with references to all classes that are considered to be
implicit - there are far less implicit events defined than explicit
ones in the system, so this array is short and the searching for a
particular class reference is fast. Now when the new event instance is
being instantiated, the event manager class searches the array for
given class reference. If it's found in the array, it's determined to
be implicit. If it's not in the array, the event instance is
considered to be explicit.

What do you thing about this design? Would you leave it that way or
rather change some part of it? I appreacite your insights.


--
Ivo Bauer [OZM Research]
Back to top
Gerard
Guest





PostPosted: Fri Mar 23, 2007 7:57 pm    Post subject: Re: Maintaining history of system events Reply with quote



Hi IVO,

What I would do:
- Make WasRead attribute false for the ancestor TBaseEvent class on
creation.
- I would let whatever code or class acknowledges the event set it's
wasread property to true.

If its a TEventReader class, for example, it's ReadEvent procedure could
be like

TEventReader.ReadEvent(aEvent: TEvent);
begin
...
aEvent.WasRead := True;
end;

Just my 2 € cents.

Regards,

Gerard.
Back to top
Bob Dawson
Guest





PostPosted: Mon Mar 26, 2007 7:07 am    Post subject: Re: Maintaining history of system events Reply with quote



"Ivo Bauer" wrote

Questions:
Quote:
property WasRead: Boolean read FWasRead write FWasRead;

Is it possible for WasRead to go from True to False? Doesn't sound like it.

Quote:
because if it was the user who invoked this event,

Is it important to to know the identity of the user that read the event?

If so, then I might not bother with an FWasRead boolean at all. The event
rather gets a field to track the identity of the reader, and a timestamp of
the read. The logical value of WasRead is then calculated as reader field is
not empty and timestamp is valid.

An explicit event takes the reader as a parameter of its constructor and
applies the time stamp.
An implicit event adds a ReadEvent(reader : TUser) method.

Quote:
if given event class is said to be explicit, it cannot become implicit
under any condition, and vice versa.

That sounds like a very strong case (immutability) for a class
differentiation between explicit and implicit events.

bobD
Back to top
Ivo Bauer
Guest





PostPosted: Mon Mar 26, 2007 1:44 pm    Post subject: Re: Maintaining history of system events Reply with quote

Hi Gerard,

Thanks for the reply.

Quote:
What I would do:
- Make WasRead attribute false for the ancestor TBaseEvent class on
creation.

Yes, that's what TEventHistory (event manager class) is doing for
every new event that occurs in the system.

Quote:
- I would let whatever code or class acknowledges the event set it's
wasread property to true.

If its a TEventReader class, for example, it's ReadEvent procedure could
be like

TEventReader.ReadEvent(aEvent: TEvent);
begin
...
aEvent.WasRead := True;
end;

Sounds reasonable, but I need to differentiate between explicit and
implicit events. Explicit events are invoked by the user and therefore
they should be marked as read (WasRead = True) upon invocation by default.


--
Ivo Bauer [OZM Research]
Back to top
Ivo Bauer
Guest





PostPosted: Mon Mar 26, 2007 1:44 pm    Post subject: Re: Maintaining history of system events Reply with quote

Hi Bob,

Thanks for the reply. For the sake of clarity, I slightly changed the
terminology, so

TBaseEvent = class that holds the required information related to
certain type of event

TEventDescriptor (previously TEventInstance) = class that describes a
specific occurence of specific event, holds a reference to TBaseEvent
class instance, timestamp of occurence, etc.

Quote:
Questions:
property WasRead: Boolean read FWasRead write FWasRead;

Is it possible for WasRead to go from True to False? Doesn't sound like it.

Of course no, my bad. Once the event is read (acknowledged) by the
user, it cannot be made "unread" under any condition. It would be more
appropriate to make WasRead property read-only and introduce method
MarkAsRead that would irreversibly change this property from False to
True (it is set to False upon creation).

TEventDescriptor = class
public
...
procedure MarkAsRead;
property WasRead: Boolean read FWasRead;
end;

Quote:
Is it important to to know the identity of the user that read the event?

No, it's important just to know if and perhaps when it was
acknowledged by the user.

Quote:
If so, then I might not bother with an FWasRead boolean at all. The event
rather gets a field to track the identity of the reader, and a timestamp of
the read. The logical value of WasRead is then calculated as reader field is
not empty and timestamp is valid.

It's not important to track the identity of the reader but thanks for
this suggestion. I think I will just introduce another time stamp
named like DateAcknowledged (initially set to zero = invalid value) -
I realized I need to track the datetime of both the
ivocation/occurence and also acknowledgement by the user. The WasRead
(or maybe better Acknowledged) property would then be calculated from
DateAcknowledged if it's greater than zero. What do you think?

Quote:
An explicit event takes the reader as a parameter of its constructor and
applies the time stamp.
An implicit event adds a ReadEvent(reader : TUser) method.

Given the fact that the reader identity need not to be tracked, could
this be changed so that the explicit event takes a DateAcknowledged in
its constructor and the implicit event introduces ReadEvent method
that takes also DateAcknowledged timestamp?

You might ask now why I insist on passing timestamps as parameters but
this is because my application uses special time provider that is
independent of system time on the target machine, so I can't use
functions like Now.

Quote:
if given event class is said to be explicit, it cannot become implicit
under any condition, and vice versa.

That sounds like a very strong case (immutability) for a class
differentiation between explicit and implicit events.

Do you mean to differentiate between explicit and implicit events
(TBaseEvent descendants) or rather explicit and implicit event
*descriptors*? I think it's not responsibility of TBaseEvent class
(and their descendants) to determined whether they're explicit or
implicit. I would rather create two descendants of TEventDescriptor -
TExplicitEventDescriptor and TImplicitEventDescriptor and do the
differentiation here because event descriptor is the class that holds
information about the *occurence* of certain event. Do you agree with
this reasoning?

TBaseEventDescriptor = class
private
FDateAcknowledged: TDateTime;
FDateInvoked: TDateTime;
FEvent: TBaseEvent;
function GetAcknowledged: Boolean;
public
constructor Create(AClass: TEventClass;
const ADateInvoked: TDateTime); virtual;
destructor Destroy; override;
property Acknowledged: Boolean read GetAcknowledged;
property DateAcknowledged: TDateTime read FDateAcknowledged;
property DateInvoked: TDateTime read FDateInvoked;
property Event: TBaseEvent read FEvent;
end;

constructor TBaseEventDescriptor.Create(AClass: TEventClass;
const ADateInvoked: TDateTime);
begin
FEvent := AClass.Create;
FDateInvoked := ADateInvoked;
FDateAcknowledged := 0.0; // invalid
end;

function TBaseEventDescriptor.GetAcknowledged: Boolean;
begin
Result := DateAcknowledged > 0.0;
end;

type
TExplicitEventDescriptor = class(TBaseEventDescriptor)
public
constructor Create(AClass: TEventClass;
const ADateInvoked: TDateTime); override;
end;

constructor TExplicitEventDescriptor.Create(AClass: TEventClass;
const ADateInvoked: TDateTime);
begin
inherited;
FDateAcknowledged := FDateInvoked;
end;

type
TImplicitEventDescriptor = class(TBaseEventDescriptor)
public
procedure AcknowledgeEvent(ADateAcknowledged: TDateTime);
end;

procedure TImplicitEventDescriptor.AcknowledgeEvent(ADateAcknowledged:
TDateTime);
begin
FDateAcknowledged := ADateAcknowledged;
end;

Or perhaps, I could get rid of this AcknowledgeEvent method and just
override DateAcknowledged property and make it also writable. Either
way, the new value of date acknowledged would need to be checked if
its equal to or greater than date invoked.

Am I on the right track?


--
Ivo Bauer [OZM Research]
Back to top
Bob Dawson
Guest





PostPosted: Mon Mar 26, 2007 6:51 pm    Post subject: Re: Maintaining history of system events Reply with quote

"Ivo Bauer" wrote
Quote:

Given the fact that the reader identity need not to be tracked,
could this be changed so that the explicit event takes a
DateAcknowledged in its constructor and the implicit event
introduces ReadEvent method that takes also DateAcknowledged
timestamp?

Sounds reasonable--though I am somewhat surprised about not tracking user
identities <g>.

Quote:
about the *occurence* of certain event. Do you agree with
this reasoning?

Not really. I'd probably want to have IsExplicit as a property of the
baseEvent and not differentiate the occurance class. The occurance knows
whether it's explicit or not by simply asking the held baseEvent instance,
and all occurances become the same.

Make the occurance timestamp a required part of the constructor. The
constructor asks its internal BaseEvent if it's explicit--if so, it applies
the timestamp to both occurance datetime and acknowleged datetime.

So

Quote:
constructor TBaseEventDescriptor.Create(AClass: TEventClass;
const ADateInvoked: TDateTime);
begin
FEvent := AClass.Create;
FDateInvoked := ADateInvoked;
if FEvent.IsExplicit then

FDateAcknowledged := ADateInvoked;
Quote:
end;


An acknowlege(timestamp) method allows implicit events to set the
acknowleged datetime later, but this method is gated as

begin
if FAcknowlegedTime = 0 then
FAcknowlegedTime := NewTime;
end;

That way an implicit event is only acknowleged once, and in the case of an
explicit event the method will never do anything. You could also, of course,
treat re-acknowlegement as an error if desired.

bobD
Back to top
Ivo Bauer
Guest





PostPosted: Tue Mar 27, 2007 1:14 am    Post subject: Re: Maintaining history of system events Reply with quote

Bob Dawson napsal(a):
Quote:
Sounds reasonable--though I am somewhat surprised about not tracking user
identities <g>.

OK, there could be multiple users accessing the application but there
could be only one user logged on at any time (logon and logoff events
are also being stored in this event history, BTW). This is to prevent
from unauthorized use of the application while it's running. There is,
however, no special requirement to associate the acknowledgement of a
system event with the current user identity. But given the fact that
logon/logoff events are a part of event history, this information
could be easily retrieved if there should be a need for it.

These implicit events are mostly error/status messages from a remote
device and one of my goals is to make sure that these messages are
brought to the screen and get noticed/acknowledged by the user.
Ignoring those implicit events by the user could have a severe impact
on the surrounding environment. Including the user itself :-)

Quote:
about the *occurence* of certain event. Do you agree with
this reasoning?

Not really. I'd probably want to have IsExplicit as a property of the
baseEvent and not differentiate the occurance class. The occurance knows
whether it's explicit or not by simply asking the held baseEvent instance,
and all occurances become the same.

Hmm, you're right. I didn't realize that the explicit/implicit
attribute needs to be related to the (base) event class itself,
otherwise one might end up with both explicit and implicit occurances
of the same event class, which is not what I want. TBaseEvent class
now looks like this:

type
TBaseEvent = class
...
protected
function GetIsExplicit: Boolean; virtual; abstract;
public
...
property IsExplicit: Boolean read GetIsExplicit;
end;

Quote:
Make the occurance timestamp a required part of the constructor. The
constructor asks its internal BaseEvent if it's explicit--if so, it applies
the timestamp to both occurance datetime and acknowleged datetime.

Occurance timestamp was already there, but I updated my code to apply
the timestamp also to DateAcknowledged for the explicit events,
according to your suggestion.

Quote:
An acknowlege(timestamp) method allows implicit events to set the
acknowleged datetime later, but this method is gated as

begin
if FAcknowlegedTime = 0 then
FAcknowlegedTime := NewTime;
end;

That way an implicit event is only acknowleged once, and in the case of an
explicit event the method will never do anything. You could also, of course,
treat re-acknowlegement as an error if desired.

Thanks for taking the time and explaining this. The Acknowledge method
now looks like this:

procedure TEventDescriptor.Acknowledge(const ADateAcknowledged:
TDateTime);
begin
if ADateAcknowledged < FDateOccured then
raise Exception.Create('Invalid datetime passed');
if FEvent.IsExplicit then
raise Exception.Create('Implicit event required');
if FDateAcknowledged >= FDateOccured then
raise Exception.Create('Re-acknowledgement not allowed');
FDateAcknowledged := ADateAcknowledged;
end;

Now I have also TEventHistory class which is merely a wrapper around
TList that holds and exposes the event descriptors (they are owned by
the history class). The internal list is sorted by DateOccured
belonging to individual items/descriptors in ascending order. Adding
new event occurences to the history is done by multitude methods whose
all call the following method to actually create and add the event
descriptor to the history:

function TEventHistory.Add(AClass: TEventClass;
const ADateOccured: TDateTime): Integer;
var
LDescriptor: TEventDescriptor;
begin
LDescriptor := TEventDescriptor.Create(AClass, ADateOccured);
try
FList.Add(LDescriptor);
FList.Sort(CompareEventDescriptors);
Result := FList.IndexOf(LDescriptor);
except
LDescriptor.Free;
raise;
end;
end;


I think I got it finally working. Many thanks to you for your valuable
suggestions and advices, Bob!


--
Ivo Bauer [OZM Research]
Back to top
Bob Dawson
Guest





PostPosted: Tue Mar 27, 2007 5:19 am    Post subject: Re: Maintaining history of system events Reply with quote

"Ivo Bauer" wrote
Quote:

I think I got it finally working. Many thanks to you for your
valuable suggestions and advices, Bob!

My pleasure--glad I could be of assistance.

bobD
Back to top
Display posts from previous:   
Post new topic   Reply to topic    BorlandTalk.com Forum Index -> Delphi OO design All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.