Skip to content

Commit efd45e8

Browse files
committed
Enhance plugin management and refactor application startup sequence
- Introduced `PluginContext` type in `exports.go` for improved plugin context handling. - Refactored the `Start` method in `forge.go` to initialize plugins before setting the application status, ensuring proper service and controller registration. - Updated `plugin.go` to align plugin type definitions with the `common` package. - Streamlined the plugin startup process in `manager.go` by reorganizing the order of service registration, plugin initialization, and middleware setup for better clarity and functionality. Signed-off-by: Rubicon Lee <rex.raphael@outlook.com>
1 parent 8dd7162 commit efd45e8

4 files changed

Lines changed: 127 additions & 67 deletions

File tree

exports.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type Service = common.Service
3939
type ServiceDefinition = common.ServiceDefinition
4040
type Plugin = common.Plugin
4141
type PluginMetrics = common.PluginMetrics
42+
type PluginContext = common.PluginContext
4243
type Hook = common.Hook
4344
type ConfigSchema = common.ConfigSchema
4445
type ConfigProperty = common.ConfigProperty

forge.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,6 @@ func (app *ForgeApplication) Start(ctx context.Context) error {
12641264
return common.ErrLifecycleError("start", fmt.Errorf("application already started or in invalid state: %s", app.status))
12651265
}
12661266

1267-
app.status = common.ApplicationStatusStarting
12681267
app.startTime = time.Now()
12691268

