source: trunk/Demo/Packages/ModularSystem/UModularSystem.pas

Last change on this file was 60, checked in by chronos, 12 years ago
File size: 14.6 KB
Line 
1unit UModularSystem;
2
3{$mode Delphi}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, URegistry, SpecializedList;
9
10type
11 TModuleManager = class;
12 TModule = class;
13 TListModule = class;
14
15 TAPI = class(TComponent)
16
17 end;
18
19 TModuleCondition = (mcAll, mcEnabled, mcNotEnabled, mcInstalled, mcNotInstalled,
20 mcRunning, mcNotRunning);
21 TModuleConditions = set of TModuleCondition;
22 TModuleAction = (maStart, maStop, maInstall, maUninstall, maUpgrade, maEnable,
23 maDisable);
24 TModuleActions = array of TModuleAction;
25
26 { TModule }
27
28 TModule = class(TComponent)
29 private
30 FCategory: string;
31 FEnabled: Boolean;
32 FReleaseTime: TDateTime;
33 FRunning: Boolean;
34 FInstalled: Boolean;
35 FManager: TModuleManager;
36 FVersion: string;
37 FIdentification: string;
38 FTitle: string;
39 FLicense: string;
40 FAuthor: string;
41 FDependencies: TListString;
42 FDescription: TListString;
43 FFileName: string;
44 FWebSite: string;
45 FStartUpTime: TDateTime;
46 procedure SetEnabled(AValue: Boolean);
47 procedure SetInstalled(AValue: Boolean);
48 procedure SetManager(AValue: TModuleManager);
49 procedure SetRunning(AValue: Boolean);
50 protected
51 procedure DoStart; virtual;
52 procedure DoStop; virtual;
53 procedure DoInstall; virtual;
54 procedure DoUninstall; virtual;
55 procedure DoUpgrade; virtual;
56 public
57 API: TAPI;
58 procedure Enable;
59 procedure Disable;
60 procedure Start;
61 procedure Stop;
62 procedure Restart;
63 procedure Install;
64 procedure Uninstall;
65 procedure Reinstall;
66 procedure Upgrade;
67 procedure EnumDependenciesCascade(ModuleList: TListModule;
68 Conditions: TModuleConditions = [mcAll]);
69 procedure EnumSuperiorDependenciesCascade(ModuleList: TListModule;
70 Conditions: TModuleConditions = [mcAll]);
71 procedure SetInstalledState(Value: Boolean);
72 constructor Create(Owner: TComponent); override;
73 destructor Destroy; override;
74 property Running: Boolean read FRunning write SetRunning;
75 property Installed: Boolean read FInstalled write SetInstalled;
76 property Enabled: Boolean read FEnabled write SetEnabled;
77 property StartUpTime: TDateTime read FStartUpTime;
78 published
79 property Identification: string read FIdentification write FIdentification; // Unique system name
80 property Manager: TModuleManager read FManager write SetManager;
81 property Version: string read FVersion write FVersion;
82 property ReleaseTime: TDateTime read FReleaseTime write FReleaseTime;
83 property Title: string read FTitle write FTitle;
84 property License: string read FLicense write FLicense;
85 property Author: string read FAuthor write FAuthor;
86 property Dependencies: TListString read FDependencies write FDependencies;
87 property Description: TListString read FDescription write FDescription;
88 property FileName: string read FFileName write FFileName;
89 property Category: string read FCategory write FCategory;
90 property WebSite: string read FWebSite write FWebSite;
91 // Screenshots, reviews, icon, weak dependencies, ...
92 end;
93
94 { TListModule }
95
96 TListModule = class(TListObject)
97 private
98 public
99 procedure Perform(Actions: array of TModuleAction; Conditions: TModuleConditions = [mcAll]);
100 function FindByName(Name: string): TModule;
101 end;
102
103 TModuleManagerOption = (moAutoInstallOnRun, moAuto);
104 TModuleManagerOptions = set of TModuleManagerOption;
105 { TModuleManager }
106
107 TModuleManager = class(TComponent)
108 private
109 FAPI: TAPI;
110 FOnUpdate: TNotifyEvent;
111 FOptions: TModuleManagerOptions;
112 procedure SetAPI(AValue: TAPI);
113 procedure DoUpdate(Sender: TObject);
114 public
115 Modules: TListModule; // TObjectList<TModule>
116 function ModuleRunning(Name: string): Boolean;
117 procedure EnumDependenciesCascade(Module: TModule; ModuleList: TListModule;
118 Conditions: TModuleConditions = [mcAll]);
119 procedure EnumSuperiorDependenciesCascade(Module: TModule;
120 ModuleList: TListModule; Conditions: TModuleConditions = [mcAll]);
121 procedure RegisterModule(Module: TModule);
122 procedure UnregisterModule(Module: TModule);
123 procedure LoadFromRegistry(AContext: TRegistryContext);
124 procedure SaveToRegistry(AContext: TRegistryContext);
125 constructor Create(AOwner: TComponent); override;
126 destructor Destroy; override;
127 property API: TAPI read FAPI write SetAPI;
128 published
129 property OnUpdate: TNotifyEvent read FOnUpdate write FOnUpdate;
130 property Options: TModuleManagerOptions read FOptions write FOptions;
131 end;
132
133procedure Register;
134
135
136implementation
137
138resourcestring
139 SModuleNotFound = 'Module "%1:s" not found as dependency for module "%0:s"';
140 SInvalidModuleName = 'Invalid module name';
141
142procedure Register;
143begin
144 RegisterComponents('ModularSystem', [TModuleManager, TModule]);
145end;
146
147{ TListModule }
148
149procedure TListModule.Perform(Actions: array of TModuleAction;
150 Conditions: TModuleConditions = [mcAll]);
151var
152 I: Integer;
153 A: Integer;
154begin
155 try
156 BeginUpdate;
157 for I := 0 to Count - 1 do
158 with TModule(Items[I]) do
159 if (mcAll in Conditions) or
160 (Running and (mcRunning in Conditions)) or
161 (not Running and (mcNotRunning in Conditions)) or
162 (Installed and (mcInstalled in Conditions)) or
163 (not Installed and (mcNotInstalled in Conditions)) or
164 (Enabled and (mcEnabled in Conditions)) or
165 (not Enabled and (mcNotEnabled in Conditions)) then
166 for A := 0 to High(Actions) do begin
167 if Actions[A] = maStart then Start;
168 if Actions[A] = maStop then Stop;
169 if Actions[A] = maInstall then Install;
170 if Actions[A] = maUninstall then Uninstall;
171 if Actions[A] = maUpgrade then Upgrade;
172 if Actions[A] = maEnable then Enable;
173 if Actions[A] = maDisable then Disable;
174 end;
175 finally
176 EndUpdate;
177 end;
178end;
179
180function TListModule.FindByName(Name: string): TModule;
181var
182 I: Integer;
183begin
184 I := 0;
185 while (I < Count) and (TModule(Items[I]).Identification <> Name) do Inc(I);
186 if I < Count then Result := TModule(Items[I])
187 else Result := nil;
188end;
189
190{ TModuleManager }
191
192procedure TModuleManager.SetAPI(AValue: TAPI);
193var
194 I: Integer;
195begin
196 if FAPI = AValue then Exit;
197 FAPI := AValue;
198 for I := 0 to Modules.Count - 1 do
199 TModule(Modules[I]).API := FAPI;
200end;
201
202procedure TModuleManager.DoUpdate(Sender: TObject);
203begin
204 if Assigned(FOnUpdate) then FOnUpdate(Self);
205end;
206
207function TModuleManager.ModuleRunning(Name: string): Boolean;
208var
209 Module: TModule;
210begin
211 Module := Modules.FindByName(Name);
212 if Assigned(Module) then begin
213 Result := Module.Running;
214 end else Result := False;
215end;
216
217procedure TModuleManager.EnumDependenciesCascade(Module: TModule;
218 ModuleList: TListModule; Conditions: TModuleConditions = [mcAll]);
219var
220 DepModule: TModule;
221 I: Integer;
222begin
223 for I := 0 to Module.Dependencies.Count - 1 do begin
224 DepModule := Modules.FindByName(Module.Dependencies[I]);
225 if Assigned(DepModule) then
226 with DepModule do begin
227 if (ModuleList.IndexOf(DepModule) = -1) and
228 ((mcAll in Conditions) or
229 (Running and (mcRunning in Conditions)) or
230 (not Running and (mcNotRunning in Conditions)) or
231 (Installed and (mcInstalled in Conditions)) or
232 (not Installed and (mcNotInstalled in Conditions)) or
233 (Enabled and (mcEnabled in Conditions)) or
234 (not Enabled and (mcNotEnabled in Conditions))) then begin
235 ModuleList.Add(DepModule);
236 Self.EnumDependenciesCascade(DepModule, ModuleList);
237 end;
238 end else raise Exception.CreateFmt(SModuleNotFound, [DepModule.Identification]);
239 end;
240end;
241
242procedure TModuleManager.EnumSuperiorDependenciesCascade(Module: TModule;
243 ModuleList: TListModule; Conditions: TModuleConditions = [mcAll]);
244var
245 I: Integer;
246begin
247 for I := 0 to Modules.Count - 1 do
248 with TModule(Modules[I]) do begin
249 if (Dependencies.IndexOf(Module.Identification) <> -1) and
250 (ModuleList.IndexOf(TModule(Modules[I])) = -1) and
251 ((mcAll in Conditions) or
252 (Running and (mcRunning in Conditions)) or
253 (not Running and (mcNotRunning in Conditions)) or
254 (Installed and (mcInstalled in Conditions)) or
255 (not Installed and (mcNotInstalled in Conditions)) or
256 (Enabled and (mcEnabled in Conditions)) or
257 (not Enabled and (mcNotEnabled in Conditions))) then begin
258 ModuleList.Add(TModule(Modules[I]));
259 Self.EnumSuperiorDependenciesCascade(TModule(Modules[I]), ModuleList);
260 end;
261 end;
262end;
263
264procedure TModuleManager.RegisterModule(Module: TModule);
265begin
266 if Module.Identification = '' then
267 raise Exception.Create(SInvalidModuleName);
268 Modules.Add(Module);
269 Module.FManager := Self;
270 Module.API := API;
271 Modules.Update;
272end;
273
274procedure TModuleManager.UnregisterModule(Module: TModule);
275begin
276 Modules.Remove(Module);
277 Modules.Update;
278end;
279
280constructor TModuleManager.Create(AOwner: TComponent);
281begin
282 inherited;
283 Modules := TListModule.Create;
284 Modules.OwnsObjects := False;
285 Modules.OnUpdate := DoUpdate;
286end;
287
288destructor TModuleManager.Destroy;
289begin
290 Modules.Perform([maStop]);
291 FreeAndNil(Modules);
292 inherited;
293end;
294
295procedure TModuleManager.LoadFromRegistry(AContext: TRegistryContext);
296var
297 I: Integer;
298begin
299 with TRegistryEx.Create do
300 try
301 RootKey := AContext.RootKey;
302 for I := 0 to Modules.Count - 1 do
303 with TModule(Modules[I]) do begin
304 OpenKey(AContext.Key + '\' + Identification, True);
305 Installed := ReadBoolWithDefault('Installed', Installed);
306 Enabled := ReadBoolWithDefault('Enabled', Enabled);
307 end;
308 finally
309 Free;
310 end;
311end;
312
313procedure TModuleManager.SaveToRegistry(AContext: TRegistryContext);
314var
315 I: Integer;
316begin
317 with TRegistryEx.Create do
318 try
319 RootKey := AContext.RootKey;
320 for I := 0 to Modules.Count - 1 do
321 with TModule(Modules[I]) do begin
322 OpenKey(AContext.Key + '\' + Identification, True);
323 WriteBool('Enabled', Enabled);
324 WriteBool('Installed', Installed);
325 end;
326 finally
327 Free;
328 end;
329end;
330
331{ TModule }
332
333procedure TModule.SetRunning(AValue: Boolean);
334begin
335 if FRunning = AValue then Exit;
336 if AValue then Start else Stop;
337end;
338
339procedure TModule.DoStart;
340begin
341
342end;
343
344procedure TModule.DoStop;
345begin
346
347end;
348
349procedure TModule.DoInstall;
350begin
351
352end;
353
354procedure TModule.DoUninstall;
355begin
356
357end;
358
359procedure TModule.DoUpgrade;
360begin
361
362end;
363
364procedure TModule.Enable;
365var
366 List: TListModule;
367begin
368 if Enabled then Exit;
369 FEnabled := True;
370 try
371 List := TListModule.Create;
372 List.OwnsObjects := False;
373 EnumDependenciesCascade(List, [mcNotEnabled]);
374 List.Perform([maEnable], [mcNotEnabled]);
375 finally
376 List.Free;
377 end;
378 Start; // Auto start enabled modules
379 //Manager.Update;
380end;
381
382procedure TModule.Disable;
383var
384 List: TListModule;
385begin
386 if not Enabled then Exit;
387 if FRunning then Stop; // Auto stop running modules
388 FEnabled := False;
389 try
390 List := TListModule.Create;
391 List.OwnsObjects := False;
392 EnumSuperiorDependenciesCascade(List, [mcEnabled]);
393 List.Perform([maDisable], [mcEnabled]);
394 finally
395 List.Free;
396 end;
397 Manager.Modules.Update;
398end;
399
400procedure TModule.SetInstalled(AValue: Boolean);
401begin
402 if FInstalled = AValue then Exit;
403 if AValue then Install else Uninstall;
404end;
405
406procedure TModule.SetManager(AValue: TModuleManager);
407begin
408 if FManager = AValue then Exit;
409 if Assigned(FManager) then FManager.UnregisterModule(Self);
410 FManager := AValue;
411 if Assigned(FManager) then AValue.RegisterModule(Self);
412end;
413
414procedure TModule.SetEnabled(AValue: Boolean);
415begin
416 if FEnabled = AValue then Exit;
417 if AValue then Enable else Disable;
418end;
419
420procedure TModule.Start;
421var
422 List: TListModule;
423 StartTime: TDateTime;
424begin
425 if not Enabled or Running then Exit;
426 if not Installed then Install; // Auto install not installed modules
427 try
428 List := TListModule.Create;
429 List.OwnsObjects := False;
430 EnumDependenciesCascade(List, [mcNotRunning]);
431 List.Perform([maStart], [mcNotRunning]);
432 finally
433 List.Free;
434 end;
435 StartTime := Now;
436 DoStart;
437 FStartUpTime := Now - StartTime;
438 FRunning := True;
439 Manager.Modules.Update;
440end;
441
442procedure TModule.Stop;
443var
444 List: TListModule;
445begin
446 if not Running then Exit;
447 FRunning := False;
448 try
449 List := TListModule.Create;
450 List.OwnsObjects := False;
451 EnumSuperiorDependenciesCascade(List, [mcRunning]);
452 List.Perform([maStop], [mcRunning]);
453 finally
454 List.Free;
455 end;
456 DoStop;
457 Manager.Modules.Update;
458end;
459
460procedure TModule.Restart;
461begin
462 Stop;
463 Start;
464end;
465
466procedure TModule.Install;
467var
468 List: TListModule;
469begin
470 if Installed then Exit;
471 try
472 List := TListModule.Create;
473 List.OwnsObjects := False;
474 EnumDependenciesCascade(List, [mcNotInstalled]);
475 List.Perform([maInstall], [mcNotInstalled]);
476 finally
477 List.Free;
478 end;
479 FInstalled := True;
480 DoInstall;
481 //Enable; // Auto enable installed module
482 Manager.Modules.Update;
483end;
484
485procedure TModule.Uninstall;
486var
487 List: TListModule;
488begin
489 if not Installed then Exit;
490 if Enabled then Disable; // Auto disable uninstalled module
491 try
492 List := TListModule.Create;
493 List.OwnsObjects := False;
494 EnumSuperiorDependenciesCascade(List, [mcInstalled]);
495 List.Perform([maUninstall], [mcInstalled]);
496 finally
497 List.Free;
498 end;
499 FInstalled := False;
500 DoUninstall;
501 Manager.Modules.Update;
502end;
503
504procedure TModule.Reinstall;
505begin
506 Uninstall;
507 Install;
508end;
509
510procedure TModule.Upgrade;
511begin
512 if not Enabled or not Installed then Exit;
513 if Running then try
514 Stop;
515 DoUpgrade;
516 finally
517 Start;
518 end else DoUpgrade;
519 Manager.Modules.Update;
520end;
521
522procedure TModule.EnumDependenciesCascade(ModuleList: TListModule;
523 Conditions: TModuleConditions = [mcAll]);
524begin
525 ModuleList.Clear;
526 Manager.EnumDependenciesCascade(Self, ModuleList, Conditions);
527end;
528
529procedure TModule.EnumSuperiorDependenciesCascade(ModuleList: TListModule;
530 Conditions: TModuleConditions = [mcAll]);
531begin
532 ModuleList.Clear;
533 Manager.EnumSuperiorDependenciesCascade(Self, ModuleList, Conditions);
534end;
535
536procedure TModule.SetInstalledState(Value: Boolean);
537begin
538 FInstalled := Value;
539 Manager.Modules.Update;
540end;
541
542constructor TModule.Create(Owner: TComponent);
543begin
544 inherited;
545 Dependencies := TListString.Create;
546 Description := TListString.Create;
547end;
548
549destructor TModule.Destroy;
550begin
551 Running := False;
552 FreeAndNil(FDescription);
553 FreeAndNil(FDependencies);
554 inherited;
555end;
556
557end.
558
Note: See TracBrowser for help on using the repository browser.