source: ModularSystem/UModularSystem.pas

Last change on this file was 439, checked in by chronos, 12 years ago
  • Modified: Optiomization using Update blocking during mass operations.
  • Added: Measuring modules start time.
File size: 14.5 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 FUpdateCount: Integer;
112 FOptions: TModuleManagerOptions;
113 procedure SetAPI(AValue: TAPI);
114 procedure DoUpdate(Sender: TObject);
115 public
116 Modules: TListModule; // TObjectList<TModule>
117 function ModuleRunning(Name: string): Boolean;
118 procedure EnumDependenciesCascade(Module: TModule; ModuleList: TListModule;
119 Conditions: TModuleConditions = [mcAll]);
120 procedure EnumSuperiorDependenciesCascade(Module: TModule;
121 ModuleList: TListModule; Conditions: TModuleConditions = [mcAll]);
122 procedure RegisterModule(Module: TModule);
123 procedure UnregisterModule(Module: TModule);
124 procedure LoadFromRegistry(Context: TRegistryContext);
125 procedure SaveToRegistry(Context: TRegistryContext);
126 constructor Create(AOwner: TComponent); override;
127 destructor Destroy; override;
128 property API: TAPI read FAPI write SetAPI;
129 published
130 property OnUpdate: TNotifyEvent read FOnUpdate write FOnUpdate;
131 property Options: TModuleManagerOptions read FOptions write FOptions;
132 end;
133
134procedure Register;
135
136
137implementation
138
139resourcestring
140 SModuleNotFound = 'Module "%1:s" not found as dependency for module "%0:s"';
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 Modules.Add(Module);
267 Module.FManager := Self;
268 Module.API := API;
269 Modules.Update;
270end;
271
272procedure TModuleManager.UnregisterModule(Module: TModule);
273begin
274 Modules.Remove(Module);
275 Modules.Update;
276end;
277
278constructor TModuleManager.Create(AOwner: TComponent);
279begin
280 inherited;
281 Modules := TListModule.Create;
282 Modules.OwnsObjects := False;
283 Modules.OnUpdate := DoUpdate;
284end;
285
286destructor TModuleManager.Destroy;
287begin
288 Modules.Perform([maStop]);
289 FreeAndNil(Modules);
290 inherited;
291end;
292
293procedure TModuleManager.LoadFromRegistry(Context: TRegistryContext);
294var
295 I: Integer;
296begin
297 with TRegistryEx.Create do
298 try
299 RootKey := Context.RootKey;
300 for I := 0 to Modules.Count - 1 do
301 with TModule(Modules[I]) do begin
302 OpenKey(Context.Key + '\' + Identification, True);
303 Installed := ReadBoolWithDefault('Installed', Installed);
304 Enabled := ReadBoolWithDefault('Enabled', Enabled);
305 end;
306 finally
307 Free;
308 end;
309end;
310
311procedure TModuleManager.SaveToRegistry(Context: TRegistryContext);
312var
313 I: Integer;
314begin
315 with TRegistryEx.Create do
316 try
317 RootKey := Context.RootKey;
318 for I := 0 to Modules.Count - 1 do
319 with TModule(Modules[I]) do begin
320 OpenKey(Context.Key + '\' + Identification, True);
321 WriteBool('Enabled', Enabled);
322 WriteBool('Installed', Installed);
323 end;
324 finally
325 Free;
326 end;
327end;
328
329{ TModule }
330
331procedure TModule.SetRunning(AValue: Boolean);
332begin
333 if FRunning = AValue then Exit;
334 if AValue then Start else Stop;
335end;
336
337procedure TModule.DoStart;
338begin
339
340end;
341
342procedure TModule.DoStop;
343begin
344
345end;
346
347procedure TModule.DoInstall;
348begin
349
350end;
351
352procedure TModule.DoUninstall;
353begin
354
355end;
356
357procedure TModule.DoUpgrade;
358begin
359
360end;
361
362procedure TModule.Enable;
363var
364 List: TListModule;
365begin
366 if Enabled then Exit;
367 FEnabled := True;
368 try
369 List := TListModule.Create;
370 List.OwnsObjects := False;
371 EnumDependenciesCascade(List, [mcNotEnabled]);
372 List.Perform([maEnable], [mcNotEnabled]);
373 finally
374 List.Free;
375 end;
376 Start; // Auto start enabled modules
377 //Manager.Update;
378end;
379
380procedure TModule.Disable;
381var
382 List: TListModule;
383begin
384 if not Enabled then Exit;
385 if FRunning then Stop; // Auto stop running modules
386 FEnabled := False;
387 try
388 List := TListModule.Create;
389 List.OwnsObjects := False;
390 EnumSuperiorDependenciesCascade(List, [mcEnabled]);
391 List.Perform([maDisable], [mcEnabled]);
392 finally
393 List.Free;
394 end;
395 Manager.Modules.Update;
396end;
397
398procedure TModule.SetInstalled(AValue: Boolean);
399begin
400 if FInstalled = AValue then Exit;
401 if AValue then Install else Uninstall;
402end;
403
404procedure TModule.SetManager(AValue: TModuleManager);
405begin
406 if FManager = AValue then Exit;
407 if Assigned(FManager) then FManager.UnregisterModule(Self);
408 FManager := AValue;
409 if Assigned(FManager) then AValue.RegisterModule(Self);
410end;
411
412procedure TModule.SetEnabled(AValue: Boolean);
413begin
414 if FEnabled = AValue then Exit;
415 if AValue then Enable else Disable;
416end;
417
418procedure TModule.Start;
419var
420 List: TListModule;
421 StartTime: TDateTime;
422begin
423 if not Enabled or Running then Exit;
424 if not Installed then Install; // Auto install not installed modules
425 try
426 List := TListModule.Create;
427 List.OwnsObjects := False;
428 EnumDependenciesCascade(List, [mcNotRunning]);
429 List.Perform([maStart], [mcNotRunning]);
430 finally
431 List.Free;
432 end;
433 StartTime := Now;
434 DoStart;
435 FStartUpTime := Now - StartTime;
436 FRunning := True;
437 Manager.Modules.Update;
438end;
439
440procedure TModule.Stop;
441var
442 List: TListModule;
443begin
444 if not Running then Exit;
445 FRunning := False;
446 try
447 List := TListModule.Create;
448 List.OwnsObjects := False;
449 EnumSuperiorDependenciesCascade(List, [mcRunning]);
450 List.Perform([maStop], [mcRunning]);
451 finally
452 List.Free;
453 end;
454 DoStop;
455 Manager.Modules.Update;
456end;
457
458procedure TModule.Restart;
459begin
460 Stop;
461 Start;
462end;
463
464procedure TModule.Install;
465var
466 List: TListModule;
467begin
468 if Installed then Exit;
469 try
470 List := TListModule.Create;
471 List.OwnsObjects := False;
472 EnumDependenciesCascade(List, [mcNotInstalled]);
473 List.Perform([maInstall], [mcNotInstalled]);
474 finally
475 List.Free;
476 end;
477 FInstalled := True;
478 DoInstall;
479 //Enable; // Auto enable installed module
480 Manager.Modules.Update;
481end;
482
483procedure TModule.Uninstall;
484var
485 List: TListModule;
486begin
487 if not Installed then Exit;
488 if Enabled then Disable; // Auto disable uninstalled module
489 try
490 List := TListModule.Create;
491 List.OwnsObjects := False;
492 EnumSuperiorDependenciesCascade(List, [mcInstalled]);
493 List.Perform([maUninstall], [mcInstalled]);
494 finally
495 List.Free;
496 end;
497 FInstalled := False;
498 DoUninstall;
499 Manager.Modules.Update;
500end;
501
502procedure TModule.Reinstall;
503begin
504 Uninstall;
505 Install;
506end;
507
508procedure TModule.Upgrade;
509begin
510 if not Enabled or not Installed then Exit;
511 if Running then try
512 Stop;
513 DoUpgrade;
514 finally
515 Start;
516 end else DoUpgrade;
517 Manager.Modules.Update;
518end;
519
520procedure TModule.EnumDependenciesCascade(ModuleList: TListModule;
521 Conditions: TModuleConditions = [mcAll]);
522begin
523 ModuleList.Clear;
524 Manager.EnumDependenciesCascade(Self, ModuleList, Conditions);
525end;
526
527procedure TModule.EnumSuperiorDependenciesCascade(ModuleList: TListModule;
528 Conditions: TModuleConditions = [mcAll]);
529begin
530 ModuleList.Clear;
531 Manager.EnumSuperiorDependenciesCascade(Self, ModuleList, Conditions);
532end;
533
534procedure TModule.SetInstalledState(Value: Boolean);
535begin
536 FInstalled := Value;
537 Manager.Modules.Update;
538end;
539
540constructor TModule.Create(Owner: TComponent);
541begin
542 inherited;
543 Dependencies := TListString.Create;
544 Description := TListString.Create;
545end;
546
547destructor TModule.Destroy;
548begin
549 Running := False;
550 FreeAndNil(FDescription);
551 FreeAndNil(FDependencies);
552 inherited;
553end;
554
555end.
556
Note: See TracBrowser for help on using the repository browser.