125 lines
2.8 KiB
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
|
||
|
}
|