Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ProgramDataSource.cs
1using System;
3using System.IO;
4using System.Threading.Tasks;
5using Waher.Events;
10using Waher.Things;
12
14{
18 public class ProgramDataSource : IDataSource, IDisposable
19 {
23 public const string SourceID = "ProgramData";
24
25 private readonly string gatewayConfigFile;
26 private readonly string wafFile;
27 private FileSystemWatcher watcher;
28 private Cache<string, NodeUpdated> delayedUpdates;
29
34 {
35 this.gatewayConfigFile = Path.Combine(Gateway.AppDataFolder, Gateway.GatewayConfigLocalFileName);
37
38 this.delayedUpdates = new Cache<string, NodeUpdated>(int.MaxValue, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(15));
39 this.delayedUpdates.Removed += this.DelayedUpdates_Removed;
40
41 this.watcher = new FileSystemWatcher(Gateway.AppDataFolder, "*.*")
42 {
43 IncludeSubdirectories = true,
44 EnableRaisingEvents = true,
45 InternalBufferSize = 65536,
46 NotifyFilter =
47 NotifyFilters.Attributes |
48 NotifyFilters.CreationTime |
49 NotifyFilters.DirectoryName |
50 NotifyFilters.FileName |
51 NotifyFilters.LastAccess |
52 NotifyFilters.LastWrite |
53 NotifyFilters.Security |
54 NotifyFilters.Size
55 };
56
57 this.watcher.Changed += this.Watcher_Changed;
58 this.watcher.Created += this.Watcher_Created;
59 this.watcher.Deleted += this.Watcher_Deleted;
60 this.watcher.Renamed += this.Watcher_Renamed;
61 this.watcher.Error += this.Watcher_Error;
62 }
63
67 public void Dispose()
68 {
69 if (!(this.delayedUpdates is null))
70 {
71 this.delayedUpdates.Removed -= this.DelayedUpdates_Removed;
72 this.delayedUpdates.Dispose();
73 this.delayedUpdates = null;
74 }
75
76 if (!(this.watcher is null))
77 {
78 this.watcher.Changed -= this.Watcher_Changed;
79 this.watcher.Created -= this.Watcher_Created;
80 this.watcher.Deleted -= this.Watcher_Deleted;
81 this.watcher.Renamed -= this.Watcher_Renamed;
82 this.watcher.Error -= this.Watcher_Error;
83
84 this.watcher.Dispose();
85 this.watcher = null;
86 }
87 }
88
92 string IDataSource.SourceID => SourceID;
93
97 public bool HasChildren => false;
98
102 public DateTime LastChanged => File.GetLastWriteTimeUtc(Gateway.AppDataFolder);
103
107 public IEnumerable<IDataSource> ChildSources => null;
108
112 public IEnumerable<INode> RootNodes => GetChildNodes(null, Gateway.AppDataFolder);
113
120 public static IEnumerable<INode> GetChildNodes(ProgramDataFolder FolderNode, string FolderPath)
121 {
122 if (FolderPath != Path.GetFullPath(FolderPath))
123 return Array.Empty<INode>();
124
125 if (!FolderPath.StartsWith(Gateway.AppDataFolder))
126 return Array.Empty<INode>();
127
128 SortedDictionary<string, INode> Result1 = new SortedDictionary<string, INode>(StringComparer.InvariantCultureIgnoreCase);
129 SortedDictionary<string, INode> Result2 = new SortedDictionary<string, INode>(StringComparer.InvariantCultureIgnoreCase);
130
131 FolderNode ??= GetProgramDataNode(FolderPath, false, true) as ProgramDataFolder;
132
133 DirectoryInfo DirInfo = new DirectoryInfo(FolderPath);
134 if (DirInfo.Exists)
135 {
136 DirectoryInfo[] Directories = DirInfo.GetDirectories();
137 FileInfo[] Files = DirInfo.GetFiles();
138
139 foreach (DirectoryInfo Directory in Directories)
140 Result1[Directory.Name] = new ProgramDataFolder(Directory.FullName, FolderNode, Directory.CreationTimeUtc, Directory.LastWriteTimeUtc);
141
142 foreach (FileInfo File in Files)
143 Result2[File.Name] = new ProgramDataFile(File.FullName, FolderNode, File.CreationTimeUtc, File.LastAccessTimeUtc, File.Length);
144 }
145
146 List<INode> Result = new List<INode>();
147
148 Result.AddRange(Result1.Values);
149 Result.AddRange(Result2.Values);
150
151 return Result;
152 }
153
157 public event EventHandlerAsync<SourceEvent> OnEvent;
158
164 public Task<bool> CanViewAsync(RequestOrigin Caller)
165 {
166 return Task.FromResult(Caller.HasPrivilege("Source." + SourceID + ".View"));
167 }
168
174 public Task<string> GetNameAsync(Language Language)
175 {
176 return Language.GetStringAsync(typeof(ProgramDataSource), 1, "Program Data Folder");
177 }
178
184 public Task<INode> GetNodeAsync(IThingReference NodeRef)
185 {
186 if (NodeRef.SourceId != SourceID || !string.IsNullOrEmpty(NodeRef.Partition))
187 return Task.FromResult<INode>(null);
188
189 return Task.FromResult(GetProgramDataNode(NodeRef.NodeId, true, true));
190 }
191
199 public static INode GetProgramDataNode(string Path, bool FileReference, bool FolderReference)
200 {
201 if (Path != System.IO.Path.GetFullPath(Path))
202 return null;
203
204 if (!Path.StartsWith(Gateway.AppDataFolder))
205 return null;
206
207 INode ParentNode;
208 string ParentFolder = Directory.GetParent(Path).FullName;
209 if (ParentFolder == Gateway.AppDataFolder)
210 ParentNode = null;
211 else
212 ParentNode = GetProgramDataNode(ParentFolder, false, true);
213
214 if (FileReference && File.Exists(Path))
215 return new ProgramDataFile(Path, ParentNode as ProgramDataFolder, null, null, null);
216
217 if (FolderReference && Directory.Exists(Path))
218 return new ProgramDataFolder(Path, ParentNode as ProgramDataFolder, null, null);
219
220 return null;
221 }
222
223 private Task RaiseSourceEvent(SourceEvent Event)
224 {
225 return this.OnEvent.Raise(this, Event);
226 }
227
228 private async Task DelayedUpdates_Removed(object Sender, CacheItemEventArgs<string, NodeUpdated> e)
229 {
230 switch (e.Reason)
231 {
232 case RemovedReason.Replaced:
233 case RemovedReason.Manual:
234 // Ignore
235 break;
236
237 case RemovedReason.NotUsed:
238 case RemovedReason.Old:
239 case RemovedReason.Space:
240 default:
241 await this.RaiseSourceEvent(e.Value);
242 break;
243 }
244 }
245
246 private void RaiseSourceEventDelayed(NodeUpdated Event)
247 {
248 this.delayedUpdates[Event.NodeId] = Event;
249 }
250
251 private async Task RaiseSourceEventFlushDelayed(NodeEvent Event, bool DiscardDelayed)
252 {
253 if (DiscardDelayed)
254 this.delayedUpdates.Remove(Event.NodeId);
255 else if (this.delayedUpdates.TryGetValue(Event.NodeId, out NodeUpdated PrevEvent))
256 {
257 this.delayedUpdates.Remove(Event.NodeId);
258 await this.RaiseSourceEvent(PrevEvent);
259 }
260
261 await this.RaiseSourceEvent(Event);
262 }
263
264 private void Watcher_Error(object Sender, ErrorEventArgs e)
265 {
266 Exception Exception = e.GetException();
267 Log.Exception(Exception);
268 }
269
270 private async void Watcher_Renamed(object Sender, RenamedEventArgs e)
271 {
272 try
273 {
274 if (!(this.OnEvent is null))
275 {
276 try
277 {
278 INode Node = GetProgramDataNode(e.FullPath, true, true);
279
280 if (!(Node is null))
281 {
282 await this.RaiseSourceEventFlushDelayed(await NodeUpdated.FromNode(Node, await Translator.GetDefaultLanguageAsync(),
283 RequestOrigin.Empty, e.OldFullPath), false);
284 }
285 }
286 catch (Exception ex)
287 {
288 Log.Exception(ex);
289 }
290 }
291 }
292 catch (Exception ex)
293 {
294 Log.Exception(ex);
295 }
296 }
297
298 private async void Watcher_Deleted(object Sender, FileSystemEventArgs e)
299 {
300 try
301 {
302 if (!(this.OnEvent is null))
303 {
304 try
305 {
306 INode Node = GetProgramDataNode(e.FullPath, true, true)
307 ?? new ProgramDataFile(e.FullPath, null, null, null, null);
308
309 await this.RaiseSourceEventFlushDelayed(NodeRemoved.FromNode(Node), true);
310 }
311 catch (Exception ex)
312 {
313 Log.Exception(ex);
314 }
315 }
316 }
317 catch (Exception ex)
318 {
319 Log.Exception(ex);
320 }
321 }
322
323 private async void Watcher_Created(object Sender, FileSystemEventArgs e)
324 {
325 try
326 {
327 if (!(this.OnEvent is null))
328 {
329 try
330 {
331 INode Node = GetProgramDataNode(e.FullPath, true, true);
332
333 if (!(Node is null))
334 await this.RaiseSourceEventFlushDelayed(await NodeAdded.FromNode(Node, await Translator.GetDefaultLanguageAsync(), RequestOrigin.Empty, false), true);
335 }
336 catch (Exception ex)
337 {
338 Log.Exception(ex);
339 }
340 }
341 }
342 catch (Exception ex)
343 {
344 Log.Exception(ex);
345 }
346 }
347
348 private async void Watcher_Changed(object Sender, FileSystemEventArgs e)
349 {
350 try
351 {
352 if (!(this.OnEvent is null))
353 {
354 try
355 {
356 INode Node = GetProgramDataNode(e.FullPath, true, true);
357
358 if (!(Node is null))
359 this.RaiseSourceEventDelayed(await NodeUpdated.FromNode(Node, await Translator.GetDefaultLanguageAsync(), RequestOrigin.Empty));
360 }
361 catch (Exception ex)
362 {
363 Log.Exception(ex);
364 }
365 }
366
367 if (string.Compare(e.FullPath, this.gatewayConfigFile, true) == 0)
368 await GatewayConfigSource.FileUpdated();
369 else if (string.Compare(e.FullPath, this.wafFile, true) == 0)
370 Gateway.ScheduleEvent(this.ReloadWafDefinition, DateTime.Now.AddSeconds(1), null);
371 }
372 catch (Exception ex)
373 {
374 Log.Exception(ex);
375 }
376 }
377
378 private async Task ReloadWafDefinition(object _)
379 {
380 try
381 {
382 await Gateway.CheckWAF();
383 }
384 catch (Exception ex)
385 {
386 Log.Exception(ex, this.wafFile);
387 }
388 }
389
390 }
391}
Class representing an event.
Definition: Event.cs:11
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:147
const string WebApplicationFirewallLocalFileName
WAF.xml
Definition: Gateway.cs:156
static string AppDataFolder
Application data folder.
Definition: Gateway.cs:3132
static DateTime ScheduleEvent(Action< object > Callback, DateTime When, object State)
Schedules a one-time event.
Definition: Gateway.cs:4253
static async Task CheckWAF()
Checks the Web Application Firewall file and loads or reloads it if necessary.
Definition: Gateway.cs:1954
const string GatewayConfigLocalFileName
Gateway.config
Definition: Gateway.cs:151
Implements an in-memory cache.
Definition: Cache.cs:17
void Dispose()
IDisposable.Dispose
Definition: Cache.cs:99
bool Remove(KeyType Key)
Removes an item from the cache.
Definition: Cache.cs:616
bool TryGetValue(KeyType Key, out ValueType Value)
Tries to get a value from the cache.
Definition: Cache.cs:311
Event arguments for cache item removal events.
ValueType Value
Value of item that was removed.
RemovedReason Reason
Reason for removing the item.
Contains information about a language.
Definition: Language.cs:17
Task< string > GetStringAsync(Type Type, int Id, string Default)
Gets the string value of a string ID. If no such string exists, a string is created with the default ...
Definition: Language.cs:209
Basic access point for runtime language localization.
Definition: Translator.cs:16
static async Task< Language > GetDefaultLanguageAsync()
Gets the default language.
Definition: Translator.cs:223
Reference to a file in the ProgramData folder of the broker.
Reference to a folder in the ProgramData folder of the broker.
Data source mirroring the ProgramData folder for the broker.
Task< string > GetNameAsync(Language Language)
Gets the displayable name of the command.
static IEnumerable< INode > GetChildNodes(ProgramDataFolder FolderNode, string FolderPath)
Gets a set of program data child nodes, for a folder.
const string SourceID
Data Source ID for the ProgramData source.
Task< INode > GetNodeAsync(IThingReference NodeRef)
Gets the node, given a reference to it.
IEnumerable< INode > RootNodes
Root node references. If no root nodes are available, null is returned.
Task< bool > CanViewAsync(RequestOrigin Caller)
If the data source is visible to the caller.
DateTime LastChanged
When the source was last updated.
EventHandlerAsync< SourceEvent > OnEvent
Event raised when a data source event has been raised.
ProgramDataSource()
Data source mirroring the ProgramData folder for the broker.
IEnumerable< IDataSource > ChildSources
Child sources. If no child sources are available, null is returned.
bool HasChildren
If the source has any child sources.
static INode GetProgramDataNode(string Path, bool FileReference, bool FolderReference)
Gets a node, given a program data path.
Tokens available in request.
Definition: RequestOrigin.cs:9
static readonly RequestOrigin Empty
Empty request origin.
bool HasPrivilege(string Privilege)
If the origin has a given privilege.
static Task< NodeAdded > FromNode(INode Node, Language Language, RequestOrigin Caller, bool Sniffable)
Creates an event object from a node object.
Definition: NodeAdded.cs:36
Abstract base class for all node events.
Definition: NodeEvent.cs:9
static NodeRemoved FromNode(INode Node)
Creates an event object from a node object.
Definition: NodeRemoved.cs:30
static Task< NodeUpdated > FromNode(INode Node, Language Language, RequestOrigin Caller)
Creates an event object from a node object.
Definition: NodeUpdated.cs:31
Abstract base class for all data source events.
Definition: SourceEvent.cs:13
Interface for datasources that are published through the concentrator interface.
Definition: IDataSource.cs:14
Interface for nodes that are published through the concentrator interface.
Definition: INode.cs:49
Interface for thing references.
string Partition
Optional partition in which the Node ID is unique.
string SourceId
Optional ID of source containing node.
Definition: ImplTypes.g.cs:58
RemovedReason
Reason for removing the item.