Context:
SW that is aimed to run on different platforms.
Problem:
Imagine that you need to write a SW that needs to run on completely different types of HW. An example for such SW can be a SW that runs in Telco products. Some of these products contain FPGAs , some of them contains Network Processors (NPU) or dedicated chip. These HW entities arrive with API that is usually wrapped by some platform code.
In addition different products might not support the same functionality - e.g. some products supports bridging functionality only, while other supports MPLS or even both.
To make things even more complicated, imagine that this SW is off the shelf product and cannot apriori tell what are the capabilities of the platform in terms of functionality.
Other considerations:
- Since the SW and the platform code might be developed by completely different companies, we would like to decouple as much as possible between the two.
- Since in some systems the acceptance tests of customers are quite long we would like to enable the replacement of SW appplication or platform code independently and allowing the customer to test only the affected code (e.g. platform code).
- Since some embedded systems have major requirements for high availability, the solution should consider an option to replace smaller modules fast enough without affecting the entire system.
- Such decoupling allows the SW to focus on the logic it should provide rather the exact details of the HW platform.
Solution:
The proposed solution is based on Plug-ins Architecture (see overview in Wikipedia http://en.wikipedia.org/wiki/Plug-in_(computing)). The binding between the host application and the Plug-ins is done on runtime allowing maximal decoupling between the host and the platform. The Plug-in (platform code) needs to know only the interface of the host (e.g. the SW application) implement it (or part of it) and the host is in-charge of dynamically locating the plug-ins and detecting what functionality it supports.
Structure:
Run-time behavior:
Initialization phase: during initialization the host maps the supported plug-ins (and potentially queries the supported functionality of each plug-in - depending on technologies).
Runtime phase: when the host needs to invoke an operation, it seeks in its internal map and invokes the Plug-in for those operations if a suitable entry is found.
Implementation:
Plugin: impelements as a dll(s) (windows) or as shared-objects (Linux) its required functionality. Let's assume one of the files is called ManageCreation.dll and it exports a function called CreateObjA(...).
Host:During initialization the host scans a well known directory (or a directory that is provided to him by configuration file for instance), and queries each DLL for its exported functions (e.g. using GetProcAddress). The host finds during the scan the ManageCreation.dll implements the interface "CreateObjA".
The host creates a map between its interfaces to the actual function that implements the interface in the plug-in.
When the host needs to invoke the creation of ObjA, it searches its internal map and invokes the corresponding function that it found in the plugin.
Implications:
- Performance: the solution is obviously slower than static binding, however the overhead of the dynamic binding is negligible.
- High Availability: a system can replace dynamic libraries easily without affecting the entire system. It requires the host and plugins to agree on a scheme that changes the mapping to from an old implementation of the dll to the new one.
References:
"Patterns of Enterprise Application Architecture", Martin Fowler, 2002. Addision Wesley publication. p. 499.


No comments:
Post a Comment