Hi Kai,
The boostrap API and the abstraction it
provides is something we need to introduce in
order to make our services framework independent
and avoid a vendor lock-in.
To be more exact, let me use the example.
Imagine that we implement a device management
service based on Leshan (kind of thing we are
doing in Rhiot). The service should be provided in
a form of the library, which can be started and
stopped.
DeviceManagementService deviceService = new DeviceManagementService();
deviceService.start();
deviceService.stop();
"Start"
method starts embedded Leshan server,
initialized database connection pool (used by
Leshan to store devices in the registry),
connects to the cache cluser (like Hazelcast or
Infinispan, used to cache devices information
and metrics, for performance reasons) and
performs all the other kind of an initialization
needed by the device service. When a service is
supposed to be shut down, we call the "stop"
method. It makes sure that all of those
resources have been properly closed. Also the
start/stop sequence has to be properly ordered,
not accidental. Our DeviceManagementService
can be used out of the box without any
additional configuration - it will use default
settings, default persistence providers, etc.
Now imagine
that the company using
our DeviceManagementService as a part of their
IoT cloud offering, would like to add some
non-trivial extension to it. Our platform be
easily extensible as this is required for its
wider adoption. So for example:
- company
Foo would like create an extra module based on
Apache Camel and its SAP connector, so the
information about the devices is
immediately available in the SAP system
- company
Bar would like to provide its own Leshan devices
registry implementation
- company
Baz would like to connect all the events related
to the devices to its Apache Spark cluster and
perform real-time analysis on it, then perform
some actions on the Leshan server based on the
data analysis
In all
those cases we need to:
a) create
proper callback interfaces in DeviceManagementService
that could be implemented to achieve
customization on the level of the client code
b) client
has to provide the customized callback code to
the classpath of the application
c) somehow
wire this custom code to the core of our service
The thing
is that c) is very different depending on what
kind of application framework we would like to
use. For example if we are using Spring Boot,
the complete customized application for company
Baz could look like:
public
class BigDataAwareDeviceManagement {
@Bean(init = "start", destroy = "stop")
DeviceManagementService deviceManagementService(DeviceEventCallback deviceEventCallback) {
return
new DeviceManagementService(deviceEventCallback);
}
@Component
static
class SparkDeviceEventCallback
implements DeviceEventCallback
{...}
}
However the
same application written in OSGi will be much
different. For example DeviceManagementService
could be deployed as a one OSGi bundle, while
the SparkDeviceEventCallback
could be a second bundle with OSGi service
exposed.
If we would
like to make DeviceManagementService
framework-agnostic, we should relay on the
vendor-neutral API when accessing
the collaborators (like DeviceEventCallback
implementations). We could name it BeanRegistry
and it could be used inside DeviceManagementService as follows:
class DeviceManagementService
{
DeviceEventCallback deviceEventCallback;
...
void
start() {
deviceEventCallback
= beanRegistry.beanByType(DeviceEventCallback.clas);
}
}
Then we
could provide various implementations of the BeanRegistry, like
SpringBeanRegistry
or OsgiServiceBeanRegistry. With
this approach, we can access beans implementing
the extensions to our services in a
framework-agnostic way.
The same
principle applies to the configuration
management. OSGi uses Configuration Admin
Service, Spring Boot uses its properties/YAML
files, Vert.x system properties and so forth. If
we would like to make our services configurable,
we should provide configuration access
abstraction, which in turn should delegate to
the proper configuration management system
(OSGi, Spring, etc):
class DeviceManagementService
{
private static
final int DEFAULT_TIMEOUT = 5000;
...
void start() {
int
maxDevicesCacheSize =
configurationResolver.intProperty("devices.cache.size.max", DEFAULT_TIMEOUT);
}
}
We can also
decide to create a library, not a service
implementation runnable out-of-the-box.
In such case it will be an
end-user responsibility to assemble the service
using our library with the gazillions of setters
and getters. Which considering all the possible
object collaborators that will be included in
service like device management, will soon become
a configuration nightmare for the end-users. I'm
strong believer that the developer adoption is
one of the key factors when designing the
middleware, so I would prefer to avoid following
pure-library path and provide a "boostrap API"
instead.
Does it
make sense to you?
Cheers!