From 5bf258673db78d8cf9f75d8f08b8ef99bac1a6f2 Mon Sep 17 00:00:00 2001 From: Ahmed Zamil Date: Fri, 14 May 2021 18:24:34 +0300 Subject: [PATCH] 1.6.1 --- Esiur.Stores.EntityCore/EntityResource.cs | 4 +- Esiur.Stores.EntityCore/EntityStore.cs | 58 ++-- Esiur.Stores.EntityCore/EntityTypeInfo.cs | 17 + .../Esiur.Stores.EntityCore.csproj | 3 +- .../EsiurExtensionOptions.cs | 3 +- Esiur.Stores.EntityCore/EsiurExtensions.cs | 152 ++++++--- Esiur.Stores.EntityCore/EsiurProxyRewrite.cs | 4 +- .../Esiur.Stores.MongoDB.csproj | 2 +- Esiur/Core/AsyncAwaiterGeneric.cs | 2 +- Esiur/Core/AsyncReply.cs | 19 +- Esiur/Core/ExceptionCode.cs | 6 +- Esiur/Data/ResourceJsonConverter.cs | 12 +- Esiur/Data/Structure.cs | 4 + Esiur/Esiur.csproj | 16 +- Esiur/Misc/Global.cs | 6 +- Esiur/Net/DataLink/PacketFilter.cs | 2 +- Esiur/Net/DataLink/PacketServer.cs | 2 +- Esiur/Net/DataLink/PacketSource.cs | 2 +- Esiur/Net/HTTP/HTTPFilter.cs | 2 +- Esiur/Net/HTTP/IIPoWS.cs | 2 +- Esiur/Net/IIP/DistributedConnection.cs | 185 ++++++---- .../Net/IIP/DistributedConnectionProtocol.cs | 316 +++++++++++++----- Esiur/Net/IIP/DistributedResource.cs | 85 +++-- Esiur/Net/IIP/DistributedSession.cs | 4 +- Esiur/Net/Packets/HTTPRequestPacket.cs | 17 +- Esiur/Net/Packets/IIPPacket.cs | 41 +-- Esiur/Net/Packets/Packet.cs | 2 +- Esiur/Net/Sockets/SSLSocket.cs | 6 +- Esiur/Net/Sockets/TCPSocket.cs | 4 +- Esiur/Net/Sockets/WSSocket.cs | 6 +- Esiur/Net/TCP/TCPFilter.cs | 2 +- Esiur/Net/UDP/UDPFilter.cs | 2 +- Esiur/Proxy/GenerationInfo.cs | 20 ++ Esiur/Proxy/ResourceGenerator.cs | 71 ++++ Esiur/Proxy/ResourceGeneratorReceiver.cs | 61 ++++ Esiur/Proxy/ResourceProxy.cs | 24 +- Esiur/Resource/IResource.cs | 1 - Esiur/Resource/ListenableAttribute.cs | 42 +++ Esiur/Resource/PublicAttribute.cs | 2 +- Esiur/Resource/Resource.cs | 1 + Esiur/Resource/ResourceAttribute.cs | 15 + Esiur/Resource/Template/EventTemplate.cs | 10 +- Esiur/Resource/Template/ResourceTemplate.cs | 18 +- Esiur/Resource/Warehouse.cs | 140 +++++--- Esiur/Security/Membership/IMembership.cs | 4 +- .../Permissions/StorePermissionsManager.cs | 1 + Esiur/Stores/MemoryStore.cs | 6 +- Esiur/Stores/TemporaryStore.cs | 11 +- 48 files changed, 1032 insertions(+), 383 deletions(-) create mode 100644 Esiur.Stores.EntityCore/EntityTypeInfo.cs create mode 100644 Esiur/Proxy/GenerationInfo.cs create mode 100644 Esiur/Proxy/ResourceGenerator.cs create mode 100644 Esiur/Proxy/ResourceGeneratorReceiver.cs create mode 100644 Esiur/Resource/ListenableAttribute.cs create mode 100644 Esiur/Resource/ResourceAttribute.cs diff --git a/Esiur.Stores.EntityCore/EntityResource.cs b/Esiur.Stores.EntityCore/EntityResource.cs index a5a18c8..44196fa 100644 --- a/Esiur.Stores.EntityCore/EntityResource.cs +++ b/Esiur.Stores.EntityCore/EntityResource.cs @@ -40,7 +40,7 @@ namespace Esiur.Stores.EntityCore //internal object _PrimaryId; public event DestroyedEvent OnDestroy; - public event PropertyChangedEventHandler PropertyChanged; + //public event PropertyChangedEventHandler PropertyChanged; [NotMapped] public Instance Instance { get; set; } @@ -66,7 +66,7 @@ namespace Esiur.Stores.EntityCore public void Destroy() { - //throw new NotImplementedException(); + OnDestroy?.Invoke(this); } diff --git a/Esiur.Stores.EntityCore/EntityStore.cs b/Esiur.Stores.EntityCore/EntityStore.cs index 24979df..3792b77 100644 --- a/Esiur.Stores.EntityCore/EntityStore.cs +++ b/Esiur.Stores.EntityCore/EntityStore.cs @@ -45,31 +45,25 @@ namespace Esiur.Stores.EntityCore Dictionary> DB = new Dictionary>(); object DBLock = new object(); - internal struct TypeInfo - { - public string Name; - public IEntityType Type; - public PropertyInfo PrimaryKey; - } + Dictionary TypesByName = new Dictionary(); + internal Dictionary TypesByType = new Dictionary(); - Dictionary TypesByName = new Dictionary(); - internal Dictionary TypesByType = new Dictionary(); + [Attribute] + public Func Getter { get; set; } - - bool Loaded; - public AsyncReply Get(string path) { var p = path.Split('/'); var ti = TypesByName[p[0]]; - var id = Convert.ChangeType(p[1], ti.PrimaryKey.PropertyType);// Convert.ToInt32(); + var id = Convert.ChangeType(p[1], ti.PrimaryKey.PropertyType); - - var db = DbContextProvider(); + // Get db + var db = Getter(); var res = db.Find(ti.Type.ClrType, id); - var ent = db.Entry(res); + // load navigation properties + var ent = db.Entry(res); foreach (var rf in ent.References) rf.Load(); @@ -78,12 +72,11 @@ namespace Esiur.Stores.EntityCore public AsyncReply Put(IResource resource) { - if (resource is EntityStore) + if (resource == this) return new AsyncReply(true); - var type = ResourceProxy.GetBaseType(resource);//.GetType().; + var type = ResourceProxy.GetBaseType(resource); - //var eid = (resource as EntityResource)._PrimaryId;// (int)resource.Instance.Variables["eid"]; var eid = TypesByType[type].PrimaryKey.GetValue(resource); @@ -112,11 +105,12 @@ namespace Esiur.Stores.EntityCore } } - [Attribute] - public Func DbContextProvider { get; set; } - [Attribute] - public DbContextOptionsBuilder Options { get; set; } + + //public T CreateDB() + //{ + + //} //DbContext dbContext; //[Attribute] @@ -195,13 +189,17 @@ namespace Esiur.Stores.EntityCore throw new NotImplementedException(); } + internal DbContextOptions Options { get; set; } + public AsyncReply Trigger(ResourceTrigger trigger) { if (trigger == ResourceTrigger.Initialize)// SystemInitialized && DbContext != null) { - if (DbContextProvider == null) - DbContextProvider = () => Activator.CreateInstance(Options.Options.ContextType, Options.Options) as DbContext; + if (Getter == null) + throw new Exception("Getter is not set for the store."); + // DbContextProvider = () => Activator.CreateInstance(Options.Options.ContextType, Options.Options) as DbContext; + ReloadModel(); } @@ -209,21 +207,23 @@ namespace Esiur.Stores.EntityCore return new AsyncReply(true); } - public void ReloadModel() + void ReloadModel() { + TypesByName.Clear(); TypesByType.Clear(); - var context = DbContextProvider();// Activator.CreateInstance(Options.Options.ContextType, Options.Options) as DbContext; + var context = Getter(); var types = context.Model.GetEntityTypes(); foreach (var t in types) { - var ti = new TypeInfo() + var ti = new EntityTypeInfo() { Name = t.ClrType.Name, PrimaryKey = t.FindPrimaryKey().Properties.FirstOrDefault()?.PropertyInfo, - Type = t + Type = t, + //Getter = getter }; TypesByName.Add(t.ClrType.Name, ti); @@ -236,7 +236,7 @@ namespace Esiur.Stores.EntityCore public void Destroy() { - //throw new NotImplementedException(); + OnDestroy?.Invoke(this); } } } diff --git a/Esiur.Stores.EntityCore/EntityTypeInfo.cs b/Esiur.Stores.EntityCore/EntityTypeInfo.cs new file mode 100644 index 0000000..7fc1e4d --- /dev/null +++ b/Esiur.Stores.EntityCore/EntityTypeInfo.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Esiur.Stores.EntityCore +{ + struct EntityTypeInfo + { + public string Name; + public IEntityType Type; + public PropertyInfo PrimaryKey; + // public Func Getter; + } +} diff --git a/Esiur.Stores.EntityCore/Esiur.Stores.EntityCore.csproj b/Esiur.Stores.EntityCore/Esiur.Stores.EntityCore.csproj index 561f5d1..6cedbd2 100644 --- a/Esiur.Stores.EntityCore/Esiur.Stores.EntityCore.csproj +++ b/Esiur.Stores.EntityCore/Esiur.Stores.EntityCore.csproj @@ -9,10 +9,11 @@ Esiur Entity Framework Extension true Esiur.Stores.EntityCore + 1.0.2 - + diff --git a/Esiur.Stores.EntityCore/EsiurExtensionOptions.cs b/Esiur.Stores.EntityCore/EsiurExtensionOptions.cs index 97fc3cb..058fd3f 100644 --- a/Esiur.Stores.EntityCore/EsiurExtensionOptions.cs +++ b/Esiur.Stores.EntityCore/EsiurExtensionOptions.cs @@ -34,10 +34,11 @@ using System.Linq; using Microsoft.EntityFrameworkCore.Metadata; using System.Reflection; using Esiur.Proxy; +using Microsoft.EntityFrameworkCore; namespace Esiur.Stores.EntityCore { - public class EsiurExtensionOptions : IDbContextOptionsExtension + public class EsiurExtensionOptions : IDbContextOptionsExtension { //public Dictionary Cache { get; } = new Dictionary(); diff --git a/Esiur.Stores.EntityCore/EsiurExtensions.cs b/Esiur.Stores.EntityCore/EsiurExtensions.cs index 75ad594..5023606 100644 --- a/Esiur.Stores.EntityCore/EsiurExtensions.cs +++ b/Esiur.Stores.EntityCore/EsiurExtensions.cs @@ -23,6 +23,9 @@ SOFTWARE. */ using Esiur.Core; +using Esiur.Data; +using Esiur.Misc; +using Esiur.Proxy; using Esiur.Resource; using Esiur.Security.Permissions; using Microsoft.EntityFrameworkCore; @@ -31,6 +34,7 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; namespace Esiur.Stores.EntityCore @@ -43,49 +47,108 @@ namespace Esiur.Stores.EntityCore //} - public static T AddResource(this DbSet dbSet, object properties = null) where T : class, IResource - => AddResourceAsync(dbSet, properties).Wait(); + public static T AddResource(this DbSet dbSet, T resource) where T : class, IResource + => AddResourceAsync(dbSet, resource).Wait(); - public static async AsyncReply AddResourceAsync(this DbSet dbSet, object properties = null) where T : class, IResource + public static async AsyncReply AddResourceAsync(this DbSet dbSet, T resource) where T : class, IResource { var store = dbSet.GetInfrastructure().GetService().FindExtension().Store; + + var manager = store.Instance.Managers.FirstOrDefault();// > 0 ? store.Instance.Managers.First() : null; //var db = dbSet.GetService().Context; //var resource = dbSet.GetInfrastructure().CreateResource(properties); //var resource = Warehouse.New("", options.Store, null, null, null, properties); - var resource = await Warehouse.New("", null, null, null, null, properties); - var entity = dbSet.Add(resource); + + var resType = typeof(T); + var proxyType = ResourceProxy.GetProxy(resType); + + + IResource res; + + if (proxyType == resType) + { + res = resource; + } + else + { + res = Activator.CreateInstance(proxyType) as IResource; + var ps = Structure.FromObject(resource); + + foreach (var p in ps) + { + + var mi = resType.GetMember(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance) + .FirstOrDefault(); + + if (mi != null) + { + if (mi is PropertyInfo) + { + var pi = mi as PropertyInfo; + if (pi.CanWrite) + { + try + { + pi.SetValue(res, p.Value); + } + catch (Exception ex) + { + Global.Log(ex); + } + } + } + else if (mi is FieldInfo) + { + try + { + (mi as FieldInfo).SetValue(res, p.Value); + } + catch (Exception ex) + { + Global.Log(ex); + } + } + } + } + } + + //await Warehouse.Put("", null, null, null, null, properties); + var entity = dbSet.Add((T)res); await entity.Context.SaveChangesAsync(); - + var id = store.TypesByType[typeof(T)].PrimaryKey.GetValue(resource); - await Warehouse.Put(resource, id.ToString(), store, null, null, 0, manager); + await Warehouse.Put(id.ToString(), res, store, null, null, 0, manager); return resource; } - public static async AsyncReply CreateResourceAsync(this IServiceProvider serviceProvider, object properties = null) where T : class, IResource - { - var options = serviceProvider.GetService().FindExtension(); + //public static async AsyncReply CreateResourceAsync(this IServiceProvider serviceProvider, T properties = null) where T : class, IResource + //{ + // var options = serviceProvider.GetService().FindExtension>(); - var resource = await Warehouse.New("", options.Store, null, null, null, properties); + // var resource = await Warehouse.New("", options.Store, null, null, null, properties); - resource.Instance.Managers.AddRange(options.Store.Instance.Managers.ToArray()); + // resource.Instance.Managers.AddRange(options.Store.Instance.Managers.ToArray()); - return resource; - } + // return resource; + //} - public static T CreateResource(this IServiceProvider serviceProvider, object properties = null) where T : class, IResource - => CreateResourceAsync(serviceProvider, properties).Wait(); + //public static T CreateResource(this IServiceProvider serviceProvider, object properties = null) where T : class, IResource + // => CreateResourceAsync(serviceProvider, properties).Wait(); public static DbContextOptionsBuilder UseEsiur(this DbContextOptionsBuilder optionsBuilder, - //DbContext context, - string name = null, - IResource parent = null, - IPermissionsManager manager = null, - Func dbContextProvider = null + EntityStore store, + Func getter = null + + //IServiceCollection services = null + //string name = null, + //IResource parent = null, + //IPermissionsManager manager = null, + //Func dbContextProvider = null ) { var extension = optionsBuilder.Options.FindExtension(); @@ -93,10 +156,9 @@ namespace Esiur.Stores.EntityCore if (extension == null) { - var store = Warehouse.New(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait(); + //var store = Warehouse.New(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait(); + store.Options = optionsBuilder.Options; extension = new EsiurExtensionOptions(store); - //store.Options = optionsBuilder; - //store.DbContext = context; } ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); @@ -105,34 +167,34 @@ namespace Esiur.Stores.EntityCore } - public static DbContextOptionsBuilder UseEsiur( - this DbContextOptionsBuilder optionsBuilder, - //DbContext context, - string name = null, - IResource parent = null, - IPermissionsManager manager = null, - Func dbContextProvider = null) - where TContext : DbContext - { + //public static DbContextOptionsBuilder UseEsiur( + // this DbContextOptionsBuilder optionsBuilder, + // //DbContext context, + // string name = null, + // IResource parent = null, + // IPermissionsManager manager = null, + // Func dbContextProvider = null) + // where TContext : DbContext + //{ - var extension = optionsBuilder.Options.FindExtension(); + // var extension = optionsBuilder.Options.FindExtension(); - if (extension == null) - { - var store = Warehouse.New(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait(); - extension = new EsiurExtensionOptions(store); - //store.Options = optionsBuilder; - //store.Options = extension; - //store.DbContext = context; - } + // if (extension == null) + // { + // var store = Warehouse.New(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait(); + // extension = new EsiurExtensionOptions(store); + // //store.Options = optionsBuilder; + // //store.Options = extension; + // //store.DbContext = context; + // } - ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); + // ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - return optionsBuilder; + // return optionsBuilder; - } + //} } } diff --git a/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs b/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs index f41a987..59b89a4 100644 --- a/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs +++ b/Esiur.Stores.EntityCore/EsiurProxyRewrite.cs @@ -40,7 +40,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace Esiur.Stores.EntityCore { - public class EsiurProxyRewrite : IModelFinalizingConvention + public class EsiurProxyRewrite : IModelFinalizingConvention { private static readonly MethodInfo _createInstance = typeof(EsiurProxyRewrite).GetTypeInfo().GetDeclaredMethod(nameof(EsiurProxyRewrite.CreateInstance)); @@ -79,7 +79,7 @@ namespace Esiur.Stores.EntityCore var obj = Warehouse.New(entityType.ClrType).Wait() as EntityResource;//, "", options.Store, null, manager); //obj._PrimaryId = id; options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, id); - Warehouse.Put(obj, id.ToString(), options.Store, null, null, 0, manager).Wait(); + Warehouse.Put(id.ToString(), obj, options.Store, null, null, 0, manager).Wait(); // obj.Instance.IntVal = id;//.Variables.Add("eid", id); diff --git a/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj b/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj index 93a5be4..cd17db4 100644 --- a/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj +++ b/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj @@ -11,7 +11,7 @@ http://www.esiur.com https://github.com/esiur/esiur-dotnet/ True - 1.4.0 + 1.4.1 Esiur.Stores.MongoDB diff --git a/Esiur/Core/AsyncAwaiterGeneric.cs b/Esiur/Core/AsyncAwaiterGeneric.cs index e41942a..f5e82e4 100644 --- a/Esiur/Core/AsyncAwaiterGeneric.cs +++ b/Esiur/Core/AsyncAwaiterGeneric.cs @@ -32,7 +32,7 @@ namespace Esiur.Core public T GetResult() { if (exception != null) - throw exception; + throw exception; return result; } diff --git a/Esiur/Core/AsyncReply.cs b/Esiur/Core/AsyncReply.cs index 8f5fd46..5e463e2 100644 --- a/Esiur/Core/AsyncReply.cs +++ b/Esiur/Core/AsyncReply.cs @@ -192,7 +192,7 @@ namespace Esiur.Core // } } - public void Trigger(object result) + public AsyncReply Trigger(object result) { lock (asyncLock) { @@ -202,7 +202,7 @@ namespace Esiur.Core Console.WriteLine($"AsyncReply: {Id} Trigger"); if (resultReady) - return; + return this; this.result = result; @@ -219,14 +219,16 @@ namespace Esiur.Core Console.WriteLine($"AsyncReply: {Id} Trigger ended"); } + + return this; } - public void TriggerError(Exception exception) + public AsyncReply TriggerError(Exception exception) { //timeout?.Dispose(); if (resultReady) - return; + return this; if (exception is AsyncException) this.exception = exception as AsyncException; @@ -242,9 +244,10 @@ namespace Esiur.Core mutex?.Set(); + return this; } - public void TriggerProgress(ProgressType type, int value, int max) + public AsyncReply TriggerProgress(ProgressType type, int value, int max) { //timeout?.Dispose(); @@ -254,10 +257,12 @@ namespace Esiur.Core cb(type, value, max); //} + + return this; } - public void TriggerChunk(object value) + public AsyncReply TriggerChunk(object value) { //timeout?.Dispose(); @@ -269,6 +274,8 @@ namespace Esiur.Core cb(value); //} + + return this; } public AsyncAwaiter GetAwaiter() diff --git a/Esiur/Core/ExceptionCode.cs b/Esiur/Core/ExceptionCode.cs index 7e75029..c251bb7 100644 --- a/Esiur/Core/ExceptionCode.cs +++ b/Esiur/Core/ExceptionCode.cs @@ -36,6 +36,10 @@ namespace Esiur.Core SetPropertyDenied, ReadOnlyProperty, GeneralFailure, - AddToStoreFailed + AddToStoreFailed, + NotAttached, + AlreadyListened, + AlreadyUnlistened, + NotListenable } } diff --git a/Esiur/Data/ResourceJsonConverter.cs b/Esiur/Data/ResourceJsonConverter.cs index f65f30d..efbbd09 100644 --- a/Esiur/Data/ResourceJsonConverter.cs +++ b/Esiur/Data/ResourceJsonConverter.cs @@ -1,4 +1,5 @@ -using Esiur.Resource; +using Esiur.Net.IIP; +using Esiur.Resource; /* Copyright (c) 2017-2021 Ahmed Kh. Zamil @@ -53,8 +54,15 @@ namespace Esiur.Data foreach (var pt in resource.Instance.Template.Properties) { var rt = pt.Info.GetValue(resource, null); + if (rt is DistributedPropertyContext) + continue; + writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name); - JsonSerializer.Serialize(writer, rt, options); + + if (rt is IResource) + JsonSerializer.Serialize(writer, (IResource) rt, options); + else + JsonSerializer.Serialize(writer, rt, options); } writer.WriteEndObject(); diff --git a/Esiur/Data/Structure.cs b/Esiur/Data/Structure.cs index b3977ef..bf677d1 100644 --- a/Esiur/Data/Structure.cs +++ b/Esiur/Data/Structure.cs @@ -110,6 +110,10 @@ namespace Esiur.Data foreach (var p in pi) st[p.Name] = p.GetValue(obj); + var fi = type.GetTypeInfo().GetFields().Where(x => x.IsPublic); + foreach (var f in fi) + st[f.Name] = f.GetValue(obj); + return st; } //else diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj index 7f55a9f..1f00c4b 100644 --- a/Esiur/Esiur.csproj +++ b/Esiur/Esiur.csproj @@ -1,22 +1,23 @@  - netstandard2.1 + netstandard2.0 Distributed Resources Platform Ahmed Kh. Zamil https://github.com/Esiur/Esiur-dotnet/blob/master/LICENSE http://www.esiur.com true - 1.5.1 + 1.6.1 https://github.com/esiur/esiur-dotnet Ahmed Kh. Zamil - 1.3.1.0 + 1.6.0 Esiur Foundation - 1.3.1.0 + 1.6.0 Esiur Esiur Esiur Esiur + 9.0 @@ -55,6 +56,7 @@ + @@ -62,8 +64,12 @@ - + + + + + \ No newline at end of file diff --git a/Esiur/Misc/Global.cs b/Esiur/Misc/Global.cs index 7923a71..bc5a7b7 100644 --- a/Esiur/Misc/Global.cs +++ b/Esiur/Misc/Global.cs @@ -69,7 +69,7 @@ namespace Esiur.Misc }catch (Exception ex) { Console.WriteLine(ex.ToString()); - return ""; + return "{}"; } } @@ -433,9 +433,9 @@ namespace Esiur.Misc - public static byte[] GenerateBytes(int Length) + public static byte[] GenerateBytes(int length) { - var b = new byte[Length]; + var b = new byte[length]; rand.NextBytes(b); return b; } diff --git a/Esiur/Net/DataLink/PacketFilter.cs b/Esiur/Net/DataLink/PacketFilter.cs index 3c6da41..e9c574e 100644 --- a/Esiur/Net/DataLink/PacketFilter.cs +++ b/Esiur/Net/DataLink/PacketFilter.cs @@ -50,7 +50,7 @@ namespace Esiur.Net.DataLink public void Destroy() { - + OnDestroy?.Invoke(this); } } } diff --git a/Esiur/Net/DataLink/PacketServer.cs b/Esiur/Net/DataLink/PacketServer.cs index 1e62ab5..e1de446 100644 --- a/Esiur/Net/DataLink/PacketServer.cs +++ b/Esiur/Net/DataLink/PacketServer.cs @@ -65,7 +65,7 @@ namespace Esiur.Net.DataLink public void Destroy() { - throw new NotImplementedException(); + OnDestroy?.Invoke(this); } public AsyncReply Trigger(ResourceTrigger trigger) diff --git a/Esiur/Net/DataLink/PacketSource.cs b/Esiur/Net/DataLink/PacketSource.cs index b2568a5..4b79cb2 100644 --- a/Esiur/Net/DataLink/PacketSource.cs +++ b/Esiur/Net/DataLink/PacketSource.cs @@ -69,7 +69,7 @@ namespace Esiur.Net.DataLink public void Destroy() { - throw new NotImplementedException(); + OnDestroy?.Invoke(this); } /* diff --git a/Esiur/Net/HTTP/HTTPFilter.cs b/Esiur/Net/HTTP/HTTPFilter.cs index 37ef101..d2ef40c 100644 --- a/Esiur/Net/HTTP/HTTPFilter.cs +++ b/Esiur/Net/HTTP/HTTPFilter.cs @@ -76,7 +76,7 @@ namespace Esiur.Net.HTTP public void Destroy() { - throw new NotImplementedException(); + OnDestroy?.Invoke(this); } } } \ No newline at end of file diff --git a/Esiur/Net/HTTP/IIPoWS.cs b/Esiur/Net/HTTP/IIPoWS.cs index cddbb69..4418be3 100644 --- a/Esiur/Net/HTTP/IIPoWS.cs +++ b/Esiur/Net/HTTP/IIPoWS.cs @@ -112,7 +112,7 @@ namespace Esiur.Net.HTTP private void IipConnection_OnReady(DistributedConnection sender) { - Warehouse.Put(sender, sender.RemoteUsername, null, sender.Server); + Warehouse.Put(sender.RemoteUsername, sender, null, sender.Server).Wait(); } public override AsyncReply Trigger(ResourceTrigger trigger) diff --git a/Esiur/Net/IIP/DistributedConnection.cs b/Esiur/Net/IIP/DistributedConnection.cs index e138d22..dc8e1bd 100644 --- a/Esiur/Net/IIP/DistributedConnection.cs +++ b/Esiur/Net/IIP/DistributedConnection.cs @@ -197,11 +197,20 @@ namespace Esiur.Net.IIP if (socket.State == SocketState.Established && session.LocalAuthentication.Type == AuthenticationType.Client) + { + Declare(); + } + } + + private void Declare() + { + var dmn = DC.ToBytes(session.LocalAuthentication.Domain);// domain); + + if (session.LocalAuthentication.Method == AuthenticationMethod.Credentials) { // declare (Credentials -> No Auth, No Enctypt) var un = DC.ToBytes(session.LocalAuthentication.Username); - var dmn = DC.ToBytes(session.LocalAuthentication.Domain);// domain); SendParams() .AddUInt8(0x60) @@ -212,8 +221,27 @@ namespace Esiur.Net.IIP .AddUInt8Array(un) .Done();//, dmn, localNonce, (byte)un.Length, un); } - } + else if (session.LocalAuthentication.Method == AuthenticationMethod.Token) + { + SendParams() + .AddUInt8(0x70) + .AddUInt8((byte)dmn.Length) + .AddUInt8Array(dmn) + .AddUInt8Array(localNonce) + .AddUInt64(session.LocalAuthentication.TokenIndex) + .Done();//, dmn, localNonce, token + + } + else if (session.LocalAuthentication.Method == AuthenticationMethod.None) + { + SendParams() + .AddUInt8(0x40) + .AddUInt8((byte)dmn.Length) + .AddUInt8Array(dmn) + .Done();//, dmn, localNonce, token + } + } /// /// Create a new distributed connection. @@ -451,12 +479,21 @@ namespace Esiur.Net.IIP IIPRequestInvokeFunctionNamedArguments(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content); break; - case IIPPacket.IIPPacketAction.GetProperty: - IIPRequestGetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex); + //case IIPPacket.IIPPacketAction.GetProperty: + // IIPRequestGetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex); + // break; + //case IIPPacket.IIPPacketAction.GetPropertyIfModified: + // IIPRequestGetPropertyIfModifiedSince(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.ResourceAge); + // break; + + case IIPPacket.IIPPacketAction.Listen: + IIPRequestListen(packet.CallbackId, packet.ResourceId, packet.MethodIndex); break; - case IIPPacket.IIPPacketAction.GetPropertyIfModified: - IIPRequestGetPropertyIfModifiedSince(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.ResourceAge); + + case IIPPacket.IIPPacketAction.Unlisten: + IIPRequestUnlisten(packet.CallbackId, packet.ResourceId, packet.MethodIndex); break; + case IIPPacket.IIPPacketAction.SetProperty: IIPRequestSetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content); break; @@ -531,14 +568,17 @@ namespace Esiur.Net.IIP IIPReplyInvoke(packet.CallbackId, packet.Content); break; - case IIPPacket.IIPPacketAction.GetProperty: - IIPReply(packet.CallbackId, packet.Content); - break; + //case IIPPacket.IIPPacketAction.GetProperty: + // IIPReply(packet.CallbackId, packet.Content); + // break; - case IIPPacket.IIPPacketAction.GetPropertyIfModified: - IIPReply(packet.CallbackId, packet.Content); - break; - case IIPPacket.IIPPacketAction.SetProperty: + //case IIPPacket.IIPPacketAction.GetPropertyIfModified: + // IIPReply(packet.CallbackId, packet.Content); + // break; + + case IIPPacketAction.Listen: + case IIPPacketAction.Unlisten: + case IIPPacketAction.SetProperty: IIPReply(packet.CallbackId); break; @@ -753,24 +793,34 @@ namespace Esiur.Net.IIP session.Id = new byte[32]; r.NextBytes(session.Id); //SendParams((byte)0x28, session.Id); - SendParams() - .AddUInt8(0x28) - .AddUInt8Array(session.Id) - .Done(); + SendParams().AddUInt8(0x28) + .AddUInt8Array(session.Id) + .Done(); - ready = true; - Warehouse.Put(this, this.LocalUsername, null, Server).Then(x => + if (this.Instance == null) { + Warehouse.Put(this.RemoteUsername, this, null, Server).Then(x => + { + + ready = true; + openReply?.Trigger(true); + OnReady?.Invoke(this); + + Server?.Membership.Login(session); + loginDate = DateTime.Now; + + }).Error(x => + { + openReply?.TriggerError(x); + }); + } + else + { + ready = true; openReply?.Trigger(true); OnReady?.Invoke(this); - Server?.Membership.Login(session); - - }).Error(x=> - { - openReply?.TriggerError(x); - }); - + } //Global.Log("auth", LogType.Warning, "U:" + RemoteUsername + " IP:" + Socket.RemoteEndPoint.Address.ToString() + " S:AUTH"); @@ -841,13 +891,21 @@ namespace Esiur.Net.IIP ready = true; // put it in the warehouse - Warehouse.Put(this, this.LocalUsername, null, Server).Then(x => + + if (this.Instance == null) + { + Warehouse.Put(this.LocalUsername, this, null, Server).Then(x => + { + openReply?.Trigger(true); + OnReady?.Invoke(this); + + }).Error(x => openReply?.TriggerError(x)); + } + else { openReply?.Trigger(true); OnReady?.Invoke(this); - - }).Error(x=> openReply?.TriggerError(x)); - + } } } else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Error) @@ -957,28 +1015,7 @@ namespace Esiur.Net.IIP } - protected void NetworkClose() - { - // clean up - ready = false; - readyToEstablish = false; - foreach (var x in requests.Values) - x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); - - foreach (var x in resourceRequests.Values) - x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); - - foreach (var x in templateRequests.Values) - x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); - - requests.Clear(); - resourceRequests.Clear(); - templateRequests.Clear(); - - foreach (var x in resources.Values) - x.Suspend(); - } public AsyncReply Connect(AuthenticationMethod method = AuthenticationMethod.Certificate, Sockets.ISocket socket = null, string hostname = null, ushort port = 0, string username = null, ulong tokenIndex = 0, byte[] passwordOrToken = null, string domain = null) { @@ -1128,32 +1165,38 @@ namespace Esiur.Net.IIP protected override void Connected() { if (session.LocalAuthentication.Type == AuthenticationType.Client) - { - // declare (Credentials -> No Auth, No Enctypt) - - var un = DC.ToBytes(session.LocalAuthentication.Username); - var dmn = DC.ToBytes(session.LocalAuthentication.Domain);// domain); - - SendParams() - .AddUInt8(0x60) - .AddUInt8((byte)dmn.Length) - .AddUInt8Array(dmn) - .AddUInt8Array(localNonce) - .AddUInt8((byte)un.Length) - .AddUInt8Array(un) - .Done(); - } + Declare(); } protected override void Disconencted() { + // clean up + readyToEstablish = false; + + foreach (var x in requests.Values) + x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); + + foreach (var x in resourceRequests.Values) + x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); + + foreach (var x in templateRequests.Values) + x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); + + requests.Clear(); + resourceRequests.Clear(); + templateRequests.Clear(); + + foreach (var x in resources.Values) + x.Suspend(); + + UnsubscribeAll(); + + Warehouse.Remove(this); + if (ready) - { Server?.Membership.Logout(session); - Warehouse.Remove(this); - ready = false; - UnsubscribeAll(); - } + + ready = false; } /* diff --git a/Esiur/Net/IIP/DistributedConnectionProtocol.cs b/Esiur/Net/IIP/DistributedConnectionProtocol.cs index 0889d0d..bb8f05a 100644 --- a/Esiur/Net/IIP/DistributedConnectionProtocol.cs +++ b/Esiur/Net/IIP/DistributedConnectionProtocol.cs @@ -36,6 +36,7 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; +using System.Security.Cryptography.X509Certificates; namespace Esiur.Net.IIP { @@ -54,7 +55,9 @@ namespace Esiur.Net.IIP volatile uint callbackCounter = 0; - List subscriptions = new List(); + //List subscriptions = new List(); + Dictionary> subscriptions = new Dictionary>();// new List(); + object subscriptionsLock = new object(); AsyncQueue queue = new AsyncQueue(); @@ -108,13 +111,40 @@ namespace Esiur.Net.IIP internal SendList SendEvent(IIPPacket.IIPPacketEvent evt) { - //var bl = new BinaryList((byte)(evt)); - //bl.AddRange(args); - //Send(bl.ToArray()); - return (SendList)SendParams().AddUInt8((byte)(evt)); } + internal AsyncReply SendListenRequest(uint instanceId, byte index) + { + var reply = new AsyncReply(); + var c = callbackCounter++; + requests.Add(c, reply); + + SendParams().AddUInt8((byte)(0x40 | (byte)Packets.IIPPacket.IIPPacketAction.Listen)) + .AddUInt32(c) + .AddUInt32(instanceId) + .AddUInt8(index) + .Done(); + + return reply; + } + + internal AsyncReply SendUnlistenRequest(uint instanceId, byte index) + { + var reply = new AsyncReply(); + var c = callbackCounter++; + requests.Add(c, reply); + + SendParams().AddUInt8((byte)(0x40 | (byte)Packets.IIPPacket.IIPPacketAction.Unlisten)) + .AddUInt32(c) + .AddUInt32(instanceId) + .AddUInt8(index) + .Done(); + + return reply; + } + + internal AsyncReply SendInvokeByArrayArguments(uint instanceId, byte index, object[] parameters) { var pb = Codec.ComposeVarArray(parameters, this, true); @@ -593,6 +623,7 @@ namespace Esiur.Net.IIP var r = res as IResource; // unsubscribe Unsubscribe(r); + Subscribe(r); //r.Instance.ResourceEventOccurred -= Instance_EventOccurred; //r.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred; @@ -610,7 +641,6 @@ namespace Esiur.Net.IIP //r.Instance.ResourceModified += Instance_PropertyModified; //r.Instance.ResourceDestroyed += Instance_ResourceDestroyed; - Subscribe(r); //r.Instance.Children.OnAdd += Children_OnAdd; //r.Instance.Children.OnRemoved += Children_OnRemoved; @@ -770,7 +800,7 @@ namespace Esiur.Net.IIP // create the resource var resource = Activator.CreateInstance(type, args) as IResource; - Warehouse.Put(resource, name, store as IStore, parent).Then(ok => + Warehouse.Put( name, resource, store as IStore, parent).Then(ok => { SendReply(IIPPacket.IIPPacketAction.CreateResource, callback) .AddUInt32(resource.Instance.Id) @@ -1126,7 +1156,7 @@ namespace Esiur.Net.IIP if (Server?.EntryPoint != null) Server.EntryPoint.Query(resourceLink, this).Then(queryCallback); else - Warehouse.Query(resourceLink).Then(x => queryCallback(x)); + Warehouse.Query(resourceLink).Then(queryCallback); } void IIPRequestResourceAttribute(uint callback, uint resourceId) @@ -1469,53 +1499,161 @@ namespace Esiur.Net.IIP }); } - void IIPRequestGetProperty(uint callback, uint resourceId, byte index) + void IIPRequestListen(uint callback, uint resourceId, byte index) { Warehouse.GetById(resourceId).Then((r) => { if (r != null) { - var pt = r.Instance.Template.GetFunctionTemplateByIndex(index); - if (pt != null) + var et = r.Instance.Template.GetEventTemplateByIndex(index); + + if (et != null) { if (r is DistributedResource) { - SendReply(IIPPacket.IIPPacketAction.GetProperty, callback) - .AddUInt8Array(Codec.Compose((r as DistributedResource)._Get(pt.Index), this)) - .Done(); + (r as DistributedResource).Listen(et).Then(x => + { + SendReply(IIPPacket.IIPPacketAction.Listen, callback).Done(); + }).Error(x => SendError(ErrorType.Exception, callback, (ushort)ExceptionCode.GeneralFailure)); } else { -#if NETSTANDARD - var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); -#else - var pi = r.GetType().GetProperty(pt.Name); -#endif + lock(subscriptionsLock) + { + if (!subscriptions.ContainsKey(r)) + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotAttached); + return; + } - if (pi != null) - { - SendReply(IIPPacket.IIPPacketAction.GetProperty, callback) - .AddUInt8Array(Codec.Compose(pi.GetValue(r), this)) - .Done(); - } - else - { - // pt found, pi not found, this should never happen + if (subscriptions[r].Contains(index)) + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AlreadyListened); + return; + } + + subscriptions[r].Add(index); + + SendReply(IIPPacket.IIPPacketAction.Listen, callback).Done(); } } } else { // pt not found + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.MethodNotFound); } } else { // resource not found + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound); } }); + } + void IIPRequestUnlisten(uint callback, uint resourceId, byte index) + { + Warehouse.GetById(resourceId).Then((r) => + { + if (r != null) + { + var et = r.Instance.Template.GetEventTemplateByIndex(index); + + if (et != null) + { + if (r is DistributedResource) + { + (r as DistributedResource).Unlisten(et).Then(x => + { + SendReply(IIPPacket.IIPPacketAction.Unlisten, callback).Done(); + }).Error(x => SendError(ErrorType.Exception, callback, (ushort)ExceptionCode.GeneralFailure)); + } + else + { + lock (subscriptionsLock) + { + if (!subscriptions.ContainsKey(r)) + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotAttached); + return; + } + + if (!subscriptions[r].Contains(index)) + { + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AlreadyUnlistened); + return; + } + + subscriptions[r].Remove(index); + + SendReply(IIPPacket.IIPPacketAction.Unlisten, callback).Done(); + } + } + } + else + { + // pt not found + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.MethodNotFound); + } + } + else + { + // resource not found + SendError(ErrorType.Management, callback, (ushort)ExceptionCode.ResourceNotFound); + } + }); + + } + + // void IIPRequestGetProperty(uint callback, uint resourceId, byte index) + // { + // Warehouse.GetById(resourceId).Then((r) => + // { + // if (r != null) + // { + // var pt = r.Instance.Template.GetPropertyTemplateByIndex(index); + // if (pt != null) + // { + // if (r is DistributedResource) + // { + // SendReply(IIPPacket.IIPPacketAction.GetProperty, callback) + // .AddUInt8Array(Codec.Compose((r as DistributedResource)._Get(pt.Index), this)) + // .Done(); + // } + // else + // { + //#if NETSTANDARD + // var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); + //#else + // var pi = r.GetType().GetProperty(pt.Name); + //#endif + + // if (pi != null) + // { + // SendReply(IIPPacket.IIPPacketAction.GetProperty, callback) + // .AddUInt8Array(Codec.Compose(pi.GetValue(r), this)) + // .Done(); + // } + // else + // { + // // pt found, pi not found, this should never happen + // } + // } + // } + // else + // { + // // pt not found + // } + // } + // else + // { + // // resource not found + // } + // }); + // } + void IIPRequestInquireResourceHistory(uint callback, uint resourceId, DateTime fromDate, DateTime toDate) { Warehouse.GetById(resourceId).Then((r) => @@ -1552,51 +1690,51 @@ namespace Esiur.Net.IIP }); } - void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, ulong age) - { - Warehouse.GetById(resourceId).Then((r) => - { - if (r != null) - { - var pt = r.Instance.Template.GetFunctionTemplateByIndex(index); - if (pt != null) - { - if (r.Instance.GetAge(index) > age) - { -#if NETSTANDARD - var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); -#else - var pi = r.GetType().GetProperty(pt.Name); -#endif - if (pi != null) - { - SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) - .AddUInt8Array(Codec.Compose(pi.GetValue(r), this)) - .Done(); - } - else - { - // pt found, pi not found, this should never happen - } - } - else - { - SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) - .AddUInt8((byte)DataType.NotModified) - .Done(); - } - } - else - { - // pt not found - } - } - else - { - // resource not found - } - }); - } +// void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, ulong age) +// { +// Warehouse.GetById(resourceId).Then((r) => +// { +// if (r != null) +// { +// var pt = r.Instance.Template.GetFunctionTemplateByIndex(index); +// if (pt != null) +// { +// if (r.Instance.GetAge(index) > age) +// { +//#if NETSTANDARD +// var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); +//#else +// var pi = r.GetType().GetProperty(pt.Name); +//#endif +// if (pi != null) +// { +// SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) +// .AddUInt8Array(Codec.Compose(pi.GetValue(r), this)) +// .Done(); +// } +// else +// { +// // pt found, pi not found, this should never happen +// } +// } +// else +// { +// SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) +// .AddUInt8((byte)DataType.NotModified) +// .Done(); +// } +// } +// else +// { +// // pt not found +// } +// } +// else +// { +// // resource not found +// } +// }); +// } void IIPRequestSetProperty(uint callback, uint resourceId, byte index, byte[] content) { @@ -1973,7 +2111,7 @@ namespace Esiur.Net.IIP // ClassId, ResourceAge, ResourceLink, Content if (resource == null) { - Warehouse.Put(dr, id.ToString(), this, null, tmp).Then((ok) => + Warehouse.Put(id.ToString(), dr, this, null, tmp).Then((ok) => { Codec.ParsePropertyValueArray((byte[])rt[3], this).Then((ar) => { @@ -2242,7 +2380,7 @@ namespace Esiur.Net.IIP resource.Instance.ResourceModified += Instance_PropertyModified; resource.Instance.ResourceDestroyed += Instance_ResourceDestroyed; - subscriptions.Add(resource); + subscriptions.Add(resource, new List()); } } @@ -2265,7 +2403,7 @@ namespace Esiur.Net.IIP { lock(subscriptionsLock) { - foreach(var resource in subscriptions) + foreach(var resource in subscriptions.Keys) { resource.Instance.ResourceEventOccurred -= Instance_EventOccurred; resource.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred; @@ -2313,12 +2451,26 @@ namespace Esiur.Net.IIP if (et == null) return; + if (et.Listenable) + { + lock (subscriptionsLock) + { + // check the client requested listen + if (!subscriptions.ContainsKey(resource)) + return; + + if (!subscriptions[resource].Contains(et.Index)) + return; + } + } + if (!receivers(this.session)) return; if (resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied) return; + // compose the packet SendEvent(IIPPacket.IIPPacketEvent.EventOccurred) .AddUInt32(resource.Instance.Id) @@ -2334,6 +2486,18 @@ namespace Esiur.Net.IIP if (et == null) return; + if (et.Listenable) + { + lock (subscriptionsLock) + { + // check the client requested listen + if (!subscriptions.ContainsKey(resource)) + return; + + if (!subscriptions[resource].Contains(et.Index)) + return; + } + } if (resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, et, null) == Ruling.Denied) return; diff --git a/Esiur/Net/IIP/DistributedResource.cs b/Esiur/Net/IIP/DistributedResource.cs index 1c9d3c3..d50791e 100644 --- a/Esiur/Net/IIP/DistributedResource.cs +++ b/Esiur/Net/IIP/DistributedResource.cs @@ -44,7 +44,7 @@ using Esiur.Resource.Template; namespace Esiur.Net.IIP { - + //[System.Runtime.InteropServices.ComVisible(true)] public class DistributedResource : DynamicObject, IResource { @@ -73,7 +73,7 @@ namespace Esiur.Net.IIP DistributedResourceEvent[] events; - + /// /// Resource template for the remotely located resource. @@ -105,7 +105,7 @@ namespace Esiur.Net.IIP /// public uint Id { - get { return instanceId; } + get { return instanceId; } } /// @@ -136,20 +136,20 @@ namespace Esiur.Net.IIP internal bool Attached => attached; internal bool Suspended => suspended; - - // public DistributedResourceStack Stack + + // public DistributedResourceStack Stack //{ - // get { return stack; } + // get { return stack; } //} - /// - /// Create a new distributed resource. - /// - /// Connection responsible for the distributed resource. - /// Resource template. - /// Instance Id given by the other end. - /// Resource age. + /// + /// Create a new distributed resource. + /// + /// Connection responsible for the distributed resource. + /// Resource template. + /// Instance Id given by the other end. + /// Resource age. public DistributedResource(DistributedConnection connection, uint instanceId, ulong age, string link) { this.link = link; @@ -207,7 +207,7 @@ namespace Esiur.Net.IIP attached = true; } - return true; + return true; } internal void _EmitEventByIndex(byte index, object args) @@ -232,6 +232,7 @@ namespace Esiur.Net.IIP return connection.SendInvokeByNamedArguments(instanceId, index, namedArgs); } + public AsyncReply _InvokeByArrayArguments(byte index, object[] args) { if (destroyed) @@ -247,14 +248,52 @@ namespace Esiur.Net.IIP return connection.SendInvokeByArrayArguments(instanceId, index, args); } - + + public AsyncReply Listen(EventTemplate et) + { + if (et == null) + return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); + + if (!et.Listenable) + return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, "")); + + return connection.SendListenRequest(instanceId, et.Index); + } + + public AsyncReply Listen(string eventName) + { + var et = Instance.Template.GetEventTemplateByName(eventName); + + return Listen(et); + } + + + public AsyncReply Unlisten(EventTemplate et) + { + if (et == null) + return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.MethodNotFound, "")); + + if (!et.Listenable) + return new AsyncReply().TriggerError(new AsyncException(ErrorType.Management, (ushort)ExceptionCode.NotListenable, "")); + + return connection.SendUnlistenRequest(instanceId, et.Index); + } + + public AsyncReply Unlisten(string eventName) + { + var et = Instance.Template.GetEventTemplateByName(eventName); + + return Unlisten(et); + } + + public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) { var ft = Instance.Template.GetFunctionTemplateByName(binder.Name); var reply = new AsyncReply(); - if (attached && ft!=null) + if (attached && ft != null) { if (args.Length == 1) { @@ -273,7 +312,7 @@ namespace Esiur.Net.IIP { result = _InvokeByArrayArguments(ft.Index, args); } - + } else { @@ -380,7 +419,7 @@ namespace Esiur.Net.IIP return false; var pt = Instance.Template.GetPropertyTemplateByName(binder.Name); - + if (pt != null) { _Set(pt.Index, value); @@ -397,7 +436,7 @@ namespace Esiur.Net.IIP return true; } - } + } /* public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender) @@ -426,10 +465,10 @@ namespace Esiur.Net.IIP */ - - /// - /// Resource interface. - /// + + /// + /// Resource interface. + /// public Instance Instance { get; diff --git a/Esiur/Net/IIP/DistributedSession.cs b/Esiur/Net/IIP/DistributedSession.cs index cd11926..c84c66f 100644 --- a/Esiur/Net/IIP/DistributedSession.cs +++ b/Esiur/Net/IIP/DistributedSession.cs @@ -32,7 +32,7 @@ namespace Esiur.Net.IIP { public class DistributedSession : NetworkSession { - Source Source { get; } - Authentication Authentication; + public Source Source { get; set; } + public Authentication Authentication { get; set; } } } diff --git a/Esiur/Net/Packets/HTTPRequestPacket.cs b/Esiur/Net/Packets/HTTPRequestPacket.cs index 97f4c10..1755f1e 100644 --- a/Esiur/Net/Packets/HTTPRequestPacket.cs +++ b/Esiur/Net/Packets/HTTPRequestPacket.cs @@ -30,6 +30,7 @@ using System.Text; using Esiur.Misc; using Esiur.Data; using System.Net; +using System.Text.Json.Serialization; namespace Esiur.Net.Packets { @@ -241,12 +242,12 @@ namespace Esiur.Net.Packets // check limit if (postSize > data.Length - headerSize) return -(postSize - (data.Length - headerSize)); - + if ( Headers["content-type"] == null || Headers["content-type"] == "" - || Headers["content-type"].StartsWith("application/x-www-form-urlencoded")) + || Headers["content-type"].StartsWith("application/x-www-form-urlencoded")) { string[] PostVars = null; PostVars = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split('&'); @@ -265,10 +266,10 @@ namespace Esiur.Net.Packets } else if (PostForms.Contains("unknown")) - PostForms["unknown"] = PostForms["unknown"] - + "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])); - else - PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]))); + PostForms["unknown"] = PostForms["unknown"] + + "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])); + else + PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]))); } } else if (Headers["content-type"].StartsWith("multipart/form-data")) @@ -291,6 +292,10 @@ namespace Esiur.Net.Packets PostForms.Add(ps[0].Substring(st, ed - st), ps[1]); } } + //else if (Headers["content-type"] == "application/json") + //{ + // var json = DC.Clip(data, headerSize, postSize); + //} else { //PostForms.Add(Headers["content-type"], Encoding.Default.GetString( )); diff --git a/Esiur/Net/Packets/IIPPacket.cs b/Esiur/Net/Packets/IIPPacket.cs index b65f1cf..182e29a 100644 --- a/Esiur/Net/Packets/IIPPacket.cs +++ b/Esiur/Net/Packets/IIPPacket.cs @@ -110,11 +110,11 @@ namespace Esiur.Net.Packets // Request Invoke InvokeFunctionArrayArguments = 0x10, - GetProperty, - GetPropertyIfModified, - SetProperty, InvokeFunctionNamedArguments, - + Listen, + Unlisten, + SetProperty, + // Request Attribute GetAllAttributes = 0x18, UpdateAllAttributes, @@ -543,7 +543,8 @@ namespace Esiur.Net.Packets offset += cl; } - else if (Action == IIPPacketAction.GetProperty) + else if (Action == IIPPacketAction.Listen + || Action == IIPPacketAction.Unlisten)// .GetProperty) { if (NotEnough(offset, ends, 5)) return -dataLengthNeeded; @@ -554,20 +555,20 @@ namespace Esiur.Net.Packets MethodIndex = data[offset++]; } - else if (Action == IIPPacketAction.GetPropertyIfModified) - { - if (NotEnough(offset, ends, 9)) - return -dataLengthNeeded; + //else if (Action == IIPPacketAction.GetPropertyIfModified) + //{ + // if (NotEnough(offset, ends, 9)) + // return -dataLengthNeeded; - ResourceId = data.GetUInt32(offset); - offset += 4; + // ResourceId = data.GetUInt32(offset); + // offset += 4; - MethodIndex = data[offset++]; + // MethodIndex = data[offset++]; - ResourceAge = data.GetUInt64(offset); - offset += 8; + // ResourceAge = data.GetUInt64(offset); + // offset += 8; - } + //} else if (Action == IIPPacketAction.SetProperty) { if (NotEnough(offset, ends, 6)) @@ -707,9 +708,9 @@ namespace Esiur.Net.Packets offset += cl; } else if (Action == IIPPacketAction.InvokeFunctionArrayArguments - || Action == IIPPacketAction.InvokeFunctionNamedArguments - || Action == IIPPacketAction.GetProperty - || Action == IIPPacketAction.GetPropertyIfModified) + || Action == IIPPacketAction.InvokeFunctionNamedArguments) + //|| Action == IIPPacketAction.GetProperty + //|| Action == IIPPacketAction.GetPropertyIfModified) { if (NotEnough(offset, ends, 1)) return -dataLengthNeeded; @@ -740,7 +741,9 @@ namespace Esiur.Net.Packets offset += (uint)size; } } - else if (Action == IIPPacketAction.SetProperty) + else if (Action == IIPPacketAction.SetProperty + || Action == IIPPacketAction.Listen + || Action == IIPPacketAction.Unlisten) { // nothing to do } diff --git a/Esiur/Net/Packets/Packet.cs b/Esiur/Net/Packets/Packet.cs index 2f61616..1d831ed 100644 --- a/Esiur/Net/Packets/Packet.cs +++ b/Esiur/Net/Packets/Packet.cs @@ -329,7 +329,7 @@ namespace Esiur.Net.Packets } */ - PosixTime Timeval; + //PosixTime Timeval; public byte[] Header; public byte[] Preamble; //public byte[] Payload; diff --git a/Esiur/Net/Sockets/SSLSocket.cs b/Esiur/Net/Sockets/SSLSocket.cs index ee1cf96..fc707ed 100644 --- a/Esiur/Net/Sockets/SSLSocket.cs +++ b/Esiur/Net/Sockets/SSLSocket.cs @@ -165,7 +165,7 @@ namespace Esiur.Net.Sockets { ssl.BeginWrite(kv.Value, 0, kv.Value.Length, SendCallback, kv.Key); } - catch (Exception ex) + catch //(Exception ex) { asyncSending = false; try @@ -179,7 +179,7 @@ namespace Esiur.Net.Sockets Close(); } } - catch (Exception ex2) + catch //(Exception ex2) { //state = SocketState.Closed;// .Terminated; Close(); @@ -439,7 +439,7 @@ namespace Esiur.Net.Sockets ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this); } - catch (Exception ex) + catch //(Exception ex) { if (state != SocketState.Closed && !sock.Connected) { diff --git a/Esiur/Net/Sockets/TCPSocket.cs b/Esiur/Net/Sockets/TCPSocket.cs index e049ea6..b6fa2d1 100644 --- a/Esiur/Net/Sockets/TCPSocket.cs +++ b/Esiur/Net/Sockets/TCPSocket.cs @@ -126,7 +126,7 @@ namespace Esiur.Net.Sockets return; } } - catch (Exception ex) + catch //(Exception ex) { if (socket.state != SocketState.Closed && !socket.sock.Connected) { @@ -500,7 +500,7 @@ namespace Esiur.Net.Sockets socket.Close(); } } - catch (Exception ex2) + catch //(Exception ex2) { socket.Close(); //socket.state = SocketState.Closed;// .Terminated; diff --git a/Esiur/Net/Sockets/WSSocket.cs b/Esiur/Net/Sockets/WSSocket.cs index 5f9cc32..dd122a1 100644 --- a/Esiur/Net/Sockets/WSSocket.cs +++ b/Esiur/Net/Sockets/WSSocket.cs @@ -118,6 +118,7 @@ namespace Esiur.Net.Sockets public void Send(byte[] message) { + lock (sendLock) { if (held) @@ -131,9 +132,8 @@ namespace Esiur.Net.Sockets pkt_send.Message = message; - if (pkt_send.Compose()) - sock.Send(pkt_send.Data); + sock?.Send(pkt_send.Data); } } @@ -229,8 +229,6 @@ namespace Esiur.Net.Sockets if (pkt_send.Compose()) sock.Send(pkt_send.Data); - - } } diff --git a/Esiur/Net/TCP/TCPFilter.cs b/Esiur/Net/TCP/TCPFilter.cs index c39ad7f..0e05f85 100644 --- a/Esiur/Net/TCP/TCPFilter.cs +++ b/Esiur/Net/TCP/TCPFilter.cs @@ -60,7 +60,7 @@ namespace Esiur.Net.TCP public void Destroy() { - throw new NotImplementedException(); + OnDestroy?.Invoke(this); } } } diff --git a/Esiur/Net/UDP/UDPFilter.cs b/Esiur/Net/UDP/UDPFilter.cs index 2ae4298..b3acde7 100644 --- a/Esiur/Net/UDP/UDPFilter.cs +++ b/Esiur/Net/UDP/UDPFilter.cs @@ -51,7 +51,7 @@ namespace Esiur.Net.UDP public void Destroy() { - throw new NotImplementedException(); + OnDestroy?.Invoke(this); } } } \ No newline at end of file diff --git a/Esiur/Proxy/GenerationInfo.cs b/Esiur/Proxy/GenerationInfo.cs new file mode 100644 index 0000000..8edc25c --- /dev/null +++ b/Esiur/Proxy/GenerationInfo.cs @@ -0,0 +1,20 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Proxy +{ + public struct GenerationInfo + { + public string Name { get; set; } + public bool ImplementInterface { get; set; } + + public bool ImplementTrigger { get; set; } + public IFieldSymbol[] Fields { get; set; } + public ITypeSymbol ClassSymbol { get; set; } + + public ClassDeclarationSyntax ClassDeclaration { get; set; } + } +} diff --git a/Esiur/Proxy/ResourceGenerator.cs b/Esiur/Proxy/ResourceGenerator.cs new file mode 100644 index 0000000..ae7b7a6 --- /dev/null +++ b/Esiur/Proxy/ResourceGenerator.cs @@ -0,0 +1,71 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Linq; + +namespace Esiur.Proxy +{ + [Generator] + public class ResourceGenerator : ISourceGenerator + { + public void Initialize(GeneratorInitializationContext context) + { + // Register receiver + context.RegisterForSyntaxNotifications(() => new ResourceGeneratorReceiver()); + } + + public void Execute(GeneratorExecutionContext context) + { + + if (!(context.SyntaxContextReceiver is ResourceGeneratorReceiver receiver)) + return; + +//#if DEBUG +// if (!Debugger.IsAttached) +// { +// Debugger.Launch(); +// } +//#endif + + //var toImplement = receiver.Classes.Where(x => x.Fields.Length > 0); + + foreach (var ci in receiver.Classes) + { + var code = @$"using Esiur.Resource; +using Esiur.Core; +namespace { ci.ClassSymbol.ContainingNamespace.ToDisplayString() } {{ +"; + + if (ci.ImplementInterface) + code += $"public partial class {ci.Name} {{"; + else + { + code += @$"public partial class {ci.Name} : IResource {{ +public Instance Instance {{ get; set; }} +public event DestroyedEvent OnDestroy; +public virtual void Destroy() {{ OnDestroy?.Invoke(this); }} +"; + + if (!ci.ImplementTrigger) + code += "public AsyncReply Trigger(ResourceTrigger trigger) => new AsyncReply(true);"; + } + + foreach (var f in ci.Fields) + { + var fn = f.Name; + var pn = fn.Substring(0, 1).ToUpper() + fn.Substring(1); + code += $@"[Public] public {f.Type} {pn} {{ get => {fn}; set {{ {fn} = value; Instance.Modified(); }} }}"; + } + + code += "}}"; + + //System.IO.File.WriteAllText("C:\\www\\class.cs", code); + context.AddSource(ci.Name + "_esiur.cs", code); + } + } + } +} diff --git a/Esiur/Proxy/ResourceGeneratorReceiver.cs b/Esiur/Proxy/ResourceGeneratorReceiver.cs new file mode 100644 index 0000000..a1d31e7 --- /dev/null +++ b/Esiur/Proxy/ResourceGeneratorReceiver.cs @@ -0,0 +1,61 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; + +namespace Esiur.Proxy +{ + public class ResourceGeneratorReceiver : ISyntaxContextReceiver + { + + public List Classes { get; } = new(); + + public void OnVisitSyntaxNode(GeneratorSyntaxContext context) + { + + if (context.Node is ClassDeclarationSyntax) + { + var cds = context.Node as ClassDeclarationSyntax; + var cls = context.SemanticModel.GetDeclaredSymbol(cds) as ITypeSymbol; + + if (cls.GetAttributes().Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.ResourceAttribute")) + { + //if (!Debugger.IsAttached) + //{ + // Debugger.Launch(); + //} + + var hasTrigger = cds.Members + .Where(x => x is MethodDeclarationSyntax) + .Select(x => context.SemanticModel.GetDeclaredSymbol(x) as IMethodSymbol) + .Any(x => x.Name == "Trigger" + && x.Parameters.Length == 1 + && x.Parameters[0].Type.ToDisplayString() == "Esiur.Resource.ResourceTrigger"); + + var fields = cds.Members.Where(x => x is FieldDeclarationSyntax) + .Select(x => context.SemanticModel.GetDeclaredSymbol((x as FieldDeclarationSyntax).Declaration.Variables.First()) as IFieldSymbol) + .Where(x => x.GetAttributes().Any(a => a.AttributeClass.ToDisplayString() == "Esiur.Resource.PublicAttribute")) + .ToArray(); + + + // get fields + Classes.Add(new GenerationInfo() + { + Name = cls.Name, + ClassDeclaration = cds, + ClassSymbol = cls, + Fields = fields, + ImplementInterface = cls.Interfaces.Any(x => x.ToDisplayString() == "Esiur.Resource.IResource"), + ImplementTrigger = hasTrigger + }); + + return; + } + } + } + } + +} diff --git a/Esiur/Proxy/ResourceProxy.cs b/Esiur/Proxy/ResourceProxy.cs index 260f8c2..fd3d319 100644 --- a/Esiur/Proxy/ResourceProxy.cs +++ b/Esiur/Proxy/ResourceProxy.cs @@ -11,7 +11,6 @@ namespace Esiur.Proxy public static class ResourceProxy { static Dictionary cache = new Dictionary(); - #if NETSTANDARD static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified"); @@ -34,14 +33,14 @@ namespace Esiur.Proxy else return type; - if (type.FullName.Contains("Esiur.Proxy.T")) -#if NETSTANDARD - return type.GetTypeInfo().BaseType; -#else - return type.BaseType; -#endif - else - return type; +// if (type.FullName.Contains("Esiur.Proxy.T")) +//#if NETSTANDARD +// return type.GetTypeInfo().BaseType; +//#else +// return type.BaseType; +//#endif +// else +// return type; } public static Type GetProxy(Type type) @@ -50,6 +49,13 @@ namespace Esiur.Proxy if (cache.ContainsKey(type)) return cache[type]; + // check if the type was made with code generation + if (type.GetCustomAttribute(false) != null) + { + cache.Add(type, type); + return type; + } + #if NETSTANDARD var typeInfo = type.GetTypeInfo(); diff --git a/Esiur/Resource/IResource.cs b/Esiur/Resource/IResource.cs index da8a0b8..9fbad4b 100644 --- a/Esiur/Resource/IResource.cs +++ b/Esiur/Resource/IResource.cs @@ -41,7 +41,6 @@ namespace Esiur.Resource { AsyncReply Trigger(ResourceTrigger trigger); - Instance Instance { get; diff --git a/Esiur/Resource/ListenableAttribute.cs b/Esiur/Resource/ListenableAttribute.cs new file mode 100644 index 0000000..3a80acc --- /dev/null +++ b/Esiur/Resource/ListenableAttribute.cs @@ -0,0 +1,42 @@ +/* + +Copyright (c) 2021 Ahmed Kh. Zamil + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Esiur.Resource +{ + + [AttributeUsage(AttributeTargets.Event)] + public class ListenableAttribute : System.Attribute + { + + public ListenableAttribute() + { + + } + } +} diff --git a/Esiur/Resource/PublicAttribute.cs b/Esiur/Resource/PublicAttribute.cs index d74cfdf..35c3c20 100644 --- a/Esiur/Resource/PublicAttribute.cs +++ b/Esiur/Resource/PublicAttribute.cs @@ -4,7 +4,7 @@ using System.Text; namespace Esiur.Resource { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Class)] public class PublicAttribute : Attribute { diff --git a/Esiur/Resource/Resource.cs b/Esiur/Resource/Resource.cs index 0201bff..d37d606 100644 --- a/Esiur/Resource/Resource.cs +++ b/Esiur/Resource/Resource.cs @@ -47,6 +47,7 @@ namespace Esiur.Resource return new AsyncReply(true); } + protected virtual bool Create() { return true; diff --git a/Esiur/Resource/ResourceAttribute.cs b/Esiur/Resource/ResourceAttribute.cs new file mode 100644 index 0000000..845bcc2 --- /dev/null +++ b/Esiur/Resource/ResourceAttribute.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Esiur.Resource +{ + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + public class ResourceAttribute : Attribute + { + public ResourceAttribute() + { + + } + } +} diff --git a/Esiur/Resource/Template/EventTemplate.cs b/Esiur/Resource/Template/EventTemplate.cs index a8ab2b5..544c8b7 100644 --- a/Esiur/Resource/Template/EventTemplate.cs +++ b/Esiur/Resource/Template/EventTemplate.cs @@ -16,6 +16,8 @@ namespace Esiur.Resource.Template set; } + public bool Listenable { get; set; } + public EventInfo Info { get; set; } public override byte[] Compose() @@ -24,9 +26,10 @@ namespace Esiur.Resource.Template if (Expansion != null) { + var exp = DC.ToBytes(Expansion); return new BinaryList() - .AddUInt8(0x50) + .AddUInt8(Listenable ? (byte) 0x58 : (byte) 0x50) .AddUInt8((byte)name.Length) .AddUInt8Array(name) .AddInt32(exp.Length) @@ -35,17 +38,18 @@ namespace Esiur.Resource.Template } else return new BinaryList() - .AddUInt8(0x40) + .AddUInt8(Listenable ? (byte) 0x48 : (byte) 0x40) .AddUInt8((byte)name.Length) .AddUInt8Array(name) .ToArray(); } - public EventTemplate(ResourceTemplate template, byte index, string name, string expansion = null) + public EventTemplate(ResourceTemplate template, byte index, string name, string expansion = null, bool listenable=false) :base(template, MemberType.Property, index, name) { this.Expansion = expansion; + this.Listenable = listenable; } } } diff --git a/Esiur/Resource/Template/ResourceTemplate.cs b/Esiur/Resource/Template/ResourceTemplate.cs index 758e281..c2b043b 100644 --- a/Esiur/Resource/Template/ResourceTemplate.cs +++ b/Esiur/Resource/Template/ResourceTemplate.cs @@ -176,7 +176,7 @@ namespace Esiur.Resource.Template var annotationAttr = pi.GetCustomAttribute(true); var storageAttr = pi.GetCustomAttribute(true); - var pt = new PropertyTemplate(this, i++, pi.Name);//, rp.ReadExpansion, rp.WriteExpansion, rp.Storage); + var pt = new PropertyTemplate(this, i++, pi.Name); if (storageAttr != null) pt.Recordable = storageAttr.Mode == StorageMode.Recordable; @@ -184,7 +184,7 @@ namespace Esiur.Resource.Template pt.ReadExpansion = annotationAttr.Annotation; else pt.ReadExpansion = pi.PropertyType.Name; - + pt.Info = pi; //pt.Serilize = publicAttr.Serialize; properties.Add(pt); @@ -209,6 +209,7 @@ namespace Esiur.Resource.Template if (privateAttr == null) { var annotationAttr = ei.GetCustomAttribute(true); + var listenableAttr = ei.GetCustomAttribute(true); var et = new EventTemplate(this, i++, ei.Name); et.Info = ei; @@ -216,6 +217,9 @@ namespace Esiur.Resource.Template if (annotationAttr != null) et.Expansion = annotationAttr.Annotation; + if (listenableAttr != null) + et.Listenable = true; + events.Add(et); } } @@ -283,6 +287,7 @@ namespace Esiur.Resource.Template if (publicAttr != null) { var annotationAttr = ei.GetCustomAttribute(true); + var listenableAttr = ei.GetCustomAttribute(true); var et = new EventTemplate(this, i++, ei.Name); et.Info = ei; @@ -290,6 +295,9 @@ namespace Esiur.Resource.Template if (annotationAttr != null) et.Expansion = annotationAttr.Annotation; + if (listenableAttr != null) + et.Listenable = true; + events.Add(et); } } @@ -340,6 +348,7 @@ namespace Esiur.Resource.Template b.AddUInt8Array(et.Compose()); content = b.ToArray(); + } public static ResourceTemplate Parse(byte[] data) @@ -436,7 +445,8 @@ namespace Esiur.Resource.Template { string expansion = null; - var hasExpansion = ((data[offset++] & 0x10) == 0x10); + var hasExpansion = ((data[offset] & 0x10) == 0x10); + var listenable = ((data[offset++] & 0x8) == 0x8); var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]); offset += (uint)data[offset] + 1; @@ -449,7 +459,7 @@ namespace Esiur.Resource.Template offset += cs; } - var et = new EventTemplate(od, eventIndex++, name, expansion); + var et = new EventTemplate(od, eventIndex++, name, expansion, listenable); od.events.Add(et); diff --git a/Esiur/Resource/Warehouse.cs b/Esiur/Resource/Warehouse.cs index c0628c3..006f370 100644 --- a/Esiur/Resource/Warehouse.cs +++ b/Esiur/Resource/Warehouse.cs @@ -57,11 +57,12 @@ namespace Esiur.Resource static bool warehouseIsOpen = false; - public delegate void StoreConnectedEvent(IStore store, string name); - public delegate void StoreDisconnectedEvent(IStore store); + public delegate void StoreEvent(IStore store);//, string name); + // public delegate void StoreDisconnectedEvent(IStore store); - public static event StoreConnectedEvent StoreConnected; - public static event StoreDisconnectedEvent StoreDisconnected; + public static event StoreEvent StoreConnected; + //public static event StoreEvent StoreOpen; + public static event StoreEvent StoreDisconnected; public delegate AsyncReply ProtocolInstance(string name, object properties); @@ -476,18 +477,43 @@ namespace Esiur.Resource } + + //public static async AsyncReply Push(string path, T resource) where T : IResource + //{ + // await Put(path, resource); + // return resource; + //} + /// /// Put a resource in the warehouse. /// - /// Resource instance. /// Resource name. + /// Resource instance. /// IStore that manages the resource. Can be null if the resource is a store. /// Parent resource. if not presented the store becomes the parent for the resource. - public static async AsyncReply Put(IResource resource, string name, IStore store = null, IResource parent = null, ResourceTemplate customTemplate = null, ulong age = 0, IPermissionsManager manager = null, object attributes = null) + public static async AsyncReply Put(string name, T resource, IStore store = null, IResource parent = null, ResourceTemplate customTemplate = null, ulong age = 0, IPermissionsManager manager = null, object attributes = null) where T:IResource { if (resource.Instance != null) throw new Exception("Resource has a store."); + var path = name.TrimStart('/').Split('/'); + + if (path.Length > 1) + { + if (parent != null) + throw new Exception("Parent can't be set when using path in instance name"); + + parent = await Warehouse.Get(string.Join("/", path.Take(path.Length - 1))); + + if (parent == null) + throw new Exception("Can't find parent"); + + store = store ?? parent.Instance.Store; + } + + var instanceName = path.Last(); + + var resourceReference = new WeakReference(resource); if (store == null) @@ -523,7 +549,7 @@ namespace Esiur.Resource throw new Exception("Can't find a store for the resource."); } - resource.Instance = new Instance(resourceCounter++, name, resource, store, customTemplate, age); + resource.Instance = new Instance(resourceCounter++, instanceName, resource, store, customTemplate, age); if (attributes != null) resource.Instance.SetAttributes(Structure.FromObject(attributes)); @@ -535,44 +561,54 @@ namespace Esiur.Resource parent = null; - - - if (resource is IStore) - stores.TryAdd(resource as IStore, new List>()); - - - if (!await store.Put(resource)) - return false; - if (parent != null) + try { - await parent.Instance.Store.AddChild(parent, resource); - await store.AddParent(resource, parent); - } - - var t = resource.GetType(); - Global.Counters["T-" + t.Namespace + "." + t.Name]++; - - - resources.TryAdd(resource.Instance.Id, resourceReference); - - if (warehouseIsOpen) - { - await resource.Trigger(ResourceTrigger.Initialize); if (resource is IStore) - await resource.Trigger(ResourceTrigger.Open); - } - - if (resource is IStore) - StoreConnected?.Invoke(resource as IStore, name); + stores.TryAdd(resource as IStore, new List>()); - return true; + + if (!await store.Put(resource)) + throw new Exception("Store failed to put the resource"); + //return default(T); + + + if (parent != null) + { + await parent.Instance.Store.AddChild(parent, resource); + await store.AddParent(resource, parent); + } + + var t = resource.GetType(); + Global.Counters["T-" + t.Namespace + "." + t.Name]++; + + + resources.TryAdd(resource.Instance.Id, resourceReference); + + if (warehouseIsOpen) + { + await resource.Trigger(ResourceTrigger.Initialize); + if (resource is IStore) + await resource.Trigger(ResourceTrigger.Open); + } + + if (resource is IStore) + StoreConnected?.Invoke(resource as IStore); + } + catch (Exception ex) + { + Warehouse.Remove(resource); + throw ex; + } + + return resource; } public static async AsyncReply New(Type type, string name = null, IStore store = null, IResource parent = null, IPermissionsManager manager = null, object attributes = null, object properties = null) { + type = ResourceProxy.GetProxy(type); @@ -618,15 +654,33 @@ namespace Esiur.Resource { var pi = type.GetProperty(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); - if (pi != null && pi.CanWrite) + if (pi != null) { - try + if (pi.CanWrite) { - pi.SetValue(res, p.Value); + try + { + pi.SetValue(res, p.Value); + } + catch (Exception ex) + { + Global.Log(ex); + } } - catch (Exception ex) + } + else + { + var fi = type.GetField(p.Key, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance); + if (fi != null) { - Global.Log(ex); + try + { + fi.SetValue(res, p.Value); + } + catch (Exception ex) + { + Global.Log(ex); + } } } } @@ -634,8 +688,10 @@ namespace Esiur.Resource if (store != null || parent != null || res is IStore) { - if (!await Put(res, name, store, parent, null, 0, manager, attributes)) - return null; + //if (!await Put(name, res, store, parent, null, 0, manager, attributes)) + // return null; + + await Put(name, res, store, parent, null, 0, manager, attributes); } return res; diff --git a/Esiur/Security/Membership/IMembership.cs b/Esiur/Security/Membership/IMembership.cs index 0fbd743..5f07ee0 100644 --- a/Esiur/Security/Membership/IMembership.cs +++ b/Esiur/Security/Membership/IMembership.cs @@ -35,11 +35,11 @@ using Esiur.Resource; namespace Esiur.Security.Membership { - public interface IMembership : IResource + public interface IMembership { AsyncReply UserExists(string username, string domain); AsyncReply GetPassword(string username, string domain); - AsyncReply GetToken(ulong TokenIndex, string domain); + AsyncReply GetToken(ulong tokenIndex, string domain); AsyncReply Login(Session session); AsyncReply Logout(Session session); diff --git a/Esiur/Security/Permissions/StorePermissionsManager.cs b/Esiur/Security/Permissions/StorePermissionsManager.cs index 01c6102..9a29156 100644 --- a/Esiur/Security/Permissions/StorePermissionsManager.cs +++ b/Esiur/Security/Permissions/StorePermissionsManager.cs @@ -46,6 +46,7 @@ namespace Esiur.Security.Permissions public bool Initialize(Structure settings, IResource resource) { + this.settings = settings; return true; } } diff --git a/Esiur/Stores/MemoryStore.cs b/Esiur/Stores/MemoryStore.cs index d17aa2a..631775b 100644 --- a/Esiur/Stores/MemoryStore.cs +++ b/Esiur/Stores/MemoryStore.cs @@ -20,7 +20,7 @@ namespace Esiur.Stores public void Destroy() { - + OnDestroy?.Invoke(this); } public string Link(IResource resource) @@ -41,14 +41,14 @@ namespace Esiur.Stores return new AsyncReply(null); } - public async AsyncReply Put(IResource resource) + public AsyncReply Put(IResource resource) { resources.Add(resource.Instance.Id, resource);// new WeakReference(resource)); resource.Instance.Variables["children"] = new AutoList(resource.Instance); resource.Instance.Variables["parents"] = new AutoList(resource.Instance); - return true; + return new AsyncReply(true); } public AsyncReply Retrieve(uint iid) diff --git a/Esiur/Stores/TemporaryStore.cs b/Esiur/Stores/TemporaryStore.cs index 8ff1757..c11ba98 100644 --- a/Esiur/Stores/TemporaryStore.cs +++ b/Esiur/Stores/TemporaryStore.cs @@ -20,6 +20,7 @@ namespace Esiur.Stores public void Destroy() { + OnDestroy?.Invoke(this); } @@ -31,19 +32,19 @@ namespace Esiur.Stores return null; } - public async AsyncReply Get(string path) + public AsyncReply Get(string path) { foreach (var r in resources) if (r.Value.IsAlive && (r.Value.Target as IResource).Instance.Name == path) - return r.Value.Target as IResource; + return new AsyncReply(r.Value.Target as IResource); - return null; + return new AsyncReply(null); } - public async AsyncReply Put(IResource resource) + public AsyncReply Put(IResource resource) { resources.Add(resource.Instance.Id, new WeakReference( resource));// new WeakReference(resource)); - return true; + return new AsyncReply(true); } public AsyncReply Retrieve(uint iid)