Building OSGi Components Carsten Ziegeler | cziegeler@apache.org - - PowerPoint PPT Presentation

building osgi components
SMART_READER_LITE
LIVE PREVIEW

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


slide-1
SLIDE 1

Building OSGi Components

Carsten Ziegeler | cziegeler@apache.org

1

ApacheCon NA 2014

slide-2
SLIDE 2

About cziegeler@apache.org @cziegeler

  • RnD Team at Adobe Research Switzerland
  • Member of the Apache Sofuware Foundation
  • Apache Felix and Apache Sling (PMC and commituer)
  • And other Apache projects
  • OSGi Core Platform and Enterprise Expert Groups
  • Member of the OSGi Board
  • Book / article author, technical reviewer, conference speaker

2

slide-3
SLIDE 3

Agenda

§ 1 OSGi Service Registry § 2 Components § 3 Declarative Services Today § 4 Next version of Declarative Services

3

slide-4
SLIDE 4

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

slide-5
SLIDE 5

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

slide-6
SLIDE 6

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

slide-7
SLIDE 7

Example Application

„ „ …†‡ˆ …†‡ˆ

7

slide-8
SLIDE 8

OSGi Service Registry

§ Service oriented architecture

§ Publish/fjnd/bind

8

Service Registry Service Provider Service Consumer

Interact

Service Description

Publish Find

slide-9
SLIDE 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)

9

slide-10
SLIDE 10

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)

slide-11
SLIDE 11

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

slide-12
SLIDE 12

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

slide-13
SLIDE 13

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

  • rg.osgi.framework.Constants;

import import org.osgi.framework.ServiceRegistration

  • rg.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);

slide-14
SLIDE 14

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); }

slide-15
SLIDE 15

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); }

slide-16
SLIDE 16

Service Properties

16

import import org.osgi.framework.Constants

  • rg.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)

  • ptional, unique identifier

Constants.SERVICE_RANKING - ordering of registrations

slide-17
SLIDE 17

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. }

slide-18
SLIDE 18

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

slide-19
SLIDE 19

Lazy Service Creation / Bundle Scope

  • Register service factory instead of service
  • Framework calls factory once per client bundle

19

public public interface interface org.osgi.framework.ServiceFactory

  • rg.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); }

slide-20
SLIDE 20

Registering a Service Factory

20

import import org.osgi.framework.Constants

  • rg.osgi.framework.Constants;

import import org.osgi.framework.ServiceRegistration

  • rg.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);

slide-21
SLIDE 21

Service Event Listener

  • Notifjcation of registration / unregistrations
  • Registered to the bundle context
  • Filter for service name, properties etc.

21

package package org.osgi.framework

  • rg.osgi.framework;

public public interface interface ServiceListener ServiceListener extends extends EventListener EventListener { { void void serviceChanged serviceChanged(ServiceEvent ServiceEvent event); event); }

slide-22
SLIDE 22

OSGi Service Registry

  • Lightweight services
  • Lookup is based on interface name
  • Direct method invocation
  • Scopes: singleton, bundle, prototype (R6)
  • Good design practice
  • Separates interface from implementation
  • Separates registration from usage
  • Enables reuse, substitutability, loose coupling, and late

binding

22

slide-23
SLIDE 23

Example Application

„ „ …†‡ˆ …†‡ˆ

23

slide-24
SLIDE 24

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

slide-25
SLIDE 25

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

slide-26
SLIDE 26

Component Container Interaction

26

OSGi Service Registry

Declarative Services

Blueprint iPojo "Manual Access"

slide-27
SLIDE 27

Advanced OSGi Development Solutions

  • Service Tracker
  • Still somewhat of a manual approach
  • Declarative Services, Blueprint, iPOJO
  • Declarative
  • Sophisticated service oriented component frameworks
  • Automated dependency injection and more
  • More modern, POJO oriented approaches
  • Straight forward with Declarative Services, Annotations, Maven/

Ant/Bndtools...

27

slide-28
SLIDE 28

Example Application

„ „ …†‡ˆ …†‡ˆ

28

slide-29
SLIDE 29

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

slide-30
SLIDE 30

My First Component

30

package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component

  • rg.apache.felix.scr.annotations.Component;

@Component public public class class MyComponent MyComponent { { }

slide-31
SLIDE 31

Component Lifecycle

31

package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Activate

  • rg.apache.felix.scr.annotations.Activate;

import import org.apache.felix.scr.annotations.Component

  • rg.apache.felix.scr.annotations.Component;

import import org.apache.felix.scr.annotations.Deactivate

  • rg.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 } }

slide-32
SLIDE 32

Providing a Service

32

package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component

  • rg.apache.felix.scr.annotations.Component;

import import org.apache.felix.scr.annotations.Service

  • rg.apache.felix.scr.annotations.Service;

import import org.osgi.service.event.EventHandler

  • rg.osgi.service.event.EventHandler;

@Component @Service(value=EventHandler.class class) public public class class MyComponent MyComponent implements implements EventHandler EventHandler { { … …

slide-33
SLIDE 33

Providing Several Services

33

package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component