12701269
if app.logger != nil {
@@ -1283,7 +1282,19 @@ func (app *ForgeApplication) Start(ctx context.Context) error {
12831282
return err
12841283
}
12851284

1286-
// Start container
1285+
// Start plugins BEFORE container starts to allow service and controller registration
1286+
// Keep status as NotStarted during plugin initialization
1287+
if app.pluginsManager != nil {
1288+
if err := app.pluginsManager.StartPlugins(ctx); err != nil {
1289+
app.status = common.ApplicationStatusError
1290+
return fmt.Errorf("failed to start plugins: %w", err)
1291+
}
1292+
}
1293+
1294+
// Now set status to Starting after plugins have registered their components
1295+
app.status = common.ApplicationStatusStarting
1296+
1297+
// Start container - this locks service registration
12871298
if app.container != nil {
12881299
if err := app.container.Start(ctx); err != nil {
12891300
app.status = common.ApplicationStatusError
@@ -1307,14 +1318,6 @@ func (app *ForgeApplication) Start(ctx context.Context) error {
13071318
}
13081319
}
13091320

1310-
// Start plugins
1311-
if app.pluginsManager != nil {
1312-
if err := app.pluginsManager.StartPlugins(ctx); err != nil {
1313-
app.status = common.ApplicationStatusError
1314-
return fmt.Errorf("failed to start plugins: %w", err)
1315-
}
1316-
}
1317-
13181321
app.status = common.ApplicationStatusRunning
13191322

13201323
if app.logger != nil {

pkg/pluginengine/plugin.go

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,34 @@ import (
99
)
1010

1111
// Plugin defines the interface for framework plugins
12-
type PluginEngine = common2.PluginEngineEngine
12+
type PluginEngine = common2.PluginEngine
1313

1414
// PluginType defines the type of plugin
15-
type PluginType = common2.PluginEngineType
15+
type PluginType = common2.PluginType
1616

1717
const (
18-
PluginTypeMiddleware = common2.PluginEngineTypeMiddleware
19-
PluginTypeDatabase = common2.PluginEngineTypeDatabase
20-
PluginTypeAuth = common2.PluginEngineTypeAuth
21-
PluginTypeCache = common2.PluginEngineTypeCache
22-
PluginTypeStorage = common2.PluginEngineTypeStorage
23-
PluginTypeMessaging = common2.PluginEngineTypeMessaging
24-
PluginTypeMonitoring = common2.PluginEngineTypeMonitoring
25-
PluginTypeAI = common2.PluginEngineTypeAI
26-
PluginTypeSecurity = common2.PluginEngineTypeSecurity
27-
PluginTypeIntegration = common2.PluginEngineTypeIntegration
28-
PluginTypeUtility = common2.PluginEngineTypeUtility
29-
PluginTypeExtension = common2.PluginEngineTypeExtension
18+
PluginTypeMiddleware = common2.PluginTypeMiddleware
19+
PluginTypeDatabase = common2.PluginTypeDatabase
20+
PluginTypeAuth = common2.PluginTypeAuth
21+
PluginTypeCache = common2.PluginTypeCache
22+
PluginTypeStorage = common2.PluginTypeStorage
23+
PluginTypeMessaging = common2.PluginTypeMessaging
24+
PluginTypeMonitoring = common2.PluginTypeMonitoring
25+
PluginTypeAI = common2.PluginTypeAI
26+
PluginTypeSecurity = common2.PluginTypeSecurity
27+
PluginTypeIntegration = common2.PluginTypeIntegration
28+
PluginTypeUtility = common2.PluginTypeUtility
29+
PluginTypeExtension = common2.PluginTypeExtension
3030
)
3131

3232
// PluginCapability describes what a plugin can do
33-
type PluginCapability = common2.PluginEngineCapability
33+
type PluginCapability = common2.PluginCapability
3434

3535
// PluginDependency describes a plugin dependency
36-
type PluginDependency = common2.PluginEngineDependency
36+
type PluginDependency = common2.PluginDependency
3737

3838
// PluginMetrics contains plugin performance metrics
39-
type PluginMetrics = common2.PluginEngineMetrics
39+
type PluginMetrics = common2.PluginMetrics
4040

4141
// CLICommand represents a CLI command provided by a plugin
4242
type CLICommand = common2.CLICommand
@@ -247,28 +247,28 @@ func (p *BasePlugin) UpdateMetrics(callCount, errorCount int64, latency time.Dur
247247
}
248248

249249
// PluginInfo contains metadata about a plugin
250-
type PluginInfo = common2.PluginEngineInfo
250+
type PluginInfo = common2.PluginInfo
251251

252252
// PluginPackage represents a plugin package
253-
type PluginPackage = common2.PluginEnginePackage
253+
type PluginPackage = common2.PluginPackage
254254

255255
// PluginStats contains plugin statistics
256-
type PluginStats = common2.PluginEngineStats
256+
type PluginStats = common2.PluginStats
257257

258258
// PluginState represents the state of a plugin
259-
type PluginState = common2.PluginEngineState
259+
type PluginState = common2.PluginState
260260

261261
const (
262-
PluginStateUnloaded = common2.PluginEngineStateUnloaded
263-
PluginStateLoaded = common2.PluginEngineStateLoaded
264-
PluginStateInitialized = common2.PluginEngineStateInitialized
265-
PluginStateStarted = common2.PluginEngineStateStarted
266-
PluginStateStopped = common2.PluginEngineStateStopped
267-
PluginStateError = common2.PluginEngineStateError
262+
PluginStateUnloaded = common2.PluginStateUnloaded
263+
PluginStateLoaded = common2.PluginStateLoaded
264+
PluginStateInitialized = common2.PluginStateInitialized
265+
PluginStateStarted = common2.PluginStateStarted
266+
PluginStateStopped = common2.PluginStateStopped
267+
PluginStateError = common2.PluginStateError
268268
)
269269

270270
// PluginOperation represents an operation that can be performed on a plugin
271-
type PluginOperation = common2.PluginEngineOperation
271+
type PluginOperation = common2.PluginOperation
272272

273273
// OperationType defines the type of operation
274274
type OperationType = common2.OperationType
@@ -284,7 +284,8 @@ const (
284284
)
285285

286286
// PluginRegistry defines the interface for plugin registry
287-
type PluginRegistry = common2.PluginEngineRegistry
287+
type PluginRegistry = common2.PluginRegistry
288+
type Plugin = common.PluginEngine
288289

289290
// RegistryStats contains plugin registry statistics
290291
type RegistryStats = common2.RegistryStats

pkg/plugins/manager.go

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -174,30 +174,8 @@ func (m *Manager) startPlugin(ctx context.Context, plugin common.Plugin) error {
174174
// Create PluginContext with container, router, and config manager
175175
pluginCtx := common.NewPluginContext(ctx, m.app.Container(), m.router, m.app.Config())
176176

177-
// Start the plugin with PluginContext
178-
if err := plugin.Start(pluginCtx); err != nil {
179-
return fmt.Errorf("Start failed: %w", err)
180-
}
181-
182-
// Register routes
183-
if m.router != nil {
184-
if err := plugin.Routes(m.router); err != nil {
185-
return fmt.Errorf("route registration failed: %w", err)
186-
}
187-
}
188-
189-
// Register middleware
190-
middleware := plugin.Middleware()
191-
if m.router != nil && len(middleware) > 0 {
192-
for _, mw := range middleware {
193-
if mwFunc, ok := mw.(func(http.Handler) http.Handler); ok {
194-
// Register middleware
195-
m.router.UseMiddleware(mwFunc)
196-
}
197-
}
198-
}
199-
200-
// Register services
177+
// Step 1: Register services FIRST (before anything else)
178+
// Controllers, routes, and middleware may depend on these services
201179
services := plugin.Services()
202180
if m.app != nil && m.app.Container() != nil {
203181
for _, svc := range services {
@@ -213,18 +191,95 @@ func (m *Manager) startPlugin(ctx context.Context, plugin common.Plugin) error {
213191
}
214192
}
215193

216-
// Register controllers
194+
// Step 2: Start the plugin with PluginContext
195+
// Plugin can now register additional services if needed
196+
if err := plugin.Start(pluginCtx); err != nil {
197+
return fmt.Errorf("start failed: %w", err)
198+
}
199+
200+
// Step 3: Register controllers
201+
// Register controllers directly with router without initializing them yet
202+
// Controllers will be initialized later after container starts
217203
controllers := plugin.Controllers()
218-
if m.app != nil {
204+
if m.router != nil && len(controllers) > 0 {
205+
// Set plugin context in router if supported
206+
if setter, ok := m.router.(interface{ SetCurrentPlugin(string) }); ok {
207+
setter.SetCurrentPlugin(name)
208+
defer func() {
209+
if clearer, ok := m.router.(interface{ ClearCurrentPlugin() }); ok {
210+
clearer.ClearCurrentPlugin()
211+
}
212+
}()
213+
}
214+
215+
// Register each controller with router
219216
for _, ctrl := range controllers {
220-
if err := m.app.AddController(ctrl); err != nil {
217+
if m.logger != nil {
218+
m.logger.Debug("registering controller from plugin",
219+
logger.String("plugin", name),
220+
logger.String("controller", ctrl.Name()),
221+
)
222+
}
223+
224+
// Initialize controller with container first
225+
// Note: This happens before container.Start(), so services cannot be resolved yet
226+
// Controllers should defer service resolution to their handler methods or
227+
// implement a lazy initialization pattern
228+
if err := ctrl.Initialize(m.app.Container()); err != nil {
221229
if m.logger != nil {
222-
m.logger.Warn("failed to register controller from plugin",
230+
m.logger.Error("failed to initialize controller from plugin",
223231
logger.String("plugin", name),
224232
logger.String("controller", ctrl.Name()),
225233
logger.Error(err),
226234
)
227235
}
236+
return fmt.Errorf("failed to initialize controller %s: %w", ctrl.Name(), err)
237+
}
238+
239+
// Register controller with router
240+
if err := m.router.RegisterController(ctrl); err != nil {
241+
if m.logger != nil {
242+
m.logger.Error("failed to register controller from plugin",
243+
logger.String("plugin", name),
244+
logger.String("controller", ctrl.Name()),
245+
logger.Error(err),
246+
)
247+
}
248+
return fmt.Errorf("failed to register controller %s: %w", ctrl.Name(), err)
249+
}
250+
251+
if m.logger != nil {
252+
m.logger.Debug("controller registered successfully",
253+
logger.String("plugin", name),
254+
logger.String("controller", ctrl.Name()),
255+
)
256+
}
257+
}
258+
259+
if m.logger != nil {
260+
m.logger.Info("plugin controllers registered",
261+
logger.String("plugin", name),
262+
logger.Int("count", len(controllers)),
263+
)
264+
}
265+
}
266+
267+
// Step 4: Register routes
268+
// Routes can reference controllers and services
269+
if m.router != nil {
270+
if err := plugin.Routes(m.router); err != nil {
271+
return fmt.Errorf("route registration failed: %w", err)
272+
}
273+
}
274+
275+
// Step 5: Register middleware
276+
// Middleware is applied last
277+
middleware := plugin.Middleware()
278+
if m.router != nil && len(middleware) > 0 {
279+
for _, mw := range middleware {
280+
if mwFunc, ok := mw.(func(http.Handler) http.Handler); ok {
281+
// Register middleware
282+
m.router.UseMiddleware(mwFunc)
228283
}
229284
}
230285
}

0 commit comments

Comments
 (0)