-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapplication.go
More file actions
170 lines (142 loc) · 4.03 KB
/
Copy pathapplication.go
File metadata and controls
170 lines (142 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package cartridge
import (
"context"
"os"
"os/signal"
"syscall"
"time"
)
// BackgroundWorker is an interface for background workers that can be started and stopped.
type BackgroundWorker interface {
Start() error
Stop()
}
// Application wires together configuration, logging, database, and HTTP server.
// It manages the complete lifecycle of a cartridge web application.
type Application struct {
Config Config
Logger Logger
DBManager DBManager
Server *Server
workers []BackgroundWorker
}
// ApplicationOptions configure application bootstrapping.
type ApplicationOptions struct {
// Core dependencies (required)
Config Config
Logger Logger
DBManager DBManager
// Server - provide a pre-built server (takes precedence over ServerConfig)
Server *Server
// Server configuration (used if Server is nil)
ServerConfig *ServerConfig
// Route mounting function
RouteMountFunc func(*Server)
// Catch-all redirect path for SPAs
CatchAllRedirect string
// Background workers to run alongside the server
BackgroundWorkers []BackgroundWorker
}
// NewApplication constructs a cartridge application.
func NewApplication(opts ApplicationOptions) (*Application, error) {
var server *Server
var err error
// Use provided server or create one from config
if opts.Server != nil {
server = opts.Server
} else {
// Use default server config if not provided
serverCfg := opts.ServerConfig
if serverCfg == nil {
serverCfg = DefaultServerConfig()
}
// Inject dependencies into server config
serverCfg.Config = opts.Config
serverCfg.Logger = opts.Logger
serverCfg.DBManager = opts.DBManager
// Create server
server, err = NewServer(serverCfg)
if err != nil {
return nil, err
}
}
// Set catch-all redirect if provided
if opts.CatchAllRedirect != "" {
server.SetCatchAllRedirect(opts.CatchAllRedirect)
}
// Mount routes if function provided
if opts.RouteMountFunc != nil {
opts.RouteMountFunc(server)
}
return &Application{
Config: opts.Config,
Logger: opts.Logger,
DBManager: opts.DBManager,
Server: server,
workers: opts.BackgroundWorkers,
}, nil
}
// AddWorker adds a background worker to the application.
func (a *Application) AddWorker(w BackgroundWorker) {
a.workers = append(a.workers, w)
}
// Start launches background workers and the HTTP server.
func (a *Application) Start() error {
// Start all background workers first
for _, w := range a.workers {
if err := w.Start(); err != nil {
// Stop any already started workers
a.stopWorkers()
return err
}
}
return a.Server.Start()
}
// StartAsync launches the HTTP server asynchronously.
func (a *Application) StartAsync() error {
// Start all background workers first
for _, w := range a.workers {
if err := w.Start(); err != nil {
// Stop any already started workers
a.stopWorkers()
return err
}
}
return a.Server.StartAsync()
}
// Shutdown gracefully stops workers and the server.
func (a *Application) Shutdown(ctx context.Context) error {
a.stopWorkers()
return a.Server.Shutdown(ctx)
}
// stopWorkers stops all background workers.
func (a *Application) stopWorkers() {
for _, w := range a.workers {
w.Stop()
}
}
// Run starts the application and waits for termination signals.
// It handles graceful shutdown with a default timeout of 10 seconds.
func (a *Application) Run() error {
return a.RunWithTimeout(10 * time.Second)
}
// RunWithTimeout starts the application and waits for termination signals.
// It handles graceful shutdown with the specified timeout.
func (a *Application) RunWithTimeout(timeout time.Duration) error {
if err := a.Start(); err != nil {
return err
}
// Wait for termination signal
stop := make(chan os.Signal, 1)
signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
<-stop
a.Logger.Info("Shutting down gracefully...")
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
if err := a.Shutdown(ctx); err != nil {
a.Logger.Error("Graceful shutdown failed", "error", err)
return err
}
a.Logger.Info("Shutdown complete")
return nil
}