func (n *node) ApplicationUnload(appName string) error {
	rb, err := n.RegisteredBehavior(appBehaviorGroup, appName)
	if err != nil {
		return ErrAppUnknown
	}
	spec, ok := rb.Data.(*gen.ApplicationSpec)
	if !ok {
		return ErrAppUnknown
	}
	if spec.Process != nil {
		return ErrAppAlreadyStarted
	}
	return n.UnregisterBehavior(appBehaviorGroup, appName)
}
ApplicationUnload unloads given application
package node
import (
	"context"
	"fmt"
	"strings"
	"time"
	"github.com/ergo-services/ergo/etf"
	"github.com/ergo-services/ergo/gen"
	"github.com/ergo-services/ergo/lib"
)
const (
	appBehaviorGroup = "ergo:applications"
)
type nodeInternal interface {
	Node
	registrarInternal
}
type node struct {
	registrarInternal
	networkInternal
	name     string
	cookie   string
	creation uint32
	opts     Options
	context  context.Context
	stop     context.CancelFunc
	version  Version
}
func StartWithContext(ctx context.Context, name string, cookie string, opts Options) (nodeInternal, error) {
	lib.Log("Start with name '%s' and cookie '%s'", name, cookie)
	nodectx, nodestop := context.WithCancel(ctx)
	creation := uint32(time.Now().Unix()) | 1
	node := &node{
		cookie:   cookie,
		context:  nodectx,
		stop:     nodestop,
		creation: creation,
	}
	if name == "" {
		return nil, fmt.Errorf("Node name must be defined")
	}
	if opts.ListenRangeBegin == 0 {
		opts.ListenRangeBegin = defaultListenRangeBegin
	}
	if opts.ListenRangeEnd == 0 {
		opts.ListenRangeEnd = defaultListenRangeEnd
	}
	lib.Log("Listening range: %d...%d", opts.ListenRangeBegin, opts.ListenRangeEnd)
	if opts.EPMDPort == 0 {
		opts.EPMDPort = defaultEPMDPort
	}
	if opts.EPMDPort != 4369 {
		lib.Log("Using custom EPMD port: %d", opts.EPMDPort)
	}
	if opts.SendQueueLength == 0 {
		opts.SendQueueLength = defaultSendQueueLength
	}
	if opts.RecvQueueLength == 0 {
		opts.RecvQueueLength = defaultRecvQueueLength
	}
	if opts.FragmentationUnit < 1500 {
		opts.FragmentationUnit = defaultFragmentationUnit
	}
	if opts.HandshakeVersion != 5 && opts.HandshakeVersion != 6 {
		opts.HandshakeVersion = defaultHandshakeVersion
	}
	if opts.Hidden {
		lib.Log("Running as hidden node")
	}
	if len(strings.Split(name, "@")) != 2 {
		return nil, fmt.Errorf("incorrect FQDN node name (example: node@localhost)")
	}
	opts.cookie = cookie
	opts.creation = creation
	node.opts = opts
	node.name = name
	registrar := newRegistrar(nodectx, name, creation, node)
	network, err := newNetwork(nodectx, name, opts, registrar)
	if err != nil {
		return nil, err
	}
	node.registrarInternal = registrar
	node.networkInternal = network
	for _, app := range opts.Applications {
		name, err := node.ApplicationLoad(app)
		if err != nil {
			nodestop()
			return nil, err
		}
		_, err = node.ApplicationStart(name)
		if err != nil {
			nodestop()
			return nil, err
		}
	}
	return node, nil
}
func (n *node) IsAlive() bool {
	return n.context.Err() == nil
}
func (n *node) Wait() {
	<-n.context.Done()
}
func (n *node) Uptime() int64 {
	return time.Now().Unix() - int64(n.creation)
}
func (n *node) Version() Version {
	return n.version
}
func (n *node) WaitWithTimeout(d time.Duration) error {
	timer := time.NewTimer(d)
	defer timer.Stop()
	select {
	case <-timer.C:
		return ErrTimeout
	case <-n.context.Done():
		return nil
	}
}
func (n *node) Spawn(name string, opts gen.ProcessOptions, object gen.ProcessBehavior, args ...etf.Term) (gen.Process, error) {
	options := processOptions{
		ProcessOptions: opts,
	}
	return n.spawn(name, options, object, args...)
}
func (n *node) Stop() {
	n.stop()
}
func (n *node) Name() string {
	return n.name
}
func (n *node) RegisterName(name string, pid etf.Pid) error {
	return n.registerName(name, pid)
}
func (n *node) UnregisterName(name string) error {
	return n.unregisterName(name)
}
func (n *node) LoadedApplications() []gen.ApplicationInfo {
	return n.listApplications(false)
}
func (n *node) WhichApplications() []gen.ApplicationInfo {
	return n.listApplications(true)
}
func (n *node) listApplications(onlyRunning bool) []gen.ApplicationInfo {
	info := []gen.ApplicationInfo{}
	for _, rb := range n.RegisteredBehaviorGroup(appBehaviorGroup) {
		spec, ok := rb.Data.(*gen.ApplicationSpec)
		if !ok {
			continue
		}
		if onlyRunning && spec.Process == nil {
			continue
		}
		appInfo := gen.ApplicationInfo{
			Name:        spec.Name,
			Description: spec.Description,
			Version:     spec.Version,
		}
		if spec.Process != nil {
			appInfo.PID = spec.Process.Self()
		}
		info = append(info, appInfo)
	}
	return info
}
func (n *node) ApplicationInfo(name string) (gen.ApplicationInfo, error) {
	rb, err := n.RegisteredBehavior(appBehaviorGroup, name)
	if err != nil {
		return gen.ApplicationInfo{}, ErrAppUnknown
	}
	spec, ok := rb.Data.(*gen.ApplicationSpec)
	if !ok {
		return gen.ApplicationInfo{}, ErrAppUnknown
	}
	pid := etf.Pid{}
	if spec.Process != nil {
		pid = spec.Process.Self()
	}
	appInfo := gen.ApplicationInfo{
		Name:        spec.Name,
		Description: spec.Description,
		Version:     spec.Version,
		PID:         pid,
	}
	return appInfo, nil
}
func (n *node) ApplicationLoad(app gen.ApplicationBehavior, args ...etf.Term) (string, error) {
	spec, err := app.Load(args...)
	if err != nil {
		return "", err
	}
	err = n.RegisterBehavior(appBehaviorGroup, spec.Name, app, &spec)
	if err != nil {
		return "", err
	}
	return spec.Name, nil
}