Building OSGi Components
Carsten Ziegeler | cziegeler@apache.org
1
ApacheCon NA 2014
Building OSGi Components Carsten Ziegeler | cziegeler@apache.org - - PowerPoint PPT Presentation
Building OSGi Components Carsten Ziegeler | cziegeler@apache.org ApacheCon NA 2014 1 About cziegeler@apache.org @cziegeler RnD Team at Adobe Research Switzerland Member of the Apache So fu ware Foundation Apache Felix and
Building OSGi Components
Carsten Ziegeler | cziegeler@apache.org
1
ApacheCon NA 2014
About cziegeler@apache.org @cziegeler
2
Agenda
§ 1 OSGi Service Registry § 2 Components § 3 Declarative Services Today § 4 Next version of Declarative Services
3
Component and Service
§ Component
§ Piece of sofuware managed by a (component) container § Java: instances created and managed by a container § Container provides confjguration and used services
4
Component and Service
§ Service
§ A component providing a service § Java:
§ Defjned through an interface § A component implementing one or more interfaces (= services)
§ Usable by components and other services
§ Clients act on the service (interface)
5
Foreword
§ Many component frameworks for OSGi exist today § Difficulty of choosing § For OSGi based component development it’s more important to focus on the
components than on the components framework
§ Focus is on developing components § Developers choice § Declarative Services is very good but it’s not the only solution
6
7
OSGi Service Registry
§ Service oriented architecture
§ Publish/fjnd/bind
8
Service Registry Service Provider Service Consumer
Interact
Service Description
Publish Find
Registering a Service
§ Each bundle has access to its bundle context object
§ Using bundle activator
§ Bundle context:
§ registerService(String, Object, Dictionary) § registerService(String[], Object, Dictionary)
9
Registering a Service
§ Each bundle has access to its bundle context object
§ Using bundle activator
§ Bundle context:
§ registerService(String, Object, Dictionary) § registerService(String[], Object, Dictionary)
10
Service name(s)
Registering a Service
§ Each bundle has access to its bundle context object
§ Using bundle activator
§ Bundle context:
§ registerService(String, Object, Dictionary) § registerService(String[], Object, Dictionary)
11
Service name(s) Service instance
Registering a Service
§ Each bundle has access to its bundle context object
§ Using bundle activator
§ Bundle context:
§ registerService(String, Object, Dictionary) § registerService(String[], Object, Dictionary)
12
Service name(s) Service instance Service properties
Registering a Service
§ Each bundle has access to its bundle context object
§ Using bundle activator
§ Bundle context:
§ registerService(String, Object, Dictionary) § registerService(String[], Object, Dictionary)
13
import import org.osgi.framework.Constants
import import org.osgi.framework.ServiceRegistration
… BundleContext bc = …; final final Dictionary<String, Object> props = Dictionary<String, Object> props = new new Hashtable Hashtable<String, Object>(); <String, Object>(); props.put(Constants.SERVICE_DESCRIPTION, "Greatest Service on Earth"); props.put(Constants.SERVICE_VENDOR, "Adobe Systems Incorporated"); final final Scheduler service = Scheduler service = new new MyScheduler MyScheduler(); (); this this.bundleContext bundleContext.registerService .registerService( new new String[] { String[] {Scheduler.class.getName Scheduler.class.getName()}, ()}, service, props); service, props);
Getuing a Service from the Service Registry
14
BundleContext bundleContext = ... ...; final final ServiceReference ServiceReference sr sr = = bundleContext.getServiceReference bundleContext.getServiceReference( Scheduler.class.getName Scheduler.class.getName()); ()); if if ( ( sr sr != != null null ) { ) { final final Scheduler s = (Scheduler) Scheduler s = (Scheduler) bundleContext.getService bundleContext.getService(sr sr); ); if if ( s != ( s != null null ) { ) { s.doSomething(); } bundleContext.ungetService(sr); }
Getuing Service Properties
15
BundleContext bundleContext = ... ...; final final ServiceReference ServiceReference sr sr = = bundleContext.getServiceReference bundleContext.getServiceReference( Scheduler.class.getName Scheduler.class.getName()); ()); if if ( ( sr sr != != null null ) { ) { // access properties // access properties final final Object value = Object value = sr.getProperty(Constants.SERVICE_VENDOR); bundleContext.ungetService(sr); }
Service Properties
16
import import org.osgi.framework.Constants
Constants.SERVICE_ID - set by the framework (long) id of the service increased for each registration dynamic - not persisted! Constants.SERVICE_DESCRIPTION - optional description (string) Constants.SERVICE_VENDOR - optional vendor (string) Constants.SERVICE_PID - persistence identifier (string)
Constants.SERVICE_RANKING - ordering of registrations
Multiple Registrations for a Service
17
BundleContext bundleContext = ... ...; final final ServiceReference ServiceReference[] refs = [] refs = bundleContext.getServiceReferences bundleContext.getServiceReferences( Scheduler.class.getName Scheduler.class.getName(), (), null); null); if if ( refs != ( refs != null null ) { ) { // iterate over references, maybe sort by ranking etc. // iterate over references, maybe sort by ranking etc. }
Getuing a Service from the Service Registry
18
BundleContext bundleContext = ... ...; final final ServiceReference ServiceReference sr sr = = bundleContext.getServiceReference bundleContext.getServiceReference( Scheduler.class.getName Scheduler.class.getName()); ()); if if ( ( sr sr != != null null ) { ) { final final Scheduler s = (Scheduler) Scheduler s = (Scheduler) bundleContext.getService bundleContext.getService(sr sr); ); if if ( s != ( s != null null ) { ) { s.doSomething(); } bundleContext.ungetService(sr); }
Highest Ranking
Lazy Service Creation / Bundle Scope
19
public public interface interface org.osgi.framework.ServiceFactory
{ Object Object getService getService(Bundle bundle, (Bundle bundle, ServiceRegistration ServiceRegistration registration); registration); void void ungetService ungetService(Bundle bundle, (Bundle bundle, ServiceRegistration ServiceRegistration registration, registration, service); service); }
Registering a Service Factory
20
import import org.osgi.framework.Constants
import import org.osgi.framework.ServiceRegistration
… BundleContext bc = …; final final Dictionary<String, Object> props = Dictionary<String, Object> props = new new Hashtable Hashtable<String, Object>(); <String, Object>(); props.put(Constants.SERVICE_DESCRIPTION, "Greatest service on Earth"); props.put(Constants.SERVICE_VENDOR, "Adobe Systems Incorporated"); final final ServiceFactory ServiceFactory factory = factory = new new MySchedulerFactory MySchedulerFactory(); (); this this.bundleContext bundleContext.registerService .registerService( new new String[] { String[] {Scheduler.class.getName Scheduler.class.getName()}, ()}, factory, props); factory, props);
Service Event Listener
21
package package org.osgi.framework
public public interface interface ServiceListener ServiceListener extends extends EventListener EventListener { { void void serviceChanged serviceChanged(ServiceEvent ServiceEvent event); event); }
OSGi Service Registry
binding
22
23
OSGi Service Registry
§ Powerful but "complicated" to use directly § Requires a different way of thinking § Dynamic
§ Packages/Bundles might come and go § Services might appear/disappear
§ Manually resolve and track services § Doable, but requires "work"
24
Components and Services with OSGi
§ Service interface
§ Public (if exported for other bundles) § Versioned through package version (Semantic versioning) § Private for internal services (sometimes useful)
§ Component / service implementation
§ Always private
25
Component Container Interaction
26
OSGi Service Registry
Blueprint iPojo "Manual Access"
Advanced OSGi Development Solutions
Ant/Bndtools...
27
28
Component Development with Declarative Services
§ Declarative Services (OSGi Compendium Spec)
§ Defjnes Service Component Runtime (SCR) § Apache Felix SCR Annotations (DS annotations) § Available tooling: Maven/Ant/Bndtools...
§ Some advantages (in combination with the tooling)
§ POJO style § Declarative § Single source: just the Java code, no XML etc. § "Integration" with Confjguration Admin and Metatype Service
29
My First Component
30
package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component
@Component public public class class MyComponent MyComponent { { }
Component Lifecycle
31
package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Activate
import import org.apache.felix.scr.annotations.Component
import import org.apache.felix.scr.annotations.Deactivate
@Component public public class class MyComponent MyComponent { { @Activate protected protected void void activate() { activate() { // do something } @Deactivate protected protected void void deactivate() { deactivate() { // do something } }
Providing a Service
32
package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component
import import org.apache.felix.scr.annotations.Service
import import org.osgi.service.event.EventHandler
@Component @Service(value=EventHandler.class class) public public class class MyComponent MyComponent implements implements EventHandler EventHandler { { … …
Providing Several Services
33
package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component
import import org.apache.felix.scr.annotations.Service
import import org.osgi.service.event.EventHandler
@Component @Service(value={EventHandler.class class, Runnable.class class}) public public class class MyComponent MyComponent implements implements EventHandler EventHandler, Runnable { , Runnable { … …
Using a Service
34
package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component
import import org.apache.felix.scr.annotations.Service
import import org.osgi.service.event.EventHandler
@Component @Service(value=EventHandler.class class) public public class class MyComponent MyComponent implements implements EventHandler EventHandler { { @Reference private privateTThreadPool threadPool; … …
Using an optional Service
35
package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component
import import org.apache.felix.scr.annotations.Service
import import org.osgi.service.event.EventHandler
@Component @Service(value=EventHandler.class class) public public class class MyComponent MyComponent implements implements EventHandler EventHandler { { @Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY, policy=ReferencePolicy.DYNAMIC) private privateTThreadPool threadPool; @Reference(cardinality=ReferenceCardinality.MANDATORY_UNARY) private privateTDistributor distributor;
Component Properties -> Service Properties
36
import import org.apache.sling.commons.osgi.PropertiesUtil
@Component @Service(value=EventHandler.class class) @Properties({ @Property(name="service.vendor", value="Who?”), @Property(name="service.ranking", intValue=500) }) public public class class DistributingEventHandler implements implements EventHandler {
Confjguration Admin
37
Confjguration – Supports Confjguration Admin
38
import import org.apache.sling.commons.osgi.PropertiesUtil
@Component @Service(value=EventHandler.class class) @Properties({ @Property(name="event.topics", value="*", propertyPrivate=true true), @Property(name="event.filter", value="(event.distribute=*)", propertyPrivate=true true) }) public public class class DistributingEventHandler implements implements EventHandler { private private static static final final int int DEFAULT_CLEANUP_PERIOD = 15; @Property(intValue=DEFAULT_CLEANUP_PERIOD) private private static static final final String PROP_CLEANUP_PERIOD ="cleanup.period"; private private int int cleanupPeriod; @Activate protected protected void void activate(final Map<String, Object> props) { this this.cleanupPeriod = PropertiesUtil.toInteger(props.get(PROP_CLEANUP_PERIOD)); }
Confjguration Update
39
import import org.apache.sling.commons.osgi.OsgiUtil
public public class class DistributingEventHandler implements implements EventHandler { … @Modified protected protected void void update(final Map<String, Object> props) { this this.cleanupPeriod = PropertiesUtil.toInteger(props.get(PROP_CLEANUP_PERIOD)); }
Without update: Component is restarted on confjg change!
Confjguration – Supports Confjguration Admin
§ Provided map contains
§ Confjguration properties from Confjguration Admin § Defjned component properties
40
@Activate protected protected void void activate(final Map<String, Object> props) { … }
Metatype and Web Console
41
Confjguration – Supports Metatype
42
import import org.apache.sling.commons.osgi.PropertiesUtil
@Component(metatype=true, label="Distributing Event Handler", description="This handler is awesome.") @Properties({ @Property(name="event.topics", value="*", propertyPrivate=true true) }) public public class class DistributingEventHandler implements implements EventHandler { private private static static final final int int DEFAULT_CLEANUP_PERIOD = 15; @Property(intValue=DEFAULT_CLEANUP_PERIOD, label="Cleanup Period", description="This is the cleanup period in seconds.") private private static static final final String PROP_CLEANUP_PERIOD ="cleanup.period";
Lifecycle Methods
§ Signatures for activate and deactivate:
43
protected protected void void activate(); activate(); protected protected void void activate( activate(final final Map<String, Object> properties); Map<String, Object> properties); protected protected void void activate( activate(final final ComponentContext ComponentContext cc); cc); protected protected void void activate( activate(final final BundleContext BundleContext cc); cc); protected protected void void activate( activate(final final Map<String, Object> properties, Map<String, Object> properties, final final ComponentContext ComponentContext cc); cc); protected protected void void activate( activate(final final Map<String, Object> properties, Map<String, Object> properties, final final BundleContext BundleContext cc); cc);
Declarative Services
44
Unary References - Revisited
45
package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component
import import org.apache.felix.scr.annotations.Service
import import org.osgi.service.event.EventHandler
@Component @Service(value=EventHandler.class class) public public class class MyComponent MyComponent implements implements EventHandler EventHandler { { @Reference private privateTDistributor distributor; protected protected void void bindDistributor bindDistributor(Distributor d) { (Distributor d) { this this.distributor .distributor = d; = d; } protected protected void void unbindDistributor unbindDistributor(Distributor d) { (Distributor d) { if if ( this this.distributor .distributor == d ) { == d ) { this this.distributor .distributor = null; = null; } } }
Generated Generated
References to Multiple Services
§ Create bind / unbind methods
46
@Reference(name="AdapterFactory", referenceInterface=AdapterFactory.class class, cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC) public public class class AdapterManagerImpl AdapterManagerImpl implements implements AdapterManager AdapterManager protected protected void void bindAdapterFactory bindAdapterFactory(ServiceReference ServiceReference reference) { reference) { // use component context to get the service } protected protected void void bindAdapterFactory bindAdapterFactory(AdapterFactory AdapterFactory factory) { factory) { } protected protected void void bindAdapterFactory bindAdapterFactory(AdapterFactory AdapterFactory factory, factory, Map<String, Object> Map<String, Object> serviceProps serviceProps) { ) { }
Apache Felix SCR Tooling
47
Component Specifjcation
48
Declarative Services
49
50
Confjguring A Component
§ Today’s problems
§ Property defjnitions are lengthy… § ..and scatuered across the code… § Conversion of confjguration values § A lot of boilerplate code
51
Complex Sample
52
@Component @Property(name="service.ranking", intValue=15) public public class class MyComponent MyComponent { { private private static static final final boolean boolean DEFAULT_ENABLED DEFAULT_ENABLED = = true true; @Property(boolValue=DEFAULT_ENABLED) private private static static final final String String PROP_ENABLED PROP_ENABLED = = "enabled" "enabled"; @Property(value = {"topicA", "topicB"}) private private static static final final String String PROP_TOPIC PROP_TOPIC = = "enabled" "enabled"; @Property private private static static final final String String PROP_USERNAME PROP_USERNAME = = "userName userName"; String userName; String[] topics; @Activate protected protected void void activate( activate(final final Map<String, Object> Map<String, Object> config config) { ) { final final boolean boolean enabled = enabled = PropertiesUtil. PropertiesUtil.toBoolean toBoolean(config.get config.get(PROP_ENABLED PROP_ENABLED), ), DEFAULT_ENABLED DEFAULT_ENABLED); ); if if ( enabled ) { ( enabled ) { this this.userName userName = = PropertiesUtil. PropertiesUtil.toString toString(config.get config.get(PROP_USERNAME PROP_USERNAME), ), null null); ); this this.topics topics = = PropertiesUtil. PropertiesUtil.toStringArray toStringArray(config.get config.get(PROP_TOPIC PROP_TOPIC)); )); } } }
Defjne Confjguration Annotation….
53
@interface @interface MyConfig MyConfig { { boolean boolean enabled() enabled() default default true true; String[] topic() default default { {"topicA topicA", , "topicB topicB"}; }; String userName(); int int service_ranking service_ranking() () default default 15; 15; }
..and use in lifecycle method
54
@Component public public class class MyComponent MyComponent { { String userName; String[] topics; @Activate protected protected void void activate( activate(final final MyConfig MyConfig config config) { ) { // note: annotation MyConfig used as interface if if ( ( config.enabled config.enabled() ) { () ) { this this.userName userName = = config.userName config.userName(); (); this this.topics topics = = config.topic config.topic(); (); } }
Or even simpler…
55
@Component public public class class MyComponent MyComponent { { private private MyConfig MyConfig configuration configuration; @Activate protected protected void void activate( activate(final final MyConfig MyConfig config config) { ) { // note: annotation MyConfig used as interface if if ( ( config.enabled config.enabled() ) { () ) { this this.configuration configuration = = config config; } } }
In the works: Metatype Support (RFC 208)
56
@ObjectClassDefinition(label="My Component", description="Coolest component in the world.") @interface @interface MyConfig MyConfig { { @AttributeDefinition(label="Enabled", description="Topic and user name are used if enabled") boolean boolean enabled() enabled() default default true true; @AttributeDefinition(...) String[] topic() default default { {"topicA topicA", , "topicB topicB"}; }; @AttributeDefinition(...) String userName(); int int service_ranking service_ranking() () default default 15; 15; // maps to // maps to service.ranking service.ranking }
Declarative Service Enhancements (RFC 190)
§ Annotation Confjguration Support § Support for service scopes (prototypes) § Introspection API
57
58