github.com/lastbackend/toolkit@v0.0.0-20241020043710-cafa37b95aad/pkg/runtime/controller/package.go (about) 1 package controller 2 3 import ( 4 "context" 5 "fmt" 6 "github.com/lastbackend/toolkit" 7 "github.com/lastbackend/toolkit/pkg/runtime" 8 "github.com/lastbackend/toolkit/pkg/runtime/logger" 9 "github.com/lastbackend/toolkit/pkg/util/types" 10 "github.com/pkg/errors" 11 "golang.org/x/sync/errgroup" 12 "reflect" 13 ) 14 15 const PackageHookMethodPreStart = "PreStart" 16 const PackageHookMethodOnStart = "OnStart" 17 const PackageHookMethodOnStartSync = "OnStartSync" 18 const PackageHookMethodOnStop = "OnStop" 19 const PackageHookMethodOnStopSync = "OnStopSync" 20 21 type packageController struct { 22 runtime.Package 23 24 log logger.Logger 25 26 constructors []any 27 packages []toolkit.Package 28 } 29 30 func (c *packageController) Provide(constructor ...any) { 31 c.constructors = append(c.constructors, constructor...) 32 return 33 } 34 35 func (c *packageController) Constructors() []any { 36 return c.constructors 37 } 38 39 func (c *packageController) Register(packages []toolkit.PackageItem) { 40 c.log.V(5).Info("packageManager.Register.start") 41 c.packages = make([]toolkit.Package, len(packages)) 42 for _, pkg := range packages { 43 c.packages[pkg.Index] = pkg.Source 44 } 45 46 c.log.V(5).Infof("packageManager.Register.packages: %v", c.packages) 47 c.log.V(5).Info("packageManager.Register.end") 48 } 49 50 func (c *packageController) PreStart(ctx context.Context) error { 51 c.log.V(5).Info("packageManager.PreStart.start") 52 err := c.hook(ctx, PackageHookMethodPreStart, true) 53 if err != nil { 54 return err 55 } 56 c.log.V(5).Info("packageManager.PreStart.end") 57 return nil 58 } 59 60 func (c *packageController) OnStart(ctx context.Context) error { 61 c.log.V(5).Info("packageManager.OnStart.start") 62 63 err := c.hook(ctx, PackageHookMethodOnStartSync, true) 64 if err != nil { 65 return err 66 } 67 68 err = c.hook(ctx, PackageHookMethodOnStart, false) 69 if err != nil { 70 return err 71 } 72 73 c.log.V(5).Info("packageManager.OnStart.end") 74 return nil 75 } 76 77 func (c *packageController) OnStop(ctx context.Context) error { 78 c.log.V(5).Info("packageManager.OnStop.start") 79 80 err := c.hook(ctx, PackageHookMethodOnStopSync, true) 81 if err != nil { 82 return err 83 } 84 85 err = c.hook(ctx, PackageHookMethodOnStop, false) 86 if err != nil { 87 return err 88 } 89 90 c.log.V(5).Info("packageManager.OnStop.end") 91 return nil 92 } 93 94 func (c *packageController) hook(ctx context.Context, kind string, sync bool) error { 95 96 ctx, cancel := context.WithCancelCause(ctx) 97 98 if sync { 99 c.log.V(5).Infof("packageManager.%s.start:sync", kind) 100 defer func() { 101 c.log.V(5).Infof("packageManager.%s.end:sync", kind) 102 }() 103 } else { 104 c.log.V(5).Infof("packageManager.%s.start:async", kind) 105 defer func() { 106 c.log.V(5).Infof("packageManager.%s.end:async", kind) 107 }() 108 } 109 110 // start all non-sync methods 111 g := errgroup.Group{} 112 113 for i := 0; i < len(c.packages); i++ { 114 115 if c.packages[i] == nil { 116 continue 117 } 118 119 pkg := c.packages[i] 120 121 if sync { 122 if err := c.call(ctx, pkg, kind); err != nil { 123 return err 124 } 125 } else { 126 g.Go(func() error { 127 return c.call(ctx, pkg, kind) 128 }) 129 } 130 } 131 132 if err := g.Wait(); err != nil { 133 c.log.V(5).Errorf("can not start toolkit:", err.Error()) 134 cancel(err) 135 return err 136 } 137 138 return nil 139 } 140 141 func (c *packageController) call(ctx context.Context, pkg toolkit.Package, kind string) error { 142 143 args := []reflect.Value{reflect.ValueOf(ctx)} 144 meth := reflect.ValueOf(pkg).MethodByName(kind) 145 name := types.Type(pkg) 146 147 if !reflect.ValueOf(meth).IsZero() { 148 c.log.V(5).Infof("packageManager.%s.call: %s", kind, name) 149 150 res := meth.Call(args) 151 if len(res) < 1 { 152 return nil 153 } 154 155 if len(res) > 1 { 156 return errors.New(fmt.Sprintf("packageManager.%s.call:%s:err: method results are not supported. Only error is supported", kind, name)) 157 } 158 159 if v := res[0].Interface(); v != nil { 160 if err, ok := v.(error); ok && err != nil { 161 return err 162 } 163 } 164 } 165 166 return nil 167 } 168 169 func newPackageController(_ context.Context, runtime runtime.Runtime) runtime.Package { 170 pl := new(packageController) 171 pl.log = runtime.Log() 172 pl.constructors = make([]any, 0) 173 pl.packages = make([]toolkit.Package, 0) 174 return pl 175 }