forked from MindTouch/DReAM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ADataUpdater.cs
332 lines (290 loc) · 12.9 KB
/
ADataUpdater.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
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/*
* MindTouch Dream - a distributed REST framework
* Copyright (C) 2006-2011 MindTouch, Inc.
* www.mindtouch.com oss@mindtouch.com
*
* For community documentation and downloads visit wiki.developer.mindtouch.com;
* please review the licensing section.
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace MindTouch.Data {
public enum MethodType { Update, DataIntegrity };
public class DbMethod : IComparable<DbMethod> {
//--- Fields ---
private readonly MethodInfo _methodInfo;
private readonly VersionInfo _targetVersion;
private readonly MethodType _methodType;
//--- Constructors ---
public DbMethod(MethodInfo methodInfo, VersionInfo targetVersion) {
_methodInfo = methodInfo;
_targetVersion = targetVersion;
_methodType = MethodType.Update;
}
public DbMethod(MethodInfo methodInfo, VersionInfo targetVersion, MethodType methodType) {
_methodInfo = methodInfo;
_targetVersion = targetVersion;
_methodType = methodType;
}
//--- Methods ---
public MethodType GetMethodType {
get { return _methodType; }
}
public MethodInfo GetMethodInfo {
get { return _methodInfo; }
}
public VersionInfo GetTargetVersion {
get { return _targetVersion; }
}
// Compares by version then by name
public int CompareTo(DbMethod other) {
var otherVersion = other.GetTargetVersion;
var change = _targetVersion.CompareTo(otherVersion).Change;
switch(change) {
case VersionChange.None:
return _methodInfo.Name.CompareTo(other._methodInfo.Name);
case VersionChange.Upgrade:
return 1;
default:
return -1;
}
}
}
/// <summary>
/// Provides an interface for updating Data in Dream Applications
/// </summary>
public abstract class ADataUpdater : IDataUpdater {
//--- Fields ---
protected VersionInfo _targetVersion = null;
protected VersionInfo _sourceVersion = null;
protected List<DbMethod> _methodList = null;
protected Type _dataUpgradeClass = null;
protected object _dataUpgradeClassInstance = null;
//--- Methods ---
/// <summary>
/// Get or set the target version
/// </summary>
/// <returns> The string representation of the target version</returns>
public string TargetVersion {
get {
if(_targetVersion == null) {
return "";
}
return _targetVersion.ToString();
}
set {
_targetVersion = new VersionInfo(value);
if(!_targetVersion.IsValid) {
throw new VersionInfoException(_targetVersion);
}
}
}
/// <summary>
/// Get or set the source version
/// </summary>
/// <returns> The string representation of the source version</returns>
public string SourceVersion {
get {
if(_sourceVersion == null) {
return "";
}
return _sourceVersion.ToString();
}
set {
_sourceVersion = new VersionInfo(value);
if(!_sourceVersion.IsValid) {
throw new VersionInfoException(_sourceVersion);
}
}
}
/// <summary>
/// Get a list of methods that will be run
/// </summary>
/// <returns>
/// List of method names
/// </returns>
public List<string> GetMethods() {
if(_methodList == null) {
return null;
}
var list = (from method in _methodList where method.GetMethodType == MethodType.Update select method.GetMethodInfo.Name).ToList();
return list;
}
/// <summary>
/// Get a list of methods that only check data integrity
/// </summary>
/// <returns>
/// List of method names
/// </returns>
public List<string> GetDataIntegrityMethods() {
if(_methodList == null) {
return null;
}
var list = (from method in _methodList where method.GetMethodType == MethodType.DataIntegrity select method.GetMethodInfo.Name).ToList();
return list;
}
/// <summary>
/// Tests the connection with the Data Store
/// that is to be updated. Throws an Exception
/// if an error occurs.
/// </summary>
/// <returns></returns>
public abstract void TestConnection();
/// <summary>
/// Loads the Methods to run into a list
/// </summary>
/// <param name="updateAssembly">Assembly object to perform reflection on</param>
/// <returns></returns>
public virtual void LoadMethods(Assembly updateAssembly) {
// Make sure we have a defined version
if(_targetVersion == null) {
throw new VersionInfoException(_targetVersion);
}
// get all the members of the Assembly
var types = updateAssembly.GetTypes();
// Find the class with attribute "DataUpgrade"
var classTypes = from type in types where type.IsClass select type;
foreach(var type in from type in classTypes from attribute in (from a in System.Attribute.GetCustomAttributes(type) where a is DataUpgradeAttribute select a) select type) {
_dataUpgradeClass = type;
}
// if no class was found exit
if(_dataUpgradeClass == null) {
throw new NoUpgradeAttributesFound();
}
// search the class for methods labeled with Attribute "EffectiveVersion("version")" and "CheckDataIntegrity("version")"
var methods = _dataUpgradeClass.GetMethods();
_methodList = new List<DbMethod>();
foreach(var methodInfo in methods) {
foreach(var attr in (from m in methodInfo.GetCustomAttributes(false) select m)) {
VersionInfo version;
var type = MethodType.Update;
if(attr.IsA<EffectiveVersionAttribute>()) {
version = new VersionInfo(((EffectiveVersionAttribute)attr).VersionString);
} else if(attr.IsA<DataIntegrityCheck>()) {
version = new VersionInfo(((DataIntegrityCheck)attr).VersionString);
type = MethodType.DataIntegrity;
} else {
continue;
}
if(version.CompareTo(_targetVersion).Change != VersionChange.Upgrade &&
(_sourceVersion == null || version.CompareTo(_sourceVersion).Change != VersionChange.Downgrade )) {
_methodList.Add(new DbMethod(methodInfo, version, type));
}
}
}
// Sort Methods by version then by name
_methodList.Sort();
}
/// <summary>
/// Loads the Methods to run into a list amd executes them
/// </summary>
/// <param name="updateAssembly">Assembly object to perform reflection on</param>
/// <returns></returns>
public virtual void LoadMethodsAndExecute(Assembly updateAssembly) {
LoadMethods(updateAssembly);
Execute();
}
/// <summary>
/// Execute the methods in the assembly
/// </summary>
public virtual void Execute() {
if(_methodList == null) {
throw new NoMethodsLoaded();
}
foreach(var method in _methodList) {
ExecuteMethod(method.GetMethodInfo.Name);
}
}
/// <summary>
/// Execute the method with the given name
/// <param name="name">Name of the method to execute. Case Sensitive.</param>
/// </summary>
public virtual void ExecuteMethod(string name) {
if(_dataUpgradeClassInstance == null) {
_dataUpgradeClassInstance = CreateActivatorInstance(_dataUpgradeClass);
}
// Check that method is in the methodlist
var nameList = (from method in _methodList select method.GetMethodInfo.Name).ToList();
if(!nameList.Contains(name)) {
throw new MethodMissingAttribute();
}
_dataUpgradeClass.InvokeMember(name, BindingFlags.Default | BindingFlags.InvokeMethod, null, _dataUpgradeClassInstance, null);
}
/// <summary>
/// Execute the method with the exact name, this method
/// does not need to be tagged with the appropriate attribute
/// </summary>
/// <param name="name">Exact name of the method to be executed</param>
/// <param name="updateAssembly">Assembly object to perform reflection on</param>
/// <param name="param">Parameter array to pass to custom method</param>
public void ExecuteCustomMethod(string name, Assembly updateAssembly, params object[] param) {
if(_dataUpgradeClass == null) {
// get all the members of the Assembly
var types = updateAssembly.GetTypes();
// Find the class with attribute "DataUpgrade"
var classTypes = from type in types where type.IsClass select type;
foreach(var type in from type in classTypes from attribute in (from a in System.Attribute.GetCustomAttributes(type) where a is DataUpgradeAttribute select a) select type) {
_dataUpgradeClass = type;
}
// if no class was found exit
if(_dataUpgradeClass == null) {
throw new NoUpgradeAttributesFound();
}
}
if(_dataUpgradeClassInstance == null) {
_dataUpgradeClassInstance = CreateActivatorInstance(_dataUpgradeClass);
}
// Attempt to execute method
_dataUpgradeClass.InvokeMember(name, BindingFlags.Default | BindingFlags.InvokeMethod, null, _dataUpgradeClassInstance, param);
}
/// <summary>
/// Create instance of class defined by the provided Type
/// <param name="dataUpgradeType">"The Type instance to activate"</param>
/// </summary>
protected virtual object CreateActivatorInstance(Type dataUpgradeType) {
return Activator.CreateInstance(dataUpgradeType);
}
/// <summary>
/// Get the Method details of a method
/// </summary>
/// <param name="name">Name of the Method</param>
/// <returns>Object of type DbMethod</returns>
public virtual DbMethod GetMethodInfo(string name) {
if(_methodList.Count == 0) {
throw new NoMethodsLoaded();
}
var methods = (from method in _methodList where method.GetMethodInfo.Name.EqualsInvariant(name) select method);
return methods.First();
}
}
public class VersionInfoException : Exception {
public VersionInfoException(VersionInfo _versionInfo)
: base(String.Format("Version string is invalid : {0}", _versionInfo.ToString())) {}
}
public class NoUpgradeAttributesFound : Exception {
public NoUpgradeAttributesFound()
: base ("Did not find any class with DataUpgrade attribute") {}
}
public class NoMethodsLoaded : Exception {
public NoMethodsLoaded()
: base("No Methods were loaded. Run LoadMethods() first.") { }
}
public class MethodMissingAttribute : Exception {
public MethodMissingAttribute()
: base("The Method you are trying to Execute does not have the proper attribute: [EffectiveVersion]") { }
}
}