2
0
mirror of https://github.com/esiur/esiur-dotnet.git synced 2025-06-26 13:03:13 +00:00
This commit is contained in:
2021-05-14 18:24:34 +03:00
parent 0af14009be
commit 5bf258673d
48 changed files with 1032 additions and 383 deletions

View File

@ -40,7 +40,7 @@ namespace Esiur.Stores.EntityCore
//internal object _PrimaryId; //internal object _PrimaryId;
public event DestroyedEvent OnDestroy; public event DestroyedEvent OnDestroy;
public event PropertyChangedEventHandler PropertyChanged; //public event PropertyChangedEventHandler PropertyChanged;
[NotMapped] [NotMapped]
public Instance Instance { get; set; } public Instance Instance { get; set; }
@ -66,7 +66,7 @@ namespace Esiur.Stores.EntityCore
public void Destroy() public void Destroy()
{ {
//throw new NotImplementedException(); OnDestroy?.Invoke(this);
} }

View File

@ -45,31 +45,25 @@ namespace Esiur.Stores.EntityCore
Dictionary<Type, Dictionary<object, WeakReference>> DB = new Dictionary<Type, Dictionary<object, WeakReference>>(); Dictionary<Type, Dictionary<object, WeakReference>> DB = new Dictionary<Type, Dictionary<object, WeakReference>>();
object DBLock = new object(); object DBLock = new object();
internal struct TypeInfo Dictionary<string, EntityTypeInfo> TypesByName = new Dictionary<string, EntityTypeInfo>();
{ internal Dictionary<Type, EntityTypeInfo> TypesByType = new Dictionary<Type, EntityTypeInfo>();
public string Name;
public IEntityType Type;
public PropertyInfo PrimaryKey;
}
Dictionary<string, TypeInfo> TypesByName = new Dictionary<string, TypeInfo>(); [Attribute]
internal Dictionary<Type, TypeInfo> TypesByType = new Dictionary<Type, TypeInfo>(); public Func<DbContext> Getter { get; set; }
bool Loaded;
public AsyncReply<IResource> Get(string path) public AsyncReply<IResource> Get(string path)
{ {
var p = path.Split('/'); var p = path.Split('/');
var ti = TypesByName[p[0]]; 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);
// Get db
var db = DbContextProvider(); var db = Getter();
var res = db.Find(ti.Type.ClrType, id); 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) foreach (var rf in ent.References)
rf.Load(); rf.Load();
@ -78,12 +72,11 @@ namespace Esiur.Stores.EntityCore
public AsyncReply<bool> Put(IResource resource) public AsyncReply<bool> Put(IResource resource)
{ {
if (resource is EntityStore) if (resource == this)
return new AsyncReply<bool>(true); return new AsyncReply<bool>(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); var eid = TypesByType[type].PrimaryKey.GetValue(resource);
@ -112,11 +105,12 @@ namespace Esiur.Stores.EntityCore
} }
} }
[Attribute]
public Func<DbContext> DbContextProvider { get; set; }
[Attribute]
public DbContextOptionsBuilder Options { get; set; } //public T CreateDB()
//{
//}
//DbContext dbContext; //DbContext dbContext;
//[Attribute] //[Attribute]
@ -195,13 +189,17 @@ namespace Esiur.Stores.EntityCore
throw new NotImplementedException(); throw new NotImplementedException();
} }
internal DbContextOptions Options { get; set; }
public AsyncReply<bool> Trigger(ResourceTrigger trigger) public AsyncReply<bool> Trigger(ResourceTrigger trigger)
{ {
if (trigger == ResourceTrigger.Initialize)// SystemInitialized && DbContext != null) if (trigger == ResourceTrigger.Initialize)// SystemInitialized && DbContext != null)
{ {
if (DbContextProvider == null) if (Getter == null)
DbContextProvider = () => Activator.CreateInstance(Options.Options.ContextType, Options.Options) as DbContext; throw new Exception("Getter is not set for the store.");
// DbContextProvider = () => Activator.CreateInstance(Options.Options.ContextType, Options.Options) as DbContext;
ReloadModel(); ReloadModel();
} }
@ -209,21 +207,23 @@ namespace Esiur.Stores.EntityCore
return new AsyncReply<bool>(true); return new AsyncReply<bool>(true);
} }
public void ReloadModel() void ReloadModel()
{ {
TypesByName.Clear(); TypesByName.Clear();
TypesByType.Clear(); TypesByType.Clear();
var context = DbContextProvider();// Activator.CreateInstance(Options.Options.ContextType, Options.Options) as DbContext; var context = Getter();
var types = context.Model.GetEntityTypes(); var types = context.Model.GetEntityTypes();
foreach (var t in types) foreach (var t in types)
{ {
var ti = new TypeInfo() var ti = new EntityTypeInfo()
{ {
Name = t.ClrType.Name, Name = t.ClrType.Name,
PrimaryKey = t.FindPrimaryKey().Properties.FirstOrDefault()?.PropertyInfo, PrimaryKey = t.FindPrimaryKey().Properties.FirstOrDefault()?.PropertyInfo,
Type = t Type = t,
//Getter = getter
}; };
TypesByName.Add(t.ClrType.Name, ti); TypesByName.Add(t.ClrType.Name, ti);
@ -236,7 +236,7 @@ namespace Esiur.Stores.EntityCore
public void Destroy() public void Destroy()
{ {
//throw new NotImplementedException(); OnDestroy?.Invoke(this);
} }
} }
} }

View File

@ -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<DbContext> Getter;
}
}

View File

@ -9,10 +9,11 @@
<Product>Esiur Entity Framework Extension</Product> <Product>Esiur Entity Framework Extension</Product>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>Esiur.Stores.EntityCore</PackageId> <PackageId>Esiur.Stores.EntityCore</PackageId>
<Version>1.0.2</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.6" />
<PackageReference Include="System.Collections" Version="4.3.0" /> <PackageReference Include="System.Collections" Version="4.3.0" />
</ItemGroup> </ItemGroup>

View File

