Documentation

Overview

Package ioc provides an Inversion of Control component container and lifecycle hooks.

This package provides the types that define and support Granitic component container, which allows your application's objects and Granitic's framework facilities to follow the inversion of control (IoC) pattern by having their lifecycle and dependencies managed for them. An in-depth discussion of Granitic IoC can be found at http://granitic.io/1.0/ref/ioc but a description of the core concepts follows.

Components

A component is defined by Granitic as an instance of a Go struct with a name that is unique within an application. Each component in your application requires a entry in the components section of your application's component definition file like:

{
  "components": {
    "componentName": {
  	  "type": "package.structType"
    }
  }
}

e.g:

{
  "components": {
    "createRecordLogic": {
  	  "type": "inventory.CreateRecordLogic"
    }
  }
}

For complete information on defining components, refer to http://granitic.io/1.0/ref/components

Granitic's documentation will use the term component and component instance interchangeably. For example, 'component field' means 'a field on the instance of the Go struct associated with that component'.

The container

When Granitic starts, it will create an instance of ioc.ComponentContainer - a structure that holds references to each of the components in your application. It is responsible for injecting dependencies and configuration into your components (see below) and managing lifecycle events (also see below). The container is also referred to as the component container or IoC container.

Framework components

For each Granitic facility that you enable in your application, one or more framework components will be created and added to the container. A framework component is exactly the same as any other component - an instance of a struct with a name. Depending on the complexity of the facility, multiple components may be created.

A very simple name-spacing is used to separate the names of framework components from application components - framework components' names all start with the string grnc

You are strongly encouraged to make sure your application components' names do not start with this string.

Dependencies and configuration

As part of its definition, your components can request that other components are injected into its fields. Your definition can also include configuration (actual values to be set when the component is instantiated) or configuration promises (values to be injected once all sources of configuration have been merged together).

{
  "components": {
    "createRecordLogic": {
  	  "type": "inventory.CreateRecordLogic",
  	  "MaxTracks": 20,
  	  "ArtistMustExist": "conf:record.disableAutoArtist",
  	  "DAO": "ref:inventoryDAO"
    }
  }
}

In the above example, the field CreateRecordLogic.MaxTracks is set to 20 when the struct is instantiated, ArtistMustExist is set to the config element 'record.disableAutoArtist' and DAO is set to a reference to another component's instance. Note that c: and r: can be used as shorthand for config: and ref: See http://granitic.io/1.0/ref/components for more information

Any error such as type mismatches or missing configuration will cause an error that will halt application startup.

Component templates

A template mechanism exists to allow multiple components that share a type, dependencies or configuration items to only have those elements defined once. This is especially useful for web service handlers. See http://granitic.io/1.0/ref/components#templates for more details.

Binding

Unlike JVM/CLR languages, Go has no runtime 'instance-for-type-name' mechanism for creating instances of a struct. As a result, unlike JVM/CLR IoC containers you may have used, the container does not instantiate the actual instances of the Go structs behind application components. Instead a 'binding' proces is used - refer to the pacakage documentation for the grnc-bind tool for more information.

Container lifecycle

The process of starting the container transitions through a number of distinct phase. It is possible for your code to be explicitly invoked during some of these phases by implementing one more lifecycle interfaces.

Populate    Application and framework components are stored in the container.

Configure   Configuration and dependencies are resolved and injected into components.

Decorate    Components implementing the ioc.ComponentDecorator are given access to all other components
            to potentially modify.

Start       Components implementing the ioc.Startable interface are invoked.

Access	    Components implementing ioc.AccessibilityBlocker and ioc.Accessible are interacted with.

Ready       Granitic application is running.

When the container is ready, a log message similar to

grncInit Ready (startup time 6.28ms)

will be logged.

There are several other possible lifecycle phases after the container is ready:

Suspend     Components implementing ioc.Suspendable  have their Suspend method invoked.

Resume      Components implementing ioc.Suspendable have their Resume method invoked.

Stop        Components implementing ioc.Stoppable are allowed to stop gracefully before the application exits.

Decorators

Decorators are special components implementing ioc.ComponentDecorator. Their main purpose is to inject dynamically created objects into other components (such as Loggers). Decorators are destroyed after the Decorate phase of container startup.

Stopping

Components that need to perform some shutdown process before an application exits should implement the Stoppable interface. See the GoDoc for ioc.Stoppable below for more detail.

Container settings

The file $GRANITIC_HOME/resource/facility-config/system.json contains configuration, that can be overridden in your application's configuration file, affecting startup, garbage collection and shutdown behaviour of the container.

