featurer/server/internal/application/application.go

115 lines
2.9 KiB
Go
Raw Permalink Normal View History

2024-10-12 13:04:09 +05:00
package application
import (
"context"
"errors"
"fmt"
"log/slog"
"os"
"os/signal"
"sync"
"syscall"
)
var (
// ErrApplication indicates that error appeared somewhere in application's lifecycle controller.
ErrApplication = errors.New("application")
// Version is application's version.
Version string
// Branch is a branch name from which application was built.
Branch string
// Commit is a commit hash from which application was built.
Commit string
// Build is a build number.
Build string
// BuildDate is a date on which application was built.
BuildDate string
)
// Application is an application's lifecycle controlling structure.
type Application struct {
ctx context.Context
shutdownDone chan struct{}
cancelFunc context.CancelFunc
services map[string]Service
dataPath string
servicesMutex sync.RWMutex
}
// New creates new application's lifecycle controlling structure.
func New() *Application {
appl := &Application{}
appl.initialize()
return appl
}
// GetContext returns application's global context.
func (a *Application) GetContext() context.Context {
return a.ctx
}
// GetShutdownDoneChannel returns channel on which lifecycle controller will tell about shutdown completion.
// Should be used only in main()!
func (a *Application) GetShutdownDoneChannel() chan struct{} {
slog.Debug("Returning shutdown completion channel.")
return a.shutdownDone
}
func (a *Application) initialize() {
a.ctx, a.cancelFunc = context.WithCancel(context.Background())
a.ctx, a.cancelFunc = signal.NotifyContext(a.ctx, os.Interrupt, syscall.SIGTERM)
a.services = make(map[string]Service)
a.shutdownDone = make(chan struct{}, 1)
}
// Shutdown stops application and all registered services.
func (a *Application) Shutdown() error {
a.cancelFunc()
a.servicesMutex.RLock()
defer a.servicesMutex.RUnlock()
for _, service := range a.services {
if err := service.Shutdown(); err != nil {
slog.Error("Error appeared when trying to shut down service", "service", service.GetName(), "error", err.Error())
}
}
a.shutdownDone <- struct{}{}
return nil
}
// Start starts application and all registered services.
func (a *Application) Start() error {
go a.signalsListener()
a.servicesMutex.RLock()
defer a.servicesMutex.RUnlock()
// First pass - connecting dependencies.
for _, service := range a.services {
if err := service.ConnectDependencies(); err != nil {
return fmt.Errorf("%w: connecting dependencies for service '%s': %w", ErrApplication, service.GetName(), err)
}
}
// Second pass - launching startup tasks.
for name, service := range a.services {
slog.Debug("Launching startup tasks for service", "service", name)
if err := service.LaunchStartupTasks(); err != nil {
return fmt.Errorf("%w: launching startup tasks for '%s': %w", ErrApplication, service.GetName(), err)
}
}
slog.Debug("Application started.")
return nil
}