fastpastebin/internal/application/application.go

125 lines
2.8 KiB
Go

package application
import (
"context"
"fmt"
"os"
"sync"
"github.com/labstack/echo"
"github.com/rs/zerolog"
databaseinterface "go.dev.pztrn.name/fastpastebin/internal/database/interface"
)
// Application is a main application superstructure. It passes around all parts of application
// and serves as lifecycle management thing as well as kind-of-dependency-injection thing.
type Application struct {
Config *Config
Database databaseinterface.Interface
Echo *echo.Echo
Log zerolog.Logger
services map[string]Service
servicesMutex sync.RWMutex
ctx context.Context
cancelFunc context.CancelFunc
}
// New creates new application superstructure.
func New() *Application {
//nolint:exhaustruct
appl := &Application{}
appl.initialize()
return appl
}
// GetContext returns application-wide context.
func (a *Application) GetContext() context.Context {
return a.ctx
}
// GetService returns interface{} with requested service or error if service wasn't registered.
func (a *Application) GetService(name string) (Service, error) {
a.servicesMutex.RLock()
srv, found := a.services[name]
a.servicesMutex.RUnlock()
if !found {
return nil, fmt.Errorf("%s: %w", ErrApplicationError, ErrApplicationServiceNotRegistered)
}
return srv, nil
}
// Initializes internal state.
func (a *Application) initialize() {
a.Log = zerolog.New(os.Stdout).With().Timestamp().Logger()
a.Log.Info().Msg("Initializing Application...")
a.ctx, a.cancelFunc = context.WithCancel(context.Background())
a.services = make(map[string]Service)
cfg, err := newConfig(a)
if err != nil {
a.Log.Fatal().Err(err).Msg("Failed to initialize configuration!")
}
a.Config = cfg
a.initializeLogger()
a.initializeHTTPServer()
}
// RegisterService registers service for later re-use everywhere it's needed.
func (a *Application) RegisterService(srv Service) error {
a.servicesMutex.Lock()
_, found := a.services[srv.GetName()]
a.servicesMutex.Unlock()
if found {
return fmt.Errorf("%s: %w", ErrApplicationError, ErrApplicationServiceAlreadyRegistered)
}
if err := srv.Initialize(); err != nil {
return fmt.Errorf("%s: %s: %w", ErrApplicationError, ErrApplicationServiceRegister, err)
}
a.services[srv.GetName()] = srv
return nil
}
// Shutdown shutdowns application.
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 {
return err
}
}
return nil
}
// Start starts application.
func (a *Application) Start() error {
a.initializeLoggerPost()
a.startHTTPServer()
a.servicesMutex.RLock()
defer a.servicesMutex.RUnlock()
for _, service := range a.services {
if err := service.Start(); err != nil {
return err
}
}
return nil
}