@ -34,10 +34,11 @@ using System.Linq;
using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata;
using System.Reflection; using System.Reflection;
using Esiur.Proxy; using Esiur.Proxy;
using Microsoft.EntityFrameworkCore;
namespace Esiur.Stores.EntityCore namespace Esiur.Stores.EntityCore
{ {
public class EsiurExtensionOptions : IDbContextOptionsExtension public class EsiurExtensionOptions : IDbContextOptionsExtension
{ {
//public Dictionary<Type, PropertyInfo> Cache { get; } = new Dictionary<Type, PropertyInfo>(); //public Dictionary<Type, PropertyInfo> Cache { get; } = new Dictionary<Type, PropertyInfo>();

View File

@ -23,6 +23,9 @@ SOFTWARE.
*/ */
using Esiur.Core; using Esiur.Core;
using Esiur.Data;
using Esiur.Misc;
using Esiur.Proxy;
using Esiur.Resource; using Esiur.Resource;
using Esiur.Security.Permissions; using Esiur.Security.Permissions;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -31,6 +34,7 @@ using Microsoft.Extensions.DependencyInjection;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
namespace Esiur.Stores.EntityCore namespace Esiur.Stores.EntityCore
@ -43,49 +47,108 @@ namespace Esiur.Stores.EntityCore
//} //}
public static T AddResource<T>(this DbSet<T> dbSet, object properties = null) where T : class, IResource public static T AddResource<T>(this DbSet<T> dbSet, T resource) where T : class, IResource
=> AddResourceAsync(dbSet, properties).Wait(); => AddResourceAsync(dbSet, resource).Wait();
public static async AsyncReply<T> AddResourceAsync<T>(this DbSet<T> dbSet, object properties = null) where T : class, IResource public static async AsyncReply<T> AddResourceAsync<T>(this DbSet<T> dbSet, T resource) where T : class, IResource
{ {
var store = dbSet.GetInfrastructure().GetService<IDbContextOptions>().FindExtension<EsiurExtensionOptions>().Store; var store = dbSet.GetInfrastructure().GetService<IDbContextOptions>().FindExtension<EsiurExtensionOptions>().Store;
var manager = store.Instance.Managers.FirstOrDefault();// > 0 ? store.Instance.Managers.First() : null; var manager = store.Instance.Managers.FirstOrDefault();// > 0 ? store.Instance.Managers.First() : null;
//var db = dbSet.GetService<ICurrentDbContext>().Context; //var db = dbSet.GetService<ICurrentDbContext>().Context;
//var resource = dbSet.GetInfrastructure().CreateResource<T>(properties); //var resource = dbSet.GetInfrastructure().CreateResource<T>(properties);
//var resource = Warehouse.New<T>("", options.Store, null, null, null, properties); //var resource = Warehouse.New<T>("", options.Store, null, null, null, properties);
var resource = await Warehouse.New<T>("", 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<T>("", null, null, null, null, properties);
var entity = dbSet.Add((T)res);
await entity.Context.SaveChangesAsync(); await entity.Context.SaveChangesAsync();
var id = store.TypesByType[typeof(T)].PrimaryKey.GetValue(resource); 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; return resource;
} }
public static async AsyncReply<T> CreateResourceAsync<T>(this IServiceProvider serviceProvider, object properties = null) where T : class, IResource //public static async AsyncReply<T> CreateResourceAsync<T>(this IServiceProvider serviceProvider, T properties = null) where T : class, IResource
{ //{
var options = serviceProvider.GetService<IDbContextOptions>().FindExtension<EsiurExtensionOptions>(); // var options = serviceProvider.GetService<IDbContextOptions>().FindExtension<EsiurExtensionOptions<T>>();
var resource = await Warehouse.New<T>("", options.Store, null, null, null, properties); // var resource = await Warehouse.New<T>("", 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<T>(this IServiceProvider serviceProvider, object properties = null) where T : class, IResource //public static T CreateResource<T>(this IServiceProvider serviceProvider, object properties = null) where T : class, IResource
=> CreateResourceAsync<T>(serviceProvider, properties).Wait(); // => CreateResourceAsync<T>(serviceProvider, properties).Wait();
public static DbContextOptionsBuilder UseEsiur(this DbContextOptionsBuilder optionsBuilder, public static DbContextOptionsBuilder UseEsiur(this DbContextOptionsBuilder optionsBuilder,
//DbContext context, EntityStore store,
string name = null, Func<DbContext> getter = null
IResource parent = null,
IPermissionsManager manager = null, //IServiceCollection services = null
Func<DbContext> dbContextProvider = null //string name = null,
//IResource parent = null,
//IPermissionsManager manager = null,
//Func<DbContext> dbContextProvider = null
) )
{ {
var extension = optionsBuilder.Options.FindExtension<EsiurExtensionOptions>(); var extension = optionsBuilder.Options.FindExtension<EsiurExtensionOptions>();
@ -93,10 +156,9 @@ namespace Esiur.Stores.EntityCore
if (extension == null) if (extension == null)
{ {
var store = Warehouse.New<EntityStore>(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait(); //var store = Warehouse.New<EntityStore>(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait();
store.Options = optionsBuilder.Options;
extension = new EsiurExtensionOptions(store); extension = new EsiurExtensionOptions(store);
//store.Options = optionsBuilder;
//store.DbContext = context;
} }
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
@ -105,34 +167,34 @@ namespace Esiur.Stores.EntityCore
} }
public static DbContextOptionsBuilder<TContext> UseEsiur<TContext>( //public static DbContextOptionsBuilder<TContext> UseEsiur<TContext>(
this DbContextOptionsBuilder<TContext> optionsBuilder, // this DbContextOptionsBuilder<TContext> optionsBuilder,
//DbContext context, // //DbContext context,
string name = null, // string name = null,
IResource parent = null, // IResource parent = null,
IPermissionsManager manager = null, // IPermissionsManager manager = null,
Func<DbContext> dbContextProvider = null) // Func<DbContext> dbContextProvider = null)
where TContext : DbContext // where TContext : DbContext
{ //{
var extension = optionsBuilder.Options.FindExtension<EsiurExtensionOptions>(); // var extension = optionsBuilder.Options.FindExtension<EsiurExtensionOptions>();
if (extension == null) // if (extension == null)
{ // {
var store = Warehouse.New<EntityStore>(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait(); // var store = Warehouse.New<EntityStore>(name, null, parent, manager, new { Options = optionsBuilder, DbContextProvider = dbContextProvider }).Wait();
extension = new EsiurExtensionOptions(store); // extension = new EsiurExtensionOptions(store);
//store.Options = optionsBuilder; // //store.Options = optionsBuilder;
//store.Options = extension; // //store.Options = extension;
//store.DbContext = context; // //store.DbContext = context;
} // }
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); // ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
return optionsBuilder; // return optionsBuilder;
} //}
} }
} }

View File

@ -40,7 +40,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace Esiur.Stores.EntityCore namespace Esiur.Stores.EntityCore
{ {
public class EsiurProxyRewrite : IModelFinalizingConvention public class EsiurProxyRewrite : IModelFinalizingConvention
{ {
private static readonly MethodInfo _createInstance private static readonly MethodInfo _createInstance
= typeof(EsiurProxyRewrite).GetTypeInfo().GetDeclaredMethod(nameof(EsiurProxyRewrite.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); var obj = Warehouse.New(entityType.ClrType).Wait() as EntityResource;//, "", options.Store, null, manager);
//obj._PrimaryId = id; //obj._PrimaryId = id;
options.Store.TypesByType[entityType.ClrType].PrimaryKey.SetValue(obj, 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); // obj.Instance.IntVal = id;//.Variables.Add("eid", id);

View File

@ -11,7 +11,7 @@
<PackageProjectUrl>http://www.esiur.com</PackageProjectUrl> <PackageProjectUrl>http://www.esiur.com</PackageProjectUrl>
<RepositoryUrl>https://github.com/esiur/esiur-dotnet/</RepositoryUrl> <RepositoryUrl>https://github.com/esiur/esiur-dotnet/</RepositoryUrl>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Version>1.4.0</Version> <Version>1.4.1</Version>
<PackageId>Esiur.Stores.MongoDB</PackageId> <PackageId>Esiur.Stores.MongoDB</PackageId>
</PropertyGroup> </PropertyGroup>

View File

@ -32,7 +32,7 @@ namespace Esiur.Core
public T GetResult() public T GetResult()
{ {
if (exception != null) if (exception != null)
throw exception; throw exception;
return result; return result;
} }

View File

@ -192,7 +192,7 @@ namespace Esiur.Core
// } // }
} }
public void Trigger(object result) public AsyncReply Trigger(object result)
{ {
lock (asyncLock) lock (asyncLock)
{ {
@ -202,7 +202,7 @@ namespace Esiur.Core
Console.WriteLine($"AsyncReply: {Id} Trigger"); Console.WriteLine($"AsyncReply: {Id} Trigger");
if (resultReady) if (resultReady)
return; return this;
this.result = result; this.result = result;
@ -219,14 +219,16 @@ namespace Esiur.Core
Console.WriteLine($"AsyncReply: {Id} Trigger ended"); Console.WriteLine($"AsyncReply: {Id} Trigger ended");
} }
return this;
} }
public void TriggerError(Exception exception) public AsyncReply TriggerError(Exception exception)
{ {
//timeout?.Dispose(); //timeout?.Dispose();
if (resultReady) if (resultReady)
return; return this;
if (exception is AsyncException) if (exception is AsyncException)
this.exception = exception as AsyncException; this.exception = exception as AsyncException;
@ -242,9 +244,10 @@ namespace Esiur.Core
mutex?.Set(); mutex?.Set();
return this;
} }
public void TriggerProgress(ProgressType type, int value, int max) public AsyncReply TriggerProgress(ProgressType type, int value, int max)
{ {
//timeout?.Dispose(); //timeout?.Dispose();
@ -254,10 +257,12 @@ namespace Esiur.Core
cb(type, value, max); cb(type, value, max);
//} //}
return this;
} }
public void TriggerChunk(object value) public AsyncReply TriggerChunk(object value)
{ {
//timeout?.Dispose(); //timeout?.Dispose();
@ -269,6 +274,8 @@ namespace Esiur.Core
cb(value); cb(value);
//} //}
return this;
} }
public AsyncAwaiter GetAwaiter() public AsyncAwaiter GetAwaiter()

View File

@ -36,6 +36,10 @@ namespace Esiur.Core
SetPropertyDenied, SetPropertyDenied,
ReadOnlyProperty, ReadOnlyProperty,
GeneralFailure, GeneralFailure,
AddToStoreFailed AddToStoreFailed,
NotAttached,
AlreadyListened,
AlreadyUnlistened,
NotListenable
} }
} }

View File

@ -1,4 +1,5 @@
using Esiur.Resource; using Esiur.Net.IIP;
using Esiur.Resource;
/* /*
Copyright (c) 2017-2021 Ahmed Kh. Zamil Copyright (c) 2017-2021 Ahmed Kh. Zamil
@ -53,8 +54,15 @@ namespace Esiur.Data
foreach (var pt in resource.Instance.Template.Properties) foreach (var pt in resource.Instance.Template.Properties)
{ {
var rt = pt.Info.GetValue(resource, null); var rt = pt.Info.GetValue(resource, null);
if (rt is DistributedPropertyContext)
continue;
writer.WritePropertyName(options.PropertyNamingPolicy?.ConvertName(pt.Name) ?? pt.Name); 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(); writer.WriteEndObject();

View File

@ -110,6 +110,10 @@ namespace Esiur.Data
foreach (var p in pi) foreach (var p in pi)
st[p.Name] = p.GetValue(obj); 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; return st;
} }
//else //else

View File

@ -1,22 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<Description>Distributed Resources Platform</Description> <Description>Distributed Resources Platform</Description>
<Copyright>Ahmed Kh. Zamil</Copyright> <Copyright>Ahmed Kh. Zamil</Copyright>
<PackageLicenseUrl>https://github.com/Esiur/Esiur-dotnet/blob/master/LICENSE</PackageLicenseUrl> <PackageLicenseUrl>https://github.com/Esiur/Esiur-dotnet/blob/master/LICENSE</PackageLicenseUrl>
<PackageProjectUrl>http://www.esiur.com</PackageProjectUrl> <PackageProjectUrl>http://www.esiur.com</PackageProjectUrl>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.5.1</Version> <Version>1.6.1</Version>
<RepositoryUrl>https://github.com/esiur/esiur-dotnet</RepositoryUrl> <RepositoryUrl>https://github.com/esiur/esiur-dotnet</RepositoryUrl>
<Authors>Ahmed Kh. Zamil</Authors> <Authors>Ahmed Kh. Zamil</Authors>
<AssemblyVersion>1.3.1.0</AssemblyVersion> <AssemblyVersion>1.6.0</AssemblyVersion>
<Company>Esiur Foundation</Company> <Company>Esiur Foundation</Company>
<FileVersion>1.3.1.0</FileVersion> <FileVersion>1.6.0</FileVersion>
<AssemblyName>Esiur</AssemblyName> <AssemblyName>Esiur</AssemblyName>
<RootNamespace>Esiur</RootNamespace> <RootNamespace>Esiur</RootNamespace>
<PackageId>Esiur</PackageId> <PackageId>Esiur</PackageId>
<Product>Esiur</Product> <Product>Esiur</Product>
<LangVersion>9.0</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
@ -55,6 +56,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="3.9.0" />
<PackageReference Include="System.Diagnostics.StackTrace" Version="4.3.0" /> <PackageReference Include="System.Diagnostics.StackTrace" Version="4.3.0" />
<PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" /> <PackageReference Include="System.Dynamic.Runtime" Version="4.3.0" />
<PackageReference Include="System.Interactive.Async" Version="5.0.0" /> <PackageReference Include="System.Interactive.Async" Version="5.0.0" />
@ -62,8 +64,12 @@
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" /> <PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Include="System.Net.Security" Version="4.3.2" /> <PackageReference Include="System.Net.Security" Version="4.3.2" />
<PackageReference Include="System.Reflection.Emit" Version="4.7.0" /> <PackageReference Include="System.Reflection.Emit" Version="4.7.0" />
<PackageReference Include="System.Text.Json" Version="5.0.1" /> <PackageReference Include="System.Text.Json" Version="5.0.2" />
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" /> <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.9.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -69,7 +69,7 @@ namespace Esiur.Misc
}catch (Exception ex) }catch (Exception ex)
{ {
Console.WriteLine(ex.ToString()); 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); rand.NextBytes(b);
return b; return b;
} }

View File

@ -50,7 +50,7 @@ namespace Esiur.Net.DataLink
public void Destroy() public void Destroy()
{ {
OnDestroy?.Invoke(this);
} }
} }
} }

View File

@ -65,7 +65,7 @@ namespace Esiur.Net.DataLink
public void Destroy() public void Destroy()
{ {
throw new NotImplementedException(); OnDestroy?.Invoke(this);
} }
public AsyncReply<bool> Trigger(ResourceTrigger trigger) public AsyncReply<bool> Trigger(ResourceTrigger trigger)

View File

@ -69,7 +69,7 @@ namespace Esiur.Net.DataLink
public void Destroy() public void Destroy()
{ {
throw new NotImplementedException(); OnDestroy?.Invoke(this);
} }
/* /*

View File

@ -76,7 +76,7 @@ namespace Esiur.Net.HTTP
public void Destroy() public void Destroy()
{ {
throw new NotImplementedException(); OnDestroy?.Invoke(this);
} }
} }
} }

View File

@ -112,7 +112,7 @@ namespace Esiur.Net.HTTP
private void IipConnection_OnReady(DistributedConnection sender) 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<bool> Trigger(ResourceTrigger trigger) public override AsyncReply<bool> Trigger(ResourceTrigger trigger)

View File

@ -197,11 +197,20 @@ namespace Esiur.Net.IIP
if (socket.State == SocketState.Established && if (socket.State == SocketState.Established &&
session.LocalAuthentication.Type == AuthenticationType.Client) 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) // declare (Credentials -> No Auth, No Enctypt)
var un = DC.ToBytes(session.LocalAuthentication.Username); var un = DC.ToBytes(session.LocalAuthentication.Username);
var dmn = DC.ToBytes(session.LocalAuthentication.Domain);// domain);
SendParams() SendParams()
.AddUInt8(0x60) .AddUInt8(0x60)
@ -212,8 +221,27 @@ namespace Esiur.Net.IIP
.AddUInt8Array(un) .AddUInt8Array(un)
.Done();//, dmn, localNonce, (byte)un.Length, 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
}
}
/// <summary> /// <summary>
/// Create a new distributed connection. /// Create a new distributed connection.
@ -451,12 +479,21 @@ namespace Esiur.Net.IIP
IIPRequestInvokeFunctionNamedArguments(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content); IIPRequestInvokeFunctionNamedArguments(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content);
break; break;
case IIPPacket.IIPPacketAction.GetProperty: //case IIPPacket.IIPPacketAction.GetProperty:
IIPRequestGetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex); // 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; 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; break;
case IIPPacket.IIPPacketAction.SetProperty: case IIPPacket.IIPPacketAction.SetProperty:
IIPRequestSetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content); IIPRequestSetProperty(packet.CallbackId, packet.ResourceId, packet.MethodIndex, packet.Content);
break; break;
@ -531,14 +568,17 @@ namespace Esiur.Net.IIP
IIPReplyInvoke(packet.CallbackId, packet.Content); IIPReplyInvoke(packet.CallbackId, packet.Content);
break; break;
case IIPPacket.IIPPacketAction.GetProperty: //case IIPPacket.IIPPacketAction.GetProperty:
IIPReply(packet.CallbackId, packet.Content); // IIPReply(packet.CallbackId, packet.Content);
break; // break;
case IIPPacket.IIPPacketAction.GetPropertyIfModified: //case IIPPacket.IIPPacketAction.GetPropertyIfModified:
IIPReply(packet.CallbackId, packet.Content); // IIPReply(packet.CallbackId, packet.Content);
break; // break;
case IIPPacket.IIPPacketAction.SetProperty:
case IIPPacketAction.Listen:
case IIPPacketAction.Unlisten:
case IIPPacketAction.SetProperty:
IIPReply(packet.CallbackId); IIPReply(packet.CallbackId);
break; break;
@ -753,24 +793,34 @@ namespace Esiur.Net.IIP
session.Id = new byte[32]; session.Id = new byte[32];
r.NextBytes(session.Id); r.NextBytes(session.Id);
//SendParams((byte)0x28, session.Id); //SendParams((byte)0x28, session.Id);
SendParams() SendParams().AddUInt8(0x28)
.AddUInt8(0x28) .AddUInt8Array(session.Id)
.AddUInt8Array(session.Id) .Done();
.Done();
ready = true; if (this.Instance == null)
Warehouse.Put(this, this.LocalUsername, null, Server).Then(x =>
{ {
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); openReply?.Trigger(true);
OnReady?.Invoke(this); OnReady?.Invoke(this);
Server?.Membership.Login(session); Server?.Membership.Login(session);
}
}).Error(x=>
{
openReply?.TriggerError(x);
});
//Global.Log("auth", LogType.Warning, "U:" + RemoteUsername + " IP:" + Socket.RemoteEndPoint.Address.ToString() + " S:AUTH"); //Global.Log("auth", LogType.Warning, "U:" + RemoteUsername + " IP:" + Socket.RemoteEndPoint.Address.ToString() + " S:AUTH");
@ -841,13 +891,21 @@ namespace Esiur.Net.IIP
ready = true; ready = true;
// put it in the warehouse // 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); openReply?.Trigger(true);
OnReady?.Invoke(this); OnReady?.Invoke(this);
}
}).Error(x=> openReply?.TriggerError(x));
} }
} }
else if (authPacket.Command == IIPAuthPacket.IIPAuthPacketCommand.Error) 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<bool> 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) public AsyncReply<bool> 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() protected override void Connected()
{ {
if (session.LocalAuthentication.Type == AuthenticationType.Client) if (session.LocalAuthentication.Type == AuthenticationType.Client)
{ Declare();
// 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();
}
} }
protected override void Disconencted() 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) if (ready)
{
Server?.Membership.Logout(session); Server?.Membership.Logout(session);
Warehouse.Remove(this);
ready = false; ready = false;
UnsubscribeAll();
}
} }
/* /*

View File

@ -36,6 +36,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
namespace Esiur.Net.IIP namespace Esiur.Net.IIP
{ {
@ -54,7 +55,9 @@ namespace Esiur.Net.IIP
volatile uint callbackCounter = 0; volatile uint callbackCounter = 0;
List<IResource> subscriptions = new List<IResource>(); //List<IResource> subscriptions = new List<IResource>();
Dictionary<IResource, List<byte>> subscriptions = new Dictionary<IResource, List<byte>>();// new List<IResource>();
object subscriptionsLock = new object(); object subscriptionsLock = new object();
AsyncQueue<DistributedResourceQueueItem> queue = new AsyncQueue<DistributedResourceQueueItem>(); AsyncQueue<DistributedResourceQueueItem> queue = new AsyncQueue<DistributedResourceQueueItem>();
@ -108,13 +111,40 @@ namespace Esiur.Net.IIP
internal SendList SendEvent(IIPPacket.IIPPacketEvent evt) internal SendList SendEvent(IIPPacket.IIPPacketEvent evt)
{ {
//var bl = new BinaryList((byte)(evt));
//bl.AddRange(args);
//Send(bl.ToArray());
return (SendList)SendParams().AddUInt8((byte)(evt)); return (SendList)SendParams().AddUInt8((byte)(evt));
} }
internal AsyncReply SendListenRequest(uint instanceId, byte index)
{
var reply = new AsyncReply<object>();
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<object>();
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<object> SendInvokeByArrayArguments(uint instanceId, byte index, object[] parameters) internal AsyncReply<object> SendInvokeByArrayArguments(uint instanceId, byte index, object[] parameters)
{ {
var pb = Codec.ComposeVarArray(parameters, this, true); var pb = Codec.ComposeVarArray(parameters, this, true);
@ -593,6 +623,7 @@ namespace Esiur.Net.IIP
var r = res as IResource; var r = res as IResource;
// unsubscribe // unsubscribe
Unsubscribe(r); Unsubscribe(r);
Subscribe(r);
//r.Instance.ResourceEventOccurred -= Instance_EventOccurred; //r.Instance.ResourceEventOccurred -= Instance_EventOccurred;
//r.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred; //r.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred;
@ -610,7 +641,6 @@ namespace Esiur.Net.IIP
//r.Instance.ResourceModified += Instance_PropertyModified; //r.Instance.ResourceModified += Instance_PropertyModified;
//r.Instance.ResourceDestroyed += Instance_ResourceDestroyed; //r.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
Subscribe(r);
//r.Instance.Children.OnAdd += Children_OnAdd; //r.Instance.Children.OnAdd += Children_OnAdd;
//r.Instance.Children.OnRemoved += Children_OnRemoved; //r.Instance.Children.OnRemoved += Children_OnRemoved;
@ -770,7 +800,7 @@ namespace Esiur.Net.IIP
// create the resource // create the resource
var resource = Activator.CreateInstance(type, args) as IResource; 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) SendReply(IIPPacket.IIPPacketAction.CreateResource, callback)
.AddUInt32(resource.Instance.Id) .AddUInt32(resource.Instance.Id)
@ -1126,7 +1156,7 @@ namespace Esiur.Net.IIP
if (Server?.EntryPoint != null) if (Server?.EntryPoint != null)
Server.EntryPoint.Query(resourceLink, this).Then(queryCallback); Server.EntryPoint.Query(resourceLink, this).Then(queryCallback);
else else
Warehouse.Query(resourceLink).Then(x => queryCallback(x)); Warehouse.Query(resourceLink).Then(queryCallback);
} }
void IIPRequestResourceAttribute(uint callback, uint resourceId) 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) => Warehouse.GetById(resourceId).Then((r) =>
{ {
if (r != null) if (r != null)
{ {
var pt = r.Instance.Template.GetFunctionTemplateByIndex(index); var et = r.Instance.Template.GetEventTemplateByIndex(index);
if (pt != null)
if (et != null)
{ {
if (r is DistributedResource) if (r is DistributedResource)
{ {
SendReply(IIPPacket.IIPPacketAction.GetProperty, callback) (r as DistributedResource).Listen(et).Then(x =>
.AddUInt8Array(Codec.Compose((r as DistributedResource)._Get(pt.Index), this)) {
.Done(); SendReply(IIPPacket.IIPPacketAction.Listen, callback).Done();
}).Error(x => SendError(ErrorType.Exception, callback, (ushort)ExceptionCode.GeneralFailure));
} }
else else
{ {
#if NETSTANDARD lock(subscriptionsLock)
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); {
#else if (!subscriptions.ContainsKey(r))
var pi = r.GetType().GetProperty(pt.Name); {
#endif SendError(ErrorType.Management, callback, (ushort)ExceptionCode.NotAttached);
return;
}
if (pi != null) if (subscriptions[r].Contains(index))
{ {
SendReply(IIPPacket.IIPPacketAction.GetProperty, callback) SendError(ErrorType.Management, callback, (ushort)ExceptionCode.AlreadyListened);
.AddUInt8Array(Codec.Compose(pi.GetValue(r), this)) return;
.Done(); }
}
else subscriptions[r].Add(index);
{
// pt found, pi not found, this should never happen SendReply(IIPPacket.IIPPacketAction.Listen, callback).Done();
} }
} }
} }
else else
{ {
// pt not found // pt not found
SendError(ErrorType.Management, callback, (ushort)ExceptionCode.MethodNotFound);
} }
} }
else else
{ {
// resource not found // 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) void IIPRequestInquireResourceHistory(uint callback, uint resourceId, DateTime fromDate, DateTime toDate)
{ {
Warehouse.GetById(resourceId).Then((r) => Warehouse.GetById(resourceId).Then((r) =>
@ -1552,51 +1690,51 @@ namespace Esiur.Net.IIP
}); });
} }
void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, ulong age) // void IIPRequestGetPropertyIfModifiedSince(uint callback, uint resourceId, byte index, ulong age)
{ // {
Warehouse.GetById(resourceId).Then((r) => // Warehouse.GetById(resourceId).Then((r) =>
{ // {
if (r != null) // if (r != null)
{ // {
var pt = r.Instance.Template.GetFunctionTemplateByIndex(index); // var pt = r.Instance.Template.GetFunctionTemplateByIndex(index);
if (pt != null) // if (pt != null)
{ // {
if (r.Instance.GetAge(index) > age) // if (r.Instance.GetAge(index) > age)
{ // {
#if NETSTANDARD //#if NETSTANDARD
var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); // var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name);
#else //#else
var pi = r.GetType().GetProperty(pt.Name); // var pi = r.GetType().GetProperty(pt.Name);
#endif //#endif
if (pi != null) // if (pi != null)
{ // {
SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) // SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback)
.AddUInt8Array(Codec.Compose(pi.GetValue(r), this)) // .AddUInt8Array(Codec.Compose(pi.GetValue(r), this))
.Done(); // .Done();
} // }
else // else
{ // {
// pt found, pi not found, this should never happen // // pt found, pi not found, this should never happen
} // }
} // }
else // else
{ // {
SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback) // SendReply(IIPPacket.IIPPacketAction.GetPropertyIfModified, callback)
.AddUInt8((byte)DataType.NotModified) // .AddUInt8((byte)DataType.NotModified)
.Done(); // .Done();
} // }
} // }
else // else
{ // {
// pt not found // // pt not found
} // }
} // }
else // else
{ // {
// resource not found // // resource not found
} // }
}); // });
} // }
void IIPRequestSetProperty(uint callback, uint resourceId, byte index, byte[] content) void IIPRequestSetProperty(uint callback, uint resourceId, byte index, byte[] content)
{ {
@ -1973,7 +2111,7 @@ namespace Esiur.Net.IIP
// ClassId, ResourceAge, ResourceLink, Content // ClassId, ResourceAge, ResourceLink, Content
if (resource == null) 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) => Codec.ParsePropertyValueArray((byte[])rt[3], this).Then((ar) =>
{ {
@ -2242,7 +2380,7 @@ namespace Esiur.Net.IIP
resource.Instance.ResourceModified += Instance_PropertyModified; resource.Instance.ResourceModified += Instance_PropertyModified;
resource.Instance.ResourceDestroyed += Instance_ResourceDestroyed; resource.Instance.ResourceDestroyed += Instance_ResourceDestroyed;
subscriptions.Add(resource); subscriptions.Add(resource, new List<byte>());
} }
} }
@ -2265,7 +2403,7 @@ namespace Esiur.Net.IIP
{ {
lock(subscriptionsLock) lock(subscriptionsLock)
{ {
foreach(var resource in subscriptions) foreach(var resource in subscriptions.Keys)
{ {
resource.Instance.ResourceEventOccurred -= Instance_EventOccurred; resource.Instance.ResourceEventOccurred -= Instance_EventOccurred;
resource.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred; resource.Instance.CustomResourceEventOccurred -= Instance_CustomEventOccurred;
@ -2313,12 +2451,26 @@ namespace Esiur.Net.IIP
if (et == null) if (et == null)
return; 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)) if (!receivers(this.session))
return; return;
if (resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied) if (resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, et, issuer) == Ruling.Denied)
return; return;
// compose the packet // compose the packet
SendEvent(IIPPacket.IIPPacketEvent.EventOccurred) SendEvent(IIPPacket.IIPPacketEvent.EventOccurred)
.AddUInt32(resource.Instance.Id) .AddUInt32(resource.Instance.Id)
@ -2334,6 +2486,18 @@ namespace Esiur.Net.IIP
if (et == null) if (et == null)
return; 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) if (resource.Instance.Applicable(this.session, ActionType.ReceiveEvent, et, null) == Ruling.Denied)
return; return;

View File

@ -44,7 +44,7 @@ using Esiur.Resource.Template;
namespace Esiur.Net.IIP namespace Esiur.Net.IIP
{ {
//[System.Runtime.InteropServices.ComVisible(true)] //[System.Runtime.InteropServices.ComVisible(true)]
public class DistributedResource : DynamicObject, IResource public class DistributedResource : DynamicObject, IResource
{ {
@ -73,7 +73,7 @@ namespace Esiur.Net.IIP
DistributedResourceEvent[] events; DistributedResourceEvent[] events;
/// <summary> /// <summary>
/// Resource template for the remotely located resource. /// Resource template for the remotely located resource.
@ -105,7 +105,7 @@ namespace Esiur.Net.IIP
/// </summary> /// </summary>
public uint Id public uint Id
{ {
get { return instanceId; } get { return instanceId; }
} }
/// <summary> /// <summary>
@ -136,20 +136,20 @@ namespace Esiur.Net.IIP
internal bool Attached => attached; internal bool Attached => attached;
internal bool Suspended => suspended; internal bool Suspended => suspended;
// public DistributedResourceStack Stack
// public DistributedResourceStack Stack
//{ //{
// get { return stack; } // get { return stack; }
//} //}
/// <summary> /// <summary>
/// Create a new distributed resource. /// Create a new distributed resource.
/// </summary> /// </summary>
/// <param name="connection">Connection responsible for the distributed resource.</param> /// <param name="connection">Connection responsible for the distributed resource.</param>
/// <param name="template">Resource template.</param> /// <param name="template">Resource template.</param>
/// <param name="instanceId">Instance Id given by the other end.</param> /// <param name="instanceId">Instance Id given by the other end.</param>
/// <param name="age">Resource age.</param> /// <param name="age">Resource age.</param>
public DistributedResource(DistributedConnection connection, uint instanceId, ulong age, string link) public DistributedResource(DistributedConnection connection, uint instanceId, ulong age, string link)
{ {
this.link = link; this.link = link;
@ -207,7 +207,7 @@ namespace Esiur.Net.IIP
attached = true; attached = true;
} }
return true; return true;
} }
internal void _EmitEventByIndex(byte index, object args) internal void _EmitEventByIndex(byte index, object args)
@ -232,6 +232,7 @@ namespace Esiur.Net.IIP
return connection.SendInvokeByNamedArguments(instanceId, index, namedArgs); return connection.SendInvokeByNamedArguments(instanceId, index, namedArgs);
} }
public AsyncReply<object> _InvokeByArrayArguments(byte index, object[] args) public AsyncReply<object> _InvokeByArrayArguments(byte index, object[] args)
{ {
if (destroyed) if (destroyed)
@ -247,14 +248,52 @@ namespace Esiur.Net.IIP
return connection.SendInvokeByArrayArguments(instanceId, index, args); 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) public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{ {
var ft = Instance.Template.GetFunctionTemplateByName(binder.Name); var ft = Instance.Template.GetFunctionTemplateByName(binder.Name);
var reply = new AsyncReply<object>(); var reply = new AsyncReply<object>();
if (attached && ft!=null) if (attached && ft != null)
{ {
if (args.Length == 1) if (args.Length == 1)
{ {
@ -273,7 +312,7 @@ namespace Esiur.Net.IIP
{ {
result = _InvokeByArrayArguments(ft.Index, args); result = _InvokeByArrayArguments(ft.Index, args);
} }
} }
else else
{ {
@ -380,7 +419,7 @@ namespace Esiur.Net.IIP
return false; return false;
var pt = Instance.Template.GetPropertyTemplateByName(binder.Name); var pt = Instance.Template.GetPropertyTemplateByName(binder.Name);
if (pt != null) if (pt != null)
{ {
_Set(pt.Index, value); _Set(pt.Index, value);
@ -397,7 +436,7 @@ namespace Esiur.Net.IIP
return true; return true;
} }
} }
/* /*
public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender) public async void InvokeMethod(byte index, object[] arguments, DistributedConnection sender)
@ -426,10 +465,10 @@ namespace Esiur.Net.IIP
*/ */
/// <summary> /// <summary>
/// Resource interface. /// Resource interface.
/// </summary> /// </summary>
public Instance Instance public Instance Instance
{ {
get; get;

View File

@ -32,7 +32,7 @@ namespace Esiur.Net.IIP
{ {
public class DistributedSession : NetworkSession public class DistributedSession : NetworkSession
{ {
Source Source { get; } public Source Source { get; set; }
Authentication Authentication; public Authentication Authentication { get; set; }
} }
} }

View File

@ -30,6 +30,7 @@ using System.Text;
using Esiur.Misc; using Esiur.Misc;
using Esiur.Data; using Esiur.Data;
using System.Net; using System.Net;
using System.Text.Json.Serialization;
namespace Esiur.Net.Packets namespace Esiur.Net.Packets
{ {
@ -241,12 +242,12 @@ namespace Esiur.Net.Packets
// check limit // check limit
if (postSize > data.Length - headerSize) if (postSize > data.Length - headerSize)
return -(postSize - (data.Length - headerSize)); return -(postSize - (data.Length - headerSize));
if ( if (
Headers["content-type"] == null Headers["content-type"] == null
|| Headers["content-type"] == "" || Headers["content-type"] == ""
|| Headers["content-type"].StartsWith("application/x-www-form-urlencoded")) || Headers["content-type"].StartsWith("application/x-www-form-urlencoded"))
{ {
string[] PostVars = null; string[] PostVars = null;
PostVars = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split('&'); PostVars = Encoding.UTF8.GetString(data, (int)headerSize, (int)postSize).Split('&');
@ -265,10 +266,10 @@ namespace Esiur.Net.Packets
} }
else else
if (PostForms.Contains("unknown")) if (PostForms.Contains("unknown"))
PostForms["unknown"] = PostForms["unknown"] PostForms["unknown"] = PostForms["unknown"]
+ "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])); + "&" + WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]));
else else
PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J]))); PostForms.Add("unknown", WebUtility.HtmlDecode(WebUtility.UrlDecode(PostVars[J])));
} }
} }
else if (Headers["content-type"].StartsWith("multipart/form-data")) 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]); 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 else
{ {
//PostForms.Add(Headers["content-type"], Encoding.Default.GetString( )); //PostForms.Add(Headers["content-type"], Encoding.Default.GetString( ));

View File

@ -110,11 +110,11 @@ namespace Esiur.Net.Packets
// Request Invoke // Request Invoke
InvokeFunctionArrayArguments = 0x10, InvokeFunctionArrayArguments = 0x10,
GetProperty,
GetPropertyIfModified,
SetProperty,
InvokeFunctionNamedArguments, InvokeFunctionNamedArguments,
Listen,
Unlisten,
SetProperty,
// Request Attribute // Request Attribute
GetAllAttributes = 0x18, GetAllAttributes = 0x18,
UpdateAllAttributes, UpdateAllAttributes,
@ -543,7 +543,8 @@ namespace Esiur.Net.Packets
offset += cl; offset += cl;
} }
else if (Action == IIPPacketAction.GetProperty) else if (Action == IIPPacketAction.Listen
|| Action == IIPPacketAction.Unlisten)// .GetProperty)
{ {
if (NotEnough(offset, ends, 5)) if (NotEnough(offset, ends, 5))
return -dataLengthNeeded; return -dataLengthNeeded;
@ -554,20 +555,20 @@ namespace Esiur.Net.Packets
MethodIndex = data[offset++]; MethodIndex = data[offset++];
} }
else if (Action == IIPPacketAction.GetPropertyIfModified) //else if (Action == IIPPacketAction.GetPropertyIfModified)
{ //{
if (NotEnough(offset, ends, 9)) // if (NotEnough(offset, ends, 9))
return -dataLengthNeeded; // return -dataLengthNeeded;
ResourceId = data.GetUInt32(offset); // ResourceId = data.GetUInt32(offset);
offset += 4; // offset += 4;
MethodIndex = data[offset++]; // MethodIndex = data[offset++];
ResourceAge = data.GetUInt64(offset); // ResourceAge = data.GetUInt64(offset);
offset += 8; // offset += 8;
} //}
else if (Action == IIPPacketAction.SetProperty) else if (Action == IIPPacketAction.SetProperty)
{ {
if (NotEnough(offset, ends, 6)) if (NotEnough(offset, ends, 6))
@ -707,9 +708,9 @@ namespace Esiur.Net.Packets
offset += cl; offset += cl;
} }
else if (Action == IIPPacketAction.InvokeFunctionArrayArguments else if (Action == IIPPacketAction.InvokeFunctionArrayArguments
|| Action == IIPPacketAction.InvokeFunctionNamedArguments || Action == IIPPacketAction.InvokeFunctionNamedArguments)
|| Action == IIPPacketAction.GetProperty //|| Action == IIPPacketAction.GetProperty
|| Action == IIPPacketAction.GetPropertyIfModified) //|| Action == IIPPacketAction.GetPropertyIfModified)
{ {
if (NotEnough(offset, ends, 1)) if (NotEnough(offset, ends, 1))
return -dataLengthNeeded; return -dataLengthNeeded;
@ -740,7 +741,9 @@ namespace Esiur.Net.Packets
offset += (uint)size; offset += (uint)size;
} }
} }
else if (Action == IIPPacketAction.SetProperty) else if (Action == IIPPacketAction.SetProperty
|| Action == IIPPacketAction.Listen
|| Action == IIPPacketAction.Unlisten)
{ {
// nothing to do // nothing to do
} }

