+var startTime time.Time
+
+func XappUpTime() time.Duration {
+ return time.Since(startTime)
+}
+
+func init() {
+ startTime = time.Now()
+}
+
+func IsReady() bool {
+ return Rmr != nil && Rmr.IsReady() && SdlStorage != nil && SdlStorage.IsReady()
+}
+
+func SetReadyCB(cb ReadyCB, params interface{}) {
+ readyCb = cb
+ readyCbParams = params
+}
+
+func XappReadyCb(params interface{}) {
+ Alarm = NewAlarmClient(viper.GetString("moId"), viper.GetString("name"))
+ if readyCb != nil {
+ readyCb(readyCbParams)
+ }
+}
+
+func SetShutdownCB(cb ShutdownCB) {
+ shutdownCb = cb
+}
+
+func XappShutdownCb() {
+ if err := doDeregister(); err != nil {
+ Logger.Info("xApp deregistration failed: %v, terminating ungracefully!", err)
+ } else {
+ Logger.Info("xApp deregistration successfull!")
+ }
+
+ if shutdownCb != nil {
+ shutdownCb()
+ }
+}
+
+func registerXapp() {
+ for {
+ time.Sleep(5 * time.Second)
+ if !IsHealthProbeReady() {
+ Logger.Debug("Application='%s' is not ready yet, waiting ...", viper.GetString("name"))
+ continue
+ }
+
+ Logger.Debug("Application='%s' is now up and ready, continue with registration ...", viper.GetString("name"))
+ if err := doRegister(); err == nil {
+ Logger.Info("Registration done, proceeding with startup ...")
+ break
+ }
+ }
+}
+
+func getService(host, service string) string {
+ appnamespace := os.Getenv("APP_NAMESPACE")
+ if appnamespace == "" {
+ appnamespace = DEFAULT_XAPP_NS
+ }
+
+ svc := fmt.Sprintf(service, strings.ToUpper(appnamespace), strings.ToUpper(host))
+ url := strings.Split(os.Getenv(strings.Replace(svc, "-", "_", -1)), "//")
+
+ Logger.Info("getService: %+v %+v", svc, url)
+ if len(url) > 1 {
+ return url[1]
+ }
+ return ""
+}
+
+func getPltNamespace(envName, defVal string) string {
+ pltnamespace := os.Getenv("PLT_NAMESPACE")
+ if pltnamespace == "" {
+ pltnamespace = defVal
+ }
+
+ return pltnamespace
+}
+
+func doPost(pltNs, url string, msg []byte, status int) error {
+ resp, err := http.Post(fmt.Sprintf(url, pltNs, pltNs), "application/json", bytes.NewBuffer(msg))
+ if err != nil || resp == nil || resp.StatusCode != status {
+ Logger.Info("http.Post to '%s' failed with error: %v", fmt.Sprintf(url, pltNs, pltNs), err)
+ return err
+ }
+ Logger.Info("Post to '%s' done, status:%v", fmt.Sprintf(url, pltNs, pltNs), resp.Status)
+
+ return err
+}
+
+func doRegister() error {
+ host, _ := os.Hostname()
+ xappname := viper.GetString("name")
+ xappversion := viper.GetString("version")
+ pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
+
+ //httpEp, rmrEp := getService(xappname, SERVICE_HTTP), getService(xappname, SERVICE_RMR)
+ httpEp, rmrEp := getService(host, SERVICE_HTTP), getService(host, SERVICE_RMR)
+ if httpEp == "" || rmrEp == "" {
+ Logger.Warn("Couldn't resolve service endpoints: httpEp=%s rmrEp=%s", httpEp, rmrEp)
+ return nil
+ }
+
+ requestBody, err := json.Marshal(map[string]string{
+ "appName": host,
+ "httpEndpoint": httpEp,
+ "rmrEndpoint": rmrEp,
+ "appInstanceName": xappname,
+ "appVersion": xappversion,
+ "configPath": CONFIG_PATH,
+ })
+
+ if err != nil {
+ Logger.Error("json.Marshal failed with error: %v", err)
+ return err
+ }
+
+ return doPost(pltNs, REGISTER_PATH, requestBody, http.StatusCreated)
+}
+
+func doDeregister() error {
+ if !IsHealthProbeReady() {
+ return nil
+ }
+
+ name, _ := os.Hostname()
+ xappname := viper.GetString("name")
+ pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
+
+ requestBody, err := json.Marshal(map[string]string{
+ "appName": name,
+ "appInstanceName": xappname,
+ })
+
+ if err != nil {
+ Logger.Error("json.Marshal failed with error: %v", err)
+ return err
+ }
+
+ return doPost(pltNs, DEREGISTER_PATH, requestBody, http.StatusNoContent)
+}
+
+func InstallSignalHandler() {
+ //
+ // Signal handlers to really exit program.
+ // shutdownCb can hang until application has
+ // made all needed gracefull shutdown actions
+ // hardcoded limit for shutdown is 20 seconds
+ //
+ interrupt := make(chan os.Signal, 1)
+ signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
+ //signal handler function
+ go func() {
+ for range interrupt {
+ if atomic.CompareAndSwapInt32(&shutdownFlag, 0, 1) {
+ // close function
+ go func() {
+ timeout := int(20)
+ sentry := make(chan struct{})
+ defer close(sentry)
+
+ // close callback
+ go func() {
+ XappShutdownCb()
+ sentry <- struct{}{}
+ }()
+ select {
+ case <-time.After(time.Duration(timeout) * time.Second):
+ Logger.Info("xapp-frame shutdown callback took more than %d seconds", timeout)
+ case <-sentry:
+ Logger.Info("xapp-frame shutdown callback handled within %d seconds", timeout)
+ }
+ os.Exit(0)
+ }()
+ } else {
+ newCnt := atomic.AddInt32(&shutdownCnt, 1)
+ Logger.Info("xapp-frame shutdown already ongoing. Forced exit counter %d/%d ", newCnt, 5)
+ if newCnt >= 5 {
+ Logger.Info("xapp-frame shutdown forced exit")
+ os.Exit(0)
+ }
+ continue
+ }
+
+ }
+ }()
+}
+