More information can be found at http://granitic.io/1.0/ref/system-settings

Gaining access to the container

If your application component needs direct access to the container it should implement the ioc.ContainerAccessor. A reference to the container will be injected into your component during the decorate phase.

External interaction with the container

If your application enables the RuntimeCtl facility, you can interact with the container and its components by using the grnc-ctl command line utility. See the package documentation for grnc-ctl for more information.

Index

Constants

View Source
const (
	StoppedState = iota
	StoppingState
	StartingState
	AwaitingAccessState
	RunningState
	SuspendingState
	SuspendedState
	ResumingState
)
View Source
const (
	None = iota
	CanStart
	CanStop
	CanSuspend
	CanBlockStart
	CanBeAccessed
)

Variables

This section is empty.

Functions

This section is empty.

Types

type AccessibilityBlocker

type AccessibilityBlocker interface {

	// BlockAccess returns true if the component wants to prevent the application from becoming ready and accessible. An
	// optional error message can be returned explaining why.
	BlockAccess() (bool, error)
}

Implemented by components that MUST be ready before an application can be made accessible. For example, a component connecting to a critical external system might implement AccessibilityBlocker to prevent an HTTP server making an API available until a connection to the critical system is established.

See http://granitic.io/1.0/ref/system-settings for information about the number of times BlockAccess is called, and how the interval between these calls can be adjusted for your application.

type Accessible

type Accessible interface {
	// AllowAccess is called by the container as the final stage of making an application ready.
	AllowAccess() error
}

Implemented by components that require a final phase of initialisation to make themselves outside of the application. Typically implemented by HTTP servers and message queue listeners to start listening on TCP ports.

type ByName

type ByName struct{ Components }

func (ByName) Less

func (s ByName) Less(i, j int) bool

type Component

type Component struct {
	// A pointer to a struct
	Instance interface{}

	// A name for this component that is unique within your application
	Name string
}

A Component is an instance of a struct with a name that is unique within your application.

func NewComponent

func NewComponent(name string, instance interface{}) *Component

NewComponent creates a new Component with the supplied name and instance

type ComponentByNameFinder

type ComponentByNameFinder interface {
	// ComponentByName returns the Component with the supplied name, or nil if it does not exist.
	ComponentByName(string) *Component
}

Implemented by components that need to be able to find other components by name.

type ComponentContainer

type ComponentContainer struct {
	FrameworkLogger logging.Logger

	Lifecycle *LifecycleManager
	// contains filtered or unexported fields
}

The Granitic IoC container. See the GoDoc for the ioc package for more information on how to interact with the container.

Most applications should never need to interact with the container programmatically.

func NewComponentContainer

func NewComponentContainer(logm *logging.ComponentLoggerManager, ca *config.ConfigAccessor, sys *instance.System) *ComponentContainer

Create a new instance of a Granitic IoC container.

func (*ComponentContainer) AddModifier

func (cc *ComponentContainer) AddModifier(comp string, field string, dep string)

AddModifier is used to override a dependency on a component (normally a built-in Granitic component) during the configure phase of container startup.

func (*ComponentContainer) AddModifiers

func (cc *ComponentContainer) AddModifiers(mods map[string]map[string]string)

AddModifiers is used to override a dependency on set of components (normally built-in Granitic components) during the configure phase of container startup.

func (*ComponentContainer) AddProto

func (cc *ComponentContainer) AddProto(proto *ProtoComponent)

AddProto registers an instantiated but un-configured proto-component.

func (*ComponentContainer) AddProtos

func (cc *ComponentContainer) AddProtos(protos []*ProtoComponent)

AddProto regsiters a collection of proto-components (see AddProto)

func (*ComponentContainer) AllComponents

func (cc *ComponentContainer) AllComponents() []*Component

AllComponents returns all of the components hosted by the container.

func (*ComponentContainer) ByLifecycleSupport

func (cc *ComponentContainer) ByLifecycleSupport(ls LifecycleSupport) []*Component

