A_Glance_At_DependencyManager.odp

Download Report

Transcript A_Glance_At_DependencyManager.odp

A glance at Apache Felix
Dependency Manager 4.0
Pierre De Rop
(adapted from “dependencies, dependencies,
dependencies”, by
Marcel Offermans:
http://www.slideshare.net/marrs/dependenciesdependencies-dependencies)
Agenda
●
●
●
●
●
●
●
●
Introduction
Basic Concepts
Dependencies
OSGi Patterns
Custom Dependencies
Add-Ons
What's new in Dependency Manager
4.0 ?
Wrap-Up
Introduction
Framework
●
Osgi Framework Layers
Introduction
Service Dependencies
●
Framework: ServiceListener
• notification whenever there is a change
in the service registry
• but: only changes that occur while you
are listening
●
Utility: ServiceTracker
• solves the listening issue
• adds ability to customize services
Introduction
Problem
●
Problem when using low level OSGi API for
dependencies management
• using the framework supplied tooling, you
are dealing with dependencies at a very
low level
• a lot of boiler plate code is needed to
implement real world scenarios
• there are very few design patterns that
deal with composing an application based
on services
Introduction
Existing Solutions
●
Declaring your dependencies with
• Service Binder
• Dependency Manager
• Declarative Services
• iPOJO
• Blueprint
• Many more ...
Introduction
Dependency Manager
●
Subproject of Apache Felix
●
Nearing a 4.0 release
●
Some interesting new features
• AutoConfig on Iterable/Map.
• ConfigurationDependency on a Factory
Object
• Total refactoring with a new Thread Model
•
Lock-Free implementation
•
Support for parallel Components
•
Basic Concepts
Service Components
●
OSGi
Service Components:
• Component: class that implements
certain behaviour (a POJO)
• Dependency: something our component
needs or wants (ie. A Service)
• Service: OSGi service interface(s) under
which our component is registered
Basic Concepts
What is
Dependency Manager ?
●
DM is a Fluent Java API to declare
dependencies
• No XML.
●
Using a Java API has many advantages:
• less error prone because of syntax and
compile time checking
• refactoring support, completion
• very readable through fluid API
• everything in one place, no magic
Basic Concepts
Hello
World Component with DM
Import-Package: org.apache.felix.dm;version=”[4.0, 5.0)”
Bundle-Activator: hello.Activator
package hello;
public class Activator extends DependencyActivatorBase {
public void init(BundleContext ctx, DependencyManager dm) {
dm.add(createComponent()
.setImplementation(HelloWorld.class));
}
}
Basic Concepts
Basic Use Cases
Declare a Component:
package hello;
public class Activator extends DependencyActivatorBase {
public void init(BundleContext ctx, DependencyManager dm) {
HelloWorld helloWorld = new HelloWorld();
dm.add(createComponent()
.setImplementation(helloWorld));
}
}
Declare it lazily:
package hello;
public class Activator extends DependencyActivatorBase {
public void init(BundleContext ctx, DependencyManager dm) {
dm.add(createComponent()
.setImplementation(HelloWorld.class));
}
}
Basic Concepts
Component Lifecycle
●
:Optional Lifecycle Callbacks of a Component
public
void
void
void
void
void
}
●
●
class HelloWorldLifeCycle {
updated(Dictionary conf) {} // if a Conf Dependency is used
init() {} // Add here some extra dependencies dynamically
start() {} // like DS @Activate
stop() {} // like DS @Deactivate
destroy() {} // Component is destroyed (bundle stopped)
setCallbacks(“init”, “start”, “stop”, “destroy”):
setCallbacks(inst, “init”, “start”, “stop”,
“destroy”);
• To invoke methods on a given object
instance
Basic Concepts
Declaring a Service
●
Component.setInterface(...)
• allows you to declare multiple services
• allows you to specify service properties
Hashtable<String, Object> props = new Hashtable<>() {{
put(“foo”, “bar”);
put(Constants.SERVICE_RANKING, 20);
}};
manager.add(createComponent()
.setInterface(LogService.class.getName(), properties)
.setImplementation(MyLogService.class));
Basic Concepts
Declaring a Dependency
dm.add(createComponent()
.setImplementation(HelloWorld.class);
.add(createServiceDependency().setService(LogService.class))
);
●
●
Inject dependencies on class fields
•
Of the same Service type, or with an Iterable<Service>, or with
a Map<Service, Dictionary> type.
•
Use NullObjects for optional dependencies (only applicable for
class fields; use optional callbacks or Iterable fields to turn off
Null Objects)
•
Inject other “OSGi” instances (BundleContext)
Inject dependencies using callbacks
•
setCallbacks(...)
Basic Concepts What happens
during Component activation ?
●
When you add a Component to a DependencyManager:
• The dependencies are tracked:
•
if one dependency needs the component instance, then create it and inject
OSGI objects
• When all required dependencies are satisfied:
●
•
Instantiate the Component instance, if not already done and inject OSGI
objects
•
Inject AutoConfig dependencies. Optional dependencies on class fields are
injected with NullObjects. If you don't want NullObjects, use callbacks for
optional dependencies.
•
Invoke required dependency callbacks
Invoke Component.init where additional dependencies can be added
• The Component itself is passed to the init() method, if this one declares a
Component param
●
If more extra dependencies have been added from Component.init():
• Wait for extra dependencies
Agenda
●
●
●
●
●
●
●
Overview
Dependencies
Design Patterns
Custom Dependencies
Add-Ons
What's new in Dependency Manager
4.0 ?
Wrap-Up
Dependencies
Different Types
●
●
●
●
Service Dependencies
Configuration Dependencies
Bundle Dependencies
Resource Dependencies
Dependencies
Service Dependency
A simple Service Dependency on a
Translator service, injected with
callbacks
● Dependencies
public
class Activator extends
DependencyActivatorBase
{
are
optional
by
default
public void init(BundleContext ctx, DependencyManager dm)
throws
{
(useException
setRequired(boolean
method) to
dm.add(createComponent()
make
dependencies required)
.setImplementation(DocumentTranslator.class)
●
.add(createServiceDependency()
.setService(Translator.class)
.setRequired(true)
.setCallbacks("added", 'change”, "removed")
)
);
}
}
Dependencies
Configuration
●
Based on Configuration Admin
•
designed for required dependencies. If your configuration
is optional, just declare your component as an
ManagedService.
•
•
service.pid to identify the configuration
allows you to only accept *valid* configurations, else
public class Activator extends DependencyActivatorBase {
throws an exception.
public void init(BundleContext ctx, DependencyManager m) {
m.add(createComponent()
.setImplementation(MyConfigurableComponent.class)
.add(m.createConfigurationDependency().setPid(“com.acme.MyPid”));
}
}
public class MyConfigurableComponent {
void updated(Dictionary conf) {
if (! isValid(conf)
throw new IllegalArgumentException(“invalid configuration”);
}
}
Dependencies
Bundle Dependencies
●
Depend on a bundle:
• in certain states
• with certain manifest entries
public class
Activator
extends DependencyActivatorBase
{
Bundle
instance
can
be
injected
•
public void init(BundleContext ctx, DependencyManager dm) {
dm.add(createComponent()
.setImplementation(MyBundleTracker.class)
.add(m.createBundleDependency()
.setRequired(true)
.setStateMask(Bundle.ACTIVE)
.setFilter("(DependencyManager-Component=*")")
.setCallbacks("add", "remove")));
}
}
public class MyBundleTracker {
void add(Bundle b) {}
Void remove(Bundle b) {}
}
Dependencies
Resource Dependencies
●
●
Resources are modeled as URLs
Are provided by a repository
• another bundle
• an Eclipse workspace (bndtools
repository)
• some external source
●
Filter on host, port, protocol, path
and URL
Dependencies
Resource Dependency
Example
public void init(BundleContext context, DependencyManager m) {
m.add(createComponent()
.setImplementation(ResourceConsumer.class)
.add(createResourceDependency()
.setFilter("(&(path=/path/to/*.txt)(host=localhost))")
.setCallbacks("add", "remove")));
}
class ResourceConsumer {
public void add(URL resource) {}
public void remove(URL resource) {}
}
Take a look at the ResourceDependencyTest for a complete example
Dependencies
Dynamic
Dependencies from init callback
public void init(BundleContext context, DependencyManager dm) {
dm.add(createComponent().setImplementation(MyComponent.class)
.add(createConfigurationDependency().setPid(“MyPid”)));
}
public class MyComponent{
OtherService m_otherService; // injected after init
void updated(Dictionary conf) {
m_conf = conf; }
void init(Component c) {
DependencyManager dm = c.getDependencyManager();
c.add(dm.createServiceDependency()
.setService(OtherService.class,
"(id=" + m_conf.get(“id”) + ")")
.setRequired(true));
}
}
Agenda
●
●
●
●
●
●
●
Overview
Dependencies
OSGi Patterns
Custom Dependencies
Add-Ons
What's new in Dependency Manager
4.0 ?
Wrap-Up
OSGi Patterns ...
with DependencyManager
●
●
●
●
●
●
Whiteboard Pattern
Null Object Pattern
Singleton Service
Aspect Service
Adapter Service
Resource Adapter Service
OSGi Patterns
Whiteboard/1
●
“Don’t call us ... we’ll call you”
• Decouple two parties from each other
(consumer/provider)
• If you want to provide something to
another service, just register your
service, the other parties will find your
service from the registry.
• Example: Instead of registering an
HttpServlet into an HttpService, just
register the HttpServlet inside the
OSGi Patterns
Whiteboard/2
●
Example: provide an HttpServlet directly in the
registry
package manageragent;
public class Activator extends DependencyActivatorBase {
public void init(BundleContext ctx, DependencyManager m) {
Hashtable props = new Hashtable();
Props.put(“alias”, “/hello”);
Props.put("init.message”, “hello world”);
dm.add(createComponent()
.setImplementation(MyHttpServlet.class)
.setInterface(Sevlet.class.getName(), props));
}
}
OSGi Patterns
Objects
●
●
●
Null
An object that implements a certain
interface, can be safely invoked and
does nothing
Only used for optional dependencies
injected on class fields.
If you don't want to use NullObjects for
optional dependencies, you can define
callbacks, or you can use a class field
with an Iterable type, and from your start
method, you can then inspect the
OSGi Patterns
“Singleton”
Service
●
●
Publishes a component as a service
Ties its life cycle to that of its
dependencies
OSGi Patterns
Service
●
“Adapter”
Works on existing services
• Adapts them, publishes a different
service for each existing one
• An Adapter is a factory: it creates an
adapter component for each existing
adapted service it finds from the OSGi
service registry
OSGi Patterns
“Adapter”
Example 1
You have multiple instances of a given
ExistingService
● For each one, you want to create an
HttpServlet to create an http
administrative endpoint for it.
public class Activator extends DependencyActivatorBase {
● The HttpServlet is injected with the
@Override
public void init(BundleContext context, DependencyManager dm)
ExistingService
(and service properties)
dm.add(createAdapterService(ExisringService.class,
null,
“setExistingService”, null, null)
using
the “setExistingService method.
.setImplementation(ExistingServiceServlet.class)
●
.setInterface(HttpServlet.class.getName(), null));
}
{
OSGi Patterns
Adapter
Example 1 (follow up)
●
The HttpServlet that adapts an
ExistingService
•
it gets injected with the adapted service
class ExistingServiceServlet extends HttpServlet
(ExistingService)
public
{
ExistingService existingService;
Dictionary
props; // service
of the ExistingService
It dynamically
sets properties
the HttpServlet
service properties
•
void setExistingService(ExistingService adaptee, Dictionary props) {
this.adaptee = adaptee; this.props = props;
}
public void start(Component c) {
String name = props.get(“name”);
Hashtable props = new Hashtable();
props.put(“alias”, “/admin/” + name);
props.put("init.message”, “Administration for “ + name);
c.setServiceProperties(props); // set servlet service properties
}
...
}
OSGi Patterns
“Adapter”
Example/2
●
Advanced example usage of an adapter
• We have some “Device” Services (with
a given device id)
• For each “Device”, we have an
associated “DeviceParameter Service
(with the same id of the
corresponding Device)
●
We want to adapt each pair of
Device/DeviceParameter (having the
same id) to a “DeviceAccess” Service
that holds a pair of
OSGi Patterns
“Adapter”
Example/2 (DeviceAccess)
●
Make the “union” of two pair of services
DeviceAccess (id=1)
Device (id=1)
adapted
Device getDevice();
DeviceParam getParam();
DeviceParam (id=1)
DeviceAccessTracker
Device (id=2)
adapted
DeviceParam (id=2)
DeviceAccess (id=2)
Device getDevice();
DeviceParam getParam();
OSGi Patterns
“Adapter”
Example/2
First, we adapt a Device Service to the
DeviceAccess Service
● For each Device, a DeviceAccess
Service will be created by the DM
Adapter
●
from the
DeviceAccess.init
publicThen
class Activator
extends
DependencyActivatorBase {
@Override
method, we'll dynamically add a
public void init(BundleContext context, DependencyManager dm)
dm.add(createAdapterService(Device.class,
null)
dependency
on
the
corresponding
.setImplementation(DeviceAccessImpl.class)
.setInterface(DeviceAccess.class.getName(),
null));
DeviceParameter
service (having the
same id).
}
●
{
OSGi Patterns
“Adapter”
Example/2
●
The DeviceAccessImpl.init method adds a dynamic dependency on the
DeviceParameter with the same id of the adapted Device
public class DeviceAccessImpl implements DeviceAccess {
volatile Device device;
// injected before init
volatile DeviceParameter param; // injected after init
void init(Component c) {
// Dynamically add an extra dependency on a DeviceParameter.
DependencyManager dm = c.getDependencyManager();
c.add(dm.createServiceDependency()
.setService(DeviceParameter.class, "(device.id=" +
device.getDeviceId() + ")")
.setRequired(true));
}
public Device getDevice() { return device; }
public DeviceParameter getDeviceParameter() { return param; }
}
OSGi Patterns
Service
●
Works at the service level
“Aspect”
• “intercepts” certain services
• can be chained based on rankings
• completely dynamic
OSGi Patterns
“Aspect”
Example (Repository)
●
●
The RepositoryCache Aspect provides a cached access to the
“Repository” Service
When the aspect is registered, the client is dynamically rebound with
the aspect, which replaces the original Repository Service
public class Activator extends DependencyActivatorBase {
@Override
public void init(BundleContext context, DependencyManager dm)
dm.add(createAspectService(Repository.class, null, 10)
.setImplementation(RepositoryCache.class));
}
Public class RepositoryCache implements Repository {
volatile Repository repo; // Injected
String document;
synchronized String get() {
Return document == null ? (document = repo.get()) : document;
}
...
}
OSGi Patterns
“Resource
Adapter”
●
Adapts resources (instead of services)
• Allows you to expose the behavior of
certain resources instead of their
“inner guts”
Agenda
●
●
●
●
●
●
●
Overview
Dependencies
OSGi Patterns
Custom Dependencies
Add-Ons
What's new in Dependency Manager
4.0 ?
Wrap-Up
Custom Dependencies
●
Dependency Manager is extensible
with custom dependencies:
• depend on time of day
• depend on some custom instance
/condition (start level, app state)
• depends on the presence of a given
file
●
Check this example:
•
http://svn.apache.org/viewvc/felix/sandb
ox/pderop/dependencymanager-
Agenda
●
●
●
●
●
●
●
Overview
Dependencies
Design Patterns
Custom Dependencies
Add-Ons
What's new in Dependency Manager
4.0 ?
Wrap-Up
Add-ons
●
Shell
“dm wtf” command to make diagnostics
• Loop detections
• Root cause for missing dependencies
• Missing configurations
• Bundles not started/unresolved
●
“dm top” to detect top consuming
components
Add-ons
Shell Options
dm - List dependency manager components
scope: dependencymanager
flags:
compact, cp
Displays components using a compact form
nodeps, nd
Hides component dependencies
notavail, na
Only displays unavailable components
stats, stat, st
Displays components statistics
wtf
Detects where are the root failures
options:
bundleIds, bid, bi, b
<List of bundle ids or bundle symbolic names to
display (comma separated)> [optional]
componentIds, cid, ci
<List of component identifiers to display (comma
separated)> [optional]
components, c
<Regex(s) used to filter on component implementation class
names (comma separated), can be negated using "!" prefix> [optional]
services, s
<OSGi filter used to filter some service properties> [optional]
top
<Max number of top components to display (0=all)> This command displays components
callbacks (init/start) times> [optional]
Add-ons
●
Dependency Filter
indices
Filter indices:
• Skip the service registry for ultra fast
filtering (useful if you have many
service dependencies)
●
Example:
•
To set an index on objectClass, speeding up lookups for
any filter that contains an objectClass in its filter (all
regular services do), you can set the following system
property:
•
-Dorg.apache.felix.dependencymanager.filterindex=objectClass
Agenda
●
●
●
●
●
●
●
Overview
Dependencies
OSGi Patterns
Custom Dependencies
Add-Ons
What's new in Dependency Manager
4.0 ?
Wrap-Up
What's new in DM 4.0 ?
New Thread Model
●
New strong thread model
•
All Component events are handled serially using a lockfree serial queue: no lock used (no risks of deadlocks)
•
No Concurrency for lifecycle callbacks and dependency
injection. No need to synchronize dependency callbacks
(but still need to use volatile or safe data structures if
services are accessed from separate threads).
•
Optionally, Components can be managed and activated
concurrently for high performance. However, Components
lifecycle callbacks and dependency callbacks are not
invoked concurrently for a given Component (a Component
remains “single threaded”, but multiple components can be
managed/activated concurrently with respect to each
other)
•
The thread model is described in the javadoc of the
org.apache.felix.dm.ComponentExecutorFactory interface
What's new in DM 4.0 ?
Component events are handled
serially
Register(Service1)
Register(Service2)
Thread1
Thread2
Serial
Queue
Component
Service2 actually handled
by Thread1, not
Thread2
Lock-Free Serial Queue (Executor = current thread)
• No synchronization, no locks (and no dead-locks)
•
•
Component callbacks are called serially (no multithreading for callbacks)
The queue jobs are executed by the first thread which triggers an event.
Lock Free Serial Queues
Thread1 Register(Service1)
Register(Service2) Thread2
Serial
Queue
Component
Service2 actually handled
by Thread1, not
Thread2
Lock-Free Serial Queue (Executor = current thread)
• No synchronization, no locks (and no dead-locks)
•
•
Component callbacks are called serially (no multithreading for callbacks)
The queue jobs are executed by the first thread which triggers an event.
Concurrent Serial Queues
Register(Service1) Register(Service2)
Serial
queue
Queue jobs are executed in FIFO,
but multiple queues are handled using a
shared thread pool
uses
Component1
Queues
ThreadPool
Serial
queue
uses
Component2
Lock-Free Serial Queue (Executor = shared threadpool)
•
Component events are handled in FIFO order, using a per-Component
serial queue
•
A Component serial queue will handle one job at a time, but independent
serial queues may each execute their job concurrently with respect to each
other, using a shared thread pool
•
Component are managed/activated concurrently but each Component
remains “single threaded” (callbacks are executed one at a time)
What's new in DM 4.0 ?
Enabling parallelism/1
●
Configure DM to use a ComponentExecutorFactory for all
components (except for the one which provides the executor factory):
-Dorg.apache.felix.dependencymanager.parallel=!management,*
●
And register your ComponentExecutorFactory
package management;
import org.apache.felix.dm.ComponentExecutorFactory;
public class Activator extends DependencyActivatorBase {
public void init(BundleContext ctx, DependencyManager m) {
dm.add(createComponent()
.setImplementation(DMExecutorProvider.class)
.setInterface(ComponentExecutorFactory.class.getName(),null));
}
}
What's new in DM 4.0 ?
Enabling parallelism/2
●
The DM ComponentExecutorFactory provider returns
a shared threadpool from the “getExecutorFor”
method
package management;
import org.apache.felix.dm.ComponentExecutorFactory;
public class DMExecutorProvider implements ComponentExecutorFactory {
final Executor sharedThreadPool = Executors.newFixedThreadPool(10);
public Executor getExecutorFor(Component c) {
return sharedThreadPool;
}
}
What's new in DM 4.0 ?
AutoConfig Iterable field
●
●
AutoConfig dependencies can be applied on Iterable<Service>
fields in order to be able to traverse currently injected services
safely. The Iterable must be parameterized with the Service
type.
Example:
dm.add(createComponent()
.setImplementation(HelloWorld.class);
.add(createServiceDependency().setService(LogService.class))
Class HelloWorld {
// allocated and injected by DM. can be safely traversed.
volatile Iterable<LogService> loggers;
void log(String msg) {
For (LogService log : loggers) {
log.log(LogService.LOG_INFO, msg);
}
}
}
What's new in DM 4.0 ?
AutoConfig Map field
●
●
AutoConfig dependencies can be applied on a field with a Map<Service,
Dictionary> type, allowing to traverse currently injected services safely,
including service properties.
The Map must be traversed using the Map.Entry iterator. See javadoc for
DependencyManager.setAutoConfig()
dm.add(createComponent()
.setImplementation(HelloWorld.class);
.add(createServiceDependency().setService(LogService.class))
Class HelloWorld {
volatile Map<LogService, Dictionary<String, String>> loggers;
void log(String msg, String loggerType) {
For (Map.Entry<LogService, Dictionary>> e : loggers.entrySet) {
if (loggerType.equals(e.getValue().get(“type”)) {
e.getKey().log(LogService.LOG_INFO, msg);
}
}
}
}
●
What's new in DM 4.0 ?
Inject Configuration on
Configuration
can be injected oncallback
a separate callbackinstance
instance, like a
separate
CompositionManager for example
public class Activator extends DependencyActivatorBase {
public void init(BundleContext ctx, DependencyManager m) {
CompositionManager compositionMngr = new CompositionManager();
m.add(createComponent()
.setFactory(compositionMngr, "create")
.setComposition(compositionMngr, "getComposition")
.add(createConfigurationDependency()
.setPid(CompositionManager.class.getName())
.setCallback(compositionMngr, "updated")));
}
}
public class CompositionManager {
private Dictionary<String, String> m_conf;
public void updated(Dictionary conf) throws Exception {
m_conf = conf;
}
Object create() {
// Use configuration to create the composition of objects
}
}
What's new in DM 4.0 ?
Miscellaneous
●
●
●
●
●
Logs unsuccesful field/callbacks injections
Added propagate flag for Service Adapters
Replaced Component.getService methods by:
•
Object[] Component.getInstances(); // returns all
component instances
•
<T> T Component.getInstance(); // returns first
component instance
Added a “top” command in the shell, to display all top
components sorted by their init/start elapsed time.
Added benchmarks app which can be run under bndtools
Agenda
●
●
●
●
●
●
●
Overview
Dependencies
OSGi Patterns
Custom Dependencies
Add-Ons
What's new in Dependency Manager
4.0 ?
Wrap-Up
Wrap-Up
●
Import DM4 in bndtools from:
•
●
Check samples:
•
●
https://svn.apache.org/repos/asf/felix/trunk/depend
encymanager/
https://svn.apache.org/repos/asf/felix/trunk/dependencym
anager/org.apache.felix.dependencymanager.samples/REA
DME.samples
Check benchmark tool (requires
eclipse/bndtools):
•
https://svn.apache.org/repos/asf/felix/trunk/dependencym
anager/org.apache.felix.dependencymanager.benchmark/R
EADME