How to Register a Service Dependencies?  Topic is solved

Discussions relating to plugin development, and the Jiwa API.

How to Register a Service Dependencies?

Postby sameermoin » Sun Dec 25, 2022 4:37 am

Hi,

I want to register a Singleton service inside a Jiwa. I registered a service inside the Configure method which resides in the IJiwaRestAPIPlugin interface and it will only run when I start the Jiwa API service this is because the REST API Service is running in a different process (I believe). I want to register a singleton service so that I can access that object from the REST, scheduler service, and Jiwa application.


Code: Select all
public class ServiceRegistry : System.MarshalByRefObject, JiwaFinancials.Jiwa.JiwaApplication.IJiwaRESTAPIPlugin{

          public void Configure(Plugin Plugin, ServiceStackHost AppHost, Container Container, Manager JiwaApplicationManager)
          {
               Container.AddSingleton<IMyService, MyService>();
          }
}
sameermoin
Regular Contributor
Regular Contributor
 
Posts: 75
Joined: Wed Jun 15, 2022 4:02 am
Topics Solved: 1

Re: How to Register a Service Dependencies?

Postby SBarnes » Mon Dec 26, 2022 10:22 am

It might be far better is you outline what you are actually trying to achieve so that the best advice can be given.

As for for the small amount you have provided the short answer is you can't.

To clear up some terminology to start with a Service as you describe it must be a ServiceStack Service and that is designed to be used within the the ServiceStack ecosystem, to explain the Jiwa ecosystem the components are as follows

  • Jiwa.Exe: A winforms application that loads and uses a number of class libraries for its interface and business logic rules.
  • Jiwa Plugin Scheduler Windows Service: A windows service that loads up the plugins and look at any schedules in the database and executes the code in the appropriate scheduled plugins.
  • Jiwa Self Hosted Rest API: A Windows Service that sets up the environment to host a ServiceStack Server and then calls the configure function on any REST API plugins to add the functionality to the server.

The first two are not designed nor intended to host a ServiceStack Server or ServiceStack Service, they also don't have any dependency injection / inversion of control support.

Without knowing what you are trying to achieve, if you want code that operates out of all three applications my advice would be to take a leaf out of Jiwa's playbook and build a class library that referenced the Jiwa manager and any business object libraries etc you needed and add this as an embedded reference to an appropriate plugin for each application and wrapper calls to your class library as appropriate. Note any class library must be targeted as 32 bit and the 4.7.1 framework.

If you need some sort of singleton version in the Jiwa application then wrapper the library in a static class and initialise anything inside the application plugin like hooking the manager into your library.

This link https://stackoverflow.com/questions/214 ... e-injected might let you get the container going in the other two applications but like I said a class library might be a simpler choice.
Regards
Stuart Barnes
SBarnes
Shihan
Shihan
 
Posts: 1619
Joined: Fri Aug 15, 2008 3:27 pm
Topics Solved: 175

Re: How to Register a Service Dependencies?

Postby sameermoin » Mon Dec 26, 2022 3:41 pm

So, there is no common Object pool available to share the same object between these three, and it's obvious as they are running in different processes but I see in the Plugin all classes are implementing System.MarshalByRefObject abstract class. I haven't used it before but I believe this will help to share the objects between processes.


The problem is if I use the static class in a class library and use its reference, will not give the same instance in both services and also in the Jiwa desktop app.


Let's consider the use case of logging. I need to write logs in a file and for that, I need a single instance between all these three. Otherwise, all three will create separate log file streams.
sameermoin
Regular Contributor
Regular Contributor
 
Posts: 75
Joined: Wed Jun 15, 2022 4:02 am
Topics Solved: 1

Re: How to Register a Service Dependencies?

Postby SBarnes » Mon Dec 26, 2022 3:59 pm

There is NO common object pool between the three programs to my knowledge, System.MarshalByRefObject is there because of the Isolate flag on plugin which means it will run in its own application domain.

As I said previously, describe what you are trying to achieve rather than how you think it might be achieved and I can give you the best advice as to what is possible in the Jiwa ecosystem.
Regards
Stuart Barnes
SBarnes
Shihan
Shihan
 
Posts: 1619
Joined: Fri Aug 15, 2008 3:27 pm
Topics Solved: 175

Re: How to Register a Service Dependencies?

Postby sameermoin » Mon Dec 26, 2022 4:02 pm

SBarnes wrote:As I said previously, describe what you are trying to achieve rather than how you think it might be achieved and I can give you the best advice as to what is possible in the Jiwa ecosystem.


I already mentioned my use case. I want to enable logging in to a file
sameermoin
Regular Contributor
Regular Contributor
 
Posts: 75
Joined: Wed Jun 15, 2022 4:02 am
Topics Solved: 1

Re: How to Register a Service Dependencies?

Postby SBarnes » Mon Dec 26, 2022 4:27 pm

Is there a reason why you would want to log to a file rather than the database itself, given all three programs have access to the database?

For instance below is an example which I have used quite well in many projects, that uses a list as well as the event log and the database as options, although you can't use the event log with standard users unless that have the windows permissions set.

Code: Select all
  public enum LogEntryType
    {
        Debug,
        Error
    }
    public class LogEntry
    {
        public string Text { get; set; }


        public DateTime When { get; set; }

        public LogEntryType LogType { get; set; }

        public LogEntry(string input)
        {
            Text = input;
            When = DateTime.Now;
            LogType = LogEntryType.Debug;
        }
    }


Code: Select all
 public class Logger : List<LogEntry>, IDisposable
    {
        private bool _ErrorsInLog;

        public bool SendToEventLog { get; set; }

        public bool SendToTableLog { get; set; }

        public bool SendToTableLogAsync { get; set; }

        private IDbConnection db;

        public bool ErrorsInLog
        {
            get { return _ErrorsInLog; }
        }

        public Logger()
        {
            _ErrorsInLog = false;
            SendToTableLog = SettingsVault.TableLog;
            SendToEventLog = SettingsVault.EventLog;
            SendToTableLogAsync = SettingsVault.TableLogAsync;
            AppDomain.CurrentDomain.UnhandledException += LogException;
            db = null;

        }



        public IDbConnection GetDatabase()
        {
            try
            {
                if (db != null)
                {
                    return db;
                }
                //System.Diagnostics.Debugger.Break();
                var dbFactory = new OrmLiteConnectionFactory(SettingsVault.ConnectionString, ServiceStack.OrmLite.SqlServer2012Dialect.Provider);
                db = dbFactory.Open();
                db.SetCommandTimeout(60000);
                return db;
            }
            catch (Exception ex)
            {

                // logger.Error("Error in GetDatabase" + ex.Message);
                // throw ex;
            }

            return null;
        }



        public void CloseDatabase()
        {
            try
            {
                db.Close();

            }
            catch (Exception ex)
            {
                //logger.Error("Error in CloseDatabase" + ex.Message);
                // throw ex;
            }

        }





        private void LogException(object sender, UnhandledExceptionEventArgs e)
        {
            try
            {
                this.WriteDBLog(((Exception)e.ExceptionObject).Message, 2);
                this.WriteDBLog(((Exception)e.ExceptionObject).StackTrace, 2);
            }
            catch (Exception exception)
            {
            }
        }


        public List<LogEntry> Errors {
            get { return this.Where(l => l.LogType == LogEntryType.Error).OrderBy(l => l.When).ToList(); }
        }


        public void Add(string s)
        {
            this.Add(new LogEntry(s));
        }

        public void Debug(string v)
        {
            this.Add(new LogEntry(v));
            if (SendToEventLog)
            {
                LogToEventLog(v, EventLogEntryType.Information);
            }
            if (this.SendToTableLogAsync)
            {
                this.WriteDBLogAsync(v, 0);
            }

            if (this.SendToTableLog)
            {
                this.WriteDBLog(v, 0);
            }
        }



        public void Error(string v)
        {
            this.Add(new LogEntry(v){LogType = LogEntryType.Error});
            _ErrorsInLog = true;
            if (SendToEventLog)
            {
                LogToEventLog(v, EventLogEntryType.Error);
            }

            if (this.SendToTableLogAsync)
            {
                this.WriteDBLogAsync(v, 1);
            }

            if (this.SendToTableLog)
            {
                this.WriteDBLog(v, 1);
            }
        }

        public void Exception(Exception e)
        {
            StackTrace stackTrace = new StackTrace();
            string cmethod = stackTrace.GetFrame(1).GetMethod().Name;
            string lnum = stackTrace.GetFrame(1).GetFileLineNumber().ToString();
            this.Add(new LogEntry("Method: " + cmethod + " Line: " + lnum + " Exception: " + e.Message + " Stack Trace: " + e.StackTrace));
            _ErrorsInLog = true;
            if (SendToEventLog)
            {
                LogToEventLog("Exception: " + e.Message, EventLogEntryType.Information);
                LogToEventLog("Method: " + cmethod + " Line: " + lnum + " Exception: " + e.Message + " Stack Trace: " + e.StackTrace, EventLogEntryType.Error);
            }

            if (this.SendToTableLogAsync)
            {
                this.WriteDBLogAsync("Exception: " + e.Message, 1);
                this.WriteDBLogAsync("Method: " + cmethod + " Line: " + lnum + " Exception: " + e.Message + " Stack Trace: " + e.StackTrace, 1);
            }

            if (this.SendToTableLog)
            {
                this.WriteDBLog("Exception: " + e.Message, 1);
                this.WriteDBLog("Method: " + cmethod + " Line: " + lnum + " Exception: " + e.Message + " Stack Trace: " + e.StackTrace, 1);
            }
        }


        public void ExceptionWithoutError(Exception e)
        {
            StackTrace stackTrace = new StackTrace();
            string cmethod = stackTrace.GetFrame(1).GetMethod().Name;
            string lnum = stackTrace.GetFrame(1).GetFileLineNumber().ToString();
            this.Add(new LogEntry("Method: " + cmethod + " Line: " + lnum + " Exception: " + e.Message + " Stack Trace: " + e.StackTrace));
            // _ErrorsInLog = true;
            if (SendToEventLog)
            {
                LogToEventLog("Exception: " + e.Message, EventLogEntryType.Information);
                LogToEventLog("Method: " + cmethod + " Line: " + lnum + " Exception: " + e.Message + " Stack Trace: " + e.StackTrace, EventLogEntryType.Error);
            }

            if (this.SendToTableLogAsync)
            {
                this.WriteDBLogAsync("Exception: " + e.Message, 1);
                this.WriteDBLogAsync("Method: " + cmethod + " Line: " + lnum + " Exception: " + e.Message + " Stack Trace: " + e.StackTrace, 1);
            }

            if (this.SendToTableLog)
            {
                this.WriteDBLog("Exception: " + e.Message, 1);
                this.WriteDBLog("Method: " + cmethod + " Line: " + lnum + " Exception: " + e.Message + " Stack Trace: " + e.StackTrace, 1);
            }
        }



        public async void WriteDBLogAsync(string msg, int level = 0)
        {
            var dbFactory = new OrmLiteConnectionFactory(SettingsVault.ConnectionString, ServiceStack.OrmLite.SqlServer2012Dialect.Provider);
            using (var ldb = GetDatabase())
            {

                try
                {
                    tAttkImportErrorLog logtable = new tAttkImportErrorLog();
                    logtable.LogMessage = msg;
                    logtable.ErrorLevel = level;
                    logtable.DateInserted = DateTime.Now;
                    var itemId = await ldb.InsertAsync(logtable, selectIdentity: true);
                }
                catch (Exception ex)
                {

                    //throw;
                }
                //db.Close();
            }
        }


        public void WriteDBLog(string msg, int level = 0)
        {
            //var dbFactory = new OrmLiteConnectionFactory(SettingsVault.ConnectionString, ServiceStack.OrmLite.SqlServer2012Dialect.Provider);
            using (var ldb = GetDatabase())
            {

                try
                {
                    tAttkImportErrorLog logtable = new tAttkImportErrorLog();
                    logtable.LogMessage = msg;
                    logtable.ErrorLevel = level;
                    logtable.DateInserted = DateTime.Now;
                    var itemId = ldb.Insert(logtable, selectIdentity: true);
                    //db.Close();
                }
                catch (Exception ex)
                {

                    // throw;
                }
            }
        }


        public string AsText()
        {
            string result = "";
            foreach (string line in this.OrderBy(l => l.When).Select(l => l.Text))
            {
                result += line + Environment.NewLine;
            }

            return result;
        }

        public string ErrorsAsHTML()
        {
            string result = "";
            foreach (string line in this.Errors.Select(l => l.Text))
            {
                result += System.Web.HttpUtility.HtmlEncode(line) + "<br/>";
            }

            return result;
        }




        public string ErrorsAsText()
        {
            string result = "";
            foreach (string line in this.Errors.Select( l => l.Text))
            {
                result += line + Environment.NewLine;
            }

            return result;
        }

        public string AsHTML()
        {
            string result = "";
            foreach (string line in this.OrderBy(l => l.When).Select(l => l.Text))
            {
                result += System.Web.HttpUtility.HtmlEncode(line) + "<br/>";
            }

            return result;
        }






        public void LogToEventLog(string Message, System.Diagnostics.EventLogEntryType EventLogEntryType)
        {
            try
            {
                System.Diagnostics.EventLog Log = new System.Diagnostics.EventLog("Application");

                //Log.Source = string.Format(this.GetType().FullName);
                Log.Source = "Attkey Import";
                Log.WriteEntry(Message, EventLogEntryType);
                Log.Close();
            }
            catch (Exception ex)
            {

              //  throw;
            }
        }

        public void Add(List<string> logs)
        {
            foreach (String log in logs)
            {
                Debug(log);
            }
        }

        public void Dispose()
        {
            CloseDatabase();
            AppDomain.CurrentDomain.UnhandledException -= LogException;
        }
    }


Code: Select all
 [Alias("tAttkImportErrorLog")]
    public partial class tAttkImportErrorLog : IHasId<int>
    {
        [Alias("ID")]
        [AutoIncrement]
        public int Id { get; set; }
        [Required]
        public string LogMessage { get; set; }
        [Required]
        public DateTime DateInserted { get; set; }
        public int? ErrorLevel { get; set; }
    }


Code: Select all
    public class SettingsVault
    {
        public static string ConnectionString { get;  set; }

        public static bool TableLog { get;  set; }
        public static bool EventLog { get;  set; }
        public static bool TableLogAsync { get;  set; }

       

        public static int ImageSize { get; set; }

        public static bool WarnImageSize { get; set; }

        public static int ImageDimension { get; set; }


        public static int Quality { get; set; }

        public static float DPI { get; set; }

        public static bool DebtorImporting { get; set; } = false;

        public static bool InventoryImporting { get; set; } = false;
    }
Regards
Stuart Barnes
SBarnes
Shihan
Shihan
 
Posts: 1619
Joined: Fri Aug 15, 2008 3:27 pm
Topics Solved: 175

Re: How to Register a Service Dependencies?

Postby sameermoin » Mon Dec 26, 2022 5:26 pm

In some cases, we don't have access to the database or the database is at a remote location. Writing a log on event viewer is fine but it's not convenient, There is a lot of flexibility available for creating a log on file.
sameermoin
Regular Contributor
Regular Contributor
 
Posts: 75
Joined: Wed Jun 15, 2022 4:02 am
Topics Solved: 1

Re: How to Register a Service Dependencies?

Postby SBarnes » Mon Dec 26, 2022 6:06 pm

Your original inquiry was

I want to register a singleton service so that I can access that object from the REST, scheduler service, and Jiwa application.


All of these have direct access to the database?

Writing to files is still going to be a log file for each application and you can then also have windows file permission issues, as already stated there is no common object pool so your only common resource is the database.

The only other option would be a rest api route that wrote to somewhere but that would be overhead that is not necessary given access to the database directly already exists in all three applications.
Regards
Stuart Barnes
SBarnes
Shihan
Shihan
 
Posts: 1619
Joined: Fri Aug 15, 2008 3:27 pm
Topics Solved: 175

Re: How to Register a Service Dependencies?  Topic is solved

Postby Mike.Sheen » Tue Jan 03, 2023 10:55 am

sameermoin wrote:So, there is no common Object pool available to share the same object between these three, and it's obvious as they are running in different processes but I see in the Plugin all classes are implementing System.MarshalByRefObject abstract class. I haven't used it before but I believe this will help to share the objects between processes.


There is no common object pool. The MarshalByRefObject is there for plugins to opt-in to be isolated to their own app domain - it was never there to allow you to interact with objects across process boundaries.

As Stuart suggested, creating a REST API route and having the various processes call that route is your best bet for inter-process communication which leverage our existing frameworks to reduce the amount of work you would have to do.

If it is just logging, simple database writes would be simpler, but if you want to do more than logging then add a REST API route.
Mike Sheen
Chief Software Engineer
Jiwa Financials

If I do answer your question to your satisfaction, please mark it as the post solving the topic so others with the same issue can readily identify the solution
User avatar
Mike.Sheen
Overflow Error
Overflow Error
 
Posts: 2444
Joined: Tue Feb 12, 2008 11:12 am
Location: Perth, Republic of Western Australia
Topics Solved: 756


Return to Technical and or Programming

Who is online

Users browsing this forum: No registered users and 28 guests

cron