osgi bootcamp - part 2
TRANSCRIPT
OSGi Bootcamp Day 2
Jan Willem Janssen
Agenda
• OSGi design patterns
• Managing service dependencies
• more convenient options for easily managing service dependencies
• The compendium
• some sample services, sending and receiving events with EventAdmin and using and implementing the Log Service
Design Patterns for OSGi
• Null-object pattern
• Whiteboard pattern
• Singleton services
• Aspect services
• Adapter services
• Resource adapter services
Null Object Pattern
An object that implements a certain interface, can be safely invoked and does nothing
http://en.wikipedia.org/wiki/Null_Object_pattern
Whiteboard Pattern
“don’t call us... we’ll call you”
http://www.osgi.org/wiki/uploads/Links/whiteboard.pdf
Singleton Services
• Binds logic in a single instance
get()getVersions()get(version)store(Document)
Document Repository
Singleton
Storagerequires
Singleton Services
manager.add(createComponent() .setInterface(DocumentRepository.class.getName(), null)
.setImplementation(DocumentRepositoryImpl.class)
.add(createServiceDependency()
.setService(Storage.class)));manager.add(createComponent()
.setInterface(Storage.class.getName(), null) .setImplementation(FileBasedStorage.class)
);
Singleton Services
• What if I don’t want singletons?
• org.osgi.framework.ServiceFactory
• org.osgi.framework.PrototypeServiceFactory
• the consumer of the service is oblivious to this!
• Transparently inject an interceptor service
• "in front of" all services matching a filter
Aspect Services
get()getVersions()get(version)store(Document)
Repository CacheAspect
get()getVersions()get(version)store(Document)
Repository
intercepts
Aspect Services
manager.add(createAspectService(DocumentRepository.class,
null /* filter */, 10 /* ranking */, null /* autoConfig */ ).setImplementation(DocumentRepositoryCache.class));
Adapter Services
• Start an instance of the adapter service for any
• "adaptee" service matching a filter
getCacheHits()setSize()setTTL()flush()
Repository CacheManageable
Adapter
get()getVersions()get(version)store(Document)
Repository Cache
adapts
Adapter Services
manager.add(createAdapterService(MyService.class, null),
.setInterface(MyAdapterInterface.class.getName(), null /* filter */),
.setImplementation(AdapterImpl.class)) );
Resource Adapters
• Start an instance of a Resource Driven Service for all resources matching a filter
• resources: any valid URL
play()pause()stop()
Audio TrackResourceAdapter
MP3Fileadapts
Resource Adapters
manager.add(createResourceAdapterService(
"(path=*.mp3)" /* filter */, false /* propagate */, null /* callbackInstance */, “changed" /* callbackMethod */) .setInterface(AudioTrack.class, null) .setImplementation(MP3AudioTrack.class)));
Dependency Management
• Components need other services before:
• they can publish their own service
• they can do their work
• Complex dependency graphs
• hard to manage by hand
• lots of boilerplate code
Dependency Management
Bundle
Component StorageService
LogService
En5tyStore
Dependency Libraries
• Blueprint Services
• spring inspired
• XML
• Declarative Services
• OSGi standard
• annotation/XML based
Dependency Libraries
• iPOJO
• similar to DS
• extends OSGi service lifecycle
• Dependency Manager
• annotation/API-based
• runtime control
Dependency Manager
• API based dependency management
• optional and required dependencies
• supports extensible types of dependencies:
• service dependency
• configuration dependency
• change dependencies dynamically at runtime
Dependency Manager
• Extend DependencyActivatorBase, override init() and destroy()
• Talk to the DependencyManager to:
• create a new service object and:
• set any service interface and properties;
• set the implementation class;
• add any service or configuration dependencies and:
• make it required or optional
• refer to a specific service or configuration
public class SampleComparator implements Comparator { private volatile LogService m_log; public int compare(Object o1, Object o2) { return o1.equals(o2) ? 0 : -1; }
void start() { m_log.log(LogService.LOG_INFO, "Hello there!"); }}
Dependency Manager - Example
public class Activator extends DependencyActivatorBase { public void init(BundleContext context, DependencyManager dm) { dm.add( createComponent() .setInterface(Comparator.class.getName(), null) .setImplementation(SampleComparator.class) .add(createServiceDependency() .setService(LogService.class) .setRequired(false))); }}
Dependency Manager - Example
@Componentpublic class SampleComparator implements Comparator { @ServiceDependency private volatile LogService m_log; public int compare(Object o1, Object o2) { return o1.equals(o2) ? 0 : -1; }
@Start void start() { m_log.log(LogService.LOG_INFO, "Hello there!"); }}
Dependency Manager - Example
• Add the DM annotation processor to bnd:-plugin: \ ${ext.repositories.-plugin},\ org.apache.felix.dm.annotation.plugin.bnd.AnnotationPlugin;\ path:=${plugin-dir}/org.apache.felix.dependencymanager.annotation.jar
Dependency Manager - Example
Dependency Manager
• Create one or more components: manager.add(createComponent() ...
• Use POJOs to implement services: .setImplementation(MyPOJO.class)
• Optionally define the service interfaces: .setInterface(MySvc.class.getName, props)
• Add dependencies
Service Dependencies• Are defined using the DependencyManager:
.add(createServiceDependency() ...
• Can be optional or required: .setRequired(boolean)
• Track a specific service: .setService(LogService.class, “(name=mine)”)
• Can be injected into an instance: .setAutoConfig(true) // this is the default
• Can trigger callbacks on the instance: .setCallbacks(“addService”, “removeService”) // NOTE: callbacks can define any of the following parameters: (ServiceReference reference, Object service)
Dependency Life Cyclestart
Installed
start
Resolved
Ac/veUninstalled
end
install
stop
uninstall Ac/ve
Wai/ng6for6required
Monitoring6op/onal
ac/vatedeac/vate
Dependency Activation
• If the reference to the implementation is a class, instantiate it, otherwise directly use the reference
• Invoke the init() method on the instance
• Inject all required and optional dependencies, using NullObjects where appropriate, and any BundleContext or ServiceRegistration
• Invoke the start() method on the instance
OSGi compendium
Log
HTTP
Device Access
Configuration Admin
Preferences
Metatype
Wire AdminUser Admin
IO Connector
Initial Provisioning
UPnP™ Device
Declarative Services
Event Admin Service Tracker
XML Parser
Position
Measurement and State
Execution Environment Spec
Remote Services
Deployment Admin
Blueprint Container
Log ServiceThe Log Service Interface Log Service Specification Version 1.3
6-698 OSGi Service Platform Release 4
Figure 101.1 Log Service Class Diagram org.osgi.service.log package
101.2 The Log Service Interface
The LogService interface allows bundle developers to log messages that can
be distributed to other bundles, which in turn can forward the logged
entries to a file system, remote system, or some other destination.
The LogService interface allows the bundle developer to:
• Specify a message and/or exception to be logged.
• Supply a log level representing the severity of the message being logged.
This should be one of the levels defined in the LogService interface but it
may be any integer that is interpreted in a user-defined way.
• Specify the Service associated with the log requests.
By obtaining a LogService object from the Framework service registry, a
bundle can start logging messages to the LogService object by calling one of
the LogService methods. A Log Service object can log any message, but it is
primarily intended for reporting events and error conditions.
The LogService interface defines these methods for logging messages:
• log( int, Str ing) – This method logs a simple message at a given log level.
• log( int, Str ing , Throwable) – This method logs a message with an
exception at a given log level.
• log(Serv iceReference, int , St r ing) – This method logs a message asso-
ciated with a specific service.
• log(Serv iceReference, int , St r ing , Throwable) – This method logs a
message with an exception associated with a specific service.
While it is possible for a bundle to call one of the log methods without pro-
viding a ServiceReference object, it is recommended that the caller supply
the ServiceReference argument whenever appropriate, because it provides
important context information to the operator in the event of problems.
<<interface>>LogService
<<interface>>LogReaderService
<<interface>>LogEntry
<<interface>>LogListener
a Log Reader Service impl.
LogEntry impl
a Log user bundle
a Log Serviceimpl
a Log reader user
Log a message
Store a message in the log for retrieval
message log
send new log entry
retrieve log
1 1
1
0..n (impl dependent maximum)
1
0..n
LogEntry has references toServiceReference,Throwable and Bundle
or registerlistener
Bundle usingLog Service
Bundle usingLog ReaderService
Log implementation bundle
Log Service
• an update of this spec is coming!
• tries to solve many of the problems:
• static logger
• dynamic reconfiguration per logger
• align API with SLF4J
Event Admin
• Publish subscribe
• Asynchronous and synchronous
• Hierarchical topics
• Decouple event creation and handling
Event Admin
Bundle
Component,invoking,a,service
Bundle
Componentproviding,a,service
method,call
MyService
Bundle
EventAdmin-implementa0on
Bundle
Component-listening-for-
events
EventAdminBundle
Component-publishing-an-
event(a)synch-event
EventHandler
Listen to Events
• Create an EventHandler that listens to all events
• Launch it in a framework that has an Event Admin implementation running
Event Admin - Exampleclass MySubscriber extends DependencyActivatorBase implements EventHandler { static final String[] topics ={ "com/acme/*", "org/osgi/service/log/LogEntry/*" }; public void init(BundleContext context, DependencyManager dm) { Dictionary dict = new Hashtable(); dict.put(EventConstants.EVENT_TOPIC, topics); dict.put(EventConstants.EVENT_FILTER, "(bundle.symbolicName=com.acme.*)"); dm.add(createComponent() .setInterface(EventHandler.class.getName(), dict) .setImplementation(SampleComparator.class); } @Override public void handleEvent(Event event) { //... }}
Sending events
• Add a service dependency to EventAdmin
• Use either sendEvent() or postEvent() to send events
Event Admin - Example
public class EventPublishingService { private volatile EventAdmin m_eventAdmin; private volatile LogService m_log;
public void someLogic() { if (m_eventAdmin != null) { Dictionary properties = new Hashtable(); properties.put("timestamp", new Date()); m_eventAdmin.postEvent( new Event("com/acme/timer", properties)); } else { m_log.log(LogService.LOG_INFO, "unable to send event: no event admin!"); } }}
Configuration Admin
• contains externally configurable settings for a service;
• service is identified by its PID (persistent ID)
• allows management systems to configure all settings;
• settings can be created even before the actual bundle is installed.
Bundle
Component
ManagedServiceservice.pid6=6nl.luminis.store
Configuration Admin
• Used for:
• dynamically configuring singleton services (ManagedService)
• creating new services based on configuration (ManagedServiceFactory)
Configuration Admin
port=8080protocol=RESTpublic=true
PID=connector
style=winxpcolorset=silver
PID=gui
Configura?onAdmin
getConfigura?on()listConfigura?ons()
Configura?on
getProper?es()update()delete()
manageEconfigura?ons no?fiesEofEchanges
ManagedService
updated()
ServicePID=connector
Configura?onEManager
Configuration Dependenciespublic class MyConfiguredService implements ManagedService { private volatile String m_name;
@Override public void updated(Properties props) throws ConfigurationException { // check given properties if (props == null) { m_name = "<default>"; } else { // update local settings Object v = props.get("name"); if (v instanceof String) { m_name = (String) v; } else { throw new ConfigurationException("name", "must be a string!"); } } } // …}
Configuration Dependencies
• Configuration updates are optional, but what about required configurations?
• DependencyManager can help:
• define a configuration dependency
• our component is started only when the configuration becomes available
• updated(props) is invoked before other life cycle methods of DM
Configuration Dependencies
// Define in the init() of your activator:manager.add(createService() .setImplementation(MyConfiguredService.class) .add(createConfigurationDependency()
.setPid("my.service.pid")));
HTTP service
• provides Servlet API
• whiteboard style registration of:
• listeners
• filters
• servlets
HTTP service Example
public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(HttpServletResponse.SC_OK); resp.getWriter().print("Hello World"); resp.flushBuffer(); }}
HTTP serviceExample
// Define in the init() of your activator:Properties props = new Properties();props.put("osgi.http.whiteboard.servlet.pattern", "/hello");
manager.add(createService() .setInterface(Servlet.class.getName(), props) .setImplementation(MyServlet.class));
OSGi tooling
• The popularity of frameworks depend on their ease of use
• OSGi development lacked in this area for long
OSGi tooling
• Enter Bndtools:
• actively developed plugin for Eclipse
• makes it really easy to develop for/with OSGi:
• automatic rebuilding of bundles
• hot-deployment of bundles
• very good support for semantic versioning
OSGi tooling
• demo
Books
• Building Modular Cloud Apps with OSGi
• Paul Bakker & Bert Ertman
• Java Application Architecture Modularity Patterns with Examples using OSGi
• Kirk Knoernschild
Links
• https://github.com/jawi/osgi-tutorial contains the demo project as shown during the demo
• http://bndtools.org/ the home of the Bndtools plugin for Eclipse
• http://bnd.bndtools.org/ contains lots of information about the nitty-gritty details of Bnd
• http://luminis.eu my current employer