View File

@ -329,7 +329,7 @@ namespace Esiur.Net.Packets
} }
*/ */
PosixTime Timeval; //PosixTime Timeval;
public byte[] Header; public byte[] Header;
public byte[] Preamble; public byte[] Preamble;
//public byte[] Payload; //public byte[] Payload;

View File

@ -165,7 +165,7 @@ namespace Esiur.Net.Sockets
{ {
ssl.BeginWrite(kv.Value, 0, kv.Value.Length, SendCallback, kv.Key); ssl.BeginWrite(kv.Value, 0, kv.Value.Length, SendCallback, kv.Key);
} }
catch (Exception ex) catch //(Exception ex)
{ {
asyncSending = false; asyncSending = false;
try try
@ -179,7 +179,7 @@ namespace Esiur.Net.Sockets
Close(); Close();
} }
} }
catch (Exception ex2) catch //(Exception ex2)
{ {
//state = SocketState.Closed;// .Terminated; //state = SocketState.Closed;// .Terminated;
Close(); Close();
@ -439,7 +439,7 @@ namespace Esiur.Net.Sockets
ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this); ssl.BeginRead(receiveBuffer, 0, receiveBuffer.Length, ReceiveCallback, this);
} }
catch (Exception ex) catch //(Exception ex)
{ {
if (state != SocketState.Closed && !sock.Connected) if (state != SocketState.Closed && !sock.Connected)
{ {

View File

@ -126,7 +126,7 @@ namespace Esiur.Net.Sockets
return; return;
} }
} }
catch (Exception ex) catch //(Exception ex)
{ {
if (socket.state != SocketState.Closed && !socket.sock.Connected) if (socket.state != SocketState.Closed && !socket.sock.Connected)
{ {
@ -500,7 +500,7 @@ namespace Esiur.Net.Sockets
socket.Close(); socket.Close();
} }
} }
catch (Exception ex2) catch //(Exception ex2)
{ {
socket.Close(); socket.Close();
//socket.state = SocketState.Closed;// .Terminated; //socket.state = SocketState.Closed;// .Terminated;

View File

@ -118,6 +118,7 @@ namespace Esiur.Net.Sockets
public void Send(byte[] message) public void Send(byte[] message)
{ {
lock (sendLock) lock (sendLock)
{ {
if (held) if (held)
@ -131,9 +132,8 @@ namespace Esiur.Net.Sockets
pkt_send.Message = message; pkt_send.Message = message;
if (pkt_send.Compose()) 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()) if (pkt_send.Compose())
sock.Send(pkt_send.Data); sock.Send(pkt_send.Data);
} }
} }

View File

@ -60,7 +60,7 @@ namespace Esiur.Net.TCP
public void Destroy() public void Destroy()
{ {
throw new NotImplementedException(); OnDestroy?.Invoke(this);
} }
} }
} }

View File

@ -51,7 +51,7 @@ namespace Esiur.Net.UDP
public void Destroy() public void Destroy()
{ {
throw new NotImplementedException(); OnDestroy?.Invoke(this);
} }
} }
} }

View File

@ -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; }
}
}

View File

@ -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<bool> Trigger(ResourceTrigger trigger) => new AsyncReply<bool>(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);
}
}
}
}

View File

@ -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<GenerationInfo> 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;
}
}
}
}
}

View File

@ -11,7 +11,6 @@ namespace Esiur.Proxy
public static class ResourceProxy public static class ResourceProxy
{ {
static Dictionary<Type, Type> cache = new Dictionary<Type, Type>(); static Dictionary<Type, Type> cache = new Dictionary<Type, Type>();
#if NETSTANDARD #if NETSTANDARD
static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified"); static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified");
@ -34,14 +33,14 @@ namespace Esiur.Proxy
else else
return type; return type;
if (type.FullName.Contains("Esiur.Proxy.T")) // if (type.FullName.Contains("Esiur.Proxy.T"))
#if NETSTANDARD //#if NETSTANDARD
return type.GetTypeInfo().BaseType; // return type.GetTypeInfo().BaseType;
#else //#else
return type.BaseType; // return type.BaseType;
#endif //#endif
else // else
return type; // return type;
} }
public static Type GetProxy(Type type) public static Type GetProxy(Type type)
@ -50,6 +49,13 @@ namespace Esiur.Proxy
if (cache.ContainsKey(type)) if (cache.ContainsKey(type))
return cache[type]; return cache[type];
// check if the type was made with code generation
if (type.GetCustomAttribute<ResourceAttribute>(false) != null)
{
cache.Add(type, type);
return type;
}
#if NETSTANDARD #if NETSTANDARD
var typeInfo = type.GetTypeInfo(); var typeInfo = type.GetTypeInfo();

View File

@ -41,7 +41,6 @@ namespace Esiur.Resource
{ {
AsyncReply<bool> Trigger(ResourceTrigger trigger); AsyncReply<bool> Trigger(ResourceTrigger trigger);
Instance Instance Instance Instance
{ {
get; get;

View File

@ -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()
{
}
}
}

View File

@ -4,7 +4,7 @@ using System.Text;
namespace Esiur.Resource 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 public class PublicAttribute : Attribute
{ {

View File

@ -47,6 +47,7 @@ namespace Esiur.Resource
return new AsyncReply<bool>(true); return new AsyncReply<bool>(true);
} }
protected virtual bool Create() protected virtual bool Create()
{ {
return true; return true;

View File

@ -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()
{
}
}
}

View File

@ -16,6 +16,8 @@ namespace Esiur.Resource.Template
set; set;
} }
public bool Listenable { get; set; }
public EventInfo Info { get; set; } public EventInfo Info { get; set; }
public override byte[] Compose() public override byte[] Compose()
@ -24,9 +26,10 @@ namespace Esiur.Resource.Template
if (Expansion != null) if (Expansion != null)
{ {
var exp = DC.ToBytes(Expansion); var exp = DC.ToBytes(Expansion);
return new BinaryList() return new BinaryList()
.AddUInt8(0x50) .AddUInt8(Listenable ? (byte) 0x58 : (byte) 0x50)
.AddUInt8((byte)name.Length) .AddUInt8((byte)name.Length)
.AddUInt8Array(name) .AddUInt8Array(name)
.AddInt32(exp.Length) .AddInt32(exp.Length)
@ -35,17 +38,18 @@ namespace Esiur.Resource.Template
} }
else else
return new BinaryList() return new BinaryList()
.AddUInt8(0x40) .AddUInt8(Listenable ? (byte) 0x48 : (byte) 0x40)
.AddUInt8((byte)name.Length) .AddUInt8((byte)name.Length)
.AddUInt8Array(name) .AddUInt8Array(name)
.ToArray(); .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) :base(template, MemberType.Property, index, name)
{ {
this.Expansion = expansion; this.Expansion = expansion;
this.Listenable = listenable;
} }
} }
} }

View File

@ -176,7 +176,7 @@ namespace Esiur.Resource.Template
var annotationAttr = pi.GetCustomAttribute<AnnotationAttribute>(true); var annotationAttr = pi.GetCustomAttribute<AnnotationAttribute>(true);
var storageAttr = pi.GetCustomAttribute<StorageAttribute>(true); var storageAttr = pi.GetCustomAttribute<StorageAttribute>(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) if (storageAttr != null)
pt.Recordable = storageAttr.Mode == StorageMode.Recordable; pt.Recordable = storageAttr.Mode == StorageMode.Recordable;
@ -184,7 +184,7 @@ namespace Esiur.Resource.Template
pt.ReadExpansion = annotationAttr.Annotation; pt.ReadExpansion = annotationAttr.Annotation;
else else
pt.ReadExpansion = pi.PropertyType.Name; pt.ReadExpansion = pi.PropertyType.Name;
pt.Info = pi; pt.Info = pi;
//pt.Serilize = publicAttr.Serialize; //pt.Serilize = publicAttr.Serialize;
properties.Add(pt); properties.Add(pt);
@ -209,6 +209,7 @@ namespace Esiur.Resource.Template
if (privateAttr == null) if (privateAttr == null)
{ {
var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true); var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true);
var listenableAttr = ei.GetCustomAttribute<ListenableAttribute>(true);
var et = new EventTemplate(this, i++, ei.Name); var et = new EventTemplate(this, i++, ei.Name);
et.Info = ei; et.Info = ei;
@ -216,6 +217,9 @@ namespace Esiur.Resource.Template
if (annotationAttr != null) if (annotationAttr != null)
et.Expansion = annotationAttr.Annotation; et.Expansion = annotationAttr.Annotation;
if (listenableAttr != null)
et.Listenable = true;
events.Add(et); events.Add(et);
} }
} }
@ -283,6 +287,7 @@ namespace Esiur.Resource.Template
if (publicAttr != null) if (publicAttr != null)
{ {
var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true); var annotationAttr = ei.GetCustomAttribute<AnnotationAttribute>(true);
var listenableAttr = ei.GetCustomAttribute<ListenableAttribute>(true);
var et = new EventTemplate(this, i++, ei.Name); var et = new EventTemplate(this, i++, ei.Name);
et.Info = ei; et.Info = ei;
@ -290,6 +295,9 @@ namespace Esiur.Resource.Template
if (annotationAttr != null) if (annotationAttr != null)
et.Expansion = annotationAttr.Annotation; et.Expansion = annotationAttr.Annotation;
if (listenableAttr != null)
et.Listenable = true;
events.Add(et); events.Add(et);
} }
} }
@ -340,6 +348,7 @@ namespace Esiur.Resource.Template
b.AddUInt8Array(et.Compose()); b.AddUInt8Array(et.Compose());
content = b.ToArray(); content = b.ToArray();
} }
public static ResourceTemplate Parse(byte[] data) public static ResourceTemplate Parse(byte[] data)
@ -436,7 +445,8 @@ namespace Esiur.Resource.Template
{ {
string expansion = null; 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]); var name = data.GetString(offset + 1, data[offset]);// Encoding.ASCII.GetString(data, (int)offset + 1, (int)data[offset]);
offset += (uint)data[offset] + 1; offset += (uint)data[offset] + 1;
@ -449,7 +459,7 @@ namespace Esiur.Resource.Template
offset += cs; offset += cs;
} }
var et = new EventTemplate(od, eventIndex++, name, expansion); var et = new EventTemplate(od, eventIndex++, name, expansion, listenable);
od.events.Add(et); od.events.Add(et);

View File

@ -57,11 +57,12 @@ namespace Esiur.Resource
static bool warehouseIsOpen = false; static bool warehouseIsOpen = false;
public delegate void StoreConnectedEvent(IStore store, string name); public delegate void StoreEvent(IStore store);//, string name);
public delegate void StoreDisconnectedEvent(IStore store); // public delegate void StoreDisconnectedEvent(IStore store);
public static event StoreConnectedEvent StoreConnected; public static event StoreEvent StoreConnected;
public static event StoreDisconnectedEvent StoreDisconnected; //public static event StoreEvent StoreOpen;
public static event StoreEvent StoreDisconnected;
public delegate AsyncReply<IStore> ProtocolInstance(string name, object properties); public delegate AsyncReply<IStore> ProtocolInstance(string name, object properties);
@ -476,18 +477,43 @@ namespace Esiur.Resource
} }
//public static async AsyncReply<T> Push<T>(string path, T resource) where T : IResource
//{
// await Put(path, resource);
// return resource;
//}
/// <summary> /// <summary>
/// Put a resource in the warehouse. /// Put a resource in the warehouse.
/// </summary> /// </summary>
/// <param name="resource">Resource instance.</param>
/// <param name="name">Resource name.</param> /// <param name="name">Resource name.</param>
/// <param name="resource">Resource instance.</param>
/// <param name="store">IStore that manages the resource. Can be null if the resource is a store.</param> /// <param name="store">IStore that manages the resource. Can be null if the resource is a store.</param>
/// <param name="parent">Parent resource. if not presented the store becomes the parent for the resource.</param> /// <param name="parent">Parent resource. if not presented the store becomes the parent for the resource.</param>
public static async AsyncReply<bool> 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<T> Put<T>(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) if (resource.Instance != null)
throw new Exception("Resource has a store."); 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<IResource>(resource); var resourceReference = new WeakReference<IResource>(resource);
if (store == null) if (store == null)
@ -523,7 +549,7 @@ namespace Esiur.Resource
throw new Exception("Can't find a store for the 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) if (attributes != null)
resource.Instance.SetAttributes(Structure.FromObject(attributes)); resource.Instance.SetAttributes(Structure.FromObject(attributes));
@ -535,44 +561,54 @@ namespace Esiur.Resource
parent = null; parent = null;
if (resource is IStore)
stores.TryAdd(resource as IStore, new List<WeakReference<IResource>>());
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) if (resource is IStore)
await resource.Trigger(ResourceTrigger.Open); stores.TryAdd(resource as IStore, new List<WeakReference<IResource>>());
}
if (resource is IStore)
StoreConnected?.Invoke(resource as IStore, name);
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<IResource> New(Type type, string name = null, IStore store = null, IResource parent = null, IPermissionsManager manager = null, object attributes = null, object properties = null) public static async AsyncReply<IResource> 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); 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); 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 (store != null || parent != null || res is IStore)
{ {
if (!await Put(res, name, store, parent, null, 0, manager, attributes)) //if (!await Put(name, res, store, parent, null, 0, manager, attributes))
return null; // return null;
await Put(name, res, store, parent, null, 0, manager, attributes);
} }
return res; return res;

View File

@ -35,11 +35,11 @@ using Esiur.Resource;
namespace Esiur.Security.Membership namespace Esiur.Security.Membership
{ {
public interface IMembership : IResource public interface IMembership
{ {
AsyncReply<bool> UserExists(string username, string domain); AsyncReply<bool> UserExists(string username, string domain);
AsyncReply<byte[]> GetPassword(string username, string domain); AsyncReply<byte[]> GetPassword(string username, string domain);
AsyncReply<byte[]> GetToken(ulong TokenIndex, string domain); AsyncReply<byte[]> GetToken(ulong tokenIndex, string domain);
AsyncReply<bool> Login(Session session); AsyncReply<bool> Login(Session session);
AsyncReply<bool> Logout(Session session); AsyncReply<bool> Logout(Session session);

View File

@ -46,6 +46,7 @@ namespace Esiur.Security.Permissions
public bool Initialize(Structure settings, IResource resource) public bool Initialize(Structure settings, IResource resource)
{ {
this.settings = settings;
return true; return true;
} }
} }

View File

@ -20,7 +20,7 @@ namespace Esiur.Stores
public void Destroy() public void Destroy()
{ {
OnDestroy?.Invoke(this);
} }
public string Link(IResource resource) public string Link(IResource resource)
@ -41,14 +41,14 @@ namespace Esiur.Stores
return new AsyncReply<IResource>(null); return new AsyncReply<IResource>(null);
} }
public async AsyncReply<bool> Put(IResource resource) public AsyncReply<bool> Put(IResource resource)
{ {
resources.Add(resource.Instance.Id, resource);// new WeakReference<IResource>(resource)); resources.Add(resource.Instance.Id, resource);// new WeakReference<IResource>(resource));
resource.Instance.Variables["children"] = new AutoList<IResource, Instance>(resource.Instance); resource.Instance.Variables["children"] = new AutoList<IResource, Instance>(resource.Instance);
resource.Instance.Variables["parents"] = new AutoList<IResource, Instance>(resource.Instance); resource.Instance.Variables["parents"] = new AutoList<IResource, Instance>(resource.Instance);
return true; return new AsyncReply<bool>(true);
} }
public AsyncReply<IResource> Retrieve(uint iid) public AsyncReply<IResource> Retrieve(uint iid)

View File

@ -20,6 +20,7 @@ namespace Esiur.Stores
public void Destroy() public void Destroy()
{ {
OnDestroy?.Invoke(this);
} }
@ -31,19 +32,19 @@ namespace Esiur.Stores
return null; return null;
} }
public async AsyncReply<IResource> Get(string path) public AsyncReply<IResource> Get(string path)
{ {
foreach (var r in resources) foreach (var r in resources)
if (r.Value.IsAlive && (r.Value.Target as IResource).Instance.Name == path) if (r.Value.IsAlive && (r.Value.Target as IResource).Instance.Name == path)
return r.Value.Target as IResource; return new AsyncReply<IResource>(r.Value.Target as IResource);
return null; return new AsyncReply<IResource>(null);
} }
public async AsyncReply<bool> Put(IResource resource) public AsyncReply<bool> Put(IResource resource)
{ {
resources.Add(resource.Instance.Id, new WeakReference( resource));// new WeakReference<IResource>(resource)); resources.Add(resource.Instance.Id, new WeakReference( resource));// new WeakReference<IResource>(resource));
return true; return new AsyncReply<bool>(true);
} }
public AsyncReply<IResource> Retrieve(uint iid) public AsyncReply<IResource> Retrieve(uint iid)