httpsvlt.pp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. {
  2. HTTP Servlet Classes
  3. Copyright (c) 2003 by
  4. Areca Systems GmbH / Sebastian Guenther, [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 HTTPSvlt;
  13. {$ENDIF FPC_DOTTEDUNITS}
  14. {$Mode objfpc}
  15. {$H+}
  16. interface
  17. {$IFDEF FPC_DOTTEDUNITS}
  18. uses System.SysUtils, System.Classes, System.Async.Fpasync, System.Net.Fpsock, HTTPBase, Servlets;
  19. {$ELSE FPC_DOTTEDUNITS}
  20. uses SysUtils, Classes, fpAsync, fpSock, HTTPBase, Servlets;
  21. {$ENDIF FPC_DOTTEDUNITS}
  22. resourcestring
  23. SErrUnknownMethod = 'Unknown HTTP method "%s" used';
  24. SErrUnsupportedMethod = 'HTTP method "%s" is not supported for this URL';
  25. type
  26. THttpSession = class
  27. public
  28. property Attributes[const AName: String]: TObject; // !!!: Implement this rw
  29. property CreationTime: TDateTime; // !!!: Implement this
  30. property ID: String; // !!!: Implement this
  31. property LastAccessedTime: TDateTime; // !!!: Implement this
  32. property MaxInactiveInterval: TDateTime; // !!!: Implement this rw
  33. property ServletContext: TServletContext; // !!!: Implement this
  34. property IsNew: Boolean; // !!!: Implement this
  35. // procedure Invalidate; // !!!: Implement this
  36. // procedure RemoveAttribute(const AName: String); // !!!: Implement this
  37. end;
  38. THttpServletRequest = class(TServletRequest)
  39. private
  40. RequestHeader: THTTPRequestHeader;
  41. protected
  42. function GetContentLength: Integer; override;
  43. function GetContentType: String; override;
  44. function GetProtocol: String; override;
  45. function GetMethod: String;
  46. function GetRequestURI: String;
  47. function GetQueryString: String;
  48. public
  49. constructor Create(ARequestHeader: THTTPRequestHeader; AInputStream: TStream;
  50. const AScheme, APathInfo: String);
  51. // GetSession
  52. // function IsRequestedSessionIdFromCookie: Boolean; // !!!: Implement this
  53. // function IsRequestedSessionIdFromURL: Boolean; // !!!: Implement this
  54. // function IsRequestedSessionIdValid: Boolean; // !!!: Implement this
  55. property AuthType: String; // !!!: How to implement?
  56. property ContextPath: String; // !!!: How to implement?
  57. property CookieCount: Integer; // !!!: How to implement?
  58. property Cookies[Index: Integer]: Pointer; // !!!: How to implement?
  59. property DateHeaders[const AName: String]: TDateTime; // !!!: Implement this
  60. property Headers[const AName: String]: String; // !!!: Implement this
  61. property IntHeaders[const AName: String]: Integer; // !!!: Implement this
  62. property Method: String read GetMethod;
  63. property PathInfo: String read FPathInfo;
  64. property PathTranslated: String; // !!!: How to implement?
  65. property QueryString: String read GetQueryString;
  66. property RemoteUser: String; // !!!: How to implement?
  67. property RequestedSessionID: String; // !!!: How to implement?
  68. property RequestURI: String read GetRequestURI;
  69. property RequestURL: String; // !!!: How to implement?
  70. property ServletPath: String; // !!!: How to implement?
  71. end;
  72. THttpServletResponse = class(TServletResponse)
  73. private
  74. ResponseHeader: THTTPResponseHeader;
  75. protected
  76. procedure SetContentType(const Value: String); override;
  77. procedure SetContentLength(Value: Int64); override;
  78. public
  79. constructor Create(AResponseHeader: THTTPResponseHeader;
  80. AOutputStream: TStream);
  81. // procedure AddCookie(Cookie: TCookie); // !!!: Implement this
  82. // procedure AddDateHeader(const AName: String; ADate: TDateTime); // !!!: Implement this
  83. // procedure AddHeader(const AName, AValue: String); // !!!: Implement this
  84. // procedure AddIntHeader(const AName: String; AValue: Int64); // !!!: Implement this
  85. // function ContainsHeader(const AName: String): Boolean; // !!!: Implement this
  86. // function EncodeRedirectURL(const URL: String): String; // !!!: Implement this
  87. // function EncodeURL(const URL: String): String; // !!!: Implement this
  88. // procedure SendError(StatusCode: Integer); // !!!: Implement this
  89. // procedure SendError(StatusCode: Integer; const Msg: String); // !!!: Implement this
  90. // procedure SendRedirect(const Location: String); // !!!: Implement this
  91. // procedure SetDateHeader(const AName: String; ADate: TDateTime); // !!!: Implement this
  92. // procedure SetHeader(const AName, AValue: String); // !!!: Implement this
  93. // procedure SetIntHeader(const AName: String; AValue: Int64); // !!!: Implement this
  94. // procedure SetStatus(StatusCode: Integer); // !!!: Implement this
  95. // procedure SetStatus(StatusCode: Integer; const Msg: String); // !!!: Implement this
  96. end;
  97. THttpServlet = class(TGenericServlet)
  98. protected
  99. // function GetLastModified(Req: THttpServletRequest): TDateTime;
  100. // Handlers for HTTP methods
  101. procedure DoDelete(Req: THttpServletRequest; Resp: THttpServletResponse);
  102. virtual; abstract;
  103. procedure DoGet(Req: THttpServletRequest; Resp: THttpServletResponse);
  104. virtual; abstract;
  105. procedure DoHead(Req: THttpServletRequest; Resp: THttpServletResponse);
  106. virtual; abstract;
  107. procedure DoOptions(Req: THttpServletRequest; Resp: THttpServletResponse);
  108. virtual; abstract;
  109. procedure DoPost(Req: THttpServletRequest; Resp: THttpServletResponse);
  110. virtual; abstract;
  111. procedure DoPut(Req: THttpServletRequest; Resp: THttpServletResponse);
  112. virtual; abstract;
  113. procedure DoTrace(Req: THttpServletRequest; Resp: THttpServletResponse);
  114. virtual; abstract;
  115. procedure Service(Req: THttpServletRequest; Resp: THttpServletResponse); virtual;
  116. end;
  117. // A simple file retreiving servlet
  118. TCustomFileServlet = class(THttpServlet)
  119. private
  120. FPath: String;
  121. protected
  122. procedure DoGet(Req: THttpServletRequest; Resp: THttpServletResponse); override;
  123. property Path: String read FPath write FPath;
  124. end;
  125. TFileServlet = class(TCustomFileServlet)
  126. published
  127. property Path;
  128. end;
  129. // HTTP server (servlet container)
  130. TServletMapping = class(TCollectionItem)
  131. private
  132. FServlet: TGenericServlet;
  133. FURLPattern: String;
  134. published
  135. property Servlet: TGenericServlet read FServlet write FServlet;
  136. property URLPattern: String read FURLPattern write FURLPattern;
  137. end;
  138. TServletMappings = class(TCollection)
  139. private
  140. function GetItem(Index: Integer): TServletMapping;
  141. procedure SetItem(Index: Integer; Value: TServletMapping);
  142. public
  143. property Items[Index: Integer]: TServletMapping read GetItem write SetItem;
  144. default;
  145. end;
  146. THttpServer = class(TCustomTCPServer)
  147. private
  148. Connections: TList; // List of THttpServerConnection objects
  149. FServletMappings: TServletMappings;
  150. protected
  151. procedure DoConnect(AStream: TSocketStream); override;
  152. public
  153. constructor Create(AOwner: TComponent); override;
  154. destructor Destroy; override;
  155. procedure AddServlet(AServlet: THttpServlet; const AURLPattern: String);
  156. // procedure RemoveServlet(const APathName: String);
  157. published
  158. property Active;
  159. property Port;
  160. property OnQueryConnect;
  161. property OnConnect;
  162. property ServletMappings: TServletMappings
  163. read FServletMappings write FServletMappings;
  164. end;
  165. { No, this one really doesn't belong to here - but as soon as we don't have a
  166. nice solution for platform-independent component streaming in the FCL classes
  167. unit, it will be left here. }
  168. function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
  169. implementation
  170. constructor THttpServletRequest.Create(ARequestHeader: THTTPRequestHeader;
  171. AInputStream: TStream; const AScheme, APathInfo: String);
  172. begin
  173. inherited Create(AInputStream, AScheme, APathInfo);
  174. RequestHeader := ARequestHeader;
  175. end;
  176. function THttpServletRequest.GetContentLength: Integer;
  177. begin
  178. Result := RequestHeader.ContentLength;
  179. end;
  180. function THttpServletRequest.GetContentType: String;
  181. begin
  182. Result := RequestHeader.ContentType;
  183. end;
  184. function THttpServletRequest.GetProtocol: String;
  185. begin
  186. Result := 'HTTP/' + RequestHeader.HttpVersion;
  187. end;
  188. function THttpServletRequest.GetMethod: String;
  189. begin
  190. Result := RequestHeader.Command;
  191. end;
  192. function THttpServletRequest.GetRequestURI: String;
  193. begin
  194. Result := RequestHeader.URI;
  195. end;
  196. function THttpServletRequest.GetQueryString: String;
  197. begin
  198. Result := RequestHeader.QueryString;
  199. end;
  200. constructor THttpServletResponse.Create(AResponseHeader: THTTPResponseHeader;
  201. AOutputStream: TStream);
  202. begin
  203. inherited Create(AOutputStream);
  204. ResponseHeader := AResponseHeader;
  205. end;
  206. procedure THttpServletResponse.SetContentType(const Value: String);
  207. begin
  208. ResponseHeader.ContentType := Value;
  209. end;
  210. procedure THttpServletResponse.SetContentLength(Value: Int64);
  211. begin
  212. ResponseHeader.ContentLength := Value;
  213. end;
  214. procedure THttpServlet.Service(Req: THttpServletRequest; Resp: THttpServletResponse);
  215. var
  216. Method: String;
  217. begin
  218. Method := Req.Method;
  219. try
  220. if Method = 'DELETE' then
  221. DoDelete(Req, Resp)
  222. else if Method = 'GET' then
  223. DoGet(Req, Resp)
  224. else if Method = 'HEAD' then
  225. DoHead(Req, Resp)
  226. else if Method = 'OPTIONS' then
  227. DoOptions(Req, Resp)
  228. else if Method = 'POST' then
  229. DoPost(Req, Resp)
  230. else if Method = 'PUT' then
  231. DoPut(Req, Resp)
  232. else if Method = 'TRACE' then
  233. DoTrace(Req, Resp)
  234. else
  235. raise EServlet.CreateFmt(SErrUnknownMethod, [Method]);
  236. except
  237. on e: EAbstractError do
  238. raise EServlet.CreateFmt(SErrUnsupportedMethod, [Method]);
  239. end;
  240. end;
  241. procedure TCustomFileServlet.DoGet(Req: THttpServletRequest;
  242. Resp: THttpServletResponse);
  243. var
  244. f: TStream;
  245. s: String;
  246. i, LastStart: Integer;
  247. begin
  248. s := Req.PathInfo;
  249. i := 1;
  250. LastStart := 1;
  251. while i <= Length(s) do
  252. begin
  253. if (s[i] = '/') or (s[i] = '\') then
  254. LastStart := i + 1
  255. else if (i = LastStart) and (s[i] = '.') and (i < Length(s)) and
  256. (s[i + 1] = '..') then
  257. exit; // !!!: are ".." allowed in URLs?
  258. Inc(i);
  259. end;
  260. if s = '' then
  261. s := 'index.html';
  262. f := TFileStream.Create(Path + '/' + s, fmOpenRead);
  263. try
  264. Resp.OutputStream.CopyFrom(f, f.Size);
  265. finally
  266. f.Free;
  267. end;
  268. end;
  269. // HTTP Server
  270. function TServletMappings.GetItem(Index: Integer): TServletMapping;
  271. begin
  272. Result := TServletMapping(inherited GetItem(Index));
  273. end;
  274. procedure TServletMappings.SetItem(Index: Integer; Value: TServletMapping);
  275. begin
  276. inherited SetItem(Index, Value);
  277. end;
  278. type
  279. THttpServerConnection = class
  280. private
  281. Server: THttpServer;
  282. Stream: TSocketStream;
  283. RequestHeader: THttpRequestHeader;
  284. RequestStream: TMemoryStream;
  285. ResponseHeader: THttpResponseHeader;
  286. ResponseStream: TMemoryStream;
  287. BytesToRead, BytesToWrite: Integer;
  288. DataAvailableNotifyHandle: Pointer;
  289. CanSendNotifyHandle: Pointer;
  290. SendBuffer: Pointer;
  291. procedure RequestHeaderReceived(Sender: TObject);
  292. procedure DataAvailable(Sender: TObject);
  293. procedure RequestStreamReceived;
  294. procedure ResponseHeaderSent(Sender: TObject);
  295. procedure CanSend(Sender: TObject);
  296. public
  297. constructor Create(AServer: THttpServer; AStream: TSocketStream);
  298. destructor Destroy; override;
  299. end;
  300. constructor THttpServerConnection.Create(AServer: THttpServer;
  301. AStream: TSocketStream);
  302. begin
  303. inherited Create;
  304. Server := AServer;
  305. Stream := AStream;
  306. RequestHeader := THttpRequestHeader.Create;
  307. RequestHeader.OnCompleted := @RequestHeaderReceived;
  308. RequestHeader.AsyncReceive(Server.EventLoop, Stream);
  309. end;
  310. destructor THttpServerConnection.Destroy;
  311. begin
  312. if Assigned(DataAvailableNotifyHandle) then
  313. Server.EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
  314. if Assigned(CanSendNotifyHandle) then
  315. Server.EventLoop.ClearCanWriteNotify(CanSendNotifyHandle);
  316. RequestHeader.Free;
  317. RequestStream.Free;
  318. ResponseHeader.Free;
  319. ResponseStream.Free;
  320. Stream.Free;
  321. Server.Connections.Remove(Self);
  322. inherited Destroy;
  323. end;
  324. procedure THttpServerConnection.RequestHeaderReceived(Sender: TObject);
  325. var
  326. BytesInBuffer: Integer;
  327. NeedMoreData: Boolean;
  328. begin
  329. // WriteLn('HTTP-Header empfangen');
  330. BytesInBuffer:= RequestHeader.Reader.BytesInBuffer;
  331. BytesToRead := RequestHeader.ContentLength;
  332. // WriteLn('Content-Length: ', BytesToRead, ', noch im Puffer: ', BytesInBuffer);
  333. RequestStream := TMemoryStream.Create;
  334. NeedMoreData := RequestHeader.Command = 'POST';
  335. if BytesInBuffer > 0 then
  336. begin
  337. RequestStream.Write(RequestHeader.Reader.Buffer^, BytesInBuffer);
  338. if BytesToRead > 0 then
  339. Dec(BytesToRead, BytesInBuffer);
  340. if BytesInBuffer = RequestHeader.ContentLength then
  341. NeedMoreData := False;
  342. end;
  343. if NeedMoreData then
  344. DataAvailableNotifyHandle := Server.EventLoop.SetDataAvailableNotify(
  345. Stream.Handle, @DataAvailable, nil)
  346. else
  347. RequestStreamReceived;
  348. end;
  349. procedure THttpServerConnection.DataAvailable(Sender: TObject);
  350. var
  351. Buffer: array[0..4095] of Byte;
  352. ReadNow, BytesRead: Integer;
  353. begin
  354. ReadNow := SizeOf(Buffer);
  355. if (BytesToRead > 0) and (ReadNow > BytesToRead) then
  356. ReadNow := BytesToRead;
  357. BytesRead := Stream.Read(Buffer, ReadNow);
  358. // WriteLn('Sollte ', ReadNow, ' Bytes lesen, ', BytesRead, ' wurden gelesen');
  359. RequestStream.Write(Buffer, BytesRead);
  360. if BytesToRead > 0 then
  361. begin
  362. Dec(BytesToRead, BytesRead);
  363. if BytesToRead = 0 then
  364. begin
  365. Server.EventLoop.ClearDataAvailableNotify(DataAvailableNotifyHandle);
  366. DataAvailableNotifyHandle := nil;
  367. RequestStreamReceived;
  368. end;
  369. end;
  370. end;
  371. procedure THttpServerConnection.RequestStreamReceived;
  372. var
  373. i : Integer;
  374. s, URI: String;
  375. Servlet: TGenericServlet;
  376. Request: THttpServletRequest;
  377. Response: THttpServletResponse;
  378. begin
  379. // WriteLn('Stream received: ', RequestStream.Size, ' bytes');
  380. URI := UpperCase(RequestHeader.URI);
  381. I:=0;
  382. Servlet:=Nil;
  383. While (Servlet=Nil) and (I<Server.ServletMappings.Count) do
  384. begin
  385. s := UpperCase(Server.ServletMappings[i].URLPattern);
  386. if ((s[Length(s)] = '*') and (Copy(s, 1, Length(s) - 1) =
  387. Copy(URI, 1, Length(s) - 1))) or (s = URI) then
  388. Servlet:=Server.ServletMappings[i].Servlet;
  389. inc(I);
  390. end;
  391. if RequestHeader.ContentLength = 0 then
  392. RequestHeader.ContentLength := RequestStream.Size;
  393. RequestStream.Position := 0;
  394. if s[Length(s)] = '*' then
  395. s := Copy(s, 1, Length(s) - 1);
  396. Request := THttpServletRequest.Create(RequestHeader, RequestStream, 'http',
  397. Copy(RequestHeader.URI, Length(s) + 1, Length(RequestHeader.URI)));
  398. ResponseHeader := THTTPResponseHeader.Create;
  399. ResponseHeader.Connection := 'Keep-Alive';
  400. ResponseStream := TMemoryStream.Create;
  401. Response := THttpServletResponse.Create(ResponseHeader, ResponseStream);
  402. try
  403. try
  404. if Assigned(Servlet) then
  405. if Servlet.InheritsFrom(THttpServlet) then
  406. THttpServlet(Servlet).Service(Request, Response)
  407. else
  408. Servlet.Service(Request, Response)
  409. else
  410. begin
  411. ResponseHeader.ContentType := 'text/plain';
  412. s := 'Invalid URL';
  413. ResponseStream.Write(s[1], Length(s));
  414. end;
  415. except
  416. on e: Exception do
  417. begin
  418. s := 'An error occurred: ' + ' ' + e.Message;
  419. ResponseHeader.ContentType := 'text/plain';
  420. ResponseStream.Write(s[1], Length(s));
  421. end;
  422. end;
  423. BytesToWrite := ResponseStream.Size;
  424. SendBuffer := ResponseStream.Memory;
  425. ResponseStream.Position := 0;
  426. ResponseHeader.ContentLength := BytesToWrite;
  427. ResponseHeader.OnCompleted := @ResponseHeaderSent;
  428. ResponseHeader.AsyncSend(Server.EventLoop, Stream);
  429. finally
  430. Response.Free;
  431. Request.Free;
  432. end;
  433. // WriteLn('Antwort wurde generiert');
  434. end;
  435. procedure THttpServerConnection.ResponseHeaderSent(Sender: TObject);
  436. begin
  437. // WriteLn('Antwortheader geschickt');
  438. if BytesToWrite > 0 then
  439. CanSendNotifyHandle := Server.EventLoop.SetCanWriteNotify(Stream.Handle,
  440. @CanSend, nil);
  441. end;
  442. procedure THttpServerConnection.CanSend(Sender: TObject);
  443. var
  444. BytesWritten: Integer;
  445. begin
  446. BytesWritten := Stream.Write(SendBuffer^, BytesToWrite);
  447. Dec(BytesToWrite, BytesWritten);
  448. Inc(SendBuffer, BytesWritten);
  449. if BytesToWrite = 0 then
  450. begin
  451. // WriteLn('Antwortdaten geschickt');
  452. Free;
  453. end;
  454. end;
  455. constructor THttpServer.Create(AOwner: TComponent);
  456. begin
  457. inherited Create(AOwner);
  458. ServletMappings := TServletMappings.Create(TServletMapping);
  459. end;
  460. destructor THttpServer.Destroy;
  461. var
  462. i: Integer;
  463. begin
  464. ServletMappings.Free;
  465. if Assigned(Connections) then
  466. begin
  467. for i := 0 to Connections.Count - 1 do
  468. THttpServerConnection(Connections[i]).Free;
  469. Connections.Free;
  470. end;
  471. inherited Destroy;
  472. end;
  473. procedure THttpServer.AddServlet(AServlet: THttpServlet;
  474. const AURLPattern: String);
  475. var
  476. Mapping: TServletMapping;
  477. begin
  478. Mapping := TServletMapping(ServletMappings.Add);
  479. Mapping.Servlet := AServlet;
  480. Mapping.URLPattern := AURLPattern;
  481. end;
  482. {procedure THttpServer.RemoveServlet(const APathName: String);
  483. var
  484. i: Integer;
  485. begin
  486. for i := 0 to Servlets.Count - 1 do
  487. if TServletInfo(Servlets[i]).PathName = APathName then
  488. begin
  489. TServletInfo(Servlets[i]).Free;
  490. Servlets.Delete(i);
  491. break;
  492. end;
  493. end;}
  494. procedure THttpServer.DoConnect(AStream: TSocketStream);
  495. begin
  496. // WriteLn('Incoming HTTP connection');
  497. if not Assigned(Connections) then
  498. Connections := TList.Create;
  499. Connections.Add(THttpServerConnection.Create(Self, AStream));
  500. end;
  501. function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean;
  502. function DoInitClass(ClassType: TClass): Boolean;
  503. var
  504. Filename: String;
  505. TextStream, BinStream: TStream;
  506. begin
  507. Result := False;
  508. if (ClassType <> TComponent) and (ClassType <> RootAncestor) then
  509. begin
  510. { Init the parent class first }
  511. Result := DoInitClass(ClassType.ClassParent);
  512. Filename := LowerCase(Copy(ClassType.ClassName, 2, 255)) + '.frm';
  513. TextStream := nil;
  514. BinStream := nil;
  515. try
  516. try
  517. TextStream := TFileStream.Create(Filename, fmOpenRead);
  518. except
  519. exit;
  520. end;
  521. BinStream := TMemoryStream.Create;
  522. ObjectTextToBinary(TextStream, BinStream);
  523. BinStream.Position := 0;
  524. BinStream.ReadComponent(Instance);
  525. Result := True;
  526. finally
  527. TextStream.Free;
  528. BinStream.Free;
  529. end;
  530. end;
  531. end;
  532. begin
  533. {!!!: GlobalNameSpace.BeginWrite;
  534. try}
  535. if (Instance.ComponentState * [csLoading, csInline]) = [] then
  536. begin
  537. BeginGlobalLoading;
  538. try
  539. Result := DoInitClass(Instance.ClassType);
  540. NotifyGlobalLoading;
  541. finally
  542. EndGlobalLoading;
  543. end;
  544. end else
  545. Result := DoInitClass(Instance.ClassType);
  546. {finally
  547. GlobalNameSpace.EndWrite;
  548. end;}
  549. end;
  550. end.