1 /**
2 Copyright: 2019 Mark Fisher
3 
4 License:
5 Permission is hereby granted, free of charge, to any person obtaining a copy of
6 this software and associated documentation files (the "Software"), to deal in
7 the Software without restriction, including without limitation the rights to
8 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 of the Software, and to permit persons to whom the Software is furnished to do
10 so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in all
13 copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 SOFTWARE.
22 **/
23 module dxx.app.services;
24 
25 private import std.exception;
26 private import std.algorithm;
27 private import std.range;
28 private import std.conv;
29 
30 private import dxx.app.cache;
31 
32 private import dxx.util;
33 private import dxx.app;
34 
35 public import dxx.util.service;
36 
37 class Registration {
38   ServiceRegistration reg;
39   alias reg this;
40 
41   class Ref {
42     ServiceReference reference;
43     alias reference this;
44 
45     int count;
46     this(string _pluginId) shared {
47       count = 0;
48       reference.pluginId = _pluginId;
49       reference._handle = cast(shared(void*))this;
50       typeId = reg.typeId;
51       properties = reg.properties;
52     }
53     void release() shared {
54       count = count - 1;
55       if(count == 0) {
56         //references.remove(reference.pluginId);
57       }
58     }
59     auto addRef() shared {
60       count = count + 1;
61       if(count == 1) {
62         //references[pluginId] = this;
63       }
64       return this;
65     }
66     /* string fullyQualifiedIdentifier() shared {
67       return this.outer.fullyQualifiedIdentifier;
68     } */
69     auto service() shared {
70       return this.outer.service;
71     }
72   }
73 
74   //Ref[string] references;
75 
76   shared(Ref) createRef(string _pluginId) shared {
77     //auto a = require(cast(shared(Ref)[string])references,_pluginId,new shared(Ref)(_pluginId));
78     /* if(_pluginId !in references) {
79       auto a = new shared(Ref)(_pluginId);
80       return a.addRef;
81     }
82     return references[pluginId].addRef; */
83     return null;
84   }
85   void releaseRef(string _pluginId) shared {
86     /* auto r = _pluginId in references;
87     if(r !is null) {
88       r.release;
89     } */
90   }
91   void unregister() shared {
92     service = null;
93     //foreach(r;references) {
94     //}
95   }
96   this(string _pluginId="<unknown>",string[] typeId=[],string[string] props=null) shared {
97     reg._regHandle = cast(shared(void*))this;
98     reg.pluginId = _pluginId;
99     reg.typeId = cast(shared(string[]))typeId.dup;
100     if(props !is null) reg.properties = cast(shared(string[string]))props.dup;
101     reg.properties[DXXConfig.serviceConstants.PROPERTY_OBJECTCLASS] = reg.typeId[0];
102     reg.properties[DXXConfig.serviceConstants.PROPERTY_OBJECTTYPES] = reg.typeId.join(",");
103     reg.properties[DXXConfig.serviceConstants.PROPERTY_PLUGINID] = reg.pluginId;
104   }
105   this(string _pluginId="<unknown>",string[] typeId=[],string[string] props=null) {
106     reg._regHandle = cast(void*)this;
107     reg.pluginId = cast(shared(string))_pluginId;
108     reg.typeId = typeId.dup;
109     if(props !is null) reg.properties = props.dup;
110     reg.properties[DXXConfig.serviceConstants.PROPERTY_OBJECTCLASS] = reg.typeId[0];
111     reg.properties[DXXConfig.serviceConstants.PROPERTY_OBJECTTYPES] = reg.typeId.join(",");
112     reg.properties[DXXConfig.serviceConstants.PROPERTY_PLUGINID] = reg.pluginId;
113   }
114   /* string fullyQualifiedIdentifier() shared {
115     //return reg.pluginId ~ "." ~ reg.id;
116     return typeId[0];
117   } */
118 }
119 
120 final class TypeRegistration {
121   string typeId;
122   //Registration[][string] registrations;
123   alias registry=Services.getRegistry;
124   Registration[] registrations;
125 
126   nothrow
127   void addRegistrations(shared(Registration)[]regs) shared {
128     /*foreach(r;regs) {
129        //if(r.pluginId !in registrations) {
130         //registrations[r.pluginId]=[];
131       //}
132       //registrations[r.pluginId]~=cast(Registration)r;
133       //registrations[]~=cast(Registration)r;
134     }*/
135     regs.each!(a=>addRegistration(a));
136     //registrations.rehash;
137   }
138   nothrow
139   void addRegistration(shared(Registration)reg) shared {
140     //auto id = typeId ~ ":regs";
141     /*auto regs = registry.get_ex!Registration[](id);
142     if(regs.isnull) {
143       registry.put!Registration[](id,[reg]);
144     } else {
145       registry.put!Registration[](id,regs.origin ~ [reg]);
146     }*/
147     registrations~=reg;
148     //registrations.rehash;
149   }
150 
151   void removeRegistration(shared(Registration) r) shared {
152       /* if(r.pluginId !in registrations) return;
153       registrations[r.pluginId] = registrations[r.pluginId].remove!(a=>a==cast(Registration)r);
154       registrations.rehash; */
155       //auto id = typeId ~ ":regs";
156       //registry.remove(id);
157       registrations = registrations.remove!(a=>a._regHandle == r._regHandle);
158   }
159 
160   shared(Registration)[] lookupRegistrations(string typeId,string[string] matches=null) shared {
161     /* if(typeId !in registrations) return [];
162     if(matches is null || matches.length==0)return registrations[typeId];
163     return registrations[typeId].filter!(a=>a.matches(matches)).array; */
164     //auto id = typeId ~ ":regs";
165     //auto regs = registry.get_ex!Registration[](id).filter!(a=>a.matches(matches));
166     //return regs.array;
167     return registrations.filter!(a=>a.matches(matches)).array;
168   }
169   nothrow
170   this(string typeId="<unknown>",shared(Registration)[]regs=null) shared {
171     this.typeId = typeId;
172     if(regs !is null) {
173       addRegistrations(regs);
174     }
175   }
176   nothrow
177   this() {
178     this.typeId = "<unknown>";
179   }
180   nothrow
181   this(shared(Registration)[]regs=null) shared {
182     this("<unknown>",regs);
183   }
184 }
185 
186 final class Services {
187   static
188   shared(ServiceRegistration) registerService(string[] typeId,string pluginId,void* svc,string[string] props) {
189     enforce(typeId.length > 0);
190     shared(Registration) reg = new shared(Registration)(pluginId);
191     reg.typeId = cast(shared(string[]))typeId.dup;
192     reg.service = cast(shared(void*))svc;
193     reg.properties = cast(shared(string[string]))props.dup;
194     reg.properties[DXXConfig.serviceConstants.PROPERTY_OBJECTCLASS] = typeId[0];
195     reg.properties[DXXConfig.serviceConstants.PROPERTY_PLUGINID] = pluginId;
196     reg.properties[DXXConfig.serviceConstants.PROPERTY_OBJECTTYPES] = typeId.join(",");
197     typeId.each!(a=>addRegistration(a,reg));
198     if(svc !is null) {
199       if(cast(ServiceNotificationHandler*)svc is null) {
200         // sendServiceEvent
201       }
202     }
203     return reg;
204   }
205   static
206   void unregisterService(shared(ServiceRegistration) reg) {
207     //getRegistry.remove(pluginId ~ "." ~ reg.id);
208     auto r = cast(shared(Registration*))reg._handle;
209     if(r !is null) {
210       foreach(t;r.typeId) {
211         //getRegistry.remove(r.fullyQualifiedIdentifier);
212         removeRegistration(t,*r);
213       }
214       r.unregister;
215       auto svc = reg.service;
216       if(svc !is null) {
217         if(cast(ServiceNotificationHandler*)svc is null) {
218           // sendServiceEvent
219         }
220       }
221     }
222 
223   }
224   static
225   shared(ServiceReference)[] lookupReferences(string typeId,string pluginId,string[string] matches) {
226     shared(ServiceReference)[] res;
227     auto tr = getRegistry.get!(TypeRegistration)(typeId);
228     if(!tr.isNull) {
229       foreach(r;(cast(shared)(tr.origin)).lookupRegistrations(typeId,matches)) {
230         res ~= r.createRef(pluginId);
231       }
232     }
233     return res;
234   }
235   static
236   shared(Registration)[] lookupRegistrations(string typeId,string[string] matches=null) {
237     auto tr = getRegistry.get!(TypeRegistration)(typeId);
238     if(!tr.isNull) {
239       return (cast(shared)(tr.origin)).lookupRegistrations(typeId,matches);
240     }
241     return [];
242   }
243   static
244   void* lookupService(ServiceReference reference) {
245     auto r = cast(shared(Registration.Ref)) reference._handle;
246     return cast(void*)r.service;
247   }
248 
249   static __gshared BasicCache registry;
250 
251   static
252   auto getRegistry() {
253       static bool instantiated = false;
254       if(!instantiated) {
255           synchronized(Services.classinfo) {
256               if(!registry) {
257                 //registry = CacheFectory.create;
258                 registry = new BasicCache;
259               }
260           }
261           instantiated = true;
262       }
263       return registry;
264   }
265 
266 
267   static
268   void addRegistration(const(string) typeId,shared(Registration) reg) {
269     //getRegistry.put!(shared(Registration))(reg.fullyQualifiedIdentifier,reg);
270     auto registry = getRegistry;
271     auto r = registry.get!(shared(TypeRegistration))(typeId);
272     if(r.isNull) {
273       //registry.put!(Registration[],[cast(Registration)reg]);
274       registry.set!(shared(TypeRegistration))(new shared(TypeRegistration)([reg]),typeId);
275     } else {
276       //registry.put!(Registration[], [ cast(Registration)reg ] ~ r);
277       r.addRegistrations([reg]);
278     }
279     //auto r = ServiceEvent(cast(const(ServiceReference))reference);
280     //Services.onRegistration(&r);
281   }
282   static
283   void removeRegistration(const(string) typeId,shared(Registration) reg) {
284     //getRegistry.put!(shared(Registration))(reg.fullyQualifiedIdentifier,reg);
285     auto registry = getRegistry;
286     auto tr = registry.get!(shared(TypeRegistration))(typeId);
287     if(tr.isNull) {
288       return;
289     } else {
290       //registry.put!(shared(Registration)[],r.remove!(a=>a==reg));
291       tr.removeRegistration(reg);
292       //auto r = ServiceEvent(cast(const(ServiceReference))reference);
293       //Services.onRelease(&r);
294     }
295   }
296 
297   static void sendServiceEvent(ServiceNotification event) {
298     auto registry = getRegistry;
299     //auto tr = registry.get_ex!(TypeRegistration)(event.reference.typeId);
300     //if(tr is null) return;
301     //tr.send!ServiceEvent(event);
302     string[string] matches = [
303       DXXConfig.serviceConstants.NOTIFICATION_TYPEID : event.notificationType.to!string
304     ];
305     auto regs = lookupRegistrations(typeid(ServiceNotificationHandler).name,matches);
306     //auto tr = registry.get_ex!(TypeRegistration);
307     //(event.reference.typeId);
308     foreach(r;regs) {
309       synchronized(Services.classinfo) {
310         auto h = cast(ServiceNotificationHandler*)r.service;
311         if(h !is null) {
312           h.handleServiceNotification(event);
313         }
314       }
315     }
316   }
317 
318 }
319 
320 final class PluginServices {
321 
322 }