fprpcrtti.pp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. {
  2. This file is part of the Free Component Library
  3. Server-side JSON-RPC functionality using Invoke.
  4. Copyright (c) 2022 by Michael Van Canneyt [email protected]
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$IFNDEF FPC_DOTTEDUNITS}
  12. unit fprpcrtti;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. {$mode ObjFPC}{$H+}
  15. interface
  16. {$IFDEF FPC_DOTTEDUNITS}
  17. uses
  18. System.Classes, System.SysUtils, FpJson.Data, FpWeb.JsonRpc.Base, System.TypInfo, System.Rtti;
  19. {$ELSE FPC_DOTTEDUNITS}
  20. uses
  21. Classes, SysUtils, fpjson, fpjsonrpc, typinfo, rtti;
  22. {$ENDIF FPC_DOTTEDUNITS}
  23. Type
  24. TRTTIInstanceCreator = Function(const aClassName : string) : IInterface;
  25. IRPCCallContext = Interface ['{F026AE43-E0E5-4F3D-9878-9B70201E34B0}']
  26. Procedure SetRPCCallContext(aCallContext : TJSONRPCCallContext);
  27. Function GetRPCCallContext : TJSONRPCCallContext;
  28. Property RPCCallContext : TJSONRPCCallContext Read GetRPCCallContext Write SetRPCCallContext;
  29. end;
  30. { TRTTIJSONRPCHandler }
  31. TRTTIJSONRPCHandler = class(TCustomJSONRPCHandler)
  32. Private
  33. FIntfType : TRttiInterfaceType;
  34. FMethod : TRttiMethod;
  35. FCreator : TRTTIInstanceCreator;
  36. FRPCClassName: String;
  37. protected
  38. class function JSONToValue(aData: TJSONData; aType: TRttiType): TValue;
  39. class function ValueToJSON(const aValue: TValue; aType: TRttiType): TJSONData;
  40. Function CreateInstance : IInterface; virtual;
  41. Function DoExecute(Const Params : TJSONData; AContext : TJSONRPCCallContext): TJSONData; override;
  42. Property Method : TRttiMethod Read FMethod;
  43. Property IntfType : TRttiInterfaceType read FIntfType;
  44. Public
  45. Procedure SetRequestClassAndMethod(const aClassName,aMethodName : String); override;
  46. Property RPCClassName : String Read FRPCClassName;
  47. end;
  48. { TRTTIJSONRPCRegistry }
  49. TRTTIJSONRPCRegistry = class
  50. Private
  51. type
  52. TIntfEntry = record
  53. Name : String;
  54. GetInstance: TRTTIInstanceCreator;
  55. IntfType: TRttiInterfaceType;
  56. end;
  57. Class var
  58. _Instance : TRTTIJSONRPCRegistry;
  59. private
  60. fIntfs: array of TIntfEntry;
  61. fIntfCount: Integer;
  62. fContext: TRttiContext;
  63. Public
  64. class var
  65. SizeDelta : Integer;
  66. Public
  67. class constructor Init;
  68. class destructor done;
  69. Constructor Create; virtual;
  70. Destructor Destroy; override;
  71. Procedure Add(P : PTypeInfo; aCreator : TRTTIInstanceCreator; const aName : string = '');
  72. generic Procedure Add<T : IInterface>(aCreator : TRTTIInstanceCreator; const aName : string = '');
  73. Function Find(const aName : string; out IntfType: TRttiInterfaceType; out aCreator : TRTTIInstanceCreator) : Boolean;
  74. Function Get(const aName : string; out IntfType: TRttiInterfaceType; out aCreator : TRTTIInstanceCreator) : Boolean;
  75. class property Instance : TRTTIJSONRPCRegistry Read _Instance;
  76. end;
  77. function RTTIJSONRPCRegistry : TRTTIJSONRPCRegistry;
  78. implementation
  79. {$IFDEF FPC_DOTTEDUNITS}
  80. uses FpWeb.JSONRPC.Strings, FpJson.Value;
  81. {$ELSE FPC_DOTTEDUNITS}
  82. uses fprpcstrings, fpjsonvalue;
  83. {$ENDIF FPC_DOTTEDUNITS}
  84. function RTTIJSONRPCRegistry : TRTTIJSONRPCRegistry;
  85. begin
  86. Result:=TRTTIJSONRPCRegistry.Instance;
  87. end;
  88. { TRTTIJSONRPCRegistry }
  89. class constructor TRTTIJSONRPCRegistry.Init;
  90. begin
  91. SizeDelta:=32;
  92. _Instance:=TRTTIJSONRPCRegistry.Create;
  93. end;
  94. class destructor TRTTIJSONRPCRegistry.done;
  95. begin
  96. _Instance.Free;
  97. end;
  98. constructor TRTTIJSONRPCRegistry.Create;
  99. begin
  100. SetLength(FIntfs,SizeDelta);
  101. FContext:=TRTTIContext.Create;
  102. FIntfCount:=0;
  103. end;
  104. destructor TRTTIJSONRPCRegistry.Destroy;
  105. begin
  106. SetLength(FIntfs,0);
  107. inherited Destroy;
  108. end;
  109. procedure TRTTIJSONRPCRegistry.Add(P: PTypeInfo; aCreator: TRTTIInstanceCreator;const aName : string = '');
  110. var
  111. entry: TIntfEntry;
  112. aMethod : TRTTIMethod;
  113. aParamCount : Integer;
  114. begin
  115. if aName='' then
  116. entry.Name:=P^.Name
  117. else
  118. entry.Name:=aName;
  119. entry.GetInstance := aCreator;
  120. entry.IntfType := fContext.GetType(P) as TRttiInterfaceType;
  121. if fIntfCount>=Length(fIntfs) then
  122. SetLength(fIntfs,Length(fIntfs)+SizeDelta);
  123. fIntfs[fIntfCount]:=entry;
  124. Inc(fIntfCount);
  125. for aMethod in entry.IntfType.GetDeclaredMethods do
  126. begin
  127. aParamCount:=Length(aMethod.GetParameters);
  128. JSONRPCHandlerManager.RegisterHandler(Entry.Name,aMethod.Name,TRTTIJSONRPCHandler,aParamCount);
  129. end;
  130. end;
  131. generic procedure TRTTIJSONRPCRegistry.Add<T>(aCreator : TRTTIInstanceCreator;const aName : string = '');
  132. begin
  133. Add(PTypeInfo(TypeInfo(T)), aCreator, aName);
  134. end;
  135. function TRTTIJSONRPCRegistry.Find(Const aName: string; out IntfType: TRttiInterfaceType; out aCreator: TRTTIInstanceCreator): Boolean;
  136. Var
  137. Idx : integer;
  138. Entry : TIntfEntry;
  139. begin
  140. Result:=False;
  141. Idx:=fIntfCount-1;
  142. While (Idx>=0) and not Result do
  143. begin
  144. Result:=SameText(fIntfs[Idx].Name,aName);
  145. if Result then
  146. begin
  147. Entry:=fIntfs[Idx];
  148. IntfType:=Entry.IntfType;
  149. aCreator:=Entry.GetInstance;
  150. end;
  151. Dec(Idx);
  152. end;
  153. end;
  154. function TRTTIJSONRPCRegistry.Get(Const aName: string; out IntfType: TRttiInterfaceType; out aCreator: TRTTIInstanceCreator): Boolean;
  155. begin
  156. Result:=Find(aName,IntfType,aCreator);
  157. end;
  158. { TRTTIJSONRPCHandler }
  159. function TRTTIJSONRPCHandler.CreateInstance: IInterface;
  160. begin
  161. Result:=FCreator(FRPCClassName);
  162. end;
  163. procedure TRTTIJSONRPCHandler.SetRequestClassAndMethod(const aClassName, aMethodName: String);
  164. begin
  165. FRPCClassName:=aClassName;
  166. RPCMethodName:=aMethodName;
  167. TRTTIJSONRPCRegistry.Instance.Get(FRPCClassName,FIntfType,FCreator);
  168. FMethod:=FIntfType.GetMethod(aMethodName);
  169. if FMethod=Nil then
  170. raise EJSONRPC.CreateFmt(SErrUnknownMethodForClass, [aClassName, aMethodName]);
  171. end;
  172. class function TRTTIJSONRPCHandler.ValueToJSON(const aValue: TValue; aType: TRttiType): TJSONData;
  173. begin
  174. result:={$IFDEF FPC_DOTTEDUNITS}FpJson.Value{$ELSE}fpjsonvalue{$ENDIF}.ValueToJSON(aValue,aType);
  175. end;
  176. class function TRTTIJSONRPCHandler.JSONToValue(aData: TJSONData; aType: TRttiType): TValue;
  177. begin
  178. result:={$IFDEF FPC_DOTTEDUNITS}FpJson.Value{$ELSE}fpjsonvalue{$ENDIF}.JSONToValue(aData,aType);
  179. end;
  180. function TRTTIJSONRPCHandler.DoExecute(const Params: TJSONData; AContext: TJSONRPCCallContext): TJSONData;
  181. var
  182. margs: specialize TArray<TRttiParameter>;
  183. arg: TRttiParameter;
  184. args: array of TValue;
  185. argidx: SizeInt;
  186. resparams,i: LongInt;
  187. res, instance: TValue;
  188. intf,APIIntf : IUnknown;
  189. aVal : TJSONData;
  190. oRes : TJSONObject;
  191. CC : IRPCCallContext;
  192. begin
  193. Result:=Nil;
  194. ResParams:=0;
  195. args:=[];
  196. if (Params.JSONType in StructuredJSONTypes) then
  197. SetLength(args, Params.Count)
  198. else
  199. args := Nil;
  200. argidx := 0;
  201. margs := method.GetParameters;
  202. for arg in margs do
  203. begin
  204. if pfHidden in arg.Flags then
  205. Continue
  206. else
  207. if ([pfVar,pfOut] * arg.Flags)<>[] then
  208. Inc(ResParams);
  209. if Params.JSONType = jtArray then
  210. aVal:=TJSONArray(Params).Items[argIdx]
  211. else
  212. aVal:=TJSONObject(Params).Elements[arg.Name];
  213. args[argidx] := JSONToValue(aVal, arg.ParamType);
  214. Inc(argidx);
  215. end;
  216. intf:=CreateInstance;
  217. if (Intf.QueryInterface(IRPCCallContext,CC)=S_OK) then
  218. CC.RPCCallContext:=aContext;
  219. if Intf.QueryInterface(FIntfType.GUID,APIIntf)<>S_OK then
  220. raise EJSONRPC.CreateFmt(SErrCreatorDoesNotSupportInterface, [FIntfType.Name]);
  221. TValue.Make(@APIIntf, PTypeInfo(FIntfType.Handle), instance);
  222. res := method.Invoke(instance, args);
  223. if ResParams=0 then
  224. begin
  225. if Assigned(method.ReturnType) then
  226. Result:=ValueToJSON(res, method.ReturnType)
  227. else
  228. Result:=TJSONNull.Create;
  229. end
  230. else
  231. begin
  232. oRes := TJSONObject.Create;
  233. Result:=oRes;
  234. try
  235. if Assigned(method.ReturnType) then
  236. oRes.Add('$result', ValueToJSON(res, method.ReturnType));
  237. argidx := 0;
  238. for i := 0 to High(margs) do
  239. begin
  240. arg := margs[i];
  241. if pfHidden in arg.Flags then
  242. Continue;
  243. if arg.Flags * [pfVar, pfOut] = [] then
  244. begin
  245. Inc(argidx);
  246. Continue;
  247. end;
  248. oRes.Add(arg.Name, ValueToJSON(args[argidx], arg.ParamType));
  249. Inc(argidx);
  250. end;
  251. except
  252. Result.Free;
  253. end;
  254. end;
  255. Intf:=nil;
  256. end;
  257. end.