using concurrent::ActorPool
using concurrent::AtomicRef
using web::WebMod
using afIoc::Registry
using afIoc::RegistryBuilder
using afIocConfig::IocConfigModule
** The top-level `web::WebMod` to be passed to [Wisp]`http://fantom.org/doc/wisp/index.html`.
const class BedSheetWebMod : WebMod {
private const static Log log := Utils.getLog(BedSheetWebMod#)
const Str moduleName
const Int port
const [Str:Obj?] bedSheetOptions
const [Str:Obj?]? registryOptions
private const AtomicRef atomicReg := AtomicRef()
private const AtomicRef atomicAppPod := AtomicRef()
** The 'afIoc' registry. Maybe 'null' if BedSheet did not startup properly.
Registry? registry {
get { atomicReg.val }
private set { atomicReg.val = it }
}
new make(Str moduleName, Int port, [Str:Obj?] bedSheetOptions, [Str:Obj?]? registryOptions := null) {
this.moduleName = moduleName
this.port = port
this.registryOptions = registryOptions
this.bedSheetOptions = bedSheetOptions
}
override Void onService() {
req.mod = this
try {
httpPipeline := (HttpPipeline) registry.dependencyByType(HttpPipeline#)
httpPipeline.service
} catch (Err err) {
// theoretically, this should have already been dealt with by our Err Pipeline Processor...
// ...but it's handy for BedSheet development!
errPrinter := (ErrPrinterStr) registry.dependencyByType(ErrPrinterStr#)
Env.cur.err.printLine(errPrinter.errToStr(err))
throw err
}
}
override Void onStart() {
log.info(BsLogMsgs.bedSheetWebModStarting(moduleName, port))
Pod? pod
Type? mod
Pod? appPod
Type? appMod
// pod name given...
// lots of start up checks looking for pods and modules...
// see https://bitbucket.org/SlimerDude/afbedsheet/issue/1/add-a-warning-when-no-appmodule-is-passed
if (!moduleName.contains("::")) {
pod = Pod.find(moduleName, true)
log.info(BsLogMsgs.bedSheetWebModFoundPod(pod))
modName := pod.meta["afIoc.module"]
if (modName != null) {
mod = Type.find(modName, false)
log.info(BsLogMsgs.bedSheetWebModFoundType(mod))
// reset back to null - so we add the whole module with trans deps
appMod = mod
mod = null
} else {
// we have a pod with no module meta... guess a type of AppModule
mod = pod.type("AppModule", false)
if (mod != null) {
log.info(BsLogMsgs.bedSheetWebModFoundType(mod))
log.warn(BsLogMsgs.bedSheetWebModAddModuleToPodMeta(pod, mod))
} else {
// we're screwed! No module = no web app!
log.warn(BsLogMsgs.bedSheetWebModNoModuleFound)
}
}
}
// mod name given...
if (moduleName.contains("::")) {
mod = Type.find(moduleName, true)
log.info(BsLogMsgs.bedSheetWebModFoundType(mod))
}
// construct after the above messages so logs look nicer ("...adding module IocModule")
bob := RegistryBuilder()
transDeps := !bedSheetOptions.containsKey("noTransDeps")
if (!transDeps)
log.info("Suppressing transitive dependencies...")
if (pod != null) {
bob.addModulesFromDependencies(pod, transDeps)
}
if (mod != null) {
bob.addModulesFromDependencies(mod.pod, transDeps)
if (!bob.moduleTypes.contains(mod))
bob.addModule(mod)
}
// A simple thing - ensure the BedSheet module is added!
// (Ensure trans deps are added explicitly via @SubModule)
if (!bob.moduleTypes.contains(BedSheetModule#))
bob.addModule(BedSheetModule#)
bannerText := easterEgg("Alien-Factory BedSheet v${typeof.pod.version}, IoC v${Registry#.pod.version}")
options := Str:Obj?["bannerText":bannerText]
if (registryOptions != null)
options.setAll(registryOptions)
if (bedSheetOptions.containsKey("iocModules"))
bob.addModules(bedSheetOptions["iocModules"])
// create meta data
appMod = (appMod != null) ? appMod : mod
appPod = (pod != null) ? pod : appMod?.pod
meta := BedSheetMetaDataImpl(appPod, appMod, bedSheetOptions)
options["bedSheetMetaData"] = meta
// startup afIoc
registry = bob.build(options).startup
// start the destroyer!
if (bedSheetOptions["pingProxy"] == true) {
pingPort := (Int) bedSheetOptions["pingProxyPort"]
destroyer := (AppDestroyer) registry.autobuild(AppDestroyer#, [ActorPool(), pingPort])
destroyer.start
}
}
override Void onStop() {
registry?.shutdown
log.info(BsLogMsgs.bedSheetWebModStopping(moduleName))
}
private Str easterEgg(Str title) {
quotes := loadQuotes
if (quotes.isEmpty || (Int.random(0..8) != 2))
return title
return quotes[Int.random(0..<quotes.size)]
}
private Str[] loadQuotes() {
typeof.pod.file(`/res/misc/quotes.txt`).readAllLines.exclude { it.isEmpty || it.startsWith("#")}
}
}