
// // Copyright (c) 2007, Brian Frank and Andy Frank // Licensed under the Academic Free License version 3.0 // // History: // 21 Dec 07 Brian Frank Creation // ** ** WebService defines the standard service interface ** for all Fan based web servers. ** ** See [docLib::Web]`docLib::Web#webServices` ** abstract const class WebService : Thread { ////////////////////////////////////////////////////////////////////////// // Construction ////////////////////////////////////////////////////////////////////////// ** ** Constructor with thread name. ** new make(Str name) : super(name) {} ////////////////////////////////////////////////////////////////////////// // Service ////////////////////////////////////////////////////////////////////////// ** ** Return true. ** override Bool isService() { return true } ////////////////////////////////////////////////////////////////////////// // Pipeline ////////////////////////////////////////////////////////////////////////// ** ** The pipeline field stores a series of WebSteps which ** are processed in sequence to service a web request. ** ** See [docLib::Web]`docLib::Web#pipeline` ** const WebStep[] pipeline ** ** Service the specified web request with the configured ** pipeline. Any exceptions raised by a step, are propagated ** to the caller - internal errors should be handled by ** subclasses. If `WebRes.done` is called, then the pipeline ** is terminated. ** virtual Void service(WebReq req, WebRes res) { // init thread locals Thread.locals["web.req"] = req Thread.locals["web.res"] = res try { // if pipeline is empty if (pipeline == null || pipeline.isEmpty) throw Err("Must set WebService.pipeline") // onBeforeService pipeline.each |WebStep step| { try { step.onBeforeService(req, res) } catch (Err e) { log.error("BeforeService $step.type", e) } } // process each step unless done pipeline.each |WebStep step| { if (!res.isDone) step.service(req, res) } // onAfterService pipeline.each |WebStep step| { try { step.onAfterService(req, res) } catch (Err e) { log.error("AfterService $step.type", e) } } } finally { // save session if accessed sessionMgr.save // cleanup thread locals Thread.locals.remove("web.req") Thread.locals.remove("web.res") } } ////////////////////////////////////////////////////////////////////////// // Threading ////////////////////////////////////////////////////////////////////////// ** ** Subclasses must call super if overridden. ** protected override Void onStart() { sessionMgr.start pipeline.each |WebStep step| { try { step.onStart(this) } catch (Err e) { log.error("Starting $step.type", e) } } } ** ** Subclasses must call super if overridden. ** protected override Void onStop() { pipeline.each |WebStep step| { try { step.onStop(this) } catch (Err e) { log.error("Stopping $step.type", e) } } sessionMgr.stop } ////////////////////////////////////////////////////////////////////////// // Fields ////////////////////////////////////////////////////////////////////////// ** Standard log for web service static const Log log := Log.get("web") ** Session management thread internal const WebSessionMgr sessionMgr := WebSessionMgr.make }