ByLifecycleSupport returns all components hosted by the container that have specific support for a lifecycle event (i.e. implement the associated lifecycle interface

func (*ComponentContainer) ComponentByName

func (cc *ComponentContainer) ComponentByName(name string) *Component

See ComponentByNameFinder.ComponentByName

func (*ComponentContainer) ModifierExists

func (cc *ComponentContainer) ModifierExists(comp string, field string) bool

ModifierExists checks to see if a modifier (see AddModifier) has previously been registered for a field on a component.

func (*ComponentContainer) Modifiers

func (cc *ComponentContainer) Modifiers(comp string) map[string]string

Modifiers returns all registered modifiers (see AddModifier)

func (*ComponentContainer) ModifiersExist

func (cc *ComponentContainer) ModifiersExist(comp string) bool

ModifiersExist returns true if any modifiers (see AddModifier) have been registered.

func (*ComponentContainer) Populate

func (cc *ComponentContainer) Populate() error

Populate converts all registered proto-components into components and populates them with configuration and dependencies.

func (*ComponentContainer) ProtoComponents

func (cc *ComponentContainer) ProtoComponents() map[string]*ProtoComponent

ProtoComponents returns all components that have been registered by the container but have not yet all their dependencies resolved. If called after the container is 'Accessible' an empty slice will be returned.

func (*ComponentContainer) ProtoComponentsByType

func (cc *ComponentContainer) ProtoComponentsByType(tm TypeMatcher) []*ProtoComponent

ProtoComponentsByType returns any ProtoComponents whose Component.Instance field matches the against the supplied TypeMatcher function. If called after the container is 'Accessible' an empty slice will be returned.

func (*ComponentContainer) WrapAndAddProto

func (cc *ComponentContainer) WrapAndAddProto(name string, instance interface{})

WrapAndAddProto registers an instance and name as an un-configured proto-component.

type ComponentDecorator

type ComponentDecorator interface {
	// OfInterest should return true if the ComponentDecorator decides that the supplied component needs to be decorated.
	OfInterest(subject *Component) bool

	// DecorateComponent modifies the subject component.
	DecorateComponent(subject *Component, container *ComponentContainer)
}

ComponentDecorator is implemented by special temporary components that only exist while the IoC container is being populated.

A ComponentDecorator's job is to examine another component (the subject) to see if it is suitable for modification by the ComponentDecorator. Typically this will involve the ComponentDecorator injecting another object into the subject if the component implements a particular interface or has a writable field of a particular name or type.

A number of built-in decorators exist to accomplish tasks like automatically adding Loggers to components with a particular field.

type ComponentNamer

type ComponentNamer interface {
	// ComponentName returns the name of the component
	ComponentName() string

	// SetComponentName injects the component's name
	SetComponentName(name string)
}

Implemented by components where the component's instance needs to be aware of its own component name.

type ComponentState

type ComponentState int

What state (stopped, running) or transition between states (stopping, starting) a component is currently in.

type Components

type Components []*Component

Type definition for a slice of components to allow sorting.

func (Components) Len

func (s Components) Len() int

func (Components) Swap

func (s Components) Swap(i, j int)

type ContainerAccessor

type ContainerAccessor interface {
	// Container accepts a reference to the Granitic IoC container.
	Container(container *ComponentContainer)
}

An interface implemented by any component that wants direct access to the IoC container.

type ContainerDecorator

type ContainerDecorator struct {
	// contains filtered or unexported fields
}

ContainerDecorator injects a reference to the IoC container into any component implementing the ContainerAccessor interface.

func (*ContainerDecorator) DecorateComponent

func (cd *ContainerDecorator) DecorateComponent(subject *Component, cc *ComponentContainer)

DecorateComponent injects a reference to the IoC container to a component that has alredy been determined to implement ContainerAccessor.

func (*ContainerDecorator) OfInterest

func (cd *ContainerDecorator) OfInterest(subject *Component) bool

OfInterest returns true if the subject component implements ContainerAccessor

type LifecycleManager

type LifecycleManager struct {
	FrameworkLogger logging.Logger
	// contains filtered or unexported fields
}

Provides an interface to the components to allow lifecycle methods/events to be applied to all components or a subset of the components.

func (*LifecycleManager) ResumeComponents

func (lm *LifecycleManager) ResumeComponents(comps []*Component) error

SuspendComponents invokes Resume on all of the supplied components that implement Suspendable

func (*LifecycleManager) Start

func (lm *LifecycleManager) Start(startable []*Component) error

Start starts the supplied components, waits for any access-blocking components to be ready, then makes all components accessible. See GoDoc for Startable, AccessibilityBlocker and Accessible for more details.

func (*LifecycleManager) StartAll

func (lm *LifecycleManager) StartAll() error

StartAll finds all Startable and Accessible components runs the Start/Block/Accessible cycle.

func (*LifecycleManager) StopAll

func (lm *LifecycleManager) StopAll() error

StopAll finds all components implementing Stoppable and passes them to Stop

func (*LifecycleManager) StopComponents

func (lm *LifecycleManager) StopComponents(comps []*Component) error

StopComponents invokes PrepareToStop on all components then waits for them to be ready to stop by calling ReadyToStop on each component. If one or more components are not ready, they are given x chances to become ready with y milliseconds between each check. See http://granitic.io/1.0/ref/system-settings

If all components are ready, or if x has been exceeded, Stop is called on all components.

func (*LifecycleManager) SuspendComponents

func (lm *LifecycleManager) SuspendComponents(comps []*Component) error

SuspendComponents invokes Suspend on all of the supplied components that implement Suspendable

type LifecycleSupport

type LifecycleSupport int

Enumeration able used to categorise types by the lifecycle events they can react to.

type ProtoComponent

type ProtoComponent struct {
	// The name of a component and the component instance (a pointer to an instantiated struct).
	Component *Component

	// A map of fields on the component instance and the names of other components that should be injected into those fields.
	Dependencies map[string]string

	// A map of fields on the component instance and the config-path that will contain the configuration that shoud be inject into the field.
	ConfigPromises map[string]string
}

A ProtoComponent is a partially configured component that will be hosted in the Granitic IoC container once it is fully configured. Typically ProtoComponents are created using the grnc-bind tool.

func CreateProtoComponent

func CreateProtoComponent(componentInstance interface{}, componentName string) *ProtoComponent

CreateProtoComponent creates a new ProtoComponent.

func (*ProtoComponent) AddConfigPromise

func (pc *ProtoComponent) AddConfigPromise(fieldName, configPath string)

AddDependency requests that the container injects the config value at the specified path into the specified field during the configure phase of container startup.

func (*ProtoComponent) AddDependency

func (pc *ProtoComponent) AddDependency(fieldName, componentName string)

AddDependency requests that the container injects another component into the specified field during the configure phase of container startup

type ProtoComponents

type ProtoComponents struct {
	// ProtoComponents to be finalised and stored in the IoC container.
	Components []*ProtoComponent

	// FrameworkDependencies are instructions to inject components into built-in Granitic components to alter their behaviour.
	// The structure is map[
	FrameworkDependencies map[string]map[string]string

	//A Base64 encoded version of the JSON files found in resource/facility-confg
	FrameworkConfig *string
}

A wrapping structure for a list of ProtoComponents and FrameworkDependencies that is required when starting Granitic. A ProtoComponents structure is built by the grnc-bind tool.

func NewProtoComponents

func NewProtoComponents(pc []*ProtoComponent, fd map[string]map[string]string, ser *string) *ProtoComponents

NewProtoComponents creates a wrapping structure for a list of ProtoComponents

func (*ProtoComponents) Clear

func (pc *ProtoComponents) Clear()

Clear removes the reference to the ProtoComponent objects held in this object, encouraging garbage collection.

type Startable

type Startable interface {
	// StartComponent performs initialisation and may start listeners/servers.
	StartComponent() error
}

Implemented by components that need to perform some initialisation before they are ready to run or use.

Components that provide services outside of the application (like HTTP servers or queue listeners) should consider implementing Accessible in addition to Startable.

type Stoppable

type Stoppable interface {
	// PrepareToStop gives notice to the component that the application is about to halt. The implementation of this method
	// should cause the component to block any new requests for work.
	PrepareToStop()

	// ReadyToStop is called by the container to query whether or not the component is ready to shutdown. A component might
	// return false if it is still processing or running a job. If the component returns false, it may optionally return
	// an error to explain why it is not ready.
	ReadyToStop() (bool, error)

	// Stop is an instruction by the container to immediately terminate any running processes and release any resources.
	// If the component is unable to do so, it may return an error, but the application will still stop.
	Stop() error
}

Implemented by components that need to be given the opportunity to release resources or perform shutdown activities before an application is halted.

See http://granitic.io/1.0/ref/system-settings for information about the number of times ReadyToStop is called, and how the interval between these calls, can be adjusted for your application.

type StructFactory

type StructFactory func() interface{}

A function able to return a pointer to an empty struct

type Suspendable

type Suspendable interface {
	// Suspend causes the component to stop performing its primary function until Resume is called.
	Suspend() error

	// Resume causes the component to resume its primary function.
	Resume() error
}

Implemented by components that are able to temporarily halt and then resume their activity at some later point in time.

type TypeMatcher

type TypeMatcher func(i interface{}) bool

TypeMatcher implementations return true if the supplied interface is (or implements) an expected type