Libs

OverviewPodBuild FileDefsHxLibLifecycleAxon FuncsFantom FuncsSettingsWebManagementStub

Overview

Libraries or libs are the fundamental unit of modularity in the Haxall architecture. All Haxall libs are proper Haystack libs packaged as Fantom pods.

Libs package one or more of the following features into a named, versioned module:

  • Haystack defs for tags
  • SkySpark defs for apps, views
  • Fantom HxLib class to perform background processing
  • Axon functions

Libs which only publish declarative data such as defs and functions are called resource libs. Resource libs cannot contain any Fantom code, but have the ability to be hot reloaded at runtime. Pods which contain one or more Fantom classes are called fantom libs. All Fantom libs must define a HxLib subclass.

Pod

All libs must be packaged into a Fantom pod. Pods are just a zip that contains a "meta.props" file for essential metadata. Any zip file can be made a Fantom pod by adding the following "meta.props" file:

pod.name=acmeFoo
pod.version=1.0.9
pod.summary=Summary of pod
pod.depends=
pod.fcode=false

There are dozens of standardized fields used by "meta.props", but the ones above are the required fields.

Most of the time you will build your pod using Fantom's build system. Your source directory will be structured as follows:

acmeFoo/
  build.fan
  pod.fandoc
  fan/
    AcmeFooLib.fan
    AcmeFooFuncs.fan
  lib/
    lib.trio
    other-defs.trio
  test/
    AcmeFooLibTest.fan

Resource pods will only contain the "lib/" directory with the definitions.

It is recommended to use the stub tool to generate the source directory for a new library.

Build File

The build file is a Fantom script used to compile your source into a pod file. Here is a sample build file:

#! /usr/bin/env fan
using build
class Build : BuildPod
{
  new make()
  {
    podName = "acmeFoo"
    summary = "Foo is something awesome"
    meta    = ["org.name":     "Acme",
               "org.uri":      "https://acme.com/",
               "proj.name":    "Cool Libs",
               "proj.uri":     "https://acme.com/cool-libs",
               "license.name": "MIT",
               "vcs.name":     "Git",
               "vcs.uri":      "https://github.com/acme/cool-libs",
               ]
    depends  = ["sys 1.0",
                "concurrent 1.0",
                "haystack 3.1",
                "axon 3.1",
                "hx 3.1"
                ]
    srcDirs = [`fan/`, `test/`]
    resDirs = [`lib/`]
    index   = ["ph.lib": "acmeFoo"]
  }
}

Resource libs should omit the depends and srcDirs fields.

Its important that all libs register the Haystack library name with the indexed prop "ph.lib". By convention a library named "acmeFoo" is packaged into a pod named "acmeFoo". Your library name should be globally unique - we recommend prefixing your library names with your company name.

Defs

All libs must define a "lib/lib.trio" file that registers the library as a Haystack 4 lib:

// lib.trio
def: ^lib:acmeFoo
depends: [^lib:ph, ^lib:axon, ^lib:hx]
typeName:"acmeFoo::AcmeFooLib"
doc: "Foo is something awesome"

Fantom libs must register the class qname using typeName; resource libs should omit this line.

You can package additional defs under the "lib/" directory using whatever naming convention you prefer. The special file "lib/skyarc.trio" is used to register SkySpark only defs - it is ignored in non-SkySpark runtimes.

HxLib

Libs which use Fantom must create a subclass of HxLib. An instance of this class is created by the runtime to receive lifecycle callbacks to perform background processing.

Here is a simple example:

using hx
const class AcmeFooLib : HxLib
{
  override Void onStart() { log.info("$typeof started!") }

  override Void onStop() { log.info("$typeof stopped!") }
}

Lifecycle

The lifecycle of a lib follows the overall runtime lifecycle:

  1. Instantiation (constructor)
  2. HxLib.onStart
  3. HxLib.onReady
  4. HxLib.onSteadyState (see Runtime)

The shutdown lib life cycle is:

  1. HxLib.onUnready
  2. HxLib.onStop

In the instantiation phase, only basic information from the runtime is available. You must wait until onStart to access services and lookup other libs.

While your lib is running, you can schedule periodic house keeping. To use this feature override the HxLib.houseKeepingFreq which causes periodic callbacks to to HxLib.onHouseKeeping.

Axon Funcs

Axon functions may be bundled as a trio file under the "lib/" directory:

// funcs.trio
def: ^func:acmeFooAxon
src:
  () => "hello world in Axon!"

In Haxall, your function must use the def=^func:name syntax shown above. The name plus func marker tag pattern used by SkySpark is not supported.

Fantom Funcs

You can also implement your Axon functions in Fantom by creating a class named "<libName>Funcs". Axon functions are implemented as public, static methods annotated with the Axon facet:

using haystack
using axon
class AcmeFooFuncs
{
  ** Fandoc is used for reference docs
  @Axon
  static Str fooAcmeFantom()
  {
    "hello world in Fantom!"
  }
}

It is critical to consider security when implementing functions in Fantom. Fantom funcs run outside of the Axon security sandbox, so its up your code to enforce any security constraints. Never create functions which would allow unconstrained access to the underlying OS.

Settings

Libs are enabled in a runtime database with a record via the ext tag. This record can store settings data that is accessible via the HxLib.rec method. You can create a statically typed class for your setting that is also used by UI tools:

  1. Create a subclass of TypedDict
  2. Override HxLib.rec to covariantly return your type

Here is an example:

const class TaskLib : HxLib
{
  ** Settings record
  override TaskSettings rec() { super.rec }
}

const class TaskSettings : TypedDict
{
  ** Constructor
  new make(Dict d, |This| f) : super(d) { f(this) }

  ** Max threads for the task actor pool
  @TypedTag { restart=true }
  const Int maxThreads:= 50
}

The HxLib.onRecUpdate callback is invoked whenever the settings rec is modified.

Web

To add HTTP handling to your lib:

  1. Create a subclass of HxLibWeb
  2. Override HxLib.web with an instance of your subclass

Here is a very simple example:

const class FooLib : HxLib
{
  override const FooWeb web := FooWeb(this)
}

const class FooWeb : HxLibWeb
{
  new make(FooLib lib) : super(lib) {}

  override Void onGet()
  {
    res.headers["Content-Type"] = "text/plain; charset=utf-8"
    res.out.printLine("Foo hello world: $req.modRel")
  }
}

Libs plug into the URI namespace as follows:

  • Haxall: /{libName}
  • SkySpark: /api/{projName}/ext/{libName}

Management

Libs can be enabled/disabled at runtime. The enabled libs are stored as records in the runtime database with the ext tag. You cannot directly add nor remove an ext record, but rather must instead use dedicated APIs.

Lib management in Fantom:

Lib management in Axon:

Note that adding/remove libs must be done in the correct order to ensure lib dependencies are met.

Stub

The hx stub tool will generate all the boiler plate code and structure for a new library. Run the following on the command line to see options:

hx help stub