-
Notifications
You must be signed in to change notification settings - Fork 16
/
AuditProcess.cs
170 lines (149 loc) · 5 KB
/
AuditProcess.cs
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
using System;
using System.Collections.Generic;
using NHibernate.Engine;
using NHibernate.Envers.RevisionInfo;
using NHibernate.Envers.Synchronization.Work;
using NHibernate.Envers.Tools;
namespace NHibernate.Envers.Synchronization
{
public class AuditProcess
{
private readonly IRevisionInfoGenerator revisionInfoGenerator;
private readonly ISessionImplementor session;
private readonly LinkedList<IAuditWorkUnit> workUnits;
private readonly Queue<IAuditWorkUnit> undoQueue;
private readonly IDictionary<Tuple<string, object>, IAuditWorkUnit> usedIds;
private readonly EntityChangeNotifier entityChangeNotifier;
private object revisionData;
private bool revisionInfoPersistedInCurrentTransaction;
public AuditProcess(IRevisionInfoGenerator revisionInfoGenerator, ISessionImplementor session)
{
this.revisionInfoGenerator = revisionInfoGenerator;
this.session = session;
workUnits = new LinkedList<IAuditWorkUnit>();
undoQueue = new Queue<IAuditWorkUnit>();
usedIds = new Dictionary<Tuple<string, object>, IAuditWorkUnit>();
entityChangeNotifier = new EntityChangeNotifier(revisionInfoGenerator, session);
}
public void AddWorkUnit(IAuditWorkUnit vwu)
{
if (!vwu.ContainsWork()) return;
var entityId = vwu.EntityId;
if (entityId == null)
{
// Just adding the work unit - it's not associated with any persistent entity.
//ORIG: workUnits.offer(vwu);
workUnits.AddLast(vwu);
}
else
{
var entityName = vwu.EntityName;
var usedIdsKey = new Tuple<string, object>(entityName, entityId);
var other = alreadyScheduledWorkUnit(usedIdsKey);
if(other!=null)
{
var result = vwu.Dispatch(other);
if (result != other)
{
removeWorkUnit(other);
if (result != null)
{
usedIds[usedIdsKey] = result;
workUnits.AddLast(result);
} // else: a null result means that no work unit should be kept
} // else: the result is the same as the work unit already added. No need to do anything.
}
else
{
usedIds[usedIdsKey] = vwu;
workUnits.AddLast(vwu);
}
}
}
/// <summary>
/// Checks if another work unit associated with the same entity hierarchy and identifier has already been scheduled.
/// </summary>
/// <param name="idKey"> Work unit's identifier.</param>
/// <returns>Corresponding work unit or <code>null</code> if no satisfying result was found.</returns>
private IAuditWorkUnit alreadyScheduledWorkUnit(Tuple<string, object> idKey)
{
var entityMetamodel = session.Factory.GetEntityPersister(idKey.Item1).EntityMetamodel;
var rootEntityName = entityMetamodel.RootName;
var rootEntityMetamodel = session.Factory.GetEntityPersister(rootEntityName).EntityMetamodel;
// Checking all possible subtypes, supertypes and the actual class.
foreach (var entityName in rootEntityMetamodel.SubclassEntityNames)
{
var key = new Tuple<string, object>(entityName, idKey.Item2);
if (usedIds.ContainsKey(key))
{
return usedIds[key];
}
}
return null;
}
private void removeWorkUnit(IAuditWorkUnit vwu)
{
workUnits.Remove(vwu);
if (vwu.IsPerformed())
{
// If this work unit has already been performed, it must be deleted (undone) first.
undoQueue.Enqueue(vwu);
}
}
private void executeInSession(ISession executeSession)
{
// Making sure the revision data is persisted.
var currentRevisionData = CurrentRevisionData(executeSession, true);
// First undoing any performed work units
while (undoQueue.Count > 0)
{
var vwu = undoQueue.Dequeue();
vwu.Undo(executeSession);
}
while (workUnits.Count > 0)
{
var vwu = workUnits.First.Value;
workUnits.RemoveFirst();
vwu.Perform(executeSession, revisionData);
entityChangeNotifier.EntityChanged(currentRevisionData, vwu);
}
}
public object CurrentRevisionData(ISession executeSession, bool persist)
{
// Generating the revision data if not yet generated
if (revisionData == null)
{
revisionData = revisionInfoGenerator.Generate();
}
// Saving the revision data, if not yet saved and persist is true
if (!revisionInfoPersistedInCurrentTransaction && persist)
{
revisionInfoGenerator.SaveRevisionData(executeSession, revisionData);
revisionInfoPersistedInCurrentTransaction = true;
}
return revisionData;
}
public void DoBeforeTransactionCompletion()
{
if (workUnits.Count == 0 && undoQueue.Count == 0)
{
return;
}
var castedSession = (ISession)session;
if (castedSession.FlushMode == FlushMode.Manual)
{
using (var tempSession = Toolz.CreateChildSession(castedSession))
{
executeInSession(tempSession);
tempSession.Flush();
}
}
else
{
executeInSession(castedSession);
// Explicity flushing the session, as the auto-flush may have already happened.
castedSession.Flush();
}
}
}
}