forked from ARSBlue/ToolBox-4-Iris
-
Notifications
You must be signed in to change notification settings - Fork 0
/
EventListener.cls
213 lines (188 loc) · 7.09 KB
/
EventListener.cls
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
Include (arsblue.OS, arsblue.Status)
/// This the base class for all event listeners.
/// NOTE: you do not need to explicit save the event listener, it is saved automatically on each set of a property!
///
/// ARSBlue ToolBox-4-Iris
/// Copyright © 2019 ARS Blue GmbH
/// http://www.ars-blue.at
Class arsblue.event.EventListener Extends (%Persistent, arsblue.io.Serializable) [ Abstract ]
{
/// The event target to call.
Property EventTarget As %String;
/// The event listener run flag.
/// If this flag is set to true, the event listener starts immediately event processing until the flag is set to false.
Property EventRun As %Boolean [ InitialExpression = {$$$NO} ];
/// The event listener deamon.
/// If the event deamon is set to a valid process id (default), the event listener is only available until the event listener object is disposed.
/// If the event deamon is set to zero, the event listener is static and will be fired immediately.
/// If the event deamon is a negative number, the event listener is working within an event queue.
Property EventDeamon As %Integer [ InitialExpression = {$JOB} ];
/// The event queue.
Property EventQueue As arsblue.event.EventQueue;
/// The event target index.
Index EventTarget On EventTarget [ Type = bitmap ];
/// The event listener run index.
Index EventRun On EventRun [ Type = bitmap ];
/// The event listener deamon index.
Index EventDeamon On EventDeamon [ Type = bitmap ];
/// The event queue index
Index EventQueue On EventQueue [ Type = bitmap ];
/// The foreign key constraint for event queue.
ForeignKey EventQueueFK(EventQueue) References arsblue.event.EventQueue() [ OnDelete = cascade ];
/// This method is called when the event listener is disposed.
Method %OnClose() As %Status
{
quit:(..EventDeamon'=$JOB) $$$OK
quit ..%Delete(..%Oid())
}
/// Set event target.
Method EventTargetSet(EventTarget As %String) As %Status
{
$$$QuitIf((EventTarget="") || '$$$comClassDefined(EventTarget),"cannot set invalid event target classname "_EventTarget)
$$$QuitIf('$$$TypeOf(EventTarget,$E(..%ClassName(1),1,*-8)_"Adapter"),"cannot set invalid event target adapter "_EventTarget)
set i%EventTarget=EventTarget
quit ..%Save()
}
/// Set event run.
Method EventRunSet(EventRun As %Boolean) As %Status
{
set i%EventRun=''EventRun
quit ..%Save()
}
/// Set event deamon.
Method EventDeamonSet(EventDeamon As %Integer) As %Status
{
if ('$IsObject(..EventQueue))
{
set i%EventDeamon=EventDeamon
quit ..%Save()
}
quit $$$OK
}
/// Set event queue.
Method EventQueueSet(EventQueue As arsblue.event.EventQueue) As %Status
{
if ($IsObject(EventQueue))
{
set i%EventQueue=EventQueue.%Id()
set i%EventDeamon=-i%EventQueue
}
else
{
set i%EventQueue=$$$NULLOREF
set i%EventDeamon=$JOB
}
quit ..%Save()
}
/// This expression delivers the event listener resource.
Method GetEventListenerResource() As %String
{
quit $$$NspDest($E(..%ClassName(1),1,*-8))_"("""_..%Id()_""""_$S(..EventDeamon:","_..EventDeamon,1:"")_")"
}
/// This trigger is responsible for starting and stopping of the event listener
Trigger EventListenerAfter [ Event = INSERT/UPDATE/DELETE, Foreach = row/object, Time = AFTER ]
{
new eventListenerResourceBefore,eventListenerResourceAfter
set eventListenerResourceBefore=$$$NspDest($E({%%CLASSNAMEQ},1,*-8))_"("""_{ID}_""""_$S({EventDeamon*O}:","_{EventDeamon*O},1:"")_")"
set eventListenerResourceAfter=$$$NspDest($E({%%CLASSNAMEQ},1,*-8))_"("""_{ID}_""""_$S({EventDeamon*N}:","_{EventDeamon*N},1:"")_")"
if ($CASE({%%OPERATION},"INSERT":$$$YES,"UPDATE":$$$YES,:$$$NO))
{
do:(({EventRun*N} && {EventDeamon*N} && '{EventQueue*N}) && '$System.Event.Defined(eventListenerResourceAfter)) $System.Event.Create(eventListenerResourceAfter)
do:(('{EventRun*N} || (eventListenerResourceBefore'=eventListenerResourceAfter)) && '{EventQueue*O} && $System.Event.Defined(eventListenerResourceBefore)) $System.Event.Delete(eventListenerResourceBefore)
}
else
{
do:('{EventQueue*O} && $System.Event.Defined(eventListenerResourceBefore)) $System.Event.Delete(eventListenerResourceBefore)
}
}
/// This method fires an event.
/// This method will be called by providers to inform listeners about the event.
/// <ul>
/// <li>Event...the event to fire.</li>
/// </ul>
/// Returns status OK if successfully fired the event, any other status signals failure!
Method FireEvent(Event As arsblue.event.Event) As %Status
{
$$$QuitIf('$$$TypeOf(Event,$E(..%ClassName(1),1,*-8)),"cannot fire invalid event type")
set Event.EventListener=$this
if (..EventDeamon)
{
$$$QuitOnError(Event.%Save())
if (..EventDeamon>0)
{
set eventListenerResource=..GetEventListenerResource()
do:($System.Event.Defined(eventListenerResource)) $System.Event.Signal(eventListenerResource,Event.%Oid())
}
}
else
{
try
{
do Event.FireEvent() // fire and forget!
}
catch (exc)
{
// nothing we can do...
}
}
quit $$$OK
}
/// This method listens for the next persistent event.
/// <ul>
/// <li>Event...the monitored persitent event or null if timeout reached.</li>
/// <li>EventProcessing...true (default) if the event should be processed automaticaly, false if the event should be processed by different implementation logic.</li>
/// <li>EventTimeout...the monitoring timeout in seconds.</li>
/// <ul>
/// Returns status OK if successfully received/processed an event, any other status signals failure and the event will be set to null!
Method GetNext(ByRef Event As arsblue.event.Event = {$$$NULLOREF}, EventProcessing As %Boolean = {$$$YES}, EventTimeout As %Integer = -1) As %Status
{
$$$QuitIf(..EventDeamon'=$JOB,"cannot get events for event deamon "_..EventDeamon)
set Event=$$$NULLOREF
set eventListenerResource=..GetEventListenerResource()
set eventmessage=$System.Event.WaitMsg(eventListenerResource,EventTimeout)
set eventstatus=$LG(eventmessage,1)
$$$QuitIf(eventstatus=-1,"event listener resource not available "_eventListenerResource)
quit:(eventstatus'=1) $$$OK // nothing to do...
set status=$$$OK
try
{
set Event=##class(arsblue.event.Event).%Open($LG(eventmessage,2),,.status) $$$Throw(status)
set:(EventProcessing) status=Event.FireEvent()
}
catch (exc)
{
set status=exc.AsStatus()
}
set:$$$ISERR(status) Event=$$$NULLOREF
quit status
}
/// Create new event listener
Storage Default
{
<Data name="EventListenerDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>EventTarget</Value>
</Value>
<Value name="3">
<Value>EventRun</Value>
</Value>
<Value name="4">
<Value>EventDeamon</Value>
</Value>
<Value name="5">
<Value>EventQueue</Value>
</Value>
</Data>
<DataLocation>^arsblue.event.EventListenerD</DataLocation>
<DefaultData>EventListenerDefaultData</DefaultData>
<Description>
<![CDATA[Create new event listener]]></Description>
<IdLocation>^arsblue.event.EventListenerD</IdLocation>
<IndexLocation>^arsblue.event.EventListenerI</IndexLocation>
<StreamLocation>^arsblue.event.EventListenerS</StreamLocation>
<Type>%Storage.Persistent</Type>
}
}