  • rg.apache.felix.scr.annotations.Component;

import import org.apache.felix.scr.annotations.Service

  • rg.apache.felix.scr.annotations.Service;

import import org.osgi.service.event.EventHandler

  • rg.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 { … …

slide-34
SLIDE 34

Using a Service

34

package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component

  • rg.apache.felix.scr.annotations.Component;

import import org.apache.felix.scr.annotations.Service

  • rg.apache.felix.scr.annotations.Service;

import import org.osgi.service.event.EventHandler

  • rg.osgi.service.event.EventHandler;

@Component @Service(value=EventHandler.class class) public public class class MyComponent MyComponent implements implements EventHandler EventHandler { { @Reference private privateTThreadPool threadPool; … …

slide-35
SLIDE 35

Using an optional Service

35

package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component

  • rg.apache.felix.scr.annotations.Component;

import import org.apache.felix.scr.annotations.Service

  • rg.apache.felix.scr.annotations.Service;

import import org.osgi.service.event.EventHandler

  • rg.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;

slide-36
SLIDE 36

Component Properties -> Service Properties

36

import import org.apache.sling.commons.osgi.PropertiesUtil

  • rg.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 {

slide-37
SLIDE 37

Confjguration Admin

  • OSGi Confjguration Admin
  • “Tie” solution to handle confjgurations
  • Confjguration Manager
  • Persistence storage
  • Service API to retrieve/update/remove confjguration
  • Integration with Declarative Services
  • Confjguration changes are propagated to the components
  • Confjgurations are stored using the PID

37

slide-38
SLIDE 38

Confjguration – Supports Confjguration Admin

38

import import org.apache.sling.commons.osgi.PropertiesUtil

  • rg.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)); }

slide-39
SLIDE 39

Confjguration Update

39

import import org.apache.sling.commons.osgi.OsgiUtil

  • rg.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!

slide-40
SLIDE 40

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) { … }

slide-41
SLIDE 41

Metatype and Web Console

  • OSGi Metatype Service
  • Description of bundle metadata
  • Description of service confjgurations
  • Property type, name, and description
  • Apache Felix Web Console
  • Great solution to confjgure the system
  • Especially component confjgurations
  • Uses metatype description

41

slide-42
SLIDE 42

Confjguration – Supports Metatype

42

import import org.apache.sling.commons.osgi.PropertiesUtil

  • rg.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";

slide-43
SLIDE 43

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);

slide-44
SLIDE 44

Declarative Services

  • A service is by default only started if someone else uses it!
  • Lazy is always good and usually sufficient!
  • Immediate fmag on @Component forces a service start (use with care!)
  • References are always bound through methods
  • SCR Plugin generates methods for unary references at built time

44

slide-45
SLIDE 45

Unary References - Revisited

45

package package com.adobe.osgitraining.impl com.adobe.osgitraining.impl; import import org.apache.felix.scr.annotations.Component

  • rg.apache.felix.scr.annotations.Component;

import import org.apache.felix.scr.annotations.Service

  • rg.apache.felix.scr.annotations.Service;

import import org.osgi.service.event.EventHandler

  • rg.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

slide-46
SLIDE 46

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) { ) { }

slide-47
SLIDE 47

Apache Felix SCR Tooling

  • Combines everything (DS, Confjguration Admin, Metatype, Maven/Ant)
  • Annotation-based
  • Single-source development = only java code
  • Annotate components
  • Properties with default values and metatype info
  • Provided services
  • Services references (policy and cardinality)
  • Generates DS XML
  • Generates Metatype descriptors
  • Generates Java code (for reference handling)
  • Extensible by “annotation plugins”

47

slide-48
SLIDE 48

Component Specifjcation

  • XML Confjguration
  • Contained in bundle
  • Manifest entry pointing to confjg(s)
  • Publishing services (through OSGi registry)
  • Consuming services
  • Reference policy (static,dynamic),
  • Reference cardinality (0..1, 1..1, 0..n)
  • Default confjguration
  • Service lifecycle management

48

slide-49
SLIDE 49

Declarative Services

  • Reads XML confjgs on bundle start
  • Registers services (service factories)
  • Keeps track of dependencies
  • Starts/stops services
  • Invokes optional activation and deactivation method
  • Provides access to confjguration
  • Leverages OSGi service registry
  • Plays well with other component management approaches!

49

slide-50
SLIDE 50

Example Application

„ „ …†‡ˆ …†‡ˆ

50

slide-51
SLIDE 51

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

slide-52
SLIDE 52

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)); )); } } }

slide-53
SLIDE 53

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; }

slide-54
SLIDE 54

..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(); (); } }

slide-55
SLIDE 55

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; } } }

slide-56
SLIDE 56

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 }

slide-57
SLIDE 57

Declarative Service Enhancements (RFC 190)

§ Annotation Confjguration Support § Support for service scopes (prototypes) § Introspection API

57

slide-58
SLIDE 58

QnA

58