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  }