using concurrent::ActorPoolusing concurrent::AtomicRefusing web::WebModusing web::WebClientusing afIoc::Registryusing afIoc::RegistryMetausing afIoc::RegistryShutdownErrusing afIocConfig::ConfigSource** The `web::WebMod` that runs in [Wisp]`pod:wisp`. constclass BedSheetWebMod : WebMod {privateconststatic Log log := Utils.log** Returns 'pod.dis' (or 'proj.name'if not found) from the application's pod meta, or the pod name if neither are defined.const Str appName** The port number this Bed App will be listening on. const Int port** The IoC registry.const Registry registryprivateconst MiddlewarePipeline pipelineprivateconst AtomicRef podCheckerRef := AtomicRef(null)** Creates this 'WebMod'. Use a 'BedSheetBuilder' to create the 'Registry' instance - it ensures all the options have been set.new make(Registry registry){ bedServer := (BedSheetServer) registry.activeScope.serviceById(BedSheetServer#.qname)this.registry = registry this.appName = bedServer.appNamethis.port = bedServer.port// BUGFIX: eager load the middleware pipeline, so we can use the ErrMiddleware// otherwise Errs thrown when instantiating middleware end up in limbo// Errs from the FileHandler ctor are a prime examplethis.pipeline = registry.activeScope.serviceById(MiddlewarePipeline#.qname)} @NoDocoverride Void onService(){ req.mod = thisif(podCheckerRef.val != null){if(req.modRel == BsConstants.pingUrl){// web pages ping us to check if we've restarted yet res.headers["Content-Type"] = MimeType("text/plain").toStr res.out.print("OK").flush.closereturn}if(appRequiresRestart){ notifyClientOfRestart restartAppreturn}}try{ registry.activeScope.createChild("httpRequest"){// this is the actual call to BedSheet! // the rest of this class is just startup and error handling fluff! pipeline.service}}catch(RegistryShutdownErr err){// nothing we can do hereif(!res.isCommitted) res.sendErr(500, "BedSheet shutting down...")return// theoretically, this should have already been dealt with by our ErrMiddleware...// ...but it's handy for BedSheet development!}catch(Err err){// try to send something to the browser errLog := err.traceToStrtry{ errPrinter := (ErrPrinterStr) registry.activeScope.serviceById(ErrPrinterStr#.qname) errLog = errPrinter.errToStr(err)}catch{}// log and throw, because we don't trust Wisp to log it Env.cur.err.printLine(errLog)if(!res.isCommitted) res.sendErr(500, "${err.typeof} - ${err.msg}")throw err}} @NoDocoverride Void onStart(){// start the destroyer! meta := (RegistryMeta) registry.activeScope.serviceById(RegistryMeta#.qname) beds := (BedSheetServer) registry.activeScope.serviceById(BedSheetServer#.qname)if(meta.options[BsConstants.meta_watchdog] == true){ pingPort := (Int) meta.options[BsConstants.meta_watchdogPort] destroyer := (AppDestroyer) registry.activeScope.build(AppDestroyer#, [ActorPool(), pingPort]) destroyer.start watchAllPods := meta.options[BsConstants.meta_watchAllPods]?.toStr?.toBool(false) ?: false appPod := (Pod) meta.options[BsConstants.meta_appPod]if(appPod.meta["pod.isScript"] == "true")throw Err(BsLogMsgs.appRestarter_canNotProxyScripts(appPod.name)) podChecker := PodChecker(appPod.name, watchAllPods)this.podCheckerRef.val = podChecker.initialise}// print BedSheet connection details configSrc := (ConfigSource) registry.activeScope.serviceByType(ConfigSource#) host := (Uri) configSrc.get(BedSheetConfigIds.host, Uri#) ver := beds.appPod?.version log.info(BsLogMsgs.bedSheetWebMod_started(appName, ver, host))} @NoDocoverride Void onStop(){ registry.shutdown log.info(BsLogMsgs.bedSheetWebMod_stopping(appName))}private Bool appRequiresRestart(){((PodChecker?) podCheckerRef.val)?.podsModifed ?: false}private Void notifyClientOfRestart(){ registry.activeScope.createChild("httpRequest"){ msg := ((PodChecker?) podCheckerRef.val)?.restartMsg ?: "Just because I feel like it." meta := (RegistryMeta)it.serviceById(RegistryMeta#.qname) pages := (BedSheetPages)it.serviceById(BedSheetPages#.qname) appPod := (Pod) meta.options[BsConstants.meta_appPod] text := pages.renderRestart(appName, appPod.version, msg) buf := text.toBuf res.statusCode = 503 res.headers["Content-Type"] = text.contentType.toStr res.headers["Content-Length"] = buf.size.toStr res.out.writeBuf(buf).flush.close}}private Void restartApp(){ meta := (RegistryMeta) registry.activeScope.serviceById(RegistryMeta#.qname) dogPort := (Int) meta.options[BsConstants.meta_watchdogPort]try{ resBody := WebClient(){ reqUri = "http://localhost:${dogPort}${BsConstants.restartUrl}".toUri}.getStr.trimif(resBody != "OK")throw IOErr("Watchdog not OK: $resBody")}catch(Err err){ log.warn("Could not restart App: $err.msg")}}}