Sunday, May 3, 2015

Steps to Create a Custom Software Plugin

A month ago, almost halfway into a year-long project I have been working on, I was tasked with taking some address book functionality from an application, and including the same address book functionality into the application I was working on. The address book functionality at a high level had the basic requirements: add, edit, delete and select functionality. After a code review on what had been developed for the address book and deciding what would be the easiest way to use this functionality in other applications, we made the decision that we must extract the address book functionality into its own plugin project so it could be included independently into any applications that may require its functionality. Our goal was to make a standalone plugin component that was self contained and could easily be used in any desired application or project in the future.

In a developer team meeting we came up with a few steps to make the transition to creating and using this plugin as simple and reusable as possible. I will review, break down and throw my personal experience into each step to try and make it understandable and justify why we used this process to create a decoupled plugin component:

1. Get the functionality working as expected while embedded in the application.

  •  A team member start off by creating the address book functionality inside of an application to ensure it was working as designed. I took over from the project when the majority of the functionality had been completed. Separating the functionality into a separate plugin project does not mean it cannot be updated and changed with ease. It was simply to make sure we could get the functionality of the address book working before worrying about any problems that may come with separating it out. Any problems or issues that arise from the core functionality should not be mixed with issues that may arise from separating out the plugin component. Our teams thinking is that it will save a lot of time because everything is contained in one project and there will not be the hassle of updating and building a dependency just to test out new functionality.
  • In my case I was not in charge of creating the address book functionality, only decoupling it from existing project so it could be reused. It was a good start to the process because it forced me to fully understand and code review for one of my team member’s code.

2. Create a new project and copy over everything that is used for the plugin functionality.

  • When creating a new self-contained plugin it is very similar to creating a new small project. In this scenario my team is using an MVC architecture, so in addition to the visible UI features for the address book I also had to create a model and controller. The model was responsible for maintain the list of address book entries while the controller was responsible for saving new and updated entries.
  • For consistency and maintainability of the plugin I also included the same template used in the applications so the components and workflow would work look, and act the same as they did when it was included in the application. From the applications perspective, we did not want there to be any change in experience so it all seemed to be part of the same process.
  • Creating a constructor for the component was interesting as I had to remember to keep simplicity and reusability in mind. It would be too easy to pass the entire state of the application into the plugin and use it, but then it would be very hard to use that plugin in any other scenario that may not have been as complex as the initial application. All said and done, the constructor really only contained a few variables that would populate the address book data appropriately. It was recommended that if anything “unusual” was being added to the constructor it should be an overloaded constructor, documented in the readme, and have a good reason for being there in the first place.
  • As with any theoretical vs. practical dilemma, I would be lying if I said the plugin was completely decoupled and independent from the application containing it. There was one thing that I could not figure out how to decouple in the time given to do the task, so I did the next best possible thing. I was not able to create a separate settings file in the plugin that would extend the containing applications settings file. The settings file contained timeouts and settings used by the plugin and application. I had to include the settings for the plugin in the application. However I made the plugin safe by creating a check when the plugin was used in an application. On initialization of the plugin I made sure that the settings needed were included in the application settings file, and if they were not I threw a detailed Initialization exception telling them exactly what to add in order to get the plugin to work.

3. Use notifications and events in the plugin to update listeners in the application.

  • Create notifications and events in the plugin that will update the application. The event will be responsible for storing all and only the information needed by the application when an updated is made. The notification is responsible for triggering whenever a new updated needs to be sent and received by all of its listeners.
  • A list of listeners should be added to the plugin, all of which will be updated when the notification triggers. It is the responsibility of the application to add a listener on the plugin component which will be triggered by the plugin notification events. The application can do whatever is needed to update its state with the plugin event information without the plugin needing to know.
  • The plugin is only responsible for triggering an update with the event information whenever it is notified, and the application is responsible for adding a listener to the plugin even and using its information to update its state accordingly. We use this pattern so that the application only knows about the plugin component if it is included and used in the containing application. In this scenario, the notifications and events were only triggered when an address book entry was selected, as the application used this information to store the owner of its product.

4. Remove the plugin functionality from application.

  • Similar to where I copied everything from the application to a new plugin project, now I needed to remove it all from the application. I removed anything related to the address book and their related dependencies from the project (except for the settings of course). I recompiled after I believed everything was removed to make sure there were no compile time errors.
  • At this point, I had completely removed the address book functionality from the application. I ran it confirmed the application still ran as expected without the address book functionality.

5. Include the plugin and listeners in the application.

  • Include the dependency for the new address book plugin into the application. I actually had some difficulty here as my project was trying to import two different version of the base template, and obviously failing to do so. I ended up updating the plugin dependency (I am using Maven POM files for dependency control) so that it had access to a range of version and was able to use the same version as the containing application.
  • The last (and most rewarding!) step was to create an instance of the component and add it to the view. Finally, I added a listener on the plugin component and used the events contents to update the application state whenever a new address book entry was selected.

6. Retest.

  • Since the address book plugin is now decoupled from the of application, if a bug is found or something is not working as expected that was related to the address book, it was an quick updated to fix it and rebuild it into the application. In addition I also tested the points of integration and made sure that the listeners on the application were being updated when I expected with the content I expected.
  • Testing was really the easiest part of the plugin process as it now has a very high cohesion, so finding an issue is much easier when there is far less code to dig through.

Creating this plugin was an exciting chance to step away from my everyday application development and take on a new challenge and create a process going forward with plugin components. It was nice to given guidelines by the team, but also given the freedom to develop the plugin my way to these standards. It gave me a chance to create a plugin that would be compatible with multiple applications that followed the core principles of software development: high cohesion and loose coupling.

The address book plugin project is still being fine-tuned for approval, but thanks to plugin all that is required to update new changes is a version update of the plugin in the application. I believe that we created a process that is easy to follow and maintain while still being cost effective. I hope to be able to apply this process to future similar projects that come my way.

If you have any specific questions or comments feel free to leave a comment or contact me a riley.pickerl@mnp.ca.