GIANGPZO
GIANGPZO

Reputation: 390

How to monitor Plugin work

My team is developing a Plugin project, in which Host application coordinates Plugins work (each Plugin has a specific function and will be executed on a seperate Thread). I'm writing Host application and define IPlugin interface; other people will develop Plugins.

The problem is: how to know when plugin completed or thrown exception. I have a solution is using event and delegate to let Plugins callback Host application, but I think this approach is not really good. Because if develop Plugin person implemented my IPlugin interface but forget writing raising event code.

In this case, the Plugin can be plug in my host application but my host application can not know when this Plugin completed or thrown exception or other communication, that's very bad.

My code likes this:

IPlugin.cs:

public delegate void OnFinishDelegate(IPlugin p, AMessageEventArgs e);
public delegate void OnExceptionDelegate(IPlugin p, AMessageEventArgs e);
public interface IPlugin
{
    event OnFinishDelegate OnFinish;
    event OnExceptionDelegate OnException;
    void DoWork();
}

EventArgs:

public class AMessageEventArgs:EventArgs
{
    private string message;
    public string Message
    {
        get { return message; }
        set { message = value; }
    }
}

Host Application:

static void Main(string[] args)
        {
            // ...ignore load plugin code
            //
            IPlugin p = new Plugin();            

            // register event with IPlugin
            p.OnFinish += new OnFinishDelegate(OnFinishHandler);
            p.OnException += new OnExceptionDelegate(OnExceptionHandler);

            // let plugin do its work on a subthread
            Thread t = new Thread(p.DoWork);
            t.Start();

            // and then main continue do other work...
        }

        // if plugin throw exception, in the host application will 
        // handle this exception...
        private static void OnExceptionHandler(IPlugin p, AMessageEventArgs e)
        {
            // in here will process exception of plugin...
        }

        // if plugin completed its work, it will use OnFinish event to 
        // notify to host application
        private static void OnFinishHandler(IPlugin p,AMessageEventArgs e)
        {
            // in here will process completed work event
        }

And I expect Plugin code will like below:

public class Plugin:IPlugin
    {
        // use event to callback host application
        public event OnFinishDelegate OnFinish;
        public event OnExceptionDelegate OnException;

        // Create an EventArgs
        AMessageEventArgs e = new AMessageEventArgs();
        public void DoWork()
        {
            try
            {
                // execute update data
                UpdateData();
            }
            catch (Exception ex)
            {
                e.Message = ex.Message;

                // if have any exception, Plugin will raise an event in host application
                // but if developer forget write below three lines of code, my host application will
                // out of control.
                if (OnException!=null)
                {
                    OnException(this,e);
                }
            }

            // if have no exception, Plugin will use OnFinish event to talk to host application
            // to know that this plugin completed its work
            // but if developer forget write below three lines of code, my host application will
            // out of control.
            if (OnFinish!=null)
            {
                OnFinish(this,e);
            }
        }

How to resolve this problem?

Additional problem: Did my IPlugin interface define well? if not well, can you advise me to improve this interface.

Thanks you!

Upvotes: 1

Views: 166

Answers (3)

GIANGPZO
GIANGPZO

Reputation: 390

I've just read HeadFist Design pattern and discovered a solution: Factory Method.
In my case, I should use abstract class instead interface (IPlugin). In this abstract class, I define a Operate() method that will report result(completed result or exception result) to my host application. The second method is DoWork() method that the third party programmer can writing his function and don't care about OnComplete or OnException event.
My code like below:
Abstract class:

public delegate void OnFinishDelegate(APlugin p, AMessageEventArgs e);
    public delegate void OnExceptionDelegate(APlugin p, AMessageEventArgs e);
    public abstract class APlugin
    {
        public  event OnFinishDelegate OnFinish;
        public event OnExceptionDelegate OnException;

        AMessageEventArgs e = new AMessageEventArgs();
        public void Operate()
        {
            try
            {
                // implement plugin work
                // we don't care how does the third party programmer write his Plugin program
                DoWork();

                // if DoWork() completed , it will raise an OnFinish event
                // in my host application
                e.Message = "Completed";
                if (OnFinish != null)
                {
                    OnFinish(this, e);
                }
            }
            catch(Exception ex)
            {
                // if DoWork() throw exception, it will raise an OnException event
                // in my host application
                e.Message = ex.Message;
                if (OnException!=null)
	            {
		             OnException(this,e);
	            }
            }            
        }

        // In here, the third party programmer will override this DoWork() method
        private abstract void DoWork();
    }

Host applicaton code:

static void Main(string[] args)
        {
            // ...ignore load plugin code
            //
            APlugin p = new Plugin();

            // register event with IPlugin
            p.OnFinish += new OnFinishDelegate(OnFinishHandler);
            p.OnException += new OnExceptionDelegate(OnExceptionHandler);

            // let plugin do its work on a subthread
            Thread t = new Thread(p.Operate);
            t.Start();

            // and then main continue do other work...
        }

        // if plugin throw exception, in the host application will 
        // handle this exception...
        private static void OnExceptionHandler(APlugin p, AMessageEventArgs e)
        {
            // in here will process exception of plugin...
        }

        // if plugin completed its work, it will use OnFinish event to 
        // notify to host application
        private static void OnFinishHandler(APlugin p, AMessageEventArgs e)
        {
            // in here will process completed work event
        }

>

And Plugin code (must like below):

class Plugin:APlugin
    {
        public override void DoWork()
        {
            // in here, the third party programmer can write anything
            // for example: update data in database
            UpdateData();
        }

        private void UpdateData()
        {
            // do update data
        }
    }

Upvotes: 0

BJ Myers
BJ Myers

Reputation: 6813

You've correctly identified the problem with the event model in the interface - when a plugin implements the IPlugin interface, they have to define the OnException event, but there is nothing requiring them to use it.

Define your IPlugin interface with a single DoWork method, and allow the standard exception pattern to report successful completion or an error status. If the DoWork method returns normally, the plugin is finished (no need for an OnFinish event). If DoWork throws an exception, your main application can catch the exception and deal with it (no need for an OnException event).

I would recommend looking into the Task Parallel Library for executing the plugins. It provides more control than using threads directly, including exception handling.

Upvotes: 2

Blorgbeard
Blorgbeard

Reputation: 103467

I don't think the plugins need to know they're running in separate threads. I'd just have:

interface IPlugin { 
    void DoWork(); 
}

And then the host program would deal with catching exceptions and all the threading, something like:

Thread t = new Thread(() => {
    try {
        plugin.DoWork();
    }
    catch (Exception ex) {
        // handle exception or save it to handle in the main thread
    }
});
t.Start();

Upvotes: 3

Related Questions