github.com/TIBCOSoftware/flogo-lib@v0.5.9/engine/engine.go (about) 1 package engine 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "os/signal" 8 "runtime/debug" 9 "syscall" 10 11 "github.com/TIBCOSoftware/flogo-lib/app" 12 "github.com/TIBCOSoftware/flogo-lib/config" 13 "github.com/TIBCOSoftware/flogo-lib/core/action" 14 "github.com/TIBCOSoftware/flogo-lib/core/data" 15 "github.com/TIBCOSoftware/flogo-lib/core/trigger" 16 "github.com/TIBCOSoftware/flogo-lib/engine/runner" 17 "github.com/TIBCOSoftware/flogo-lib/logger" 18 "github.com/TIBCOSoftware/flogo-lib/util" 19 "github.com/TIBCOSoftware/flogo-lib/util/managed" 20 "sync" 21 "github.com/TIBCOSoftware/flogo-lib/engine/channels" 22 ) 23 24 var managedServices []managed.Managed 25 var lock = &sync.Mutex{} 26 27 // Interface for the engine behaviour 28 type Engine interface { 29 // Init initialize the engine 30 Init(directRunner bool) error 31 32 // Start starts the engine 33 Start() error 34 35 // Stop stop the engine 36 Stop() error 37 38 // TriggerInfos get info for the triggers 39 TriggerInfos() []*managed.Info 40 } 41 42 func LifeCycle(managedEntity managed.Managed) { 43 defer lock.Unlock() 44 lock.Lock() 45 managedServices = append(managedServices, managedEntity) 46 } 47 48 49 // engineImpl is the type for the Default Engine Implementation 50 type engineImpl struct { 51 app *app.Config 52 initialized bool 53 logLevel string 54 actionRunner action.Runner 55 serviceManager *util.ServiceManager 56 57 triggers map[string]trigger.Trigger 58 triggerInfos map[string]*managed.Info 59 } 60 61 // New creates a new Engine 62 func New(appCfg *app.Config) (Engine, error) { 63 // App is required 64 if appCfg == nil { 65 return nil, errors.New("no App configuration provided") 66 } 67 // Name is required 68 if len(appCfg.Name) == 0 { 69 return nil, errors.New("no App name provided") 70 } 71 // Version is required 72 if len(appCfg.Version) == 0 { 73 return nil, errors.New("no App version provided") 74 } 75 76 //fix up app configuration if it is older 77 //app.FixUpApp(appCfg) 78 79 logLevel := config.GetLogLevel() 80 81 return &engineImpl{app: appCfg, serviceManager: util.GetDefaultServiceManager(), logLevel: logLevel}, nil 82 } 83 84 func (e *engineImpl) Init(directRunner bool) error { 85 86 if !e.initialized { 87 e.initialized = true 88 89 if directRunner { 90 e.actionRunner = runner.NewDirect() 91 } else { 92 e.actionRunner = runner.NewPooled(NewPooledRunnerConfig()) 93 } 94 95 propProvider := app.GetPropertyProvider() 96 // Initialize the properties 97 props, err := app.GetProperties(e.app.Properties) 98 if err != nil { 99 return err 100 } 101 propProvider.SetProperties(props) 102 data.SetPropertyProvider(propProvider) 103 104 actionFactories := action.Factories() 105 for _, factory := range actionFactories { 106 if initializable, ok := factory.(managed.Initializable); ok { 107 108 if err := initializable.Init(); err != nil { 109 return err 110 } 111 } 112 } 113 114 //add engine channels 115 channelDescriptors := e.app.Channels 116 if len(channelDescriptors) > 0 { 117 for _, descriptor := range channelDescriptors { 118 name, buffSize := channels.Decode(descriptor) 119 120 logger.Debugf("Creating Engine Channel '%s'", name) 121 channels.New(name, buffSize) 122 } 123 } 124 125 err = app.RegisterResources(e.app.Resources) 126 if err != nil { 127 return err 128 } 129 130 actions, err := app.CreateSharedActions(e.app.Actions) 131 if err != nil { 132 errorMsg := fmt.Sprintf("Error creating shared action instances - %s", err.Error()) 133 logger.Error(errorMsg) 134 panic(errorMsg) 135 } 136 137 //todo add all actions to engine (will make cleanup easier) 138 139 triggers, err := app.CreateTriggers(e.app.Triggers, actions, e.actionRunner) 140 e.triggerInfos = make(map[string]*managed.Info) 141 142 if err != nil { 143 errorMsg := fmt.Sprintf("Error Creating trigger instances - %s", err.Error()) 144 logger.Error(errorMsg) 145 panic(errorMsg) 146 } 147 148 e.triggers = triggers 149 } 150 151 return nil 152 } 153 154 //Start initializes and starts the Triggers and initializes the Actions 155 func (e *engineImpl) Start() error { 156 157 logger.SetDefaultLogger("engine") 158 159 logger.Infof("Starting app [ %s ] with version [ %s ]", e.app.Name, e.app.Version) 160 logger.Info("Engine Starting...") 161 162 // Todo document RunnerType for engine configuration 163 runnerType := GetRunnerType() 164 err := e.Init(runnerType == "DIRECT") 165 if err != nil { 166 return err 167 } 168 169 logger.Info("Starting Services...") 170 171 actionRunner := e.actionRunner.(interface{}) 172 173 if managedRunner, ok := actionRunner.(managed.Managed); ok { 174 managed.Start("ActionRunner Service", managedRunner) 175 } 176 177 err = e.serviceManager.Start() 178 179 if err != nil { 180 logger.Error("Error Starting Services - " + err.Error()) 181 } else { 182 logger.Info("Started Services") 183 } 184 185 if len(managedServices) > 0 { 186 for _, mService := range managedServices { 187 err = mService.Start() 188 if err != nil { 189 logger.Error("Error Starting Services - " + err.Error()) 190 //TODO Should we exit here? 191 } 192 } 193 } 194 195 // Start the triggers 196 logger.Info("Starting Triggers...") 197 198 var failed []string 199 200 for key, value := range e.triggers { 201 triggerInfo := &managed.Info{Name: key} 202 err := managed.Start(fmt.Sprintf("Trigger [ %s ]", key), value) 203 if err != nil { 204 logger.Infof("Trigger [%s] failed to start due to error [%s]", key, err.Error()) 205 triggerInfo.Status = managed.StatusFailed 206 triggerInfo.Error = err 207 logger.Debugf("StackTrace: %s", debug.Stack()) 208 if config.StopEngineOnError() { 209 logger.Debugf("{%s=true}. Stopping engine", config.ENV_STOP_ENGINE_ON_ERROR_KEY) 210 logger.Info("Stopped") 211 os.Exit(1) 212 } 213 failed = append(failed, key) 214 } else { 215 triggerInfo.Status = managed.StatusStarted 216 logger.Infof("Trigger [ %s ]: Started", key) 217 logger.Debugf("Trigger [ %s ] has ref [ %s ] and version [ %s ]", key, value.Metadata().ID, value.Metadata().Version) 218 } 219 220 e.triggerInfos[key] = triggerInfo 221 } 222 223 if len(failed) > 0 { 224 //remove failed trigger, we have no use for them 225 for _, triggerId := range failed { 226 delete(e.triggers, triggerId) 227 } 228 } 229 230 logger.Info("Triggers Started") 231 232 if channels.Count() > 0 { 233 logger.Info("Starting Engine Channels...") 234 channels.Start() 235 logger.Info("Engine Channels Started") 236 } 237 238 logger.Info("Engine Started") 239 240 return nil 241 } 242 243 func (e *engineImpl) Stop() error { 244 logger.Info("Engine Stopping...") 245 246 if channels.Count() > 0 { 247 logger.Info("Stopping Engine Channels...") 248 channels.Stop() 249 logger.Info("Engine Channels Stopped...") 250 } 251 252 logger.Info("Stopping Triggers...") 253 254 // Stop Triggers 255 for trgId, tgr := range e.triggers { 256 managed.Stop("Trigger [ "+trgId+" ]", tgr) 257 e.triggerInfos[trgId].Status = managed.StatusStopped 258 } 259 260 logger.Info("Triggers Stopped") 261 262 //TODO temporarily add services 263 logger.Info("Stopping Services...") 264 265 actionRunner := e.actionRunner.(interface{}) 266 267 if managedRunner, ok := actionRunner.(managed.Managed); ok { 268 managed.Stop("ActionRunner", managedRunner) 269 } 270 271 err := e.serviceManager.Stop() 272 273 if err != nil { 274 logger.Error("Error Stopping Services - " + err.Error()) 275 } else { 276 logger.Info("Stopped Services") 277 } 278 279 if len(managedServices) > 0 { 280 for _, mService := range managedServices { 281 err = mService.Stop() 282 if err != nil { 283 logger.Error("Error Stopping Services - " + err.Error()) 284 } 285 } 286 } 287 288 logger.Info("Engine Stopped") 289 return nil 290 } 291 292 func (e *engineImpl) TriggerInfos() []*managed.Info { 293 294 infos := make([]*managed.Info, 0, len(e.triggerInfos)) 295 296 for _, info := range e.triggerInfos { 297 infos = append(infos, info) 298 } 299 300 return infos 301 } 302 303 func RunEngine(e Engine) { 304 305 err := e.Start() 306 307 if err != nil { 308 fmt.Println("Error starting engine", err.Error()) 309 os.Exit(1) 310 } 311 312 exitChan := setupSignalHandling() 313 314 code := <-exitChan 315 316 e.Stop() 317 318 os.Exit(code) 319 } 320 321 func setupSignalHandling() chan int { 322 323 signalChan := make(chan os.Signal, 1) 324 signal.Notify(signalChan, 325 syscall.SIGHUP, 326 syscall.SIGINT, 327 syscall.SIGTERM, 328 syscall.SIGQUIT) 329 330 exitChan := make(chan int) 331 go func() { 332 for { 333 s := <-signalChan 334 switch s { 335 // kill -SIGHUP 336 case syscall.SIGHUP: 337 exitChan <- 0 338 // kill -SIGINT/Ctrl+c 339 case syscall.SIGINT: 340 exitChan <- 0 341 // kill -SIGTERM 342 case syscall.SIGTERM: 343 exitChan <- 0 344 // kill -SIGQUIT 345 case syscall.SIGQUIT: 346 exitChan <- 0 347 default: 348 logger.Debug("Unknown signal.") 349 exitChan <- 1 350 } 351 } 352 }() 353 354 return exitChan 355 }