DynamicWebpagePlugin.Mod 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954
  1. MODULE DynamicWebpagePlugin; (** AUTHOR "Luc Blaeser"; PURPOSE "HTTP Webserver Plugin for Dynamic Webpages"; *)
  2. IMPORT
  3. DynamicWebpage, HTTPSupport, HTTPSession, WebHTTP, WebHTTPServer, Files, Dates, Strings, Streams, Commands,
  4. KernelLog, XML, XMLScanner, XMLParser, XMLObjects, DynamicStrings, TFClasses, Configuration, Modules;
  5. CONST
  6. DEBUG = FALSE; (* disply debug information *)
  7. ShowRegisteredElements = FALSE; (* show all registered active elements *)
  8. PluginName = "Dynamic Webpage Plugin";
  9. PreTransformation = TRUE;
  10. PostTransformation = FALSE;
  11. MaxTransformationDepth = 40; (* maxmimum number of recursive steps to transform a transformation result *)
  12. DocType = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
  13. TYPE
  14. DynamicWebpagePlugin = OBJECT (WebHTTPServer.HTTPPlugin)
  15. PROCEDURE &Init*(CONST name: WebHTTPServer.Name);
  16. BEGIN
  17. Init^(PluginName)
  18. END Init;
  19. PROCEDURE CanHandle*(host: WebHTTPServer.Host; VAR request: WebHTTP.RequestHeader; secure : BOOLEAN) : BOOLEAN;
  20. VAR name, ext: Files.FileName; f: Files.File; newuri : ARRAY 4096 OF CHAR;
  21. BEGIN
  22. HTTPSupport.RemoveVariablesFromURI(request.uri, newuri);
  23. IF (request.method IN {WebHTTP.GetM, WebHTTP.PostM, WebHTTP.HeadM}) THEN
  24. IF (newuri[Strings.Length(newuri)-1] = "/") THEN (* check for default webpage "index.dxp" *)
  25. COPY(host.prefix, name); Strings.Append(name, newuri);
  26. Strings.Append(name, DynamicWebpage.DefaultWebpage);
  27. f := Files.Old(name);
  28. RETURN (f # NIL)
  29. ELSE
  30. Files.SplitExtension(newuri, name, ext);
  31. Strings.UpperCase(ext);
  32. RETURN (ext = DynamicWebpage.DynamicWebpageExtension)
  33. END
  34. ELSE
  35. RETURN FALSE
  36. END
  37. END CanHandle;
  38. PROCEDURE Handle*(host: WebHTTPServer.Host; VAR requestHeader: WebHTTP.RequestHeader; VAR reply: WebHTTP.ResponseHeader;
  39. VAR in: Streams.Reader; VAR out: Streams.Writer);
  40. VAR chunker: WebHTTP.ChunkedOutStream; w: Streams.Writer; f: Files.File; backupUri: ARRAY 4096 OF CHAR;
  41. request: HTTPSupport.HTTPRequest; session: HTTPSession.Session;
  42. BEGIN
  43. (* requestHeader.method IN {WebHTTP.GetM, WebHTTP.PostM, WebHTTP.HeadM} *)
  44. NEW(request, requestHeader, in);
  45. WebHTTP.SetAdditionalFieldValue(requestHeader.additionalFields, "If-Modified-Since", " "); (* prohibit conditional get for dynamic webpages *)
  46. WebHTTPServer.GetDefaultResponseHeader(requestHeader, reply);
  47. IF (request.shortUri[Strings.Length(request.shortUri)-1] = "/") THEN (* get default webpage "index.dxp" in a directory *)
  48. Strings.Append(request.shortUri, DynamicWebpage.DefaultWebpage);
  49. Strings.Concat("http://", requestHeader.host, reply.contentlocation);
  50. Strings.Append(reply.contentlocation, request.shortUri);
  51. END;
  52. (* LocateResource works only with variable-free URI in requestHeader *)
  53. COPY(requestHeader.uri, backupUri); COPY(request.shortUri, requestHeader.uri);
  54. LocateResource(host, requestHeader, reply, f); (* sets also reply.statuscode *)
  55. COPY(backupUri, requestHeader.uri);
  56. IF ((f # NIL) & ((reply.statuscode = WebHTTP.OK) OR (reply.statuscode = WebHTTP.NotModified))) THEN
  57. reply.statuscode := WebHTTP.OK;
  58. Strings.FormatDateTime(WebHTTP.DateTimeFormat, Dates.Now(), reply.lastmodified); (* dynamic webpages have no last modified *)
  59. WebHTTP.SetAdditionalFieldValue(reply.additionalFields, "Expires", reply.lastmodified);
  60. (* deny web caching; Expires=0 is illegal but often used to deny web caching *)
  61. WebHTTP.SetAdditionalFieldValue(reply.additionalFields, "Pragma", "no-cache"); (* deny web caching *)
  62. COPY("text/html", reply.contenttype); (* result is XHTML *)
  63. NEW(chunker, w, out, requestHeader, reply);
  64. WebHTTP.SendResponseHeader(reply, out);
  65. session := HTTPSession.GetSession(request);
  66. session.IncreaseLifeTime;
  67. (* detect whether the user has used the back or refresh button of the navigation bar *)
  68. IF (BackRefreshButtonWasPressed(request, session)) THEN
  69. HandleBackRefreshButtonError(request, w)
  70. ELSE
  71. HandleClientAction(request); (* first handle a client event *)
  72. IF ((requestHeader.method = WebHTTP.GetM) OR (requestHeader.method = WebHTTP.PostM)) THEN
  73. GenerateDynamicWebpage(f, request, w)
  74. END
  75. END;
  76. chunker.Close
  77. ELSIF (reply.statuscode = WebHTTP.ObjectMoved) THEN
  78. NEW(chunker, w, out, requestHeader, reply);
  79. WebHTTP.SendResponseHeader(reply, out);
  80. w.String(DocType); w.Ln;
  81. w.String("<html><head><title>Document Moved</title></head>"); w.Ln;
  82. w.String('<body><h1>Document Moved</h1>This document may be found <a href="http://');
  83. w.String(requestHeader.uri); w.String(">here</a>.<hr/><address>");
  84. w.String(WebHTTPServer.ServerVersion); w.String("</address></body></html>"); w.Ln;
  85. w.Update;
  86. chunker.Close
  87. ELSIF ((reply.statuscode = WebHTTP.NotFound) OR (f = NIL)) THEN
  88. reply.statuscode := WebHTTP.NotFound;
  89. NEW(chunker, w, out, requestHeader, reply);
  90. WebHTTP.SendResponseHeader(reply, out);
  91. w.String(DocType); w.Ln;
  92. w.String("<html><head><title>404 - Not Found</title></head>");
  93. w.String("<body>HTTP 404 - File Not Found<hr/><address>");
  94. w.String(WebHTTPServer.ServerVersion); w.String("</address></body></html>");
  95. w.Ln;
  96. w.Update;
  97. chunker.Close
  98. ELSE
  99. reply.statuscode := WebHTTP.NotImplemented;
  100. WebHTTP.WriteStatus(reply, out)
  101. END
  102. END Handle;
  103. END DynamicWebpagePlugin;
  104. ParserError = POINTER TO RECORD
  105. pos, line, row: LONGINT;
  106. msg: ARRAY 1024 OF CHAR
  107. END;
  108. SessionStateFullElement = OBJECT
  109. VAR
  110. (* each statefull object instance is identified by an object id (constructed out of file and explicit id in xml representation) *)
  111. objectId: Strings.String;
  112. session: HTTPSession.Session;
  113. activeElem: DynamicWebpage.StateFullActiveElement;
  114. eventHandlers: DynamicWebpage.EventHandlerList;
  115. PROCEDURE &Init*(id: Strings.String; sess: HTTPSession.Session; elem: DynamicWebpage.StateFullActiveElement;
  116. handlerList : DynamicWebpage.EventHandlerList);
  117. BEGIN (* id # NIL & sess # NIL & elem # NIL *)
  118. NEW(objectId, LEN(id)); COPY(id^, objectId^);
  119. session := sess; activeElem := elem; eventHandlers:= handlerList
  120. END Init;
  121. END SessionStateFullElement;
  122. (* Abstracts the creation and delegation to a statefull or stateless active element instances.
  123. The type of the active element (stateless or statefull) is defined by the creation on the first access
  124. Stateless active elements are singleton and used by all sessions. Statefull active elements belong
  125. to exactly one session and have multiple instances identified explicitly by the webpage file and
  126. an "id" attribute in the XML representation. *)
  127. ActiveElementFactory = OBJECT
  128. VAR
  129. moduleName: ARRAY 128 OF CHAR;
  130. activeElemDesc: DynamicWebpage.ActiveElementDescriptor;
  131. (* used if it is a stateless active element, otherwise NIL *)
  132. stateLessActiveElem: DynamicWebpage.StateLessActiveElement; (* singleton *)
  133. stateLessEventHandlers: DynamicWebpage.EventHandlerList;
  134. (* used if it is a statefull active element, otherwise NIL *)
  135. stateFullActiveElems: TFClasses.List; (* List of SessionStateFullElement *)
  136. PROCEDURE &Init*(module: Strings.String; desc: DynamicWebpage.ActiveElementDescriptor);
  137. BEGIN
  138. ASSERT(module # NIL); ASSERT(desc # NIL); ASSERT(desc.factory # NIL);
  139. COPY(module^, moduleName); activeElemDesc := desc
  140. END Init;
  141. PROCEDURE SessionExpired(session: HTTPSession.Session);
  142. VAR sessionElem: SessionStateFullElement; expElemList: TFClasses.List; (* List of SessionStateFullElement *)
  143. i : LONGINT; p: ANY;
  144. BEGIN {EXCLUSIVE}
  145. (* there could be multiple instances of a statefull active element belonging to session *)
  146. NEW(expElemList);
  147. stateFullActiveElems.Lock;
  148. FOR i := 0 TO stateFullActiveElems.GetCount()-1 DO
  149. p := stateFullActiveElems.GetItem(i); sessionElem := p(SessionStateFullElement); (* sessionElem # NIL *)
  150. IF (sessionElem.session = session) THEN
  151. expElemList.Add(sessionElem)
  152. END
  153. END;
  154. stateFullActiveElems.Unlock;
  155. FOR i:= 0 TO expElemList.GetCount()-1 DO
  156. p := expElemList.GetItem(i);
  157. stateFullActiveElems.Remove(p)
  158. END;
  159. IF (DEBUG) THEN
  160. KernelLog.String("Statefull active element instances '"); KernelLog.String(activeElemDesc.elementName);
  161. KernelLog.String("' in module '"); KernelLog.String(moduleName); KernelLog.String("' have been freed for session '");
  162. KernelLog.String(session.sessionId); KernelLog.String("'."); KernelLog.Ln
  163. END
  164. END SessionExpired;
  165. (* must be called before disposing the object *)
  166. PROCEDURE PrepareDisposal;
  167. BEGIN
  168. IF (stateFullActiveElems # NIL) THEN (* contains statefull active element *)
  169. HTTPSession.RemoveExpirationHandler(SessionExpired)
  170. END
  171. END PrepareDisposal;
  172. (* objectId is only used if a statefull active element is requested, otherwise objectId can be NIL *)
  173. PROCEDURE GetElementInstance(session : HTTPSession.Session; objectId: Strings.String) : DynamicWebpage.ActiveElement;
  174. VAR i: LONGINT; p: ANY; sessionElem: SessionStateFullElement; elem: DynamicWebpage.ActiveElement;
  175. stateFullElem: DynamicWebpage.StateFullActiveElement; eventHandlerList: DynamicWebpage.EventHandlerList;
  176. BEGIN {EXCLUSIVE}
  177. IF (stateLessActiveElem # NIL) THEN (* it is a stateless active element *)
  178. RETURN stateLessActiveElem
  179. ELSIF (stateFullActiveElems # NIL) THEN (* it is a statefull active element *)
  180. IF (objectId # NIL) THEN
  181. stateFullActiveElems.Lock;
  182. FOR i := 0 TO stateFullActiveElems.GetCount()-1 DO
  183. p := stateFullActiveElems.GetItem(i); sessionElem := p(SessionStateFullElement);
  184. (* sessionElem # NIL & sessionElem.objectId # NIL *)
  185. IF ((sessionElem.session = session) & (sessionElem.objectId^ = objectId^)) THEN
  186. stateFullActiveElems.Unlock;
  187. RETURN sessionElem.activeElem;
  188. END
  189. END;
  190. stateFullActiveElems.Unlock;
  191. (* create a new statefull element *)
  192. elem := activeElemDesc.factory();
  193. (* elem # NIL since there was already a statefull element instance created by this factory method *)
  194. stateFullElem := elem(DynamicWebpage.StateFullActiveElement);
  195. eventHandlerList := elem.GetEventHandlers();
  196. NEW(sessionElem, objectId, session, stateFullElem, eventHandlerList);
  197. stateFullActiveElems.Add(sessionElem);
  198. RETURN elem
  199. ELSE
  200. KernelLog.String("Dynamic Webpage Plugin: The statefull active element '");
  201. KernelLog.String(activeElemDesc.elementName); KernelLog.String("' in module '");
  202. KernelLog.String(moduleName); KernelLog.String("' must be used together with an id in a webpage file.");
  203. KernelLog.Ln;
  204. RETURN NIL
  205. END
  206. ELSE (* it is not yet determined if it is a statefull or stateless active element *)
  207. elem := activeElemDesc.factory();
  208. IF (elem # NIL) THEN
  209. IF (elem IS DynamicWebpage.StateFullActiveElement) THEN
  210. IF (objectId # NIL) THEN
  211. (* initialize as statefull active element factory *)
  212. NEW(stateFullActiveElems);
  213. HTTPSession.AddExpirationHandler(SessionExpired);
  214. stateFullElem := elem(DynamicWebpage.StateFullActiveElement);
  215. eventHandlerList := elem.GetEventHandlers();
  216. NEW(sessionElem, objectId, session, stateFullElem, eventHandlerList);
  217. stateFullActiveElems.Add(sessionElem);
  218. RETURN stateFullElem
  219. ELSE
  220. KernelLog.String("Dynamic Webpage Plugin: The statefull active element '");
  221. KernelLog.String(activeElemDesc.elementName); KernelLog.String("' in module '");
  222. KernelLog.String(moduleName); KernelLog.String("' must be used together with an attribute '");
  223. KernelLog.String(DynamicWebpage.XMLAttributeObjectIdName); KernelLog.String("'.");
  224. KernelLog.Ln;
  225. RETURN NIL
  226. END
  227. ELSIF (elem IS DynamicWebpage.StateLessActiveElement) THEN
  228. (* initialize as stateless active element factory *)
  229. stateLessActiveElem := elem(DynamicWebpage.StateLessActiveElement);
  230. stateLessEventHandlers := elem.GetEventHandlers();
  231. RETURN elem
  232. ELSE (* elem IS DynamicWebpage.ActiveElement *)
  233. KernelLog.String("Dynamic Webpage Plugin: The active element '");
  234. KernelLog.String(activeElemDesc.elementName); KernelLog.String("' in module '");
  235. KernelLog.String(moduleName); KernelLog.String("' must be either a stateless or statefull active element.");
  236. KernelLog.Ln;
  237. RETURN NIL
  238. END
  239. ELSE
  240. KernelLog.String("Dynamic Webpage Plugin: Invalid result from the factory for the active element '");
  241. KernelLog.String(activeElemDesc.elementName); KernelLog.String("' in module '");
  242. KernelLog.String(moduleName); KernelLog.String("'"); KernelLog.Ln;
  243. RETURN NIL
  244. END
  245. END
  246. END GetElementInstance;
  247. (* objectId is only used for statefull activ elements and is NIL in case of stateless active elements *)
  248. PROCEDURE FindEventHandler(session: HTTPSession.Session; objectId: Strings.String; CONST handlerName: ARRAY OF CHAR) : DynamicWebpage.EventHandler;
  249. VAR elem: DynamicWebpage.ActiveElement; sessionElem: SessionStateFullElement; p: ANY; i : LONGINT;
  250. PROCEDURE GetEventHandlerFromList(eventList: DynamicWebpage.EventHandlerList) : DynamicWebpage.EventHandler;
  251. VAR j: LONGINT;
  252. BEGIN
  253. IF (eventList # NIL) THEN
  254. FOR j := 0 TO LEN(eventList^)-1 DO
  255. IF (eventList[j] # NIL) THEN
  256. IF (eventList[j].methodName = handlerName) THEN
  257. RETURN eventList[j].handler
  258. END
  259. ELSE
  260. KernelLog.String("Dynamic Webpage Plugin: The "); KernelLog.Int(j, 0);
  261. KernelLog.String(".th event handler is not defined in the event handler list in the active element '");
  262. KernelLog.String(activeElemDesc.elementName); KernelLog.String("' in module '");
  263. KernelLog.String(moduleName); KernelLog.String("'"); KernelLog.Ln
  264. END
  265. END
  266. END;
  267. RETURN NIL
  268. END GetEventHandlerFromList;
  269. BEGIN
  270. elem := GetElementInstance(session, objectId); (* this guarantees that the needed active element instance is now present *)
  271. IF ((elem # NIL) & (elem IS DynamicWebpage.StateLessActiveElement)) THEN (* it is a stateless active element *)
  272. RETURN GetEventHandlerFromList(stateLessEventHandlers)
  273. ELSIF ((objectId # NIL) & (elem # NIL) & (elem IS DynamicWebpage.StateFullActiveElement)) THEN (* it is a statefull active element *)
  274. (* stateFullActiveElems # NIL by GetElementInstance *)
  275. stateFullActiveElems.Lock;
  276. FOR i := 0 TO stateFullActiveElems.GetCount()-1 DO
  277. p := stateFullActiveElems.GetItem(i); sessionElem := p(SessionStateFullElement); (* sessionElem # NIL *)
  278. IF ((sessionElem.session = session) & (sessionElem.objectId^ = objectId^)) THEN
  279. stateFullActiveElems.Unlock;
  280. RETURN GetEventHandlerFromList(sessionElem.eventHandlers);
  281. END
  282. END;
  283. stateFullActiveElems.Unlock;
  284. RETURN NIL
  285. ELSE (* error message already displayed by GetElementInstance *)
  286. RETURN NIL
  287. END
  288. END FindEventHandler;
  289. END ActiveElementFactory;
  290. VAR
  291. dynamicPagePlugin: DynamicWebpagePlugin; (* singleton instance to be able to uninstall *)
  292. lockServingHosts: BOOLEAN; (* lock hold when operating on servingHosts *)
  293. servingHosts: TFClasses.List; (* List of WebHTTPServer.Host *)
  294. registeredActiveElemFact: TFClasses.List; (* List of ActiveElementFactory *)
  295. parserError: ParserError; (* since there is no DELEGATE for the reportErrorHandler XML-Module possible *)
  296. (* Returns true iff back or refresh button was pressed and increases the state counter if back button was not pressed *)
  297. PROCEDURE BackRefreshButtonWasPressed(request: HTTPSupport.HTTPRequest; session: HTTPSession.Session) : BOOLEAN;
  298. VAR httpVar: HTTPSupport.HTTPVariable; httpCounter, sessionCounter: LONGINT; p: ANY;
  299. dynStr: DynamicStrings.DynamicString; str: Strings.String; numberStr: ARRAY 14 OF CHAR;
  300. BEGIN (* request # NIL & session # NIL *)
  301. httpVar := request.GetVariableByName(DynamicWebpage.StateCounterVariable);
  302. httpCounter := 0;
  303. IF (httpVar # NIL) THEN
  304. Strings.StrToInt(httpVar.value, httpCounter);
  305. p := session.GetVariableValue(DynamicWebpage.StateCounterVariable);
  306. IF ((p # NIL) & (p IS DynamicStrings.DynamicString)) THEN
  307. dynStr := p(DynamicStrings.DynamicString); str := dynStr.ToArrOfChar(); (* str # NIL *)
  308. Strings.StrToInt(str^, sessionCounter);
  309. IF (httpCounter < sessionCounter) THEN
  310. RETURN TRUE
  311. END
  312. END
  313. END;
  314. INC(sessionCounter);
  315. Strings.IntToStr(sessionCounter, numberStr); NEW(dynStr); dynStr.Append(numberStr);
  316. session.AddVariableValue(DynamicWebpage.StateCounterVariable, dynStr);
  317. RETURN FALSE
  318. END BackRefreshButtonWasPressed;
  319. (* handle the case that the user has used the back button of the browser's navigation bar *)
  320. PROCEDURE HandleBackRefreshButtonError(request: HTTPSupport.HTTPRequest; w: Streams.Writer);
  321. VAR sessionId: HTTPSession.SessionId;
  322. BEGIN
  323. HTTPSession.GetSessionId(request, sessionId);
  324. w.String(DocType); w.Ln;
  325. w.String("<html><head><title>Do not use the back or refresh button</title></head>"); w.Ln;
  326. w.String("<body><h1>Do not use the back or refresh button in the navigation bar</h1>");
  327. w.String("Using the back or refresh button in the navigation bar of this browser is not allowed when using dynamic ");
  328. w.String("webpages.<br/>To continue click ");w.String('<a href="');
  329. w.String(request.shortUri); w.String("?");
  330. w.String(HTTPSession.HTTPVarSessionIdName); w.String("="); w.String(sessionId);
  331. w.String('">here</a>.<hr/><address>'); w.String(WebHTTPServer.ServerVersion);
  332. w.String("</address></body></html>"); w.Ln;
  333. w.Update;
  334. END HandleBackRefreshButtonError;
  335. PROCEDURE GenerateDynamicWebpage(f: Files.File; request: HTTPSupport.HTTPRequest; w: Streams.Writer);
  336. VAR scanner: XMLScanner.Scanner; parser: XMLParser.Parser; doc: XML.Document;
  337. root: XML.Element; rootContent: XML.Content; errormsg: ARRAY 1024 OF CHAR;
  338. reader : Files.Reader;
  339. BEGIN (* f # NIL *)
  340. NEW(reader, f, 0);
  341. NEW(scanner, reader);
  342. NEW(parser, scanner);
  343. scanner.reportError := ReportXMLParserScannerError;
  344. parser.reportError := ReportXMLParserScannerError;
  345. BEGIN {EXCLUSIVE}
  346. (* the error handler needs additional information about the file and writer but is not a delegate *)
  347. parserError := NIL;
  348. doc := parser.Parse();
  349. IF (parserError # NIL) THEN
  350. Strings.Concat("Error while parsing: ", parserError.msg, errormsg);
  351. ReportGeneratorError(f, w, parserError.pos, parserError.line, parserError.row, errormsg);
  352. doc := NIL
  353. END
  354. END;
  355. IF doc # NIL THEN
  356. rootContent := doc;
  357. IF (TransformXMLTree(f, rootContent, request, 0, w)) THEN (* transformation worked successfully *)
  358. (* SetDocument should be provided by the XML module *)
  359. IF (rootContent IS XML.Element) THEN
  360. root := rootContent(XML.Element);
  361. ELSE
  362. (* Error "transformation result has not a root element"*)
  363. END;
  364. w.String(DocType); w.Ln;
  365. doc.Write(w, NIL, 0); (* externalize transformation result *)
  366. END
  367. END;
  368. w.Update
  369. END GenerateDynamicWebpage;
  370. (* returns true iff the transformation worked without an error *)
  371. PROCEDURE TransformXMLTree(file: Files.File; VAR n: XML.Content; VAR request: HTTPSupport.HTTPRequest;
  372. transformationDepth: INTEGER; w: Streams.Writer) : BOOLEAN;
  373. VAR enum, resultEnum: XMLObjects.Enumerator; pChild, pResultChild: ANY; elem: XML.Element;
  374. child, newChild, resultChild: XML.Content; errormsg: ARRAY 256 OF CHAR; wasTransformed: BOOLEAN;
  375. elemName : Strings.String; container, snapshot, resultContainer: XML.Container;
  376. BEGIN
  377. IF ((n # NIL) & (n IS XML.Element) & (transformationDepth > MaxTransformationDepth)) THEN
  378. elem := n(XML.Element); elemName := elem.GetName();
  379. Strings.Concat("In element '", elemName^, errormsg);
  380. Strings.Append(errormsg, "': Maximum recursive transformation steps reached. There could be an endless loop in a transformation procedure.");
  381. ReportGeneratorError(file, w, elem.GetPos(), 0, 0, errormsg);
  382. KernelLog.String("Error in Stream: "); KernelLog.String(errormsg); KernelLog.Ln;
  383. RETURN FALSE (* stop the further traversal *)
  384. ELSIF ((n # NIL) & (n IS XML.Container)) THEN
  385. (* pre-postorder traversal with recursive traversal of the post transformation result
  386. increase transformation depth only if it is a transformation of the transformation result *)
  387. wasTransformed := FALSE;
  388. (* pre transformation *)
  389. IF (n IS XML.Element) THEN
  390. elem := n(XML.Element);
  391. IF (IsActive(elem))THEN
  392. IF (~TransformActiveElement(file, n, PreTransformation, request, w)) THEN RETURN FALSE END;
  393. wasTransformed := TRUE
  394. END
  395. END;
  396. IF ((n # NIL) & (n IS XML.Container)) THEN (* transformation result of PreTransform could be not a container *)
  397. container := n(XML.Container);
  398. (* no modification while iteration allowed, extract first the contents into a snapshot *)
  399. ExtractContentsOfContainer(container, snapshot);
  400. enum := snapshot.GetContents();
  401. WHILE (enum.HasMoreElements()) DO
  402. pChild := enum.GetNext(); child := pChild(XML.Content); newChild := child;
  403. IF (~TransformXMLTree(file, newChild, request, transformationDepth, w)) THEN RETURN FALSE END;
  404. IF (newChild # NIL) THEN
  405. IF ((newChild IS XML.Container) & (~(newChild IS XML.Element))) THEN (* avoid nested containers *)
  406. resultContainer := newChild(XML.Container);
  407. resultEnum := resultContainer.GetContents();
  408. WHILE(resultEnum.HasMoreElements()) DO
  409. pResultChild := resultEnum.GetNext(); resultChild := pResultChild(XML.Content);
  410. container.AddContent(resultChild)
  411. END
  412. ELSE
  413. container.AddContent(newChild)
  414. END
  415. END
  416. END;
  417. (* post transformation *)
  418. IF (n IS XML.Element) THEN
  419. elem := n(XML.Element);
  420. IF (IsActive(elem)) THEN
  421. IF (~TransformActiveElement(file, n, PostTransformation, request, w)) THEN RETURN FALSE END
  422. END
  423. END
  424. END;
  425. IF (wasTransformed) THEN
  426. (* transformation of the transformation result is needed *)
  427. IF (~TransformXMLTree(file, n, request, transformationDepth+1, w)) THEN RETURN FALSE END;
  428. IF (DEBUG) THEN Log(elem) END
  429. END
  430. END; (* transformation result could be not a container *)
  431. RETURN TRUE
  432. END TransformXMLTree;
  433. PROCEDURE Log(elem: XML.Element);
  434. VAR sw: Streams.StringWriter; w: Streams.Writer; msg: ARRAY 1024 OF CHAR;
  435. BEGIN
  436. NEW(sw, LEN(msg)); w := sw; elem.Write(w, NIL, 0);
  437. sw.Get(msg); KernelLog.String(msg); KernelLog.Ln
  438. END Log;
  439. PROCEDURE ExtractContentsOfContainer(input: XML.Container; VAR output: XML.Container);
  440. VAR e: XMLObjects.Enumerator; p : ANY; child: XML.Content;
  441. BEGIN
  442. NEW(output);
  443. (* first copy contents to output *)
  444. e := input.GetContents();
  445. WHILE (e.HasMoreElements()) DO
  446. p := e.GetNext(); child := p(XML.Content);
  447. output.AddContent(child);
  448. END;
  449. (* then remove contents from input *)
  450. e := output.GetContents();
  451. WHILE (e.HasMoreElements()) DO
  452. p := e.GetNext(); child := p(XML.Content);
  453. input.RemoveContent(child);
  454. END
  455. END ExtractContentsOfContainer;
  456. PROCEDURE IsActive(n : XML.Element) : BOOLEAN;
  457. VAR module, obj: Strings.String;
  458. BEGIN (* n # NIL *)
  459. ExtractModuleObjectName(n, module, obj);
  460. (* check whether the module is declared to represent an active namespace *)
  461. IF ((module # NIL) & (obj # NIL)) THEN
  462. RETURN IsModuleRegistered(module^)
  463. ELSE
  464. RETURN FALSE
  465. END
  466. END IsActive;
  467. (* get the objectId if specfified used for statefull active elements *)
  468. PROCEDURE GetObjectId(CONST id: ARRAY OF CHAR; request: HTTPSupport.HTTPRequest) : Strings.String;
  469. VAR objectId: Strings.String;
  470. BEGIN
  471. (* the object id is composed by the actual uri and id attribute for the active element *)
  472. (* '&' does occur neither in a xml attribute nor in shortUri *)
  473. NEW(objectId, LEN(id)+Strings.Length(request.shortUri)+1);
  474. Strings.Concat(request.shortUri, "&", objectId^);
  475. Strings.Append(objectId^, id);
  476. RETURN objectId
  477. END GetObjectId;
  478. (** if isPreTransformation is TRUE then PreTansform() is called otherwise PostTransform(),
  479. * returns true iff the transformation worked without an error *)
  480. PROCEDURE TransformActiveElement(file: Files.File; VAR n: XML.Content; isPreTransformation: BOOLEAN;
  481. request: HTTPSupport.HTTPRequest; w: Streams.Writer) : BOOLEAN;
  482. VAR moduleName, objName, elemName, objectId, oidAttrVal: Strings.String; elem: XML.Element;
  483. activeElemFact: ActiveElementFactory; errormsg: ARRAY 256 OF CHAR; activeElem: DynamicWebpage.ActiveElement;
  484. session: HTTPSession.Session;
  485. BEGIN (* n IS XML.ELement & IsActive(n) is TRUE *)
  486. elem := n(XML.Element); elemName := elem.GetName();
  487. IF (DEBUG) THEN KernelLog.String(elemName^); KernelLog.String(" is active"); KernelLog.Ln END;
  488. ExtractModuleObjectName(elem, moduleName, objName);
  489. (* moduleName # NIL & objName # NIL since IsActive(n) = TRUE *)
  490. activeElemFact := FindActiveElemFactory(moduleName^, objName^);
  491. IF (activeElemFact # NIL) THEN
  492. session := HTTPSession.GetSession(request);
  493. oidAttrVal := elem.GetAttributeValue(DynamicWebpage.XMLAttributeObjectIdName);
  494. IF (oidAttrVal # NIL) THEN (* seems to be a statefull active element *)
  495. objectId := GetObjectId(oidAttrVal^, request);
  496. ELSE (* seems to be a stateless active element *)
  497. objectId := NIL
  498. END;
  499. activeElem := activeElemFact.GetElementInstance(session, objectId);
  500. IF (activeElem # NIL) THEN
  501. (* here would be an exception handler fine *)
  502. IF (isPreTransformation) THEN
  503. n := activeElem.PreTransform(elem, request)
  504. ELSE
  505. n := activeElem.Transform(elem, request)
  506. END
  507. ELSE
  508. Strings.Concat("In element '", elemName^, errormsg);
  509. Strings.Append(errormsg, "': Could not create an instance for the active element '");
  510. Strings.Append(errormsg, moduleName^); Strings.Append(errormsg, ".");
  511. Strings.Append(errormsg, objName^);
  512. Strings.Append(errormsg, "'. If you use a statefull active element then you must identify the instance with the xml attribute '");
  513. Strings.Append(errormsg, DynamicWebpage.XMLAttributeObjectIdName); Strings.Append(errormsg, "'.");
  514. ReportGeneratorError(file, w, elem.GetPos(), 0, 0, errormsg);
  515. KernelLog.String("Error in Stream: "); KernelLog.String(errormsg); KernelLog.Ln;
  516. RETURN FALSE (* stop transformation process *)
  517. END
  518. ELSE
  519. Strings.Concat("In element '", elemName^, errormsg);
  520. Strings.Append(errormsg, "': The active element '");
  521. Strings.Append(errormsg, moduleName^); Strings.Append(errormsg, ".");
  522. Strings.Append(errormsg, objName^); Strings.Append(errormsg, "' is not defined.");
  523. ReportGeneratorError(file, w, elem.GetPos(), 0, 0, errormsg);
  524. KernelLog.String("Error in Stream: "); KernelLog.String(errormsg); KernelLog.Ln;
  525. RETURN FALSE (* stop transformation process *)
  526. END;
  527. RETURN TRUE
  528. END TransformActiveElement;
  529. PROCEDURE IsModuleRegistered(CONST moduleName: ARRAY OF CHAR) : BOOLEAN;
  530. VAR i : LONGINT; p : ANY; obj: ActiveElementFactory;
  531. BEGIN
  532. registeredActiveElemFact.Lock;
  533. FOR i := 0 TO registeredActiveElemFact.GetCount()-1 DO
  534. p := registeredActiveElemFact.GetItem(i);
  535. obj := p(ActiveElementFactory);
  536. IF (obj.moduleName = moduleName) THEN
  537. registeredActiveElemFact.Unlock;
  538. RETURN TRUE
  539. END
  540. END;
  541. registeredActiveElemFact.Unlock;
  542. RETURN FALSE
  543. END IsModuleRegistered;
  544. PROCEDURE FindActiveElemFactory(CONST moduleName, objName: ARRAY OF CHAR) : ActiveElementFactory;
  545. VAR i : LONGINT; p : ANY; obj: ActiveElementFactory;
  546. BEGIN
  547. registeredActiveElemFact.Lock;
  548. FOR i := 0 TO registeredActiveElemFact.GetCount()-1 DO
  549. p := registeredActiveElemFact.GetItem(i);
  550. obj := p(ActiveElementFactory);
  551. IF ((obj.moduleName = moduleName) & (obj.activeElemDesc.elementName = objName)) THEN
  552. registeredActiveElemFact.Unlock;
  553. RETURN obj
  554. END
  555. END;
  556. registeredActiveElemFact.Unlock;
  557. RETURN NIL
  558. END FindActiveElemFactory;
  559. PROCEDURE ExtractModuleObjectName(n: XML.Element; VAR moduleName: Strings.String; VAR objName: Strings.String);
  560. VAR elemNameDyn : DynamicStrings.DynamicString; elemName, namespaceId, attrVal: Strings.String;
  561. pos: LONGINT; attrName: ARRAY 128 OF CHAR; attr: XML.Attribute; tempElem : XML.Element;
  562. BEGIN (* n # NIL *)
  563. moduleName := NIL; objName := NIL;
  564. elemName := n.GetName();
  565. DynamicStrings.Search(":", elemName^, pos);
  566. IF ((pos > 0) & (Strings.Length(elemName^) > pos+1)) THEN (* elemName^ = "a:b" and len(a) > 0 and len(b) > 0 *)
  567. NEW(elemNameDyn); elemNameDyn.FromArrOfChar(elemName);
  568. namespaceId := elemNameDyn.Extract(0, pos);
  569. Strings.Concat("xmlns:", namespaceId^, attrName);
  570. (* look for the namespace declaration recursively in parent elements *)
  571. tempElem := n; attr := NIL;
  572. WHILE ((tempElem # NIL) & (attr = NIL)) DO
  573. attr := tempElem.GetAttribute(attrName);
  574. tempElem := tempElem.GetParent();
  575. END;
  576. IF (attr # NIL) THEN
  577. attrVal := attr.GetValue();
  578. moduleName := attrVal; objName := elemNameDyn.Extract(pos+1, Strings.Length(elemName^)-pos)
  579. END
  580. END
  581. END ExtractModuleObjectName;
  582. PROCEDURE ReportGeneratorError(f: Files.File; w: Streams.Writer; pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR);
  583. VAR fname: Files.FileName;
  584. BEGIN
  585. IF (f # NIL) THEN
  586. f.GetName(fname);
  587. ELSE
  588. COPY("?", fname);
  589. END;
  590. KernelLog.String("DynamicWebpagePlugin while processing file '"); KernelLog.String(fname); KernelLog.String("':");
  591. KernelLog.Ln; KernelLog.String("pos "); KernelLog.Int(pos, 6); KernelLog.String(", line "); KernelLog.Int(line, 0);
  592. KernelLog.String(", row "); KernelLog.Int(row, 0); KernelLog.String(" "); KernelLog.String(msg); KernelLog.Ln;
  593. w.String(DocType); w.Ln;
  594. w.String("<html><head><title>Error while processing dynamic webpage</title></head>");
  595. w.Ln; w.String("<body><h1>Error while processing dynamic webpage</h1><p>file '");
  596. w.String(fname); w.String("' pos "); w.Int(pos, 6);
  597. w.String(", line "); w.Int(line, 0); w.String(", row ");
  598. w.Int(row, 0); w.String(" "); w.String(msg); w.Ln;
  599. w.String("</p><hr/><address>"); w.String(WebHTTPServer.ServerVersion);
  600. w.String("</address></body></html>")
  601. END ReportGeneratorError;
  602. PROCEDURE ReportXMLParserScannerError(pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR); (* Error handler for the XML parser *)
  603. BEGIN
  604. NEW(parserError); parserError.pos := pos; parserError.line := line; COPY(msg, parserError.msg)
  605. END ReportXMLParserScannerError;
  606. PROCEDURE HandleClientAction(request: HTTPSupport.HTTPRequest);
  607. VAR moduleVar, objectVar, methodVar, objectIdVar, var: HTTPSupport.HTTPVariable; par: DynamicWebpage.Parameter;
  608. params : DynamicWebpage.ParameterList; paramTempList : TFClasses.List; activeFact: ActiveElementFactory;
  609. handler: DynamicWebpage.EventHandler; p : ANY; varPrefix : ARRAY 40 OF CHAR;
  610. i, prefixLength, restLength: LONGINT; session: HTTPSession.Session; objectId: Strings.String;
  611. BEGIN
  612. prefixLength := Strings.Length(DynamicWebpage.HTTPVarCommandParamPrefix);
  613. moduleVar := request.GetVariableByName(DynamicWebpage.HTTPVarCommandModule);
  614. objectVar := request.GetVariableByName(DynamicWebpage.HTTPVarCommandObject);
  615. objectIdVar := request.GetVariableByName(DynamicWebpage.HTTPVarCommandObjectId);
  616. methodVar := request.GetVariableByName(DynamicWebpage.HTTPVarCommandMethod);
  617. IF (DEBUG) THEN
  618. IF (moduleVar # NIL) THEN KernelLog.String(moduleVar.value) END;
  619. KernelLog.String(".");
  620. IF (objectVar # NIL) THEN KernelLog.String(objectVar.value) END;
  621. KernelLog.String(".");
  622. IF (methodVar # NIL) THEN KernelLog.String(methodVar.value) END;
  623. IF (objectIdVar # NIL) THEN KernelLog.String(" id="); KernelLog.String(objectIdVar.value) END;
  624. KernelLog.Ln
  625. END;
  626. IF ((moduleVar # NIL) & (objectVar # NIL) & (methodVar # NIL)) THEN
  627. (* search all parameters *)
  628. NEW(paramTempList);
  629. request.variables.Lock;
  630. FOR i := 0 TO request.variables.GetCount()-1 DO
  631. p := request.variables.GetItem(i); var := p(HTTPSupport.HTTPVariable);
  632. Strings.Copy(var.name, 0, prefixLength, varPrefix);
  633. restLength := Strings.Length(var.name)-prefixLength;
  634. IF ((varPrefix = DynamicWebpage.HTTPVarCommandParamPrefix) & (restLength > 0)) THEN
  635. NEW(par); NEW(par.name, restLength+1);
  636. Strings.Copy(var.name, prefixLength, restLength, par.name^);
  637. NEW(par.value, Strings.Length(var.value)+1); COPY(var.value, par.value^);
  638. paramTempList.Add(par)
  639. END
  640. END;
  641. request.variables.Unlock;
  642. NEW(params);
  643. IF paramTempList.GetCount() > 0 THEN
  644. NEW(params.parameters, paramTempList.GetCount());
  645. FOR i := 0 TO paramTempList.GetCount()-1 DO
  646. p := paramTempList.GetItem(i); params.parameters[i] := p(DynamicWebpage.Parameter)
  647. END
  648. ELSE
  649. params.parameters := NIL
  650. END;
  651. (* invoke the event delegate *)
  652. activeFact := FindActiveElemFactory(moduleVar.value, objectVar.value);
  653. IF (activeFact # NIL) THEN
  654. session := HTTPSession.GetSession(request);
  655. IF (objectIdVar # NIL) THEN
  656. objectId:= GetObjectId(objectIdVar.value, request)
  657. ELSE
  658. objectId := NIL
  659. END;
  660. handler := activeFact.FindEventHandler(session, objectId, methodVar.value);
  661. IF (handler # NIL) THEN
  662. (* here would be an exception handler fine *)
  663. handler(request, params)
  664. ELSE
  665. KernelLog.String("Dynamic Webpage Plugin: Event handler '"); KernelLog.String(methodVar.value);
  666. KernelLog.String("' in "); KernelLog.String(moduleVar.value); KernelLog.String("."); KernelLog.String(objectVar.value);
  667. KernelLog.String(" is not registered to handle webclient events. If you use a statefull active element then you");
  668. KernelLog.String(" have to specify the instance id."); KernelLog.Ln
  669. END
  670. ELSE
  671. KernelLog.String("Dynamic Webpage Plugin: Active element ");
  672. KernelLog.String(moduleVar.value); KernelLog.String("."); KernelLog.String(objectVar.value);
  673. KernelLog.String(" is not registered."); KernelLog.Ln
  674. END
  675. END
  676. END HandleClientAction;
  677. PROCEDURE ClearFactoryList;
  678. VAR p: ANY; fact: ActiveElementFactory; i: LONGINT;
  679. BEGIN
  680. IF (registeredActiveElemFact # NIL) THEN
  681. registeredActiveElemFact.Lock;
  682. FOR i := 0 TO registeredActiveElemFact.GetCount()-1 DO
  683. p := registeredActiveElemFact.GetItem(i); fact := p(ActiveElementFactory); (* fact # NIL *)
  684. fact.PrepareDisposal
  685. END;
  686. registeredActiveElemFact.Unlock;
  687. registeredActiveElemFact := NIL
  688. END
  689. END ClearFactoryList;
  690. PROCEDURE ReadRegisteredModules;
  691. VAR elem, child: XML.Element; enum: XMLObjects.Enumerator; p: ANY; childName, moduleName: Strings.String;
  692. attr: XML.Attribute;
  693. BEGIN
  694. ClearFactoryList;
  695. NEW(registeredActiveElemFact);
  696. IF (Configuration.config # NIL) THEN
  697. elem := Configuration.config.GetRoot();
  698. elem := Configuration.GetNamedElement(elem, "Section", DynamicWebpage.ConfigurationSupperSectionName);
  699. IF (elem # NIL) THEN
  700. elem := Configuration.GetNamedElement(elem, "Section", DynamicWebpage.ConfigurationSubSectionName);
  701. IF (elem # NIL) THEN
  702. enum := elem.GetContents();
  703. WHILE (enum.HasMoreElements()) DO
  704. p := enum.GetNext();
  705. IF (p IS XML.Element) THEN
  706. child := p(XML.Element); childName := child.GetName();
  707. IF (childName^ = "Setting") THEN
  708. attr := child.GetAttribute("value");
  709. IF (attr # NIL) THEN
  710. moduleName := attr.GetValue();
  711. RegisterModuleByName(moduleName)
  712. END
  713. END
  714. END
  715. END
  716. ELSE
  717. KernelLog.String("Dynamic Webpage plugin: In Configuration.XML under '");
  718. KernelLog.String(DynamicWebpage.ConfigurationSupperSectionName); KernelLog.String("' is no section '");
  719. KernelLog.String(DynamicWebpage.ConfigurationSubSectionName); KernelLog.String(" defined."); KernelLog.Ln
  720. END
  721. ELSE
  722. KernelLog.String("Dynamic Webpage plugin: In Configuration.XML is no section '");
  723. KernelLog.String(DynamicWebpage.ConfigurationSupperSectionName); KernelLog.String("' defined."); KernelLog.Ln
  724. END
  725. ELSE
  726. KernelLog.String("Dynamic Webpage plugin: Cannot open Configuration.XML"); KernelLog.Ln
  727. END
  728. END ReadRegisteredModules;
  729. PROCEDURE RegisterModuleByName(moduleName: Strings.String);
  730. VAR module: Modules.Module; factory : DynamicWebpage.ActiveElementDescSetFactory; i, res: LONGINT;
  731. msg: ARRAY 1024 OF CHAR; desc: DynamicWebpage.ActiveElementDescriptor;
  732. descList: DynamicWebpage.ActiveElementDescSet;
  733. BEGIN
  734. (* load the module if not already loaded *)
  735. module := Modules.ThisModule(moduleName^, res, msg);
  736. IF ((res = 0) & (module # NIL)) THEN
  737. GETPROCEDURE(moduleName^, DynamicWebpage.ProcNameGetDescriptors, factory);
  738. IF (factory # NIL) THEN
  739. descList := factory();
  740. IF (descList # NIL) THEN (* register all present descriptors *)
  741. FOR i := 0 TO descList.GetCount()-1 DO
  742. desc := descList.GetItem(i);
  743. RegisterActiveElement(moduleName, desc)
  744. END
  745. ELSE
  746. KernelLog.String("Dynamic Webpage Plugin: Wrong result type from procedure '");
  747. KernelLog.String(DynamicWebpage.ProcNameGetDescriptors); KernelLog.String("' in module '");
  748. KernelLog.String(moduleName^); KernelLog.String("'"); KernelLog.Ln
  749. END
  750. ELSE
  751. KernelLog.String("Dynamic Webpage Plugin: Procedure '"); KernelLog.String(DynamicWebpage.ProcNameGetDescriptors);
  752. KernelLog.String("' in module '"); KernelLog.String(moduleName^); KernelLog.String("' is not present."); KernelLog.Ln
  753. END
  754. ELSE
  755. KernelLog.String("Dynamic Webpage Plugin: Module '"); KernelLog.String(moduleName^);
  756. KernelLog.String("' is not present."); KernelLog.Ln
  757. END
  758. END RegisterModuleByName;
  759. PROCEDURE RegisterActiveElement(moduleName: Strings.String; desc: DynamicWebpage.ActiveElementDescriptor);
  760. VAR activeElemFact : ActiveElementFactory;
  761. BEGIN
  762. IF (desc.factory # NIL) THEN
  763. NEW(activeElemFact, moduleName, desc);
  764. (* the new active element instance is created by the first usage and it is then determined by the dynamic type of
  765. factory method result whether it is a statefull or stateless active element *)
  766. registeredActiveElemFact.Add(activeElemFact);
  767. IF ((DEBUG) OR (ShowRegisteredElements)) THEN
  768. KernelLog.String("Active element '"); KernelLog.String(moduleName^); KernelLog.String(".");
  769. KernelLog.String(desc.elementName); KernelLog.String("' has been registered."); KernelLog.Ln
  770. END
  771. ELSE
  772. KernelLog.String("Dynamic Webpage Plugin: No factory method defined for active element '");
  773. KernelLog.String(desc.elementName); KernelLog.String("' in module '");
  774. KernelLog.String(moduleName^); KernelLog.String("'"); KernelLog.Ln
  775. END
  776. END RegisterActiveElement;
  777. PROCEDURE LockServingHosts;
  778. BEGIN {EXCLUSIVE}
  779. AWAIT(~lockServingHosts); lockServingHosts := TRUE
  780. END LockServingHosts;
  781. PROCEDURE UnlockServingHosts;
  782. BEGIN {EXCLUSIVE}
  783. lockServingHosts := FALSE
  784. END UnlockServingHosts;
  785. PROCEDURE Install*(context : Commands.Context); (** [{host}]. Host may include wildcards. *)
  786. VAR host: ARRAY 1024 OF CHAR; hl: WebHTTPServer.HostList;
  787. BEGIN
  788. LockServingHosts;
  789. IF dynamicPagePlugin = NIL THEN (* Singleton *)
  790. NEW(dynamicPagePlugin, PluginName)
  791. END;
  792. IF (servingHosts.GetCount() = 0) THEN
  793. ReadRegisteredModules
  794. END;
  795. REPEAT
  796. context.arg.String(host); Strings.Trim(host, " ");
  797. hl := WebHTTPServer.FindHosts(host);
  798. IF (hl # NIL) THEN
  799. WHILE (hl # NIL) DO
  800. context.out.String(PluginName);
  801. IF (servingHosts.IndexOf(hl.host) >= 0) THEN
  802. context.out.String(" already installed at ")
  803. ELSE
  804. hl.host.AddPlugin(dynamicPagePlugin);
  805. servingHosts.Add(hl.host);
  806. context.out.String(" added to ")
  807. END;
  808. IF (hl.host.name = "") THEN context.out.String("default host ")
  809. ELSE context.out.String(hl.host.name)
  810. END;
  811. context.out.Ln;
  812. hl := hl.next
  813. END
  814. ELSE
  815. context.error.String("Host '"); context.error.String(host); context.error.String("' not present."); context.error.Ln
  816. END
  817. UNTIL ((context.arg.res # Streams.Ok) OR (Strings.Length(host) = 0));
  818. UnlockServingHosts;
  819. END Install;
  820. PROCEDURE ModuleTerminator;
  821. VAR p: ANY; h: WebHTTPServer.Host; i : LONGINT;
  822. BEGIN
  823. LockServingHosts;
  824. FOR i := 0 TO servingHosts.GetCount()-1 DO
  825. p := servingHosts.GetItem(i); h := p(WebHTTPServer.Host);
  826. UnInstallHost(h)
  827. END;
  828. UnlockServingHosts;
  829. ClearFactoryList
  830. END ModuleTerminator;
  831. PROCEDURE UnInstallHost(host: WebHTTPServer.Host);
  832. BEGIN
  833. host.RemovePlugin(dynamicPagePlugin);
  834. KernelLog.String(PluginName); KernelLog.String(" removed from ");
  835. IF (host.name = "") THEN KernelLog.String("default host ")
  836. ELSE KernelLog.String(host.name)
  837. END;
  838. KernelLog.Ln
  839. END UnInstallHost;
  840. PROCEDURE Uninstall*(context : Commands.Context); (** [{host}]. Host may include wildcards *)
  841. VAR host: ARRAY 1024 OF CHAR; hl: WebHTTPServer.HostList;
  842. BEGIN
  843. IF dynamicPagePlugin # NIL THEN
  844. LockServingHosts;
  845. REPEAT
  846. context.arg.String(host); Strings.Trim(host, " ");
  847. hl := WebHTTPServer.FindHosts(host);
  848. IF (hl # NIL) THEN
  849. WHILE (hl # NIL) DO
  850. UnInstallHost(hl.host);
  851. servingHosts.Remove(hl.host);
  852. hl := hl.next
  853. END
  854. ELSE
  855. context.error.String("Host '"); context.error.String(host); context.error.String("' not present."); context.error.Ln
  856. END
  857. UNTIL ((context.arg.res # Streams.Ok) OR (Strings.Length(host) = 0));
  858. UnlockServingHosts
  859. ELSE
  860. context.error.String(PluginName); context.error.String(" is not installed"); context.error.Ln
  861. END;
  862. IF (servingHosts.GetCount() = 0) THEN
  863. ClearFactoryList
  864. END;
  865. END Uninstall;
  866. BEGIN
  867. NEW(servingHosts); lockServingHosts := FALSE;
  868. Modules.InstallTermHandler(ModuleTerminator)
  869. END DynamicWebpagePlugin.
  870. System.Free DynamicWebpagePlugin~
  871. System.Free WebHTTPServerTools WebHTTPServer WebHTTP~
  872. DynamicWebpagePlugin.Install ~
  873. DynamicWebpagePlugin.Uninstall ~