How to create a BedSheet Extension for SkySpark 3.0

SkySpark Logo

BedSheet is a great Fantom framework for web applications. It provides not only an IoC container but also a rich Middleware mechanism for routing and manipulating HTTP content. But given I wrote BedSheet, I guess I'm also quite biased!

Anyway, BedSheet is built on top of Fantom's Wisp, one of the cornerstones of SkySpark. So just for fun I thought I'd build a SkySpark web extension that integrates with a BedSheet application - and it turned out to be quite easy.

Following is a SkySpark web Ext that delegates all web requests to BedSheet.

select all
using skyarcd::Ext
using skyarcd::ExtMeta
using concurrent::AtomicRef
using afIoc
using afBedSheet::MiddlewarePipeline
using web::Weblet

@ExtMeta { name = "bedsheet" }
const class BedSheetExt : Ext, Weblet {

    private const AtomicRef    registryRef := AtomicRef(null)
    private Registry           registry {
        get { registryRef.val }
        set { registryRef.val = it }
    }

    private const AtomicRef    pipelineRef := AtomicRef(null)
    private MiddlewarePipeline pipeline {
        get { pipelineRef.val }
        set { pipelineRef.val = it }
    }

    override Void onStart() {
        this.registry = RegistryBuilder().addModulesFromPod("afBedSheet").addModule(AppModule#).build
        this.pipeline = registry.activeScope.serviceById(MiddlewarePipeline#.qname)
    }

    override Void onService() {
        registry.activeScope.createChild("request") {
            // this is the actual call to BedSheet!
            pipeline.service
        }
    }

    override Void onStop() {
        registry.shutdown
    }
}

The actual BedSheet application I used is just the Quick Start example from the BedSheet documentation.

select all
using afIoc
using afBedSheet

const class AppModule {
    @Contribute { serviceType=Routes# }
    Void contributeRoutes(Configuration conf) {
        conf.add(Route(`/`, Text.fromHtml("<html><body>Welcome to BedSheet!</body></html>")))
        conf.add(Route(`/hello/**`, HelloPage#hello))
    }
}

class HelloPage {
    Text hello(Str name, Int iq := 666) {
        return Text.fromPlain("Hello! I'm $name and I have an IQ of $iq!")
    }
}

And for completeness, here is my build.fan

select all
using build

class Build : BuildPod {

    new make() {
        podName = "afBedSheetExt"
        summary = "My Awesome BedSheetExt Project"
        version = Version("1.0")

        index = [
            "skyarc.ext": "afBedSheetExt::BedSheetExt"
        ]

        depends = [
            // ---- Core Fantom ----
            "sys        1.0.69 - 1.0",
            "web        1.0.69 - 1.0",
            "concurrent 1.0.69 - 1.0",

            // ---- Sky Spark ----
            "skyarcd    3.0.6 - 3.0",

            // ---- BedSheet ----
            "afIoc      3.0.4 - 3.0",
            "afBedSheet 1.5.2 - 1.5",
        ]

        srcDirs = [`fan/`]
        resDirs = [`locale/`]
    }
}

It was then a simple case of building the pod, adding it to SkySpark and enabling the ext in an example project.

I then used the following URLS to test the BedSheet application:

C:\> curl http://localhost:8080/api/example/ext/bedsheet/
<html><body>Welcome to BedSheet!</body></html>

C:\> curl http://localhost:8080/api/example/ext/bedsheet/hello/Traci/69
Hello! I'm Traci and I have an IQ of 69!

C:\> curl http://localhost:8080/api/example/ext/bedsheet/hello/Luci
Hello! I'm Luci and I have an IQ of 666!

And I could see the usual re-assuring Alien-Factory logo in the SkySpark console window.

Note that BedSheet has it's own pod dependencies which also need copying to SkySpark's /lib/fan/ dir:

But how you may maintain and distribute these with your SkySpark application is an entirely different problem!

Edits

  • 29 April 2017 - Posted article on Alien-Factory.
  • 18 October 2016 - Original article posted on SkyFoundry Forum.

Discuss