From 5e87ea5247cee0d3b87ee97d92c65db727632c04 Mon Sep 17 00:00:00 2001 From: Ahmed Zamil Date: Sun, 10 Nov 2019 12:41:31 +0300 Subject: [PATCH] WeakRef --- .../Esiur.Stores.MongoDB.csproj | 8 +- Esiur.Stores.MongoDB/MongoDBStore.cs | 303 ++++++-- Esiur.Stores.MySql/Esiur.Stores.MySql.csproj | 15 + Esiur.Stores.MySql/MySqlStore.cs | 718 ++++++++++++++++++ Esiur.sln | 38 +- Esiur/Core/AsyncAwaiter.cs | 30 +- Esiur/Core/AsyncBag.cs | 6 + Esiur/Core/AsyncReply.cs | 138 +++- Esiur/Core/IAsyncReply.cs | 2 +- Esiur/Data/AutoList.cs | 4 +- Esiur/Data/BinaryList.cs | 6 +- Esiur/Data/Codec.cs | 19 +- Esiur/Data/DataConverter.cs | 6 +- Esiur/Data/KeyList.cs | 2 +- Esiur/Data/Structure.cs | 21 + Esiur/Esiur.csproj | 11 +- Esiur/Net/DataLink/PacketServer.cs | 14 +- Esiur/Net/HTTP/HTTPServer.cs | 50 +- Esiur/Net/HTTP/IIPoWS.cs | 32 +- Esiur/Net/IIP/DistributedConnection.cs | 77 +- .../Net/IIP/DistributedConnectionProtocol.cs | 111 ++- Esiur/Net/IIP/DistributedResource.cs | 24 +- Esiur/Net/IIP/DistributedServer.cs | 9 +- Esiur/Net/IIP/EntryPoint.cs | 40 + Esiur/Net/Packets/IIPPacket.cs | 4 +- Esiur/Net/Sockets/TCPSocket.cs | 21 +- Esiur/Net/TCP/TCPServer.cs | 36 +- .../PublishProfiles/FolderProfile.pubxml | 4 +- Esiur/Proxy/ResourceProxy.cs | 6 +- Esiur/Resource/IStore.cs | 18 +- Esiur/Resource/Instance.cs | 240 ++++-- Esiur/Resource/Resource.cs | 7 +- Esiur/Resource/ResourceQuery.cs | 28 + Esiur/Resource/Template/ResourceTemplate.cs | 2 +- Esiur/Resource/Warehouse.cs | 227 ++++-- ...sManager.cs => StorePermissionsManager.cs} | 14 +- Esiur/Stores/MemoryStore.cs | 71 +- Esiur/Stores/TemporaryStore.cs | 118 +++ Esiurd.sln | Bin 0 -> 2609 bytes Test/MyObject.cs | 12 +- Test/Program.cs | 15 +- 41 files changed, 2076 insertions(+), 431 deletions(-) create mode 100644 Esiur.Stores.MySql/Esiur.Stores.MySql.csproj create mode 100644 Esiur.Stores.MySql/MySqlStore.cs create mode 100644 Esiur/Net/IIP/EntryPoint.cs create mode 100644 Esiur/Resource/ResourceQuery.cs rename Esiur/Security/Permissions/{ParentalPermissionsManager.cs => StorePermissionsManager.cs} (79%) create mode 100644 Esiur/Stores/TemporaryStore.cs create mode 100644 Esiurd.sln diff --git a/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj b/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj index 55baa8f..3608435 100644 --- a/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj +++ b/Esiur.Stores.MongoDB/Esiur.Stores.MongoDB.csproj @@ -1,7 +1,7 @@  - netstandard1.5 + netstandard2.0 Ahmed Kh. Zamil Esiur Esiur MongoDB Store @@ -11,12 +11,12 @@ http://www.esiur.com https://github.com/esiur/esiur-dotnet/ True - 1.2.5 + 1.2.8 - - + + diff --git a/Esiur.Stores.MongoDB/MongoDBStore.cs b/Esiur.Stores.MongoDB/MongoDBStore.cs index aab580f..b09f5fd 100644 --- a/Esiur.Stores.MongoDB/MongoDBStore.cs +++ b/Esiur.Stores.MongoDB/MongoDBStore.cs @@ -23,15 +23,22 @@ namespace Esiur.Stores.MongoDB MongoClient client; IMongoDatabase database; IMongoCollection resourcesCollection; + + //List storeParents = new List(); + //List storeChildren = new List(); + //string collectionName; //string dbName; - Dictionary resources = new Dictionary(); + Dictionary resources = new Dictionary(); - public int Count + public long Count { - get { return resources.Count; } + get + { + return resourcesCollection.CountDocuments(x => true); + }// resources.Count; } } public void Destroy() @@ -39,7 +46,7 @@ namespace Esiur.Stores.MongoDB } - + public bool Record(IResource resource, string propertyName, object value, ulong age, DateTime date) { var objectId = resource.Instance.Attributes["objectId"].ToString(); @@ -77,21 +84,35 @@ namespace Esiur.Stores.MongoDB return true; } - AsyncReply Fetch(string id) + AsyncReply Fetch(string id) where T : IResource { + + if (resources.ContainsKey(id) && resources[id].IsAlive) + { + if (resources[id].Target is T) + return new AsyncReply((T)resources[id].Target); + else + return new AsyncReply(default(T)); ; + } + var filter = Builders.Filter.Eq("_id", new BsonObjectId(new ObjectId(id))); var list = resourcesCollection.Find(filter).ToList(); if (list.Count == 0) - return new AsyncReply(null); + return new AsyncReply(default(T)); var document = list[0]; var type = Type.GetType(document["classname"].AsString); if (type == null) - return new AsyncReply(null); + return new AsyncReply(default(T)); IResource resource = (IResource)Activator.CreateInstance(ResourceProxy.GetProxy(type)); - resources.Add(document["_id"].AsObjectId.ToString(), resource); + + //var iid = document["_id"].AsObjectId.ToString(); + if (resources.ContainsKey(id)) + resources[id] = new WeakReference(resource); + else + resources.Add(id, new WeakReference(resource)); Warehouse.Put(resource, document["name"].AsString, this); @@ -100,14 +121,16 @@ namespace Esiur.Stores.MongoDB var children = document["children"].AsBsonArray; //var managers = document["managers"].AsBsonArray; - var attributes = Parse(document["attributes"]).Then(x=> { + var attributes = Parse(document["attributes"]).Then(x => + { resource.Instance.SetAttributes(x as Structure); }); var bag = new AsyncBag(); + /* foreach (var p in parents) - { + { var ap = Warehouse.Get(p.AsString); bag.Add(ap); ap.Then((x) => @@ -128,6 +151,10 @@ namespace Esiur.Stores.MongoDB resource.Instance.Children.Add(x); }); } + */ + + resource.Instance.Attributes.Add("children", children.Select(x => x.AsString).ToArray()); + resource.Instance.Attributes.Add("parents", parents.Select(x => x.AsString).ToArray()); // Apply store managers foreach (var m in this.Instance.Managers) @@ -154,27 +181,34 @@ namespace Esiur.Stores.MongoDB foreach (var v in values) { - - var valueInfo = v.Value as BsonDocument; var av = Parse(valueInfo["value"]); - bag.Add(av); av.Then((x) => { - resource.Instance.LoadProperty(v.Name, (ulong)valueInfo["age"].AsInt64, valueInfo["modification"].ToUniversalTime(), x); + resource.Instance.LoadProperty(v.Name, + (ulong)valueInfo["age"].AsInt64, + valueInfo["modification"].ToUniversalTime(), + x); }); + + bag.Add(av); } - bag.Seal(); - - var rt = new AsyncReply(); + var rt = new AsyncReply(); bag.Then((x) => { - rt.Trigger(resource); + if (resource is T) + rt.Trigger(resource); + else + rt.Trigger(null); }); + bag.Seal(); + + + return rt; } @@ -230,7 +264,7 @@ namespace Esiur.Stores.MongoDB } else { - + return new AsyncReply(value.RawValue); } } @@ -242,16 +276,22 @@ namespace Esiur.Stores.MongoDB if (p[0] == "id") { // load from Id + return Fetch(p[1]); + + /* if (resources.ContainsKey(p[1])) return new AsyncReply(resources[p[1]]); else return Fetch(p[1]); + */ } return new AsyncReply(null); } + + public string Link(IResource resource) { return this.Instance.Name + "/id/" + (string)resource.Instance.Attributes["objectId"]; @@ -259,11 +299,17 @@ namespace Esiur.Stores.MongoDB public bool Put(IResource resource) { + PutResource(resource).Wait(); + return true; + } + + private async Task PutResource(IResource resource) + { var attrs = resource.Instance.GetAttributes(); foreach (var kv in resources) - if (kv.Value == resource) + if (kv.Value.Target == resource) { resource.Instance.Attributes.Add("objectId", kv.Key); return true; @@ -290,34 +336,58 @@ namespace Esiur.Stores.MongoDB var template = resource.Instance.Template; - foreach (IResource c in resource.Instance.Children) - children.Add(c.Instance.Link); + // setup attributes + resource.Instance.Attributes["children"] = new string[0]; + resource.Instance.Attributes["parents"] = new string[] { this.Instance.Link }; + + // copy old children (in case we are moving a resource from a store to another. + if (resource.Instance.Store != this) + { + var resourceChildren = await resource.Instance.Children(); + + if (resourceChildren != null) + foreach (IResource c in resourceChildren) + children.Add(c.Instance.Link); + + var resourceParents = await resource.Instance.Parents(); + + if (resourceParents == null) + { + parents.Add(this.Instance.Link); + } + else + { + foreach (IResource p in resourceParents) + parents.Add(p.Instance.Link); + } + } + else + { + // just add self + parents.Add(this.Instance.Link); + } - foreach (IResource p in resource.Instance.Parents) - parents.Add(p.Instance.Link); - var attrsDoc = ComposeStructure(attrs); - - var values = new BsonDocument(); foreach (var pt in template.Properties) { var rt = pt.Info.GetValue(resource, null); - + values.Add(pt.Name, new BsonDocument { { "age", BsonValue.Create(resource.Instance.GetAge(pt.Index)) }, { "modification", resource.Instance.GetModificationDate(pt.Index) }, { "value", Compose(rt) } }); } -// var filter = Builders.Filter.Eq("_id", document["_id"]); -// var update = Builders.Update -// .Set("values", values); -// col.UpdateOne(filter, update); + + // var filter = Builders.Filter.Eq("_id", document["_id"]); + // var update = Builders.Update + // .Set("values", values); + // col.UpdateOne(filter, update); /* @@ -340,6 +410,7 @@ namespace Esiur.Stores.MongoDB //resource.Instance.Attributes["objectId"] = document["_id"].ToString(); + return true; } @@ -409,7 +480,7 @@ namespace Esiur.Stores.MongoDB case DataType.Resource: case DataType.DistributedResource: - + return new BsonDocument { { "type", 0 }, { "link", (value as IResource).Instance.Link } }; //return new BsonObjectId(new ObjectId((string)(value as IResource).Instance.Attributes["objectId"])); @@ -454,8 +525,10 @@ namespace Esiur.Stores.MongoDB resourcesCollection = this.database.GetCollection(collectionName); - // return new AsyncReply(true); + // return new AsyncReply(true); + + /* var filter = new BsonDocument(); var list = resourcesCollection.Find(filter).ToList(); @@ -476,15 +549,24 @@ namespace Esiur.Stores.MongoDB var rt = new AsyncReply(); - bag.Then((x) => { rt.Trigger(true); }); + bag.Then((x) => { + + // storeChildren.AddRange(x); + rt.Trigger(true); + + }); return rt; + */ + + return new AsyncReply(true); } else if (trigger == ResourceTrigger.Terminate) { // save all resources foreach (var resource in resources.Values) - SaveResource(resource); + if (resource.IsAlive) + SaveResource(resource.Target as IResource); return new AsyncReply(true); } @@ -501,12 +583,14 @@ namespace Esiur.Stores.MongoDB var children = new BsonArray(); var template = resource.Instance.Template; - foreach (IResource c in resource.Instance.Children) - children.Add(c.Instance.Link); + //foreach (IResource c in resource.Instance.Children) + // children.Add(c.Instance.Link); + + var plist = resource.Instance.Attributes["parents"] as string[]; + + foreach (var link in plist)// Parents) + parents.Add(link); - foreach (IResource p in resource.Instance.Parents) - parents.Add(p.Instance.Link); - var values = new BsonDocument(); @@ -536,7 +620,7 @@ namespace Esiur.Stores.MongoDB { { "parents", parents }, { "children", children }, - {"attributes", attrsDoc }, + { "attributes", attrsDoc }, { "classname", type.FullName + "," + type.GetTypeInfo().Assembly.GetName().Name }, { "name", resource.Instance.Name }, { "_id", new BsonObjectId(new ObjectId(resource.Instance.Attributes["objectId"].ToString())) }, @@ -576,15 +660,15 @@ namespace Esiur.Stores.MongoDB var bag = new AsyncBag(); - foreach(var v in values) + foreach (var v in values) bag.Add(Parse(v["value"])); - + bag.Seal(); bag.Then((results) => { var list = new List(); - for(var i = 0; i < results.Length; i++) + for (var i = 0; i < results.Length; i++) list.Add(new PropertyValue(results[i], (ulong)values[i]["age"].AsInt64, values[i]["date"].ToUniversalTime())); reply.Trigger(list.ToArray()); @@ -653,7 +737,7 @@ namespace Esiur.Stores.MongoDB reply.Trigger(list); }); - + return reply; } @@ -685,7 +769,7 @@ namespace Esiur.Stores.MongoDB public bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime) { - + var objectId = resource.Instance.Attributes["objectId"].ToString(); @@ -701,5 +785,130 @@ namespace Esiur.Stores.MongoDB } + + public AsyncBag Children(IResource resource, string name) where T : IResource + { + + if (resource == this) + { + IFindFluent match; + + if (name == null) + match = resourcesCollection.Find(x => (x["parents"] as BsonArray).Contains(this.Instance.Name)); + else + match = resourcesCollection.Find(x => (x["parents"] as BsonArray).Contains(this.Instance.Name) && x["name"] == name); + + + var st = match.ToList().Select(x => x["_id"].ToString()).ToArray(); + + + var bag = new AsyncBag(); + + foreach (var s in st) + { + var r = Fetch(s); + if (r.Ready && r.Result == null) + continue; + + bag.Add(r); + } + + bag.Seal(); + return bag; + } + else + { + var children = (string[])resource.Instance.Attributes["children"]; + + if (children == null) + { + return new AsyncBag(null); + } + + var rt = new AsyncBag(); + + + foreach (var child in children) + { + var r = Warehouse.Get(child); + if (r is IAsyncReply) + rt.Add((IAsyncReply)r); + } + + rt.Seal(); + return rt; + } + } + + public AsyncBag Parents(IResource resource, string name) where T : IResource + { + Console.WriteLine("Parents start"); + + if (resource == this) + { + return new AsyncBag(null); + } + else + { + Console.WriteLine("Parents 1"); + + var parents = (string[])resource.Instance.Attributes["parents"]; + + if (parents == null) + { + return new AsyncBag(null); + } + + var rt = new AsyncBag(); + + Console.WriteLine("Parents 2"); + + foreach (var parent in parents) + { + var r = Warehouse.Get(parent); + if (r is IAsyncReply) + rt.Add((IAsyncReply)r); + } + + Console.WriteLine($"Parents 3 {parents.Length}"); + + rt.Seal(); + + Console.WriteLine("Parents end"); + + return rt; + } + } + + + public AsyncReply AddChild(IResource resource, IResource child) + { + var list = (string[])resource.Instance.Attributes["children"]; + resource.Instance.Attributes["children"] = list.Concat(new string[] { child.Instance.Link }).ToArray(); + + SaveResource(resource); + + return new AsyncReply(true); + } + + public AsyncReply RemoveChild(IResource parent, IResource child) + { + throw new NotImplementedException(); + } + + public AsyncReply AddParent(IResource resource, IResource parent) + { + var list = (string[])resource.Instance.Attributes["parents"]; + resource.Instance.Attributes["parents"] = list.Concat(new string[] { parent.Instance.Link }).ToArray(); + + SaveResource(resource); + + return new AsyncReply(true); + } + + public AsyncReply RemoveParent(IResource child, IResource parent) + { + throw new NotImplementedException(); + } } } diff --git a/Esiur.Stores.MySql/Esiur.Stores.MySql.csproj b/Esiur.Stores.MySql/Esiur.Stores.MySql.csproj new file mode 100644 index 0000000..bb0d4d5 --- /dev/null +++ b/Esiur.Stores.MySql/Esiur.Stores.MySql.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/Esiur.Stores.MySql/MySqlStore.cs b/Esiur.Stores.MySql/MySqlStore.cs new file mode 100644 index 0000000..ad77bc2 --- /dev/null +++ b/Esiur.Stores.MySql/MySqlStore.cs @@ -0,0 +1,718 @@ +using Esiur.Resource; +using System; +using Esiur.Core; +using Esiur.Data; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using Esiur.Resource.Template; +using System.Linq; +using Esiur.Security.Permissions; +using Esiur.Proxy; +using MySql.Data.MySqlClient; + +namespace Esiur.Stores.MySql +{ + public class MySqlStore : IStore + { + public Instance Instance { get; set; } + + public event DestroyedEvent OnDestroy; + + string connectionString; + + + Dictionary resources = new Dictionary(); + + + public int Count + { + get { return resources.Count; } + } + + public void Destroy() + { + + + } + + + public bool Record(IResource resource, string propertyName, object value, ulong age, DateTime date) + { + var objectId = resource.Instance.Attributes["objectId"].ToString(); + //var bsonObjectId = new BsonObjectId(new ObjectId(objectId)); + + var record = this.database.GetCollection("record_" + objectId); + + record.InsertOne(new BsonDocument() + { + {"property", propertyName}, {"age", BsonValue.Create(age) }, {"date", date}, {"value", Compose(value) } + }); + + //var col = this.database.GetCollection(collectionName); + + + + var filter = Builders.Filter.Eq("_id", new BsonObjectId(new ObjectId(objectId))); + var update = Builders.Update + .Set("values." + propertyName, new BsonDocument { { "age", BsonValue.Create(age) }, + { "modification", date }, + { "value", Compose(value) } }); + resourcesCollection.UpdateOne(filter, update); + + return true; + } + + public bool Remove(IResource resource) + { + var objectId = resource.Instance.Attributes["objectId"].ToString(); + var filter = Builders.Filter.Eq("_id", new BsonObjectId(new ObjectId(objectId))); + + this.database.DropCollection("record_" + objectId); + resourcesCollection.DeleteOne(filter); + + return true; + } + + AsyncReply Fetch(string id) + { + + MySqlHelper. + + var filter = Builders.Filter.Eq("_id", new BsonObjectId(new ObjectId(id))); + var list = resourcesCollection.Find(filter).ToList(); + if (list.Count == 0) + return new AsyncReply(null); + var document = list[0]; + + var type = Type.GetType(document["classname"].AsString); + + if (type == null) + return new AsyncReply(null); + + IResource resource = (IResource)Activator.CreateInstance(ResourceProxy.GetProxy(type)); + resources.Add(document["_id"].AsObjectId.ToString(), resource); + + Warehouse.Put(resource, document["name"].AsString, this); + + + var parents = document["parents"].AsBsonArray; + var children = document["children"].AsBsonArray; + //var managers = document["managers"].AsBsonArray; + + var attributes = Parse(document["attributes"]).Then(x => { + resource.Instance.SetAttributes(x as Structure); + }); + + var bag = new AsyncBag(); + + foreach (var p in parents) + { + var ap = Warehouse.Get(p.AsString); + bag.Add(ap); + ap.Then((x) => + { + if (!resource.Instance.Parents.Contains(x)) + resource.Instance.Parents.Add(x); + }); + } + + foreach (var c in children) + { + + var ac = Warehouse.Get(c.AsString); + bag.Add(ac); + ac.Then((x) => + { + if (!resource.Instance.Children.Contains(x)) + resource.Instance.Children.Add(x); + }); + } + + // Apply store managers + foreach (var m in this.Instance.Managers) + resource.Instance.Managers.Add(m); + + /* + // load managers + foreach(var m in managers) + { + IPermissionsManager pm = (IPermissionsManager)Activator.CreateInstance(Type.GetType(m["classname"].AsString)); + var sr = Parse(m["settings"]); + bag.Add(sr); + sr.Then((x) => + { + pm.Initialize((Structure)x, resource); + resource.Instance.Managers.Add(pm); + }); + } + */ + + // Load values + var values = document["values"].AsBsonDocument; + + + foreach (var v in values) + { + + + var valueInfo = v.Value as BsonDocument; + + var av = Parse(valueInfo["value"]); + bag.Add(av); + av.Then((x) => + { + resource.Instance.LoadProperty(v.Name, (ulong)valueInfo["age"].AsInt64, valueInfo["modification"].ToUniversalTime(), x); + }); + } + + bag.Seal(); + + var rt = new AsyncReply(); + + bag.Then((x) => + { + rt.Trigger(resource); + }); + + return rt; + } + + IAsyncReply Parse(BsonValue value) + { + if (value.BsonType == BsonType.Document) + { + var doc = value.AsBsonDocument; + if (doc["type"] == 0) + { + return Warehouse.Get(doc["link"].AsString); + } // structure + else if (doc["type"] == 1) + { + var bag = new AsyncBag(); + var rt = new AsyncReply(); + + var bs = (BsonDocument)doc["values"].AsBsonDocument; + var s = new Structure(); + + foreach (var v in bs) + bag.Add(Parse(v.Value)); + + bag.Seal(); + bag.Then((x) => + { + for (var i = 0; i < x.Length; i++) + s[bs.GetElement(i).Name] = x[i]; + + rt.Trigger(s); + }); + + return rt; + } + else + return new AsyncReply(null); + } + else if (value.BsonType == BsonType.Array) + { + var array = value.AsBsonArray; + var bag = new AsyncBag(); + + foreach (var v in array) + bag.Add(Parse(v)); + + bag.Seal(); + + return bag; + } + else if (value.BsonType == BsonType.DateTime) + { + return new AsyncReply(value.ToUniversalTime()); + } + else + { + + return new AsyncReply(value.RawValue); + } + } + + public AsyncReply Get(string path) + { + var p = path.Split('/'); + if (p.Length == 2) + if (p[0] == "id") + { + // load from Id + + if (resources.ContainsKey(p[1])) + return new AsyncReply(resources[p[1]]); + else + return Fetch(p[1]); + } + + return new AsyncReply(null); + } + + public string Link(IResource resource) + { + return this.Instance.Name + "/id/" + (string)resource.Instance.Attributes["objectId"]; + } + + + string MakeTable(Type type) + { + var props = type.GetTypeInfo().GetProperties(); + + + foreach(var p in props) + { + var rp = p.GetCustomAttribute(); + if (rp == null) + continue; + + + } + } + + public bool Put(IResource resource) + { + + var attrs = resource.Instance.GetAttributes(); + + foreach (var kv in resources) + if (kv.Value == resource) + { + resource.Instance.Attributes.Add("objectId", kv.Key); + return true; + } + + var type = ResourceProxy.GetBaseType(resource); + + // insert the document + var document = new BsonDocument + { + { "classname", type.FullName + "," + type.GetTypeInfo().Assembly.GetName().Name }, + { "name", resource.Instance.Name }, + }; + + resourcesCollection.InsertOne(document); + resource.Instance.Attributes["objectId"] = document["_id"].ToString(); + + + // now update the document + // * insert first to get the object id, update values, attributes, children and parents after in case the same resource has a property references self + + var parents = new BsonArray(); + var children = new BsonArray(); + + var template = resource.Instance.Template; + + foreach (IResource c in resource.Instance.Children) + children.Add(c.Instance.Link); + + foreach (IResource p in resource.Instance.Parents) + parents.Add(p.Instance.Link); + + + var attrsDoc = ComposeStructure(attrs); + + + + + var values = new BsonDocument(); + + foreach (var pt in template.Properties) + { + var rt = pt.Info.GetValue(resource, null); + + values.Add(pt.Name, + new BsonDocument { { "age", BsonValue.Create(resource.Instance.GetAge(pt.Index)) }, + { "modification", resource.Instance.GetModificationDate(pt.Index) }, + { "value", Compose(rt) } }); + } + + // var filter = Builders.Filter.Eq("_id", document["_id"]); + // var update = Builders.Update + // .Set("values", values); + // col.UpdateOne(filter, update); + + + /* + var document = new BsonDocument + { + { "parents", parents }, + { "children", children }, + { "attributes", attrsDoc }, + { "classname", resource.GetType().FullName + "," + resource.GetType().GetTypeInfo().Assembly.GetName().Name }, + { "name", resource.Instance.Name }, + { "values", values } + }; + */ + + var filter = Builders.Filter.Eq("_id", document["_id"]); + var update = Builders.Update + .Set("values", values).Set("parents", parents).Set("children", children).Set("attributes", attrsDoc); + resourcesCollection.UpdateOne(filter, update); + + + //resource.Instance.Attributes["objectId"] = document["_id"].ToString(); + + return true; + } + + + public BsonDocument ComposeStructure(Structure value) + { + var rt = new BsonDocument { { "type", 1 } }; + + var values = new BsonDocument(); + foreach (var i in value) + values.Add(i.Key, Compose(i.Value)); + + rt.Add("values", values); + return rt; + } + + public BsonArray ComposeVarArray(object[] array) + { + var rt = new BsonArray(); + + for (var i = 0; i < array.Length; i++) + rt.Add(Compose(array[i])); + + return rt; + } + + BsonArray ComposeStructureArray(Structure[] structures) + { + var rt = new BsonArray(); + + if (structures == null || structures?.Length == 0) + return rt; + + foreach (var s in structures) + rt.Add(ComposeStructure(s)); + + return rt; + } + + BsonArray ComposeResourceArray(IResource[] array) + { + var rt = new BsonArray(); + foreach (var r in array) + { + rt.Add(new BsonDocument { { "type", 0 }, { "link", r.Instance.Link } }); + + //if (r.Instance.Attributes.ContainsKey("objectId")) + + //rt.Add(new BsonObjectId(new ObjectId((string)r.Instance.Attributes["objectId"]))); + } + + return rt; + } + + private BsonValue Compose(object value) + { + var type = Codec.GetDataType(value, null); + + switch (type) + { + case DataType.Void: + // nothing to do; + return BsonNull.Value; + + case DataType.String: + return new BsonString((string)value); + + case DataType.Resource: + case DataType.DistributedResource: + + return new BsonDocument { { "type", 0 }, { "link", (value as IResource).Instance.Link } }; + + //return new BsonObjectId(new ObjectId((string)(value as IResource).Instance.Attributes["objectId"])); + + case DataType.Structure: + return ComposeStructure((Structure)value); + + case DataType.VarArray: + return ComposeVarArray((object[])value); + + case DataType.ResourceArray: + if (value is IResource[]) + return ComposeResourceArray((IResource[])value); + else + return ComposeResourceArray((IResource[])DC.CastConvert(value, typeof(IResource[]))); + + + case DataType.StructureArray: + return ComposeStructureArray((Structure[])value); + + + default: + return BsonValue.Create(value); + } + } + + public AsyncReply Retrieve(uint iid) + { + throw new NotImplementedException(); + } + + public AsyncReply Trigger(ResourceTrigger trigger) + { + + if (trigger == ResourceTrigger.Initialize) + { + + var collectionName = Instance.Attributes["Collection"] as string ?? "resources"; + var dbName = Instance.Attributes["Database"] as string ?? "esiur"; + client = new MongoClient(Instance.Attributes["Connection"] as string ?? "mongodb://localhost"); + database = client.GetDatabase(dbName); + + resourcesCollection = this.database.GetCollection(collectionName); + + // return new AsyncReply(true); + + var filter = new BsonDocument(); + + var list = resourcesCollection.Find(filter).ToList(); + + + // if (list.Count == 0) + // return new AsyncBag(new IResource[0]); + + var bag = new AsyncBag(); + + for (var i = 0; i < list.Count; i++) + { + //Console.WriteLine("Loading {0}/{1}", i, list.Count); + bag.Add(Get("id/" + list[i]["_id"].AsObjectId.ToString())); + } + + bag.Seal(); + + var rt = new AsyncReply(); + + bag.Then((x) => { rt.Trigger(true); }); + + return rt; + } + else if (trigger == ResourceTrigger.Terminate) + { + // save all resources + foreach (var resource in resources.Values) + SaveResource(resource); + + return new AsyncReply(true); + } + else + return new AsyncReply(true); + } + + + public void SaveResource(IResource resource) + { + var attrs = resource.Instance.GetAttributes(); + + var parents = new BsonArray(); + var children = new BsonArray(); + var template = resource.Instance.Template; + + foreach (IResource c in resource.Instance.Children) + children.Add(c.Instance.Link); + + foreach (IResource p in resource.Instance.Parents) + parents.Add(p.Instance.Link); + + + var values = new BsonDocument(); + + foreach (var pt in template.Properties) + { + /* +#if NETSTANDARD1_5 + var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name); +#else + var pi = resource.GetType().GetProperty(pt.Name); +#endif +*/ + var rt = pt.Info.GetValue(resource, null); + + values.Add(pt.Name, + new BsonDocument { { "age", BsonValue.Create(resource.Instance.GetAge(pt.Index)) }, + { "modification", resource.Instance.GetModificationDate(pt.Index) }, + { "value", Compose(rt) } }); + + } + + var attrsDoc = ComposeStructure(attrs); + + var type = ResourceProxy.GetBaseType(resource); + + var document = new BsonDocument + { + { "parents", parents }, + { "children", children }, + {"attributes", attrsDoc }, + { "classname", type.FullName + "," + type.GetTypeInfo().Assembly.GetName().Name }, + { "name", resource.Instance.Name }, + { "_id", new BsonObjectId(new ObjectId(resource.Instance.Attributes["objectId"].ToString())) }, + {"values", values } + }; + + + + var filter = Builders.Filter.Eq("_id", document["_id"]); + + /* + var update = Builders.Update + .Set("values", values); + + var update = Builders.Update.Set("values", values).Set("parents", parents; + col.UpdateOne(filter, update); + + */ + + resourcesCollection.ReplaceOne(filter, document); + } + + public AsyncReply GetPropertyRecordByAge(IResource resource, string propertyName, ulong fromAge, ulong toAge) + { + var objectId = resource.Instance.Attributes["objectId"].ToString(); + + var record = this.database.GetCollection("record_" + objectId); + var builder = Builders.Filter; + + var filter = builder.Gte("age", fromAge) & builder.Lte("age", toAge) & builder.Eq("property", propertyName); + + var reply = new AsyncReply(); + + record.FindAsync(filter).ContinueWith((x) => + { + var values = ((Task>)x).Result.ToList(); + + var bag = new AsyncBag(); + + foreach (var v in values) + bag.Add(Parse(v["value"])); + + bag.Seal(); + + bag.Then((results) => + { + var list = new List(); + for (var i = 0; i < results.Length; i++) + list.Add(new PropertyValue(results[i], (ulong)values[i]["age"].AsInt64, values[i]["date"].ToUniversalTime())); + + reply.Trigger(list.ToArray()); + }); + + }); + + return reply; + } + + public AsyncReply GetPropertyRecordByDate(IResource resource, string propertyName, DateTime fromDate, DateTime toDate) + { + var objectId = resource.Instance.Attributes["objectId"].ToString(); + + var record = this.database.GetCollection("record_" + objectId); + var builder = Builders.Filter; + + var filter = builder.Gte("date", fromDate) & builder.Lte("date", toDate) & builder.Eq("property", propertyName); + + var reply = new AsyncReply(); + + record.FindAsync(filter).ContinueWith((x) => + { + var values = ((Task>)x).Result.ToList(); + + var bag = new AsyncBag(); + + foreach (var v in values) + bag.Add(Parse(v["value"])); + + bag.Seal(); + + bag.Then((results) => + { + var list = new List(); + for (var i = 0; i < results.Length; i++) + list.Add(new PropertyValue(results[i], (ulong)values[i]["age"].AsInt64, values[i]["date"].ToUniversalTime())); + + reply.Trigger(list.ToArray()); + }); + + }); + + return reply; + } + + AsyncReply> GetRecordByAge(IResource resource, ulong fromAge, ulong toAge) + { + var properties = resource.Instance.Template.Properties.Where(x => x.Storage == StorageMode.Recordable).ToList(); + + var reply = new AsyncReply>(); + + AsyncBag bag = new AsyncBag(); + + foreach (var p in properties) + bag.Add(GetPropertyRecordByAge(resource, p.Name, fromAge, toAge)); + + bag.Seal(); + + bag.Then(x => + { + var list = new KeyList(); + + for (var i = 0; i < x.Length; i++) + list.Add(properties[i], x[i]); + + reply.Trigger(list); + }); + + return reply; + } + + public AsyncReply> GetRecord(IResource resource, DateTime fromDate, DateTime toDate) + { + var properties = resource.Instance.Template.Properties.Where(x => x.Storage == StorageMode.Recordable).ToList(); + + var reply = new AsyncReply>(); + + AsyncBag bag = new AsyncBag(); + + foreach (var p in properties) + bag.Add(GetPropertyRecordByDate(resource, p.Name, fromDate, toDate)); + + bag.Seal(); + + bag.Then(x => + { + var list = new KeyList(); + + for (var i = 0; i < x.Length; i++) + list.Add(properties[i], x[i]); + + reply.Trigger(list); + }); + + return reply; + } + + public bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime) + { + + var sql = $"UPDATE `{resource.Instance.Template.ClassName}` SET `{propertyName}` = @value, `{propertyName}_age` = @age, `{propertyName}_date` = @date WHERE `_id` = @id"; + + MySqlHelper.ExecuteNonQuery(connectionString, sql, + new MySqlParameter("@value", value), + new MySqlParameter("@age", age), + new MySqlParameter("@date", dateTime), + new MySqlParameter("@id", resource.Instance.Attributes["objectId"])); + + return true; + + } + + } +} diff --git a/Esiur.sln b/Esiur.sln index c5967c0..9af5401 100644 --- a/Esiur.sln +++ b/Esiur.sln @@ -1,13 +1,12 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.9 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29324.140 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esiur", "Esiur\Esiur.csproj", "{DEBF78DB-E3B3-47F4-994C-5C970E98C686}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esiur", "Esiur\Esiur.csproj", "{4F74A8C1-D38F-4CC0-ACD1-24459BA0EAFC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test", "Test\Test.csproj", "{08AC2E1C-24F0-4309-B35E-73D36A427D2D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esiur.Stores.MongoDB", "Esiur.Stores.MongoDB\Esiur.Stores.MongoDB.csproj", "{4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Esiur.Stores.MongoDB", "Esiur.Stores.MongoDB\Esiur.Stores.MongoDB.csproj", "{F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Esiur.Stores.MySql", "Esiur.Stores.MySql\Esiur.Stores.MySql.csproj", "{7BD6148A-3335-411C-9189-3803B1824264}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,20 +14,23 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {DEBF78DB-E3B3-47F4-994C-5C970E98C686}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DEBF78DB-E3B3-47F4-994C-5C970E98C686}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DEBF78DB-E3B3-47F4-994C-5C970E98C686}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DEBF78DB-E3B3-47F4-994C-5C970E98C686}.Release|Any CPU.Build.0 = Release|Any CPU - {08AC2E1C-24F0-4309-B35E-73D36A427D2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {08AC2E1C-24F0-4309-B35E-73D36A427D2D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {08AC2E1C-24F0-4309-B35E-73D36A427D2D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {08AC2E1C-24F0-4309-B35E-73D36A427D2D}.Release|Any CPU.Build.0 = Release|Any CPU - {F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F7FB2243-12AB-46A6-9DA4-FF8DABC77D8F}.Release|Any CPU.Build.0 = Release|Any CPU + {4F74A8C1-D38F-4CC0-ACD1-24459BA0EAFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F74A8C1-D38F-4CC0-ACD1-24459BA0EAFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F74A8C1-D38F-4CC0-ACD1-24459BA0EAFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F74A8C1-D38F-4CC0-ACD1-24459BA0EAFC}.Release|Any CPU.Build.0 = Release|Any CPU + {4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C90D4B3-8EA2-48AE-A2F9-2B722FCEF9C4}.Release|Any CPU.Build.0 = Release|Any CPU + {7BD6148A-3335-411C-9189-3803B1824264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7BD6148A-3335-411C-9189-3803B1824264}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BD6148A-3335-411C-9189-3803B1824264}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7BD6148A-3335-411C-9189-3803B1824264}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C584421D-5EC0-4821-B7D8-2633D8D405F2} + EndGlobalSection EndGlobal diff --git a/Esiur/Core/AsyncAwaiter.cs b/Esiur/Core/AsyncAwaiter.cs index cead3c4..0967708 100644 --- a/Esiur/Core/AsyncAwaiter.cs +++ b/Esiur/Core/AsyncAwaiter.cs @@ -2,39 +2,51 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Text; +using System.Threading.Tasks; namespace Esiur.Core { public class AsyncAwaiter : INotifyCompletion { - public Action callback = null; - public T result; - private bool completed; + Action callback = null; + + AsyncException exception = null; + + T result; public AsyncAwaiter(AsyncReply reply) { reply.Then(x => { - this.completed = true; + this.IsCompleted = true; this.result = x; this.callback?.Invoke(); + }).Error(x => + { + exception = x; + this.IsCompleted = true; + this.callback?.Invoke(); }); } public T GetResult() { + if (exception != null) + throw exception; return result; } - public bool IsCompleted => completed; + public bool IsCompleted { get; private set; } public void OnCompleted(Action continuation) { - // Continue.... - callback = continuation; + if (IsCompleted) + continuation?.Invoke(); + else + // Continue.... + callback = continuation; } - - + } } diff --git a/Esiur/Core/AsyncBag.cs b/Esiur/Core/AsyncBag.cs index 7109eda..f122f7e 100644 --- a/Esiur/Core/AsyncBag.cs +++ b/Esiur/Core/AsyncBag.cs @@ -95,5 +95,11 @@ namespace Esiur.Core } + public AsyncBag(T[] results) + { + resultReady = true; + base.result = results; + } + } } diff --git a/Esiur/Core/AsyncReply.cs b/Esiur/Core/AsyncReply.cs index 54f35c3..b9d5d37 100644 --- a/Esiur/Core/AsyncReply.cs +++ b/Esiur/Core/AsyncReply.cs @@ -34,25 +34,29 @@ using System.Runtime.CompilerServices; namespace Esiur.Core { - public class AsyncReply: IAsyncReply + [AsyncMethodBuilder(typeof(AsyncReply<>))] + public class AsyncReply : IAsyncReply { + public bool Debug = false; + protected List> callbacks = new List>(); protected T result; protected List> errorCallbacks = new List>(); - + protected List> progressCallbacks = new List>(); protected List> chunkCallbacks = new List>(); //List awaiters = new List(); - object callbacksLock = new object(); + // object callbacksLock = new object(); protected bool resultReady = false; AsyncException exception; + AutoResetEvent mutex = new AutoResetEvent(false); public bool Ready { @@ -60,7 +64,24 @@ namespace Esiur.Core } - + public T Wait() + { + + if (resultReady) + return result; + + if (Debug) + Console.WriteLine($"AsyncReply: {GetHashCode()} Wait"); + + //mutex = new AutoResetEvent(false); + mutex.WaitOne(); + + if (Debug) + Console.WriteLine($"AsyncReply: {GetHashCode()} Wait ended"); + + return result; + } + public object Result { @@ -69,53 +90,91 @@ namespace Esiur.Core public IAsyncReply Then(Action callback) { - callbacks.Add(callback); + //lock (callbacksLock) + //{ + if (resultReady) + { + if (Debug) + Console.WriteLine($"AsyncReply: {GetHashCode()} Then ready"); + callback(result); + return this; + } + + + if (Debug) + Console.WriteLine($"AsyncReply: {GetHashCode()} Then pending"); + + + + callbacks.Add(callback); return this; + //} } public IAsyncReply Error(Action callback) { + // lock (callbacksLock) + // { errorCallbacks.Add(callback); if (exception != null) callback(exception); return this; + //} } public IAsyncReply Progress(Action callback) { + //lock (callbacksLock) + //{ progressCallbacks.Add(callback); return this; + //} } - + public IAsyncReply Chunk(Action callback) { + // lock (callbacksLock) + // { chunkCallbacks.Add(callback); return this; + // } } public void Trigger(object result) { - lock (callbacksLock) - { - if (resultReady) - return; - - this.result = (T)result; - resultReady = true; - - foreach (var cb in callbacks) - cb((T)result); + if (Debug) + Console.WriteLine($"AsyncReply: {GetHashCode()} Trigger"); - } + //lock (callbacksLock) + //{ + if (resultReady) + return; + + this.result = (T)result; + + resultReady = true; + + //if (mutex != null) + mutex.Set(); + + foreach (var cb in callbacks) + cb((T)result); + + + if (Debug) + Console.WriteLine($"AsyncReply: {GetHashCode()} Trigger ended"); + + + //} } @@ -128,13 +187,15 @@ namespace Esiur.Core this.exception = exception as AsyncException; else this.exception = new AsyncException(ErrorType.Management, 0, exception.Message); - - lock (callbacksLock) - { - foreach (var cb in errorCallbacks) - cb(this.exception); - } + + // lock (callbacksLock) + // { + foreach (var cb in errorCallbacks) + cb(this.exception); + // } + + mutex?.Set(); } @@ -143,26 +204,26 @@ namespace Esiur.Core if (resultReady) return; - lock (callbacksLock) - { - foreach (var cb in progressCallbacks) - cb(type, value, max); + //lock (callbacksLock) + //{ + foreach (var cb in progressCallbacks) + cb(type, value, max); - } + //} } - + public void TriggerChunk(object value) { if (resultReady) return; - lock (callbacksLock) - { - foreach (var cb in chunkCallbacks) - cb((T)value); + //lock (callbacksLock) + //{ + foreach (var cb in chunkCallbacks) + cb((T)value); - } + //} } public AsyncAwaiter GetAwaiter() @@ -170,21 +231,20 @@ namespace Esiur.Core return new AsyncAwaiter(this); } - - public AsyncReply() { - + //this.Debug = true; } public AsyncReply(T result) { + //this.Debug = true; resultReady = true; this.result = result; } - + /* public AsyncReply Then(Action callback) { @@ -218,7 +278,7 @@ namespace Esiur.Core return base.Task.ContinueWith((t) => { -#if NETSTANDARD1_5 +#if NETSTANDARD return (T)t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t); #else return (T)t.GetType().GetProperty("Result").GetValue(t); diff --git a/Esiur/Core/IAsyncReply.cs b/Esiur/Core/IAsyncReply.cs index bf1a01d..5c49389 100644 --- a/Esiur/Core/IAsyncReply.cs +++ b/Esiur/Core/IAsyncReply.cs @@ -16,6 +16,6 @@ namespace Esiur.Core void TriggerProgress(ProgressType type, int value, int max); void TriggerChunk(object value); - + T Wait(); } } diff --git a/Esiur/Data/AutoList.cs b/Esiur/Data/AutoList.cs index cb5dd14..a8a2a0b 100644 --- a/Esiur/Data/AutoList.cs +++ b/Esiur/Data/AutoList.cs @@ -96,7 +96,7 @@ namespace Esiur.Data public AutoList(ST state) { this.state = state; -#if NETSTANDARD1_5 +#if NETSTANDARD removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); #else removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T))); @@ -111,7 +111,7 @@ namespace Esiur.Data public AutoList(ST state, T[] values) { this.state = state; - #if NETSTANDARD1_5 + #if NETSTANDARD removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); #else removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T))); diff --git a/Esiur/Data/BinaryList.cs b/Esiur/Data/BinaryList.cs index 21ad2ff..60001f0 100644 --- a/Esiur/Data/BinaryList.cs +++ b/Esiur/Data/BinaryList.cs @@ -62,7 +62,7 @@ namespace Esiur.Data list.Add((byte)i); else { -#if NETSTANDARD1_5 +#if NETSTANDARD MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() }); #else MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() }); @@ -100,7 +100,7 @@ namespace Esiur.Data list.Add((byte)i); else { -#if NETSTANDARD1_5 +#if NETSTANDARD MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() }); #else MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() }); @@ -138,7 +138,7 @@ namespace Esiur.Data } else { -#if NETSTANDARD1_5 +#if NETSTANDARD MethodInfo mi = typeof(DC).GetTypeInfo().GetMethod("ToBytes", new Type[] { i.GetType() }); #else MethodInfo mi = typeof(DC).GetMethod("ToBytes", new Type[] { i.GetType() }); diff --git a/Esiur/Data/Codec.cs b/Esiur/Data/Codec.cs index 9b45275..2e63327 100644 --- a/Esiur/Data/Codec.cs +++ b/Esiur/Data/Codec.cs @@ -34,6 +34,7 @@ using Esiur.Resource; using System.Linq; using System.Reflection; using Esiur.Resource.Template; +using System.Runtime.CompilerServices; namespace Esiur.Data { @@ -171,7 +172,7 @@ namespace Esiur.Data var result = (StructureComparisonResult)data[offset++]; - IAsyncReply previous = null; + AsyncReply previous = null; // string[] previousKeys = null; // DataType[] previousTypes = null; @@ -1064,6 +1065,16 @@ namespace Esiur.Data return rt.ToArray(); } + + public static bool IsAnonymous(Type type) + { + // Detect anonymous types + var info = type.GetTypeInfo(); + var hasCompilerGeneratedAttribute = info.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0; + var nameContainsAnonymousType = type.FullName.Contains("AnonymousType"); + return hasCompilerGeneratedAttribute && nameContainsAnonymousType; + } + /// /// Check if a type implements an interface /// @@ -1086,7 +1097,7 @@ namespace Esiur.Data if (type == iface) return true; -#if NETSTANDARD1_5 +#if NETSTANDARD if (type.GetTypeInfo().GetInterfaces().Contains(iface))// (x=>x.GetTypeInfo().IsGenericType (iface)) return true; @@ -1109,7 +1120,7 @@ namespace Esiur.Data if (type == iface) return true; -#if NETSTANDARD1_5 +#if NETSTANDARD if (type.GetTypeInfo().GetInterfaces().Contains(iface)) return true; @@ -1137,7 +1148,7 @@ namespace Esiur.Data { if (childType == parentType) return true; -#if NETSTANDARD1_5 +#if NETSTANDARD childType = childType.GetTypeInfo().BaseType; #else childType = childType.BaseType; diff --git a/Esiur/Data/DataConverter.cs b/Esiur/Data/DataConverter.cs index c23d5ed..4af0173 100644 --- a/Esiur/Data/DataConverter.cs +++ b/Esiur/Data/DataConverter.cs @@ -67,7 +67,7 @@ namespace Esiur.Data { try { -#if NETSTANDARD1_5 +#if NETSTANDARD if (destinationType.GetTypeInfo().IsInstanceOfType(v.GetValue(i))) #else if (destinationType.IsInstanceOfType(v.GetValue(i))) @@ -97,7 +97,7 @@ namespace Esiur.Data destinationType = underType; } -#if NETSTANDARD1_5 +#if NETSTANDARD if (destinationType.GetTypeInfo().IsInstanceOfType(value)) #else if (destinationType.IsInstanceOfType(value)) @@ -589,7 +589,7 @@ namespace Esiur.Data { try { -#if NETSTANDARD1_5 +#if NETSTANDARD var tryParse = typeof(T).GetTypeInfo().GetDeclaredMethod("TryParse"); if ((bool)tryParse.Invoke(null, new object[] { Input, null })) { diff --git a/Esiur/Data/KeyList.cs b/Esiur/Data/KeyList.cs index eaac428..e056154 100644 --- a/Esiur/Data/KeyList.cs +++ b/Esiur/Data/KeyList.cs @@ -220,7 +220,7 @@ namespace Esiur.Data public KeyList(object owner = null) { - #if NETSTANDARD1_5 + #if NETSTANDARD removableList = (typeof(IDestructible).GetTypeInfo().IsAssignableFrom(typeof(T).GetTypeInfo())); #else removableList = (typeof(IDestructible).IsAssignableFrom(typeof(T))); diff --git a/Esiur/Data/Structure.cs b/Esiur/Data/Structure.cs index b90f762..f17c138 100644 --- a/Esiur/Data/Structure.cs +++ b/Esiur/Data/Structure.cs @@ -31,6 +31,7 @@ using System.Threading.Tasks; using Esiur.Data; using Esiur.Misc; using Esiur.Core; +using System.Reflection; namespace Esiur.Data { @@ -61,6 +62,26 @@ namespace Esiur.Data return rt.TrimEnd('\r', '\n'); } + + public static Structure FromObject(object obj) + { + var type = obj.GetType(); + + if (obj is Structure) + return obj as Structure; + else if (Codec.IsAnonymous(type)) + { + var st = new Structure(); + + var pi = type.GetTypeInfo().GetProperties(); + foreach (var p in pi) + st[p.Name] = p.GetValue(obj); + + return st; + } + else + return null; + } public IEnumerator> GetEnumerator() { return dic.GetEnumerator(); diff --git a/Esiur/Esiur.csproj b/Esiur/Esiur.csproj index 9117ea0..92e44f1 100644 --- a/Esiur/Esiur.csproj +++ b/Esiur/Esiur.csproj @@ -1,23 +1,23 @@  - netstandard1.5 + netstandard2.0 Distributed Resources Platform Ahmed Kh. Zamil https://github.com/esiur/esiur-dotnet/blob/master/LICENSE http://www.esiur.com true - 1.2.7 + 1.4.0 https://github.com/esiur/esiur-dotnet Ahmed Kh. Zamil - 1.2.7.0 + 1.3.1.0 Esiur Foundation - 1.2.7.0 + 1.3.1.0 True - TRACE;DEBUG;NETSTANDARD1_5 + TRACE;DEBUG;NETSTANDARD @@ -46,6 +46,7 @@ + \ No newline at end of file diff --git a/Esiur/Net/DataLink/PacketServer.cs b/Esiur/Net/DataLink/PacketServer.cs index 8eac7da..1e62ab5 100644 --- a/Esiur/Net/DataLink/PacketServer.cs +++ b/Esiur/Net/DataLink/PacketServer.cs @@ -72,20 +72,20 @@ namespace Esiur.Net.DataLink { if (trigger == ResourceTrigger.Initialize) { - - foreach (Instance instance in Instance.Children) + /* + foreach (var resource in Instance.Children()) { - if (instance.Resource is PacketFilter) + if (resource is PacketFilter) { - filters.Add(instance.Resource as PacketFilter); + filters.Add(resource as PacketFilter); } - else if (instance.Resource is PacketSource) + else if (resource is PacketSource) { - sources.Add(instance.Resource as PacketSource); + sources.Add(resource as PacketSource); } } - + */ foreach (var src in sources) { src.OnNewPacket += PacketReceived; diff --git a/Esiur/Net/HTTP/HTTPServer.cs b/Esiur/Net/HTTP/HTTPServer.cs index 5b43499..5b6299a 100644 --- a/Esiur/Net/HTTP/HTTPServer.cs +++ b/Esiur/Net/HTTP/HTTPServer.cs @@ -44,6 +44,7 @@ namespace Esiur.Net.HTTP public class HTTPServer : NetworkServer, IResource { Dictionary sessions= new Dictionary(); + HTTPFilter[] filters = null; public Instance Instance { @@ -183,13 +184,8 @@ namespace Esiur.Net.HTTP { //Console.WriteLine("OUT: " + this.Connections.Count); - foreach (IResource resource in Instance.Children) - { - if (resource is HTTPFilter) - { - (resource as HTTPFilter).ClientDisconnected(sender); - } - } + foreach (var filter in filters) + filter.ClientDisconnected(sender); } @@ -268,16 +264,11 @@ namespace Esiur.Net.HTTP try { - foreach (IResource resource in Instance.Children) - { - if (resource is HTTPFilter) - { - if ((resource as HTTPFilter).Execute(sender)) - return; - } - } + foreach (var resource in filters) + resource.Execute(sender); + - sender.Send("Bad Request"); + sender.Send("Bad Request"); sender.Close(); } catch (Exception ex) @@ -289,21 +280,19 @@ namespace Esiur.Net.HTTP //Console.WriteLine(ex.ToString()); //EventLog.WriteEntry("HttpServer", ex.ToString(), EventLogEntryType.Error); - sender.Send(Return500(ex.Message)); + sender.Send(Error500(ex.Message)); } } } - private string Return500(string sMessage) + private string Error500(string msg) { - string sTMP = null; - sTMP = "500 Internal Server Error
\r\n"; - sTMP = sTMP + "
\r\n"; - sTMP = sTMP + "500 Sorry - Internal Server Error
" + sMessage + "\r\n"; - sTMP = sTMP + "
\r\n"; - sTMP = sTMP + "
\r\n"; - return sTMP; + return "500 Internal Server Error
\r\n" + + "
\r\n" + + "500 Internal Server Error
" + msg + "\r\n" + + "
\r\n" + + "
\r\n"; } @@ -382,6 +371,10 @@ namespace Esiur.Net.HTTP Trigger(ResourceTrigger.Terminate); Trigger(ResourceTrigger.Initialize); } + else if (trigger == ResourceTrigger.SystemInitialized) + { + Instance.Children().Then(x => filters = x); + } return new AsyncReply(true); @@ -395,12 +388,9 @@ namespace Esiur.Net.HTTP //Console.WriteLine("IN: " + this.Connections.Count); - foreach (var resource in Instance.Children) + foreach (var resource in filters) { - if (resource is HTTPFilter) - { - (resource as HTTPFilter).ClientConnected(sender); - } + resource.ClientConnected(sender); } } diff --git a/Esiur/Net/HTTP/IIPoWS.cs b/Esiur/Net/HTTP/IIPoWS.cs index f9cd2cd..73714f7 100644 --- a/Esiur/Net/HTTP/IIPoWS.cs +++ b/Esiur/Net/HTTP/IIPoWS.cs @@ -46,25 +46,31 @@ namespace Esiur.Net.HTTP public override bool Execute(HTTPConnection sender) { - if (DistributedServer == null) - return false; + if (sender.IsWebsocketRequest()) + { + if (DistributedServer == null) + return false; - var tcpSocket = sender.Unassign(); + var tcpSocket = sender.Unassign(); - if (tcpSocket == null) - return false; + if (tcpSocket == null) + return false; - var httpServer = sender.Parent; - var wsSocket = new WSSocket(tcpSocket); - httpServer.RemoveConnection(sender); + var httpServer = sender.Parent; + var wsSocket = new WSSocket(tcpSocket); + httpServer.RemoveConnection(sender); - var iipConnection = new DistributedConnection(); + var iipConnection = new DistributedConnection(); - DistributedServer.AddConnection(iipConnection); - iipConnection.Assign(wsSocket); - wsSocket.Begin(); + DistributedServer.AddConnection(iipConnection); + iipConnection.Assign(wsSocket); + wsSocket.Begin(); + + return true; + } + + return false; - return true; /* if (sender.Request.Filename.StartsWith("/iip/")) { diff --git a/Esiur/Net/IIP/DistributedConnection.cs b/Esiur/Net/IIP/DistributedConnection.cs index cd5a3dd..3c7e918 100644 --- a/Esiur/Net/IIP/DistributedConnection.cs +++ b/Esiur/Net/IIP/DistributedConnection.cs @@ -312,7 +312,7 @@ namespace Esiur.Net.IIP { var rt = packet.Parse(msg, offset, ends); //Console.WriteLine("Rec: " + chunkId + " " + packet.ToString()); - + /* if (packet.Command == IIPPacketCommand.Event) Console.WriteLine("Rec: " + packet.Command.ToString() + " " + packet.Event.ToString()); @@ -321,21 +321,27 @@ namespace Esiur.Net.IIP else Console.WriteLine("Rec: " + packet.Command.ToString() + " " + packet.Action.ToString() + " " + packet.ResourceId + " " + offset + "/" + ends); */ - + //packs.Add(packet.Command.ToString() + " " + packet.Action.ToString() + " " + packet.Event.ToString()); //if (packs.Count > 1) - // Console.WriteLine("P2"); + // Console.WriteLine("P2"); + + //Console.WriteLine(""); if (rt <= 0) { + //Console.WriteLine("Hold"); var size = ends - offset; data.HoldFor(msg, offset, size, size + (uint)(-rt)); return ends; } else { + + //Console.WriteLine($"CMD {packet.Command} {offset} {ends}"); + offset += (uint)rt; if (packet.Command == IIPPacket.IIPPacketCommand.Event) @@ -807,7 +813,9 @@ namespace Esiur.Net.IIP sock.Connect(domain, port).Then((x)=> { Assign(sock); //rt.trigger(true); - }).Error((x) => openReply.TriggerError(x)); + }).Error((x) => + openReply.TriggerError(x) + ); return openReply; } @@ -840,6 +848,65 @@ namespace Esiur.Net.IIP // nothing to do return true; } - + + AsyncReply IStore.AddChild(IResource parent, IResource child) + { + // not implemented + throw new NotImplementedException(); + } + + AsyncReply IStore.RemoveChild(IResource parent, IResource child) + { + // not implemeneted + throw new NotImplementedException(); + } + + public AsyncReply AddParent(IResource child, IResource parent) + { + throw new NotImplementedException(); + } + + public AsyncReply RemoveParent(IResource child, IResource parent) + { + throw new NotImplementedException(); + } + + public AsyncBag Children(IResource resource, string name) where T : IResource + { + throw new Exception("SS"); + + //if (Codec.IsLocalResource(resource, this)) + // return new AsyncBag((resource as DistributedResource).children.Where(x => x.GetType() == typeof(T)).Select(x => (T)x)); + + return null; + } + + public AsyncBag Parents(IResource resource, string name) where T : IResource + { + throw new Exception("SS"); + //if (Codec.IsLocalResource(resource, this)) + // return (resource as DistributedResource).parents.Where(x => x.GetType() == typeof(T)).Select(x => (T)x); + + return null; + } + + /* + public AsyncBag Children(IResource resource) + { + if (Codec.IsLocalResource(resource, this)) + return (resource as DistributedResource).children.Where(x => x.GetType() == typeof(T)).Select(x => (T)x); + + return null; + } + + public AsyncBag Parents(IResource resource) + { + if (Codec.IsLocalResource(resource, this)) + return (resource as DistributedResource).parents.Where(x => x.GetType() == typeof(T)).Select(x => (T)x); + + return null; + } + */ + } } diff --git a/Esiur/Net/IIP/DistributedConnectionProtocol.cs b/Esiur/Net/IIP/DistributedConnectionProtocol.cs index a58401c..7c0bc88 100644 --- a/Esiur/Net/IIP/DistributedConnectionProtocol.cs +++ b/Esiur/Net/IIP/DistributedConnectionProtocol.cs @@ -387,7 +387,10 @@ namespace Esiur.Net.IIP { Fetch(childId).Then(child => { - parent.Instance.Children.Add(child); + parent.children.Add(child); + child.parents.Add(parent); + + //parent.Instance.Children.Add(child); }); }); } @@ -398,7 +401,10 @@ namespace Esiur.Net.IIP { Fetch(childId).Then(child => { - parent.Instance.Children.Remove(child); + parent.children.Remove(child); + child.parents.Remove(parent); + +// parent.Instance.Children.Remove(child); }); }); } @@ -443,16 +449,16 @@ namespace Esiur.Net.IIP r.Instance.ResourceEventOccurred -= Instance_EventOccurred; r.Instance.ResourceModified -= Instance_PropertyModified; r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed; - r.Instance.Children.OnAdd -= Children_OnAdd; - r.Instance.Children.OnRemoved -= Children_OnRemoved; + // r.Instance.Children.OnAdd -= Children_OnAdd; + // r.Instance.Children.OnRemoved -= Children_OnRemoved; r.Instance.Attributes.OnModified -= Attributes_OnModified; // subscribe r.Instance.ResourceEventOccurred += Instance_EventOccurred; r.Instance.ResourceModified += Instance_PropertyModified; r.Instance.ResourceDestroyed += Instance_ResourceDestroyed; - r.Instance.Children.OnAdd += Children_OnAdd; - r.Instance.Children.OnRemoved += Children_OnRemoved; + //r.Instance.Children.OnAdd += Children_OnAdd; + //r.Instance.Children.OnRemoved += Children_OnRemoved; r.Instance.Attributes.OnModified += Attributes_OnModified; var link = DC.ToBytes(r.Instance.Link); @@ -520,6 +526,28 @@ namespace Esiur.Net.IIP .Done(); } + + public bool RemoveChild(IResource parent, IResource child) + { + SendEvent(IIPPacket.IIPPacketEvent.ChildRemoved) + .AddUInt32((parent as DistributedResource).Id) + .AddUInt32((child as DistributedResource).Id) + .Done(); + + return true; + } + + public bool AddChild(IResource parent, IResource child) + { + SendEvent(IIPPacket.IIPPacketEvent.ChildAdded) + .AddUInt32((parent as DistributedResource).Id) + .AddUInt32((child as DistributedResource).Id) + .Done(); + + return true; + } + + void IIPRequestReattachResource(uint callback, uint resourceId, ulong resourceAge) { Warehouse.Get(resourceId).Then((res) => @@ -531,16 +559,16 @@ namespace Esiur.Net.IIP r.Instance.ResourceEventOccurred -= Instance_EventOccurred; r.Instance.ResourceModified -= Instance_PropertyModified; r.Instance.ResourceDestroyed -= Instance_ResourceDestroyed; - r.Instance.Children.OnAdd -= Children_OnAdd; - r.Instance.Children.OnRemoved -= Children_OnRemoved; + //r.Instance.Children.OnAdd -= Children_OnAdd; + //r.Instance.Children.OnRemoved -= Children_OnRemoved; r.Instance.Attributes.OnModified -= Attributes_OnModified; // subscribe r.Instance.ResourceEventOccurred += Instance_EventOccurred; r.Instance.ResourceModified += Instance_PropertyModified; r.Instance.ResourceDestroyed += Instance_ResourceDestroyed; - r.Instance.Children.OnAdd += Children_OnAdd; - r.Instance.Children.OnRemoved += Children_OnRemoved; + //r.Instance.Children.OnAdd += Children_OnAdd; + //r.Instance.Children.OnRemoved += Children_OnRemoved; r.Instance.Attributes.OnModified += Attributes_OnModified; // reply ok @@ -646,7 +674,7 @@ namespace Esiur.Net.IIP Codec.ParseStructure(content, offset, cl, this).Then(values => { -#if NETSTANDARD1_5 +#if NETSTANDARD var constructors = Type.GetType(className).GetTypeInfo().GetConstructors(); #else var constructors = Type.GetType(className).GetConstructors(); @@ -791,7 +819,7 @@ namespace Esiur.Net.IIP return; } - parent.Instance.Children.Add(child); + parent.Instance.Store.AddChild(parent, child); SendReply(IIPPacket.IIPPacketAction.AddChild, callback).Done(); //child.Instance.Parents @@ -830,7 +858,7 @@ namespace Esiur.Net.IIP return; } - parent.Instance.Children.Remove(child); + parent.Instance.Store.RemoveChild(parent, child);// Children.Remove(child); SendReply(IIPPacket.IIPPacketAction.RemoveChild, callback).Done(); //child.Instance.Parents @@ -871,9 +899,14 @@ namespace Esiur.Net.IIP return; } - SendReply(IIPPacket.IIPPacketAction.ResourceChildren, callback) - .AddUInt8Array(Codec.ComposeResourceArray(resource.Instance.Children.ToArray(), this, true)) - .Done(); + resource.Instance.Children().Then(children => + { + SendReply(IIPPacket.IIPPacketAction.ResourceChildren, callback) + .AddUInt8Array(Codec.ComposeResourceArray(children, this, true)) + .Done(); + + }); + }); } @@ -888,9 +921,14 @@ namespace Esiur.Net.IIP return; } - SendReply(IIPPacket.IIPPacketAction.ResourceParents, callback) - .AddUInt8Array(Codec.ComposeResourceArray(resource.Instance.Parents.ToArray(), this, true)) - .Done(); + resource.Instance.Parents().Then(parents => + { + SendReply(IIPPacket.IIPPacketAction.ResourceParents, callback) + .AddUInt8Array(Codec.ComposeResourceArray(parents, this, true)) + .Done(); + + }); + }); } @@ -1010,7 +1048,8 @@ namespace Esiur.Net.IIP void IIPRequestQueryResources(uint callback, string resourceLink) { - Warehouse.Query(resourceLink).Then((r) => + + Action queryCallback = (r) => { //if (r != null) //{ @@ -1022,12 +1061,12 @@ namespace Esiur.Net.IIP SendReply(IIPPacket.IIPPacketAction.QueryLink, callback) .AddUInt8Array(Codec.ComposeResourceArray(list, this, true)) .Done(); - //} - //else - //{ - // reply failed - //} - }); + }; + + if (Server?.EntryPoint != null) + Server.EntryPoint.Query(resourceLink, this).Then(queryCallback); + else + Warehouse.Query(resourceLink).ContinueWith(x => queryCallback(x.Result)); } void IIPRequestResourceAttribute(uint callback, uint resourceId) @@ -1068,7 +1107,7 @@ namespace Esiur.Net.IIP } else { -#if NETSTANDARD1_5 +#if NETSTANDARD var fi = r.GetType().GetTypeInfo().GetMethod(ft.Name); #else var fi = r.GetType().GetMethod(ft.Name); @@ -1134,7 +1173,7 @@ namespace Esiur.Net.IIP { (rt as Task).ContinueWith(t => { -#if NETSTANDARD1_5 +#if NETSTANDARD var res = t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t); #else var res = t.GetType().GetProperty("Result").GetValue(t); @@ -1225,7 +1264,7 @@ namespace Esiur.Net.IIP } else { -#if NETSTANDARD1_5 +#if NETSTANDARD var fi = r.GetType().GetTypeInfo().GetMethod(ft.Name); #else var fi = r.GetType().GetMethod(ft.Name); @@ -1285,7 +1324,7 @@ namespace Esiur.Net.IIP { (rt as Task).ContinueWith(t => { -#if NETSTANDARD1_5 +#if NETSTANDARD var res = t.GetType().GetTypeInfo().GetProperty("Result").GetValue(t); #else var res = t.GetType().GetProperty("Result").GetValue(t); @@ -1358,7 +1397,7 @@ namespace Esiur.Net.IIP } else { -#if NETSTANDARD1_5 +#if NETSTANDARD var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); #else var pi = r.GetType().GetProperty(pt.Name); @@ -1435,7 +1474,7 @@ namespace Esiur.Net.IIP { if (r.Instance.GetAge(index) > age) { -#if NETSTANDARD1_5 +#if NETSTANDARD var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); #else var pi = r.GetType().GetProperty(pt.Name); @@ -1498,7 +1537,7 @@ namespace Esiur.Net.IIP { /* -#if NETSTANDARD1_5 +#if NETSTANDARD var pi = r.GetType().GetTypeInfo().GetProperty(pt.Name); #else var pi = r.GetType().GetProperty(pt.Name); @@ -1748,12 +1787,18 @@ namespace Esiur.Net.IIP Query(path).Then(ar => { + + //if (filter != null) + // ar = ar?.Where(filter).ToArray(); + + // MISSING: should dispatch the unused resources. if (ar?.Length > 0) rt.Trigger(ar[0]); else rt.Trigger(null); }).Error(ex => rt.TriggerError(ex)); + return rt; /* diff --git a/Esiur/Net/IIP/DistributedResource.cs b/Esiur/Net/IIP/DistributedResource.cs index dbd6519..dfcc2c7 100644 --- a/Esiur/Net/IIP/DistributedResource.cs +++ b/Esiur/Net/IIP/DistributedResource.cs @@ -53,7 +53,7 @@ namespace Esiur.Net.IIP /// Raised when the distributed resource is destroyed. /// public event DestroyedEvent OnDestroy; - + public event Instance.ResourceModifiedEvent OnModified; uint instanceId; DistributedConnection connection; @@ -68,6 +68,9 @@ namespace Esiur.Net.IIP //ulong age; //ulong[] ages; object[] properties; + internal List parents = new List(); + internal List children = new List(); + DistributedResourceEvent[] events; //ResourceTemplate template; @@ -171,10 +174,12 @@ namespace Esiur.Net.IIP this.link = link; this.connection = connection; this.instanceId = instanceId; + //this.Instance.Template = template; //this.Instance.Age = age; //this.template = template; //this.age = age; + } internal void _Ready() @@ -270,16 +275,12 @@ namespace Esiur.Net.IIP if (args.Length == 1) { // Detect anonymous types - var type = args[0].GetType().GetTypeInfo(); - var hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0; - var nameContainsAnonymousType = type.FullName.Contains("AnonymousType"); - var isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType; - - if (isAnonymousType) + var type = args[0].GetType(); + if (Codec.IsAnonymous(type)) { var namedArgs = new Structure(); - var pi = type.GetProperties(); + var pi = type.GetTypeInfo().GetProperties(); foreach (var p in pi) namedArgs[p.Name] = p.GetValue(args[0]); result = _InvokeByNamedArguments(ft.Index, namedArgs); @@ -453,9 +454,10 @@ namespace Esiur.Net.IIP public DistributedResource() { //stack = new DistributedResourceStack(this); + //this.Instance.ResourceModified += this.OnModified; } - + /// /// Resource interface. /// @@ -463,6 +465,10 @@ namespace Esiur.Net.IIP /// public AsyncReply Trigger(ResourceTrigger trigger) { + + if (trigger == ResourceTrigger.Initialize) + this.Instance.ResourceModified += this.OnModified; + // do nothing. return new AsyncReply(true); } diff --git a/Esiur/Net/IIP/DistributedServer.cs b/Esiur/Net/IIP/DistributedServer.cs index b3611c9..04e2b02 100644 --- a/Esiur/Net/IIP/DistributedServer.cs +++ b/Esiur/Net/IIP/DistributedServer.cs @@ -56,6 +56,12 @@ namespace Esiur.Net.IIP set; } + public EntryPoint EntryPoint + { + get; + set; + } + //[Storable] //[ResourceProperty] public ushort port @@ -128,8 +134,7 @@ namespace Esiur.Net.IIP protected override void ClientConnected(DistributedConnection sender) { - Console.WriteLine("DistributedConnection Client Connected"); - + //Console.WriteLine("DistributedConnection Client Connected"); } private void Sender_OnReady(DistributedConnection sender) diff --git a/Esiur/Net/IIP/EntryPoint.cs b/Esiur/Net/IIP/EntryPoint.cs new file mode 100644 index 0000000..253b528 --- /dev/null +++ b/Esiur/Net/IIP/EntryPoint.cs @@ -0,0 +1,40 @@ +/* + +Copyright (c) 2019 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.Text; +using Esiur.Core; +using Esiur.Data; +using Esiur.Resource; +using Esiur.Resource.Template; + +namespace Esiur.Net.IIP +{ + public abstract class EntryPoint : Esiur.Resource.Resource + { + + public abstract AsyncReply Query(string path, DistributedConnection sender); + public abstract override bool Create(); + } +} diff --git a/Esiur/Net/Packets/IIPPacket.cs b/Esiur/Net/Packets/IIPPacket.cs index 5cfb1aa..ade655a 100644 --- a/Esiur/Net/Packets/IIPPacket.cs +++ b/Esiur/Net/Packets/IIPPacket.cs @@ -209,8 +209,8 @@ namespace Esiur.Net.Packets { if (offset + needed > ends) { - //dataLengthNeeded = needed - (ends - offset); - dataLengthNeeded = needed - (ends - originalOffset); + dataLengthNeeded = needed - (ends - offset); + //dataLengthNeeded = needed - (ends - originalOffset); return true; } diff --git a/Esiur/Net/Sockets/TCPSocket.cs b/Esiur/Net/Sockets/TCPSocket.cs index 9cb77fd..75b58f1 100644 --- a/Esiur/Net/Sockets/TCPSocket.cs +++ b/Esiur/Net/Sockets/TCPSocket.cs @@ -53,6 +53,7 @@ namespace Esiur.Net.Sockets bool asyncSending; bool began = false; + SocketState state = SocketState.Initial; public event ISocketReceiveEvent OnReceive; @@ -313,23 +314,23 @@ namespace Esiur.Net.Sockets public void Close() { if (state != SocketState.Closed && state != SocketState.Terminated) + { state = SocketState.Closed; - if (sock.Connected) - { - try + if (sock.Connected) { - sock.Shutdown(SocketShutdown.Both); - } - catch - { - state = SocketState.Terminated; + try + { + sock.Shutdown(SocketShutdown.Both); + } + catch + { + state = SocketState.Terminated; + } } - sock.Shutdown(SocketShutdown.Both);// Close(); OnClose?.Invoke(); } - } public void Send(byte[] message) diff --git a/Esiur/Net/TCP/TCPServer.cs b/Esiur/Net/TCP/TCPServer.cs index 3c2ccd5..05dbe73 100644 --- a/Esiur/Net/TCP/TCPServer.cs +++ b/Esiur/Net/TCP/TCPServer.cs @@ -65,18 +65,24 @@ namespace Esiur.Net.TCP } public Instance Instance { get; set; } + TCPFilter[] filters = null; + + public AsyncReply Trigger(ResourceTrigger trigger) { if (trigger == ResourceTrigger.Initialize) { TCPSocket listener; + if (ip != null) listener =new TCPSocket(new IPEndPoint(IPAddress.Parse(ip), port)); else listener = new TCPSocket(new IPEndPoint(IPAddress.Any, port)); Start(listener, timeout, clock); + + } else if (trigger == ResourceTrigger.Terminate) { @@ -87,6 +93,10 @@ namespace Esiur.Net.TCP Trigger(ResourceTrigger.Terminate); Trigger(ResourceTrigger.Initialize); } + else if (trigger == ResourceTrigger.SystemInitialized) + { + Instance.Children().Then(x => filters = x); + } return new AsyncReply(true); } @@ -97,14 +107,10 @@ namespace Esiur.Net.TCP { var msg = data.Read(); - foreach (var resource in Instance.Children) - { - if (resource is TCPFilter) - { - var f = resource as TCPFilter; - if (f.Execute(msg, data, sender)) + foreach (var filter in filters) + { + if (filter.Execute(msg, data, sender)) return; - } } } @@ -115,25 +121,17 @@ namespace Esiur.Net.TCP protected override void ClientConnected(TCPConnection sender) { - foreach (var resource in Instance.Children) + foreach (var filter in filters) { - if (resource is TCPFilter) - { - var f = resource as TCPFilter; - f.Connected(sender); - } + filter.Connected(sender); } } protected override void ClientDisconnected(TCPConnection sender) { - foreach (var resource in Instance.Children) + foreach (var filter in filters) { - if (resource is TCPFilter) - { - var f = resource as TCPFilter; - f.Disconnected(sender); - } + filter.Disconnected(sender); } } diff --git a/Esiur/Properties/PublishProfiles/FolderProfile.pubxml b/Esiur/Properties/PublishProfiles/FolderProfile.pubxml index 2716dc5..7f2b03b 100644 --- a/Esiur/Properties/PublishProfiles/FolderProfile.pubxml +++ b/Esiur/Properties/PublishProfiles/FolderProfile.pubxml @@ -5,8 +5,8 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem - Release - netstandard1.5 + Debug + netstandard2.0 M:\opt\esiur Any CPU diff --git a/Esiur/Proxy/ResourceProxy.cs b/Esiur/Proxy/ResourceProxy.cs index 45300f6..050aff0 100644 --- a/Esiur/Proxy/ResourceProxy.cs +++ b/Esiur/Proxy/ResourceProxy.cs @@ -12,7 +12,7 @@ namespace Esiur.Proxy { static Dictionary cache = new Dictionary(); -#if NETSTANDARD1_5 +#if NETSTANDARD static MethodInfo modifyMethod = typeof(Instance).GetTypeInfo().GetMethod("Modified"); static MethodInfo instanceGet = typeof(IResource).GetTypeInfo().GetProperty("Instance").GetGetMethod(); #else @@ -44,7 +44,7 @@ namespace Esiur.Proxy if (cache.ContainsKey(type)) return cache[type]; -#if NETSTANDARD1_5 +#if NETSTANDARD var typeInfo = type.GetTypeInfo(); if (typeInfo.IsSealed) @@ -78,7 +78,7 @@ namespace Esiur.Proxy -#if NETSTANDARD1_5 +#if NETSTANDARD var t = typeBuilder.CreateTypeInfo().AsType(); cache.Add(type, t); return t; diff --git a/Esiur/Resource/IStore.cs b/Esiur/Resource/IStore.cs index 577a252..f446451 100644 --- a/Esiur/Resource/IStore.cs +++ b/Esiur/Resource/IStore.cs @@ -35,13 +35,27 @@ namespace Esiur.Resource { public interface IStore:IResource { - AsyncReply Get(string path); - AsyncReply Retrieve(uint iid); + AsyncReply Get(string path);//, Func filter = null); + //AsyncReply Retrieve(uint iid); bool Put(IResource resource); string Link(IResource resource); bool Record(IResource resource, string propertyName, object value, ulong age, DateTime dateTime); bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime); bool Remove(IResource resource); + + + AsyncReply AddChild(IResource parent, IResource child); + AsyncReply RemoveChild(IResource parent, IResource child); + + AsyncReply AddParent(IResource child, IResource parent); + AsyncReply RemoveParent(IResource child, IResource parent); + + + AsyncBag Children(IResource resource, string name) where T : IResource; + AsyncBag Parents(IResource resource, string name) where T : IResource; + + + //AsyncReply GetPropertyRecord(IResource resource, string propertyName, ulong fromAge, ulong toAge); //AsyncReply GetPropertyRecordByDate(IResource resource, string propertyName, DateTime fromDate, DateTime toDate); diff --git a/Esiur/Resource/Instance.cs b/Esiur/Resource/Instance.cs index 1524b92..f4cd329 100644 --- a/Esiur/Resource/Instance.cs +++ b/Esiur/Resource/Instance.cs @@ -12,6 +12,7 @@ using Esiur.Security.Permissions; using Esiur.Resource.Template; using Esiur.Security.Authority; using Esiur.Proxy; +using Esiur.Core; namespace Esiur.Resource { @@ -19,16 +20,17 @@ namespace Esiur.Resource { string name; - AutoList children;// = new AutoList(); - IResource resource; + //IQueryable children;// + //AutoList children;// = new AutoList(); + WeakReference resource; IStore store; - AutoList parents;// = new AutoList(); + //AutoList parents;// = new AutoList(); //bool inherit; ResourceTemplate template; AutoList managers;// = new AutoList(); - + public delegate void ResourceModifiedEvent(IResource resource, string propertyName, object newValue); //public delegate void ResourceEventOccurredEvent(IResource resource, string eventName, string[] users, DistributedConnection[] connections, object[] args); @@ -40,6 +42,8 @@ namespace Esiur.Resource public event ResourceEventOccurredEvent ResourceEventOccurred; public event ResourceDestroyedEvent ResourceDestroyed; + bool loading = false; + KeyList attributes; List ages = new List(); @@ -90,7 +94,7 @@ namespace Esiur.Resource attributes = clone.ToArray();// this.attributes.Keys.ToList().Add("managers"); } - foreach(var attr in attributes) + foreach (var attr in attributes) { if (attr == "name") st["name"] = this.name; @@ -109,15 +113,15 @@ namespace Esiur.Resource } else if (attr == "parents") { - st["parents"] = parents.ToArray(); + //st["parents"] = parents.ToArray(); } else if (attr == "children") { - st["children"] = children.ToArray(); + //st["children"] = children.ToArray(); } else if (attr == "childrenCount") { - st["childrenCount"] = children.Count; + //st["childrenCount"] = children.Count; } else if (attr == "type") { @@ -131,7 +135,7 @@ namespace Esiur.Resource } public bool SetAttributes(Structure attributes, bool clearAttributes = false) - { + { try { @@ -155,8 +159,13 @@ namespace Esiur.Resource { var settings = m["settings"] as Structure; var manager = Activator.CreateInstance(type) as IPermissionsManager; - manager.Initialize(settings, this.resource); - this.managers.Add(manager); + + IResource res; + if (this.resource.TryGetTarget(out res)) + { + manager.Initialize(settings, res); + this.managers.Add(manager); + } } else return false; @@ -166,7 +175,7 @@ namespace Esiur.Resource { this.attributes[attr.Key] = attr.Value; } - + } catch { @@ -175,7 +184,7 @@ namespace Esiur.Resource return true; } - + /* public Structure GetAttributes() { @@ -256,7 +265,7 @@ namespace Esiur.Resource return DateTime.MinValue; } - + /// /// Load property value (used by stores) /// @@ -266,13 +275,19 @@ namespace Esiur.Resource /// public bool LoadProperty(string name, ulong age, DateTime modificationDate, object value) { + + IResource res; + + if (!resource.TryGetTarget(out res)) + return false; + var pt = template.GetPropertyTemplateByName(name); if (pt == null) return false; /* -#if NETSTANDARD1_5 +#if NETSTANDARD var pi = resource.GetType().GetTypeInfo().GetProperty(name, new[] { resource.GetType() }); #else var pi = resource.GetType().GetProperty(pt.Name); @@ -282,18 +297,25 @@ namespace Esiur.Resource if (pt.Info.PropertyType == typeof(DistributedPropertyContext)) return false; - - try + + if (pt.Info.CanWrite) { - if (pt.Info.CanWrite) - pt.Info.SetValue(resource, DC.CastConvert(value, pt.Info.PropertyType)); - } - catch(Exception ex) - { - //Console.WriteLine(resource.ToString() + " " + name); - Global.Log(ex); + try + { + loading = true; + + pt.Info.SetValue(res, DC.CastConvert(value, pt.Info.PropertyType)); + } + catch (Exception ex) + { + //Console.WriteLine(resource.ToString() + " " + name); + Global.Log(ex); + } + + loading = false; } + SetAge(pt.Index, age); SetModificationDate(pt.Index, modificationDate); @@ -359,16 +381,19 @@ namespace Esiur.Resource foreach (var pt in template.Properties) { /* -#if NETSTANDARD1_5 +#if NETSTANDARD var pi = resource.GetType().GetTypeInfo().GetProperty(pt.Name); #else var pi = resource.GetType().GetProperty(pt.Name); #endif */ - - var rt = pt.Info.GetValue(resource, null); - props.Add(new PropertyValue(rt, ages[pt.Index], modificationDates[pt.Index])); + IResource res; + if (resource.TryGetTarget(out res)) + { + var rt = pt.Info.GetValue(res, null); + props.Add(new PropertyValue(rt, ages[pt.Index], modificationDates[pt.Index])); + } } return props.ToArray(); @@ -446,7 +471,7 @@ namespace Esiur.Resource /// public bool IsStorable() { -#if NETSTANDARD1_5 +#if NETSTANDARD var attrs = resource.GetType().GetTypeInfo().GetCustomAttributes(typeof(Storable), true).ToArray(); #else var attrs = resource.GetType().GetCustomAttributes(typeof(Storable), true); @@ -458,22 +483,27 @@ namespace Esiur.Resource internal void EmitModification(PropertyTemplate pt, object value) { - instanceAge++; - var now = DateTime.UtcNow; - ages[pt.Index] = instanceAge; - modificationDates[pt.Index] = now; - - if (pt.Storage == StorageMode.NonVolatile) + IResource res; + if (this.resource.TryGetTarget(out res)) { - store.Modify(resource, pt.Name, value, ages[pt.Index], now); - } - else if (pt.Storage == StorageMode.Recordable) - { - store.Record(resource, pt.Name, value, ages[pt.Index], now); - } + instanceAge++; + var now = DateTime.UtcNow; - ResourceModified?.Invoke(resource, pt.Name, value); + ages[pt.Index] = instanceAge; + modificationDates[pt.Index] = now; + + if (pt.Storage == StorageMode.NonVolatile) + { + store.Modify(res, pt.Name, value, ages[pt.Index], now); + } + else if (pt.Storage == StorageMode.Recordable) + { + store.Record(res, pt.Name, value, ages[pt.Index], now); + } + + ResourceModified?.Invoke(res, pt.Name, value); + } } /// @@ -484,6 +514,9 @@ namespace Esiur.Resource /// public void Modified([CallerMemberName] string propertyName = "") { + if (loading) + return; + object value; if (GetPropertyValue(propertyName, out value)) { @@ -496,7 +529,12 @@ namespace Esiur.Resource internal void EmitResourceEvent(object issuer, Session[] receivers, string name, object[] args) { - ResourceEventOccurred?.Invoke(resource, issuer, receivers, name, args); + IResource res; + if (this.resource.TryGetTarget(out res)) + { + + ResourceEventOccurred?.Invoke(res, issuer, receivers, name, args); + } } /// @@ -508,7 +546,7 @@ namespace Esiur.Resource public bool GetPropertyValue(string name, out object value) { /* -#if NETSTANDARD1_5 +#if NETSTANDARD PropertyInfo pi = resource.GetType().GetTypeInfo().GetProperty(name, BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); #else @@ -521,7 +559,7 @@ namespace Esiur.Resource if (pt != null && pt.Info != null) { /* -#if NETSTANDARD1_5 +#if NETSTANDARD object[] ca = pi.GetCustomAttributes(typeof(ResourceProperty), false).ToArray(); #else @@ -537,7 +575,15 @@ namespace Esiur.Resource } */ - value = pt.Info.GetValue(resource, null); + IResource res; + if (resource.TryGetTarget(out res)) + value = pt.Info.GetValue(res, null); + else + { + value = null; + return false; + } + return true; } @@ -556,10 +602,7 @@ namespace Esiur.Resource /// /// List of parents. /// - public AutoList Parents - { - get { return parents; } - } + //public AutoList Parents => parents; /// /// Store responsible for creating and keeping the resource. @@ -572,15 +615,54 @@ namespace Esiur.Resource /// /// List of children. /// - public AutoList Children - { - get { return children; } - } + // public AutoList Children => children; /// /// The unique and permanent link to the resource. /// public string Link + { + get + { + IResource res; + if (this.resource.TryGetTarget(out res)) + { + if (res == res.Instance.store) + return name; // root store + else + return store.Link(res); + } + else + return null; + } + } + + public AsyncBag Children(string name = null) where T : IResource + { + IResource res; + if (this.resource.TryGetTarget(out res)) + { + //if (!(store is null)) + return store.Children(res, name); + //else + // return (res as IStore).Children(res, name); + } + else + return new AsyncBag(null); + } + + public AsyncBag Parents(string name = null) where T : IResource + { + IResource res; + if (this.resource.TryGetTarget(out res)) + { + return store.Parents(res, name); + } + else + return new AsyncBag(null); + } + + /* { get { @@ -607,6 +689,8 @@ namespace Esiur.Resource } } } + * + */ /// /// Instance name. @@ -623,7 +707,16 @@ namespace Esiur.Resource /// public IResource Resource { - get { return resource; } + get + { + IResource res; + if (this.resource.TryGetTarget(out res)) + { + return res; + } + else + return null; + } } /// @@ -658,11 +751,15 @@ namespace Esiur.Resource /// Ruling. public Ruling Applicable(Session session, ActionType action, MemberTemplate member, object inquirer = null) { - foreach (IPermissionsManager manager in managers) + IResource res; + if (this.resource.TryGetTarget(out res)) { - var r = manager.Applicable(this.resource, session, action, member, inquirer); - if (r != Ruling.DontCare) - return r; + foreach (IPermissionsManager manager in managers) + { + var r = manager.Applicable(res, session, action, member, inquirer); + if (r != Ruling.DontCare) + return r; + } } return Ruling.DontCare; @@ -684,19 +781,19 @@ namespace Esiur.Resource public Instance(uint id, string name, IResource resource, IStore store, ResourceTemplate customTemplate = null, ulong age = 0) { this.store = store; - this.resource = resource; + this.resource = new WeakReference(resource); this.id = id; this.name = name; this.instanceAge = age; this.attributes = new KeyList(this); - children = new AutoList(this); - parents = new AutoList(this); + //children = new AutoList(this); + //parents = new AutoList(this); managers = new AutoList(this); - children.OnAdd += Children_OnAdd; - children.OnRemoved += Children_OnRemoved; - parents.OnAdd += Parents_OnAdd; - parents.OnRemoved += Parents_OnRemoved; + //children.OnAdd += Children_OnAdd; + //children.OnRemoved += Children_OnRemoved; + //parents.OnAdd += Parents_OnAdd; + //parents.OnRemoved += Parents_OnRemoved; resource.OnDestroy += Resource_OnDestroy; @@ -715,7 +812,7 @@ namespace Esiur.Resource // connect events Type t = ResourceProxy.GetBaseType(resource); -#if NETSTANDARD1_5 +#if NETSTANDARD var events = t.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); #else @@ -783,7 +880,12 @@ namespace Esiur.Resource } } - private void Children_OnRemoved(Instance parent, IResource value) + + //IQueryable Children => store.GetChildren(this); + + + /* + * private void Children_OnRemoved(Instance parent, IResource value) { value.Instance.parents.Remove(resource); } @@ -804,7 +906,7 @@ namespace Esiur.Resource if (!value.Instance.children.Contains(resource)) value.Instance.children.Add(resource); } - + */ private void Resource_OnDestroy(object sender) { diff --git a/Esiur/Resource/Resource.cs b/Esiur/Resource/Resource.cs index 0b31c8f..990785c 100644 --- a/Esiur/Resource/Resource.cs +++ b/Esiur/Resource/Resource.cs @@ -36,7 +36,7 @@ namespace Esiur.Resource public virtual void Destroy() { - + OnDestroy?.Invoke(this); } public virtual AsyncReply Trigger(ResourceTrigger trigger) @@ -51,5 +51,10 @@ namespace Esiur.Resource { return true; } + + ~Resource() + { + Destroy(); + } } } diff --git a/Esiur/Resource/ResourceQuery.cs b/Esiur/Resource/ResourceQuery.cs new file mode 100644 index 0000000..fb08457 --- /dev/null +++ b/Esiur/Resource/ResourceQuery.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace Esiur.Resource +{ + public class ResourceQuery : IQueryable + { + public Type ElementType => throw new NotImplementedException(); + + public Expression Expression => throw new NotImplementedException(); + + public IQueryProvider Provider => throw new NotImplementedException(); + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } +} diff --git a/Esiur/Resource/Template/ResourceTemplate.cs b/Esiur/Resource/Template/ResourceTemplate.cs index 6e05d58..6b7e1f1 100644 --- a/Esiur/Resource/Template/ResourceTemplate.cs +++ b/Esiur/Resource/Template/ResourceTemplate.cs @@ -139,7 +139,7 @@ namespace Esiur.Resource.Template className = type.FullName; -#if NETSTANDARD1_5 +#if NETSTANDARD PropertyInfo[] propsInfo = type.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); EventInfo[] eventsInfo = type.GetTypeInfo().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); MethodInfo[] methodsInfo = type.GetTypeInfo().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); diff --git a/Esiur/Resource/Warehouse.cs b/Esiur/Resource/Warehouse.cs index 45ea9e6..6453e60 100644 --- a/Esiur/Resource/Warehouse.cs +++ b/Esiur/Resource/Warehouse.cs @@ -32,6 +32,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Esiur.Net.IIP; +using System.Text.RegularExpressions; namespace Esiur.Resource { @@ -40,13 +42,13 @@ namespace Esiur.Resource { //static byte prefixCounter; - static AutoList stores = new AutoList(null); - static Dictionary resources = new Dictionary(); + static AutoList stores = new AutoList(null); + static Dictionary> resources = new Dictionary>(); static uint resourceCounter = 0; static KeyList templates = new KeyList(); - static bool storeIsOpen = false; + static bool warehouseIsOpen = false; public delegate void StoreConnectedEvent(IStore store, string name); public delegate void StoreDisconnectedEvent(IStore store); @@ -54,7 +56,15 @@ namespace Esiur.Resource public static event StoreConnectedEvent StoreConnected; public static event StoreDisconnectedEvent StoreDisconnected; - public static KeyList> Protocols { get; } = new KeyList>(); + public static KeyList> Protocols { get; } = getSupportedProtocols(); + + + static KeyList> getSupportedProtocols() + { + var rt = new KeyList>(); + rt.Add("iip", () => new DistributedConnection()); + return rt; + } /// /// Get a store by its name. @@ -77,7 +87,13 @@ namespace Esiur.Resource public static AsyncReply Get(uint id) { if (resources.ContainsKey(id)) - return new AsyncReply(resources[id]); + { + IResource r; + if (resources[id].TryGetTarget(out r)) + return new AsyncReply(r); + else + return new AsyncReply(null); + } else return new AsyncReply(null); } @@ -109,7 +125,11 @@ namespace Esiur.Resource var rBag = new AsyncBag(); foreach (var rk in resources) - rBag.Add(rk.Value.Trigger(ResourceTrigger.SystemInitialized)); + { + IResource r; + if (rk.Value.TryGetTarget(out r)) + rBag.Add(r.Trigger(ResourceTrigger.SystemInitialized)); + } rBag.Seal(); @@ -123,7 +143,7 @@ namespace Esiur.Resource } rt.Trigger(true); - storeIsOpen = true; + warehouseIsOpen = true; }); }); @@ -142,15 +162,30 @@ namespace Esiur.Resource var bag = new AsyncBag(); foreach (var resource in resources.Values) - if (!(resource is IStore)) - bag.Add(resource.Trigger(ResourceTrigger.Terminate)); + { + IResource r; + if (resource.TryGetTarget(out r)) + { + if (!(r is IStore)) + bag.Add(r.Trigger(ResourceTrigger.Terminate)); + + } + } foreach (var store in stores) bag.Add(store.Trigger(ResourceTrigger.Terminate)); + foreach (var resource in resources.Values) - if (!(resource is IStore)) - bag.Add(resource.Trigger(ResourceTrigger.SystemTerminated)); + { + IResource r; + if (resource.TryGetTarget(out r)) + { + if (!(r is IStore)) + bag.Add(r.Trigger(ResourceTrigger.SystemTerminated)); + } + } + foreach (var store in stores) bag.Add(store.Trigger(ResourceTrigger.SystemTerminated)); @@ -174,7 +209,8 @@ namespace Esiur.Resource } - private static IResource[] QureyIn(string[] path, int index, AutoList resources) + /* + private static IResource[] QureyIn(string[] path, int index, IEnumerable resources)// AutoList resources) { var rt = new List(); @@ -191,18 +227,16 @@ namespace Esiur.Resource else foreach (IResource child in resources) if (child.Instance.Name == path[index]) - rt.AddRange(QureyIn(path, index+1, child.Instance.Children)); + rt.AddRange(QureyIn(path, index+1, child.Instance.Children())); return rt.ToArray(); } public static AsyncReply Query(string path) { - - if (path == null || path == "") { - var roots = stores.Where(s => s.Instance.Parents.Count == 0).ToArray(); + var roots = stores.Where(s => s.Instance.Parents().Count() == 0).ToArray(); return new AsyncReply(roots); } else @@ -229,6 +263,51 @@ namespace Esiur.Resource } } + */ + + + + public static async Task Query(string path) + { + var rt = new AsyncReply(); + + var p = path.Trim().Split('/'); + IResource resource; + + foreach (var store in stores) + if (p[0] == store.Instance.Name) + { + + if (p.Length == 1) + return new IResource[] { store }; + + var res = await store.Get(String.Join("/", p.Skip(1).ToArray())); + if (res != null) + return new IResource[] { res }; + + + resource = store; + for (var i = 1; i < p.Length; i++) + { + var children = await resource.Instance.Children(p[i]); + if (children.Length > 0) + { + if (i == p.Length - 1) + return children; + else + resource = children[0]; + } + else + break; + } + + return null; + } + + + + return null; + } /// /// Get a resource by its path. @@ -236,38 +315,15 @@ namespace Esiur.Resource /// /// /// Resource instance. - public static AsyncReply Get(string path, Structure attributes = null, IResource parent = null, IPermissionsManager manager = null) + public static AsyncReply Get(string path, object attributes = null, IResource parent = null, IPermissionsManager manager = null) { - var p = path.Split('/'); - IResource res; - foreach(IStore d in stores) - if (p[0] == d.Instance.Name) - { - var i = 1; - res = d; - while(p.Length > i) - { - var si = i; - - foreach (IResource r in res.Instance.Children) - if (r.Instance.Name == p[i]) - { - i++; - res = r; - break; - } - - if (si == i) - // not found, ask the store - return d.Get(path.Substring(p[0].Length + 1)); - } - - return new AsyncReply(res); - } + var rt = new AsyncReply(); + // Should we create a new store ? + if (path.Contains("://")) { var url = path.Split(new string[] { "://" }, 2, StringSplitOptions.None); @@ -275,17 +331,18 @@ namespace Esiur.Resource var pathname = string.Join("/", url[1].Split(new char[] { '/' }).Skip(1)); - var rt = new AsyncReply(); - if (Protocols.ContainsKey(url[0])) { var handler = Protocols[url[0]]; - var store = handler();// Activator.CreateInstance(handler.GetType()) as IStore; - Put(store, url[0] + "://" + hostname, null, parent, null, 0, manager, attributes); + var store = handler(); + Put(store, hostname, null, parent, null, 0, manager, attributes); store.Trigger(ResourceTrigger.Open).Then(x => { + + warehouseIsOpen = true; + if (pathname.Length > 0 && pathname != "") store.Get(pathname).Then(r => { rt.Trigger(r); @@ -296,13 +353,24 @@ namespace Esiur.Resource rt.TriggerError(e); Warehouse.Remove(store); }); + + return rt; } - - return rt; } + + + Query(path).ContinueWith(rs => + { + // rt.TriggerError(new Exception()); + if (rs.Result != null && rs.Result.Length > 0) + rt.Trigger(rs.Result[0]); + else + rt.Trigger(null); + }); + + return rt; + - - return new AsyncReply(null); } /// @@ -312,12 +380,31 @@ namespace Esiur.Resource /// Resource name. /// 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 void Put(IResource resource, string name, IStore store = null, IResource parent = null, ResourceTemplate customTemplate = null, ulong age = 0, IPermissionsManager manager = null, Structure attributes = null) + public static void Put(IResource resource, string name, IStore store = null, IResource parent = null, ResourceTemplate customTemplate = null, ulong age = 0, IPermissionsManager manager = null, object attributes = null) { + + if (store == null) + { + // assign parent as a store + if (parent is IStore) + store = (IStore)parent; + // assign parent's store as a store + else if (parent != null) + store = parent.Instance.Store; + // assign self as a store (root store) + else if (resource is IStore) + { + store = (IStore)resource; + stores.Add(resource as IStore); + } + else + throw new Exception("Can't find a store for the resource."); + } + resource.Instance = new Instance(resourceCounter++, name, resource, store, customTemplate, age); if (attributes != null) - resource.Instance.SetAttributes(attributes); + resource.Instance.SetAttributes(Structure.FromObject(attributes)); if (manager != null) resource.Instance.Managers.Add(manager); @@ -325,6 +412,8 @@ namespace Esiur.Resource if (store == parent) parent = null; + + /* if (parent == null) { if (!(resource is IStore)) @@ -332,20 +421,26 @@ namespace Esiur.Resource } else parent.Instance.Children.Add(resource); - + */ - if (resource is IStore) - { - stores.Add(resource as IStore); + if (resource is IStore) StoreConnected?.Invoke(resource as IStore, name); - } else store.Put(resource); - resources.Add(resource.Instance.Id, resource); - if (storeIsOpen) + if (parent != null) + { + parent.Instance.Store.AddChild(parent, resource); + store.AddParent(resource, parent); + //store.AddChild(parent, resource); + + } + + resources.Add(resource.Instance.Id, new WeakReference(resource)); + + if (warehouseIsOpen) resource.Trigger(ResourceTrigger.Initialize); } @@ -430,9 +525,17 @@ namespace Esiur.Resource stores.Remove(resource as IStore); // remove all objects associated with the store - var toBeRemoved = resources.Values.Where(x => x.Instance.Store == resource); + var toBeRemoved = resources.Values.Where(x => { + IResource r; + return x.TryGetTarget(out r) && r.Instance.Store == resource; + }); + foreach (var o in toBeRemoved) - Remove(o); + { + IResource r; + if (o.TryGetTarget(out r)) + Remove(r); + } StoreDisconnected?.Invoke(resource as IStore); } diff --git a/Esiur/Security/Permissions/ParentalPermissionsManager.cs b/Esiur/Security/Permissions/StorePermissionsManager.cs similarity index 79% rename from Esiur/Security/Permissions/ParentalPermissionsManager.cs rename to Esiur/Security/Permissions/StorePermissionsManager.cs index fb878d6..01c6102 100644 --- a/Esiur/Security/Permissions/ParentalPermissionsManager.cs +++ b/Esiur/Security/Permissions/StorePermissionsManager.cs @@ -33,7 +33,7 @@ using Esiur.Security.Authority; namespace Esiur.Security.Permissions { - public class ParentalPermissionsManager : IPermissionsManager + public class StorePermissionsManager : IPermissionsManager { Structure settings; @@ -41,20 +41,12 @@ namespace Esiur.Security.Permissions public Ruling Applicable(IResource resource, Session session, ActionType action, MemberTemplate member, object inquirer = null) { - - foreach (IResource parent in resource.Instance.Parents) - { - var ruling = parent.Instance.Applicable(session, action, member, inquirer); - if (ruling != Ruling.DontCare) - return ruling; - } - - return Ruling.DontCare; + return resource.Instance.Store.Instance.Applicable(session, action, member, inquirer); } public bool Initialize(Structure settings, IResource resource) { - throw new NotImplementedException(); + return true; } } } diff --git a/Esiur/Stores/MemoryStore.cs b/Esiur/Stores/MemoryStore.cs index f80d048..23ef5e9 100644 --- a/Esiur/Stores/MemoryStore.cs +++ b/Esiur/Stores/MemoryStore.cs @@ -33,19 +33,31 @@ namespace Esiur.Stores public AsyncReply Get(string path) { + foreach (var r in resources) + if (r.Value.Instance.Name == path) + return new AsyncReply(r.Value); + return new AsyncReply(null); } public bool Put(IResource resource) { - resources.Add(resource.Instance.Id, resource); + resources.Add(resource.Instance.Id, resource);// new WeakReference(resource)); + resource.Instance.Attributes["children"] = new AutoList(resource.Instance); + resource.Instance.Attributes["parents"] = new AutoList(resource.Instance); + return true; } public AsyncReply Retrieve(uint iid) { if (resources.ContainsKey(iid)) - return new AsyncReply(resources[iid]); + { + if (resources.ContainsKey(iid))// .TryGetTarget(out r)) + return new AsyncReply(resources[iid]); + else + return new AsyncReply(null); + } else return new AsyncReply(null); } @@ -75,6 +87,59 @@ namespace Esiur.Stores { return true; } - + + public AsyncReply AddChild(IResource parent, IResource child) + { + if (parent.Instance.Store == this) + { + (parent.Instance.Attributes["children"] as AutoList).Add(child); + return new AsyncReply(true); + } + else + return new AsyncReply(false); + } + + public AsyncReply RemoveChild(IResource parent, IResource child) + { + throw new NotImplementedException(); + } + + public AsyncReply AddParent(IResource resource, IResource parent) + { + + if (resource.Instance.Store == this) + { + (resource.Instance.Attributes["parents"] as AutoList).Add(parent); + return new AsyncReply(true); + } + else + return new AsyncReply(false); + } + + public AsyncReply RemoveParent(IResource child, IResource parent) + { + throw new NotImplementedException(); + } + + public AsyncBag Children(IResource resource, string name) where T : IResource + { + var children = (resource.Instance.Attributes["children"] as AutoList); + + if (name == null) + return new AsyncBag(children.Where(x=>x is T).Select(x=>(T)x).ToArray()); + else + return new AsyncBag(children.Where(x => x is T && x.Instance.Name == name).Select(x => (T)x).ToArray()); + + } + + public AsyncBag Parents(IResource resource, string name) where T : IResource + { + var parents = (resource.Instance.Attributes["parents"] as AutoList); + + if (name == null) + return new AsyncBag(parents.Where(x => x is T).Select(x => (T)x).ToArray()); + else + return new AsyncBag(parents.Where(x => x is T && x.Instance.Name == name).Select(x => (T)x).ToArray()); + } } } diff --git a/Esiur/Stores/TemporaryStore.cs b/Esiur/Stores/TemporaryStore.cs new file mode 100644 index 0000000..c1ca559 --- /dev/null +++ b/Esiur/Stores/TemporaryStore.cs @@ -0,0 +1,118 @@ +using Esiur.Resource; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Esiur.Core; +using Esiur.Data; +using Esiur.Resource.Template; + +namespace Esiur.Stores +{ + public class TemporaryStore : IStore + { + public Instance Instance { get; set; } + + public event DestroyedEvent OnDestroy; + + Dictionary resources = new Dictionary(); + + public void Destroy() + { + + } + + public string Link(IResource resource) + { + if (resource.Instance.Store == this) + return this.Instance.Name + "/" + resource.Instance.Id; + + return null; + } + + public AsyncReply Get(string path) + { + foreach (var r in resources) + if (r.Value.IsAlive && (r.Value.Target as IResource).Instance.Name == path) + return new AsyncReply(r.Value.Target as IResource); + + return new AsyncReply(null); + } + + public bool Put(IResource resource) + { + resources.Add(resource.Instance.Id, new WeakReference( resource));// new WeakReference(resource)); + return true; + } + + public AsyncReply Retrieve(uint iid) + { + if (resources.ContainsKey(iid)) + { + if (resources.ContainsKey(iid) && resources[iid].IsAlive)// .TryGetTarget(out r)) + return new AsyncReply(resources[iid].Target as IResource); + else + return new AsyncReply(null); + } + else + return new AsyncReply(null); + } + + public AsyncReply Trigger(ResourceTrigger trigger) + { + return new AsyncReply(true); + } + + public bool Record(IResource resource, string propertyName, object value, ulong age, DateTime dateTime) + { + throw new NotImplementedException(); + } + + public AsyncReply> GetRecord(IResource resource, DateTime fromDate, DateTime toDate) + { + throw new NotImplementedException(); + } + + public bool Remove(IResource resource) + { + resources.Remove(resource.Instance.Id); + return true; + } + + public bool Modify(IResource resource, string propertyName, object value, ulong age, DateTime dateTime) + { + return true; + } + + public AsyncReply AddChild(IResource parent, IResource child) + { + throw new NotImplementedException(); + } + + public AsyncReply RemoveChild(IResource parent, IResource child) + { + throw new NotImplementedException(); + } + + public AsyncReply AddParent(IResource child, IResource parent) + { + throw new NotImplementedException(); + } + + public AsyncReply RemoveParent(IResource child, IResource parent) + { + throw new NotImplementedException(); + } + + public AsyncBag Children(IResource resource, string name) where T : IResource + { + throw new NotImplementedException(); + } + + public AsyncBag Parents(IResource resource, string name) where T : IResource + { + throw new NotImplementedException(); + } + } +} diff --git a/Esiurd.sln b/Esiurd.sln new file mode 100644 index 0000000000000000000000000000000000000000..d62c2b14b3475a2bb8a27e1ef673a1e5dd2b9da4 GIT binary patch literal 2609 icmeIufdBvi0K=g9Q{UhOg-~I@fB^#r3>YwA;0z23F#rJo literal 0 HcmV?d00001 diff --git a/Test/MyObject.cs b/Test/MyObject.cs index 9e6f44d..cfcce58 100644 --- a/Test/MyObject.cs +++ b/Test/MyObject.cs @@ -9,7 +9,7 @@ using System.Threading; namespace Test { - public class MyObject : Resource + public class MyObject : EntryPoint { [ResourceEvent] @@ -90,6 +90,16 @@ namespace Test return reply; } + public override AsyncReply Query(string path, DistributedConnection sender) + { + return new AsyncReply(new IResource[] { this }); + } + + public override bool Create() + { + return true; + } + [ResourceProperty] public Structure Info { diff --git a/Test/Program.cs b/Test/Program.cs index 2b53bf6..54c8bbb 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -51,8 +51,6 @@ namespace Test { - Warehouse.Protocols.Add("iip", () => new DistributedConnection()); - // Create stores to keep objects. var system = Warehouse.New("system"); var remote = Warehouse.New("remote"); @@ -64,12 +62,12 @@ namespace Test // Set membership which handles authentication. iip.Membership = Warehouse.New("ms", system); // Start the server on port 5000. - iip.Start(new TCPSocket(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 5000)), 600000, 60000); + iip.Start(new TCPSocket(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 500)), 600000, 60000); // Create http server to handle IIP over Websockets var http = Warehouse.New("http", system); - http.Start(new TCPSocket(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 5001)), 600000, 60000); + http.Start(new TCPSocket(new System.Net.IPEndPoint(System.Net.IPAddress.Any, 501)), 600000, 60000); // Create IIP over Websocket HTTP module and give it to HTTP server. var wsOverHttp = Warehouse.New("IIPoWS", system, http); @@ -106,13 +104,10 @@ namespace Test } })); else - localObject = (MyObject)(await Warehouse.Get("db/my"));//.Then((o) => { myObject = (MyObject)o; }); + localObject = (MyObject)(await Warehouse.Get("db/my")); - //var obj = ProxyObject.(); - //Warehouse.Put(obj, "dd", system); - //obj.Level2= 33; - + iip.EntryPoint = localObject; Warehouse.StoreConnected += (store, name) => { @@ -160,7 +155,7 @@ namespace Test private static async void TestClient() { - remoteObject = await Warehouse.Get("iip://localhost:5000/db/my", new Structure() { ["username"] = "demo", ["password"] = "1234" }); + remoteObject = await Warehouse.Get("iip://localhost:500/db/my", new { username= "demo", password = 1234 }); dynamic x = remoteObject;