Difference between revisions of "Tutorial OSGi avec Apache Felix - Partie 4"

From air
Jump to navigation Jump to search
 
(22 intermediate revisions by the same user not shown)
Line 4: Line 4:
   
 
* Felix Framework Distribution (voir [[Tutorial OSGi avec Apache Felix - Partie 1]])
 
* Felix Framework Distribution (voir [[Tutorial OSGi avec Apache Felix - Partie 1]])
  +
** with the iPOJO Core bundle[http://felix.apache.org/site/download.html] (see next section)
 
* [http://www.eclipse.org/downloads/ Eclipse].
 
* [http://www.eclipse.org/downloads/ Eclipse].
 
** To support OSGi development, Eclipse needs the '''PDE''' (Plug-in Development Environment) plug-in, bundled in ''Eclipse IDE for Java EE Developers''
 
** To support OSGi development, Eclipse needs the '''PDE''' (Plug-in Development Environment) plug-in, bundled in ''Eclipse IDE for Java EE Developers''
* iPOJO Nature plug-in for Eclipse [https://dl.dropbox.com/u/59622687/org.ow2.chameleon.eclipse.ipojo.updatesite-1.0.1-SNAPSHOT.zip as an archive distribution]
+
* iPOJO Nature plug-in for Eclipse, available [https://dl.dropbox.com/u/59622687/org.ow2.chameleon.eclipse.ipojo.updatesite-1.0.1-SNAPSHOT.zip as an archive distribution]
 
** Install the ''iPOJO Project Nature'' and ''iPOJO Nature Dependencies'' plug-ins
 
** Install the ''iPOJO Project Nature'' and ''iPOJO Nature Dependencies'' plug-ins
  +
  +
==Set up your Felix installation==
  +
  +
# Run Felix using the following command in the Felix installation directory
  +
#* <code>java -jar bin/felix.jar</code>
  +
# Install iPOJO, using the following Felix Gogo shell commands:
  +
#* <code>obr:list</code>
  +
#* <code>obr:deploy -s "Apache Felix Log Service"</code>
  +
#* <code>obr:deploy -s "Apache Felix iPOJO"</code>
  +
#* <code>obr:deploy -s "Apache Felix iPOJO Gogo Command"</code>
  +
  +
==Useful Gogo commands==
  +
  +
''Note'': To ease shell lines edition on Linux, you might use the [http://utopia.knoware.nl/~hlub/rlwrap/#rlwrap rlwrap] tool: <code>rlwrap java -jar bin/felix.jar</code>
  +
  +
* <code>help {command}</code>
  +
** Print the help of the given command
  +
* <code>lb</code>
  +
** Lists the bundles installed in the framework
  +
* <code>inspect capability service {bundle ID}</code>
  +
** Lists the services provided by the given bundle
  +
** <code>capability</code> can be written <code>c</code>
  +
* <code>inspect requirement service {bundle ID}</code>
  +
** Lists the services consumed by the given bundle
  +
** <code>requirement</code> can be written <code>r</code>
  +
* <code>ipojo:factories</code>
  +
** Prints the publicly available iPOJO factories
  +
* <code>ipojo:factory {factory name}</code>
  +
** Prints the details of the given iPOJO factory
  +
* <code>ipojo:instances</code>
  +
** Prints the names and states of iPOJO instances
  +
* <code>ipojo:instance {instance name}</code>
  +
** Prints the details of the given iPOJO component instance
   
 
==Eclipse Configuration==
 
==Eclipse Configuration==
Line 22: Line 56:
   
 
=I. Activator=
 
=I. Activator=
<pre>
 
   
1. Create Eclipse Project "bundle.1"
+
# Create an Eclipse Plug-in project ''fr.erods.tp.bundle1''
New > Plug-in project
+
#* New > Plug-in project
- "Target Platform" > "an OSGi framework" > "standard"
+
#* Select the option "Target Platform" > "an OSGi framework" > ''standard''
- Next
+
#* Next
- Check "Generate an activator"
+
#* Check "Generate an activator"
- Finish
+
#* Finish
  +
# Open the activator class and print some text when the bundle starts and stops
  +
# Export the bundle JAR file with iPOJO Nature
  +
#* Right click on the bundle project > Export > iPOJO Bundle
  +
#* Choose an output folder (the Felix installation folder)
  +
#* Finish
  +
# Install and test the bundle using the Felix shell (start/update/stop)
   
  +
Questions:
2. Print Hello/Bye in the activator
 
  +
* Indicate when start() and stop() methods are called.
  +
* Why the BundleContext can be kept as a <code>private static</code> class member ?
   
  +
=II. Pure-OSGi service=
3. Export bundle
 
Right clic on project > Export > iPOJO Bundle
 
- choose an output folder (the felix framework folder)
 
- finish
 
   
  +
# Create an new bundle project ''fr.erods.tp.api''.
(note: discuss about Eclipse plug-in export and iPOJO Nature export)
 
  +
#* Activator is not needed: uncheck "Generate an activator" in step 2
  +
# Create the <code>fr.erods.tp.api.IScheduleService</code> interface with two methods:
  +
#* <code>int createJob(Runnable aRunnable, int aDelay)</code>
  +
#* <code>void deleteJob(int aId)</code>
  +
# Export the API package
  +
#* Open the Manifest.mf file of ''fr.erods.tp.api''
  +
#* In tab "Runtime" > "Exported Packages" > Add
  +
#** Select the <code>fr.erods.tp.api</code> package
  +
# Import the API package in bundle 1
  +
#* Open the Manifest.mf file of ''fr.erods.tp.bundle1''
  +
#* In tab "Dependencies" > "Imported Packages" > Add
  +
#* Select the <code>fr.erods.tp.api</code> package
  +
# Implement the service in bundle 1
  +
#* Create the class <code>fr.erods.tp.bundle1.ScheduleImpl</code>, implementing <code>IScheduleService</code> in the bundle 1.
  +
# Register the service with the activator of bundle 1
  +
#* see [http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleContext.html#registerService%28java.lang.Class,%20S,%20java.util.Dictionary%29 registerService()]
  +
#* see [http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/ServiceRegistration.html#unregister%28%29 unregister()]
  +
# Implement a service consumer in a new bundle
  +
#* Create project ''fr.erods.tp.bundle2''
  +
#* Import the API package
  +
#** In the Manifest.mf file of bundle 2
  +
#** In tab "Dependencies" > "Imported Packages" > Add
  +
#** Select the <code>fr.erods.tp.api</code> package
  +
#* Implement a <code>Consumer</code> class, implementing the OSGi <code>ServiceListener</code>[http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/ServiceListener.html] interface
  +
#* Print a message when the service is bound/unbound
  +
# Test the bundles in Felix
   
  +
Questions:
4. Test bundle
 
  +
* What happens when the consumer is started after the service bundle and why ?
- start felix
 
  +
* Correct this behavior, using [http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleContext.html#getServiceReference%28java.lang.Class%29 getServiceReference()] and [http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/BundleContext.html#getService%28org.osgi.framework.ServiceReference%29 getService()]
- install the bundle
 
- start / stop / update
 
</pre>
 
   
  +
=II. iPOJO: provide and consume with iPOJO=
=II. Pure-OSGi service=
 
<pre>
 
   
1. Create the API bundle "bundle.api"
+
# Add the iPOJO Nature to projects ''bundle 1'' and ''bundle 2''
  +
#* Select the projects
- "Target Platform" > "an OSGi framework" > "standard"
 
  +
#* Right click > Add iPOJO Nature
- Next
 
  +
#* Check ''Use annotations''
- Uncheck "Generate an activator"
 
- Finish
 
   
  +
==Consumer==
2. Create the IScheduleService interface in the package fr.erods.tp.api
 
   
  +
# Implement a <code>ConsumerIpojo</code> class in bundle 2
3. Export the API package
 
  +
#* See [http://felix.apache.org/site/how-to-use-ipojo-annotations.html How to use iPOJO Annotations]
- Open Manifest.mf of bundle.api
 
  +
#* The component must be instantiated automatically
- Runtime > "exported packages" > Add
 
  +
#* Print a message when the component is (in)validated and when the service is (un)bound
  +
# Test
  +
# Set the service requirement as optional
  +
# Test
   
  +
Questions:
4. Import the API package in bundle 1
 
  +
* Why are the callbacks called in that order ?
- Open Manifest.mf of bundle.api
 
  +
* What happens if you use a missing optional requirement ?
- Dependencies > Imported Packages > Add
 
   
  +
==Provider==
5. Implement the service in bundle 1
 
- Create class ScheduleImpl implementing IScheduleService in a package in bundle 1
 
   
  +
# Implement a <code>ServiceProviderIpojo</code> class in bundle 1
6. Export the service in the Activator
 
  +
#* See [http://felix.apache.org/site/how-to-use-ipojo-annotations.html How to use iPOJO Annotations]
- regSvc -> start()
 
  +
#* The component must be instantiated automatically
- unregSvc -> stop()
 
  +
#* The component must provide its service
  +
#* Print a message when the component is (in)validated
  +
# Test
  +
# Add a ''service controller'' and alternate the service presence
  +
# Test
   
  +
Question:
7. Implement the client in bundle 2
 
  +
* How does the consumers react to the service presence cycle ?
- Create project bundle.2 (with activator)
 
- Import package API
 
- Implement a Consumer class, implementing ServiceListener
 
- Print a message when a service is bound/unbound
 
   
  +
==Bonus==
8. Test the bundles
 
</pre>
 
   
  +
# Add a "service.ranking" property to the pure-OSGi and the iPOJO service, and an "implementation" property to identify them
=II. iPOJO: provide and consume with iPOJO=
 
  +
# Print the name of the implementation the consumers are bound to
<pre>
 
  +
# Modify the ranking value
1. Either use new projects or rewrite existing ones
 
-> Import package API
 
   
  +
Question:
2. Set iPOJO Nature to the project, with annotations
 
  +
* How does a service the service selection works ?
   
  +
''Note'': The consumer should call methods of the service and traces should be printed in order to show which service the consumer is bound to.
3. Implement ConsumerIpojo and ProviderIpojo
 
 
4. Test
 
</pre>
 
   
 
=III. Whiteboard pattern=
 
=III. Whiteboard pattern=
  +
  +
# Create interface <code>IJobListener</code> in the API package, with two methods:
  +
#* <code>void onJobCreation(int aId)</code>
  +
#* <code>void onJobDeletion(int aId)</code>
  +
# Create a new bundle project ''fr.erods.tp.bundle3''
  +
#* Add the iPOJO Nature and annotations
  +
#* Let it import the API package
  +
# Create a component providing an <code>IJobListener</code> service in bundle 3
  +
# Add job creation / deletion operations in a consumer
  +
  +
==Subscription pattern==
  +
  +
# Modify the schedule component to allow listeners registration:
  +
#* <code>void addListener(IJobListener)</code>
  +
#* <code>void removeListener(IJobListener)</code>
  +
# Register component of bundle 3 as a job listener
  +
# Test
  +
  +
==Whiteboard pattern==
  +
  +
# Modify the schedule component (in bundle 1)
  +
#* Remove the registration methods created above
  +
#* Add a requirement to an array of <code>IJobListener</code>s, as an optional requirement
  +
#* Notify them on job creation/deletion
  +
# Test
  +
  +
Questions:
  +
* What are the pros/cons of the whiteboard pattern vs. the subscription pattern ?
  +
* Bonus: compare this whiteboard pattern implementation with the iPOJO Whiteboard pattern handler[http://felix.apache.org/site/white-board-pattern-handler.html]
  +
  +
=IV. JMX MBean with iPOJO=
  +
  +
# Create a new bundle, with iPOJO Nature and annotations
  +
# Create a component, which doesn't provide any service, but contains public methods to create/delete/list <code>IScheduleService</code> jobs
  +
# Use iPOJO JMX annotations[http://felix.apache.org/site/how-to-use-ipojo-annotations.html#HowtouseiPOJOAnnotations-ExposinginstancesasaJMXMBean%28externalhandler%29] to export those methods
  +
# You will need the iPOJO JMX handler to test this component: install it using the following command in the Felix shell:
  +
#* <code>obr:deploy -s "Apache Felix iPOJO JMX Handler</code>
  +
# Install the bundle in the Felix framework
  +
# Run <code>jconsole</code> (in the Oracle Java distribution) or <code>jvisualvm</code> (in the Oracle Java distribution) or <code>visualvm</code>[http://visualvm.java.net/download.html]
  +
#* You might need to install the JMX plug-in in VisualVM:
  +
#** Tools > Plugins > Available Plugi-ns
  +
#** Install the ''VisualVM-MBeans'' plug-in
  +
# Connect VisualVM to your Felix JVM and play with the JMX beans
  +
  +
=V. Gogo Command with iPOJO=
  +
  +
# Create a new bundle, with iPOJO Nature and annotations
  +
# Create a Gogo command provider to create and delete jobs, and to list current jobs status
  +
  +
==Minimal Gogo command component==
  +
 
<pre>
 
<pre>
  +
import org.apache.felix.ipojo.annotations.Component;
1. Create interface IJobListener in package API (onJobCreation, onJobDeletion)
 
  +
import org.apache.felix.ipojo.annotations.Instantiate;
2. Create a new bundle, with iPOJO annoations
 
  +
import org.apache.felix.ipojo.annotations.Provides;
3. Import package API
 
  +
import org.apache.felix.ipojo.annotations.ServiceProperty;
4. Create a component providing IJobListener
 
  +
import org.apache.felix.service.command.Descriptor;
5. Modify the schedule component to require the listeners, as optional requirement, and notify them on job creation/deletion
 
6. Test
 
</pre>
 
=IV. Config Admin with iPOJO=
 
   
  +
@Component(immediate = true)
=V. JMX MBean with iPOJO=
 
  +
// Provide the component class as a service
1. Create a new bundle, with iPOJO annotations to list current jobs status
 
  +
@Provides(specifications = ListComponentsCommand.class)
  +
// Instantiate the component automatically
  +
@Instantiate
  +
public class ListComponentsCommand {
   
  +
// Gogo scope/namespace
  +
@ServiceProperty(name = "osgi.command.scope", value = "test")
  +
String scope;
   
  +
// Gogo commands provided by this class (must be methods names)
=VI. Gogo Command with iPOJO=
 
  +
@ServiceProperty(name = "osgi.command.function", value = "{}")
1. Create a new bundle, with iPOJO annotations to list current jobs status
 
  +
String[] function = new String[] { "test" };
  +
  +
// Method description
  +
@Descriptor("test")
  +
public void test() {
  +
System.out.println("test!");
  +
}
  +
}
  +
</pre>
  +
  +
=VI. Config Admin with iPOJO=
  +
  +
See [http://felix.apache.org/site/combining-ipojo-and-configuration-admin.html Combining iPOJO and Configuration Admin]
   
 
=VII. Event Admin with iPOJO=
 
=VII. Event Admin with iPOJO=
 
1. Create a new bundle, with iPOJO annotations to send events on jobs status changes
 
1. Create a new bundle, with iPOJO annotations to send events on jobs status changes
   
=IX. Extender with iPOJO=
+
=VIII. Extender with iPOJO=
 
??
 
??
   
=X. Guidelines & Good Practices ("OSGi Zen")=
+
=Guidelines & Good Practices ("OSGi Zen")=
  +
  +
* Always separate API and implementations
  +
* Always release services and resources once you're done or when their bundle is stopping
  +
* Always indicate requested/provided packages versions
  +
* Avoid <code>public static</code> class members
  +
** <code>private static</code> are allowed
  +
** Use interfaces to declare constants

Latest revision as of 12:17, 20 December 2012

Revenir au sommaire

Requirements

  • Felix Framework Distribution (voir Tutorial OSGi avec Apache Felix - Partie 1)
    • with the iPOJO Core bundle[1] (see next section)
  • Eclipse.
    • To support OSGi development, Eclipse needs the PDE (Plug-in Development Environment) plug-in, bundled in Eclipse IDE for Java EE Developers
  • iPOJO Nature plug-in for Eclipse, available as an archive distribution
    • Install the iPOJO Project Nature and iPOJO Nature Dependencies plug-ins

Set up your Felix installation

  1. Run Felix using the following command in the Felix installation directory
    • java -jar bin/felix.jar
  2. Install iPOJO, using the following Felix Gogo shell commands:
    • obr:list
    • obr:deploy -s "Apache Felix Log Service"
    • obr:deploy -s "Apache Felix iPOJO"
    • obr:deploy -s "Apache Felix iPOJO Gogo Command"

Useful Gogo commands

Note: To ease shell lines edition on Linux, you might use the rlwrap tool: rlwrap java -jar bin/felix.jar

  • help {command}
    • Print the help of the given command
  • lb
    • Lists the bundles installed in the framework
  • inspect capability service {bundle ID}
    • Lists the services provided by the given bundle
    • capability can be written c
  • inspect requirement service {bundle ID}
    • Lists the services consumed by the given bundle
    • requirement can be written r
  • ipojo:factories
    • Prints the publicly available iPOJO factories
  • ipojo:factory {factory name}
    • Prints the details of the given iPOJO factory
  • ipojo:instances
    • Prints the names and states of iPOJO instances
  • ipojo:instance {instance name}
    • Prints the details of the given iPOJO component instance

Eclipse Configuration

By default, Eclipse uses its installation directory as the Target Platform, i.e. the group of bundles accessible by your bundle project. You should declare the installation folder of the Felix Framework as the target platform to avoid compilation and access rights errors.

  1. Open Eclipse preferences
  2. Plug-in Development > Target Platform
  3. Add...
    1. Create an empty target platform (check nothing...)
    2. Add the bin and bundle folders of your Felix installation to the Target Platform
  4. Set the new target platform as the current one

I. Activator

  1. Create an Eclipse Plug-in project fr.erods.tp.bundle1
    • New > Plug-in project
    • Select the option "Target Platform" > "an OSGi framework" > standard
    • Next
    • Check "Generate an activator"
    • Finish
  2. Open the activator class and print some text when the bundle starts and stops
  3. Export the bundle JAR file with iPOJO Nature
    • Right click on the bundle project > Export > iPOJO Bundle
    • Choose an output folder (the Felix installation folder)
    • Finish
  4. Install and test the bundle using the Felix shell (start/update/stop)

Questions:

  • Indicate when start() and stop() methods are called.
  • Why the BundleContext can be kept as a private static class member ?

II. Pure-OSGi service

  1. Create an new bundle project fr.erods.tp.api.
    • Activator is not needed: uncheck "Generate an activator" in step 2
  2. Create the fr.erods.tp.api.IScheduleService interface with two methods:
    • int createJob(Runnable aRunnable, int aDelay)
    • void deleteJob(int aId)
  3. Export the API package
    • Open the Manifest.mf file of fr.erods.tp.api
    • In tab "Runtime" > "Exported Packages" > Add
      • Select the fr.erods.tp.api package
  4. Import the API package in bundle 1
    • Open the Manifest.mf file of fr.erods.tp.bundle1
    • In tab "Dependencies" > "Imported Packages" > Add
    • Select the fr.erods.tp.api package
  5. Implement the service in bundle 1
    • Create the class fr.erods.tp.bundle1.ScheduleImpl, implementing IScheduleService in the bundle 1.
  6. Register the service with the activator of bundle 1
  7. Implement a service consumer in a new bundle
    • Create project fr.erods.tp.bundle2
    • Import the API package
      • In the Manifest.mf file of bundle 2
      • In tab "Dependencies" > "Imported Packages" > Add
      • Select the fr.erods.tp.api package
    • Implement a Consumer class, implementing the OSGi ServiceListener[2] interface
    • Print a message when the service is bound/unbound
  8. Test the bundles in Felix

Questions:

II. iPOJO: provide and consume with iPOJO

  1. Add the iPOJO Nature to projects bundle 1 and bundle 2
    • Select the projects
    • Right click > Add iPOJO Nature
    • Check Use annotations

Consumer

  1. Implement a ConsumerIpojo class in bundle 2
    • See How to use iPOJO Annotations
    • The component must be instantiated automatically
    • Print a message when the component is (in)validated and when the service is (un)bound
  2. Test
  3. Set the service requirement as optional
  4. Test

Questions:

  • Why are the callbacks called in that order ?
  • What happens if you use a missing optional requirement ?

Provider

  1. Implement a ServiceProviderIpojo class in bundle 1
    • See How to use iPOJO Annotations
    • The component must be instantiated automatically
    • The component must provide its service
    • Print a message when the component is (in)validated
  2. Test
  3. Add a service controller and alternate the service presence
  4. Test

Question:

  • How does the consumers react to the service presence cycle ?

Bonus

  1. Add a "service.ranking" property to the pure-OSGi and the iPOJO service, and an "implementation" property to identify them
  2. Print the name of the implementation the consumers are bound to
  3. Modify the ranking value

Question:

  • How does a service the service selection works ?

Note: The consumer should call methods of the service and traces should be printed in order to show which service the consumer is bound to.

III. Whiteboard pattern

  1. Create interface IJobListener in the API package, with two methods:
    • void onJobCreation(int aId)
    • void onJobDeletion(int aId)
  2. Create a new bundle project fr.erods.tp.bundle3
    • Add the iPOJO Nature and annotations
    • Let it import the API package
  3. Create a component providing an IJobListener service in bundle 3
  4. Add job creation / deletion operations in a consumer

Subscription pattern

  1. Modify the schedule component to allow listeners registration:
    • void addListener(IJobListener)
    • void removeListener(IJobListener)
  2. Register component of bundle 3 as a job listener
  3. Test

Whiteboard pattern

  1. Modify the schedule component (in bundle 1)
    • Remove the registration methods created above
    • Add a requirement to an array of IJobListeners, as an optional requirement
    • Notify them on job creation/deletion
  2. Test

Questions:

  • What are the pros/cons of the whiteboard pattern vs. the subscription pattern ?
  • Bonus: compare this whiteboard pattern implementation with the iPOJO Whiteboard pattern handler[3]

IV. JMX MBean with iPOJO

  1. Create a new bundle, with iPOJO Nature and annotations
  2. Create a component, which doesn't provide any service, but contains public methods to create/delete/list IScheduleService jobs
  3. Use iPOJO JMX annotations[4] to export those methods
  4. You will need the iPOJO JMX handler to test this component: install it using the following command in the Felix shell:
    • obr:deploy -s "Apache Felix iPOJO JMX Handler
  5. Install the bundle in the Felix framework
  6. Run jconsole (in the Oracle Java distribution) or jvisualvm (in the Oracle Java distribution) or visualvm[5]
    • You might need to install the JMX plug-in in VisualVM:
      • Tools > Plugins > Available Plugi-ns
      • Install the VisualVM-MBeans plug-in
  7. Connect VisualVM to your Felix JVM and play with the JMX beans

V. Gogo Command with iPOJO

  1. Create a new bundle, with iPOJO Nature and annotations
  2. Create a Gogo command provider to create and delete jobs, and to list current jobs status

Minimal Gogo command component

import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.ServiceProperty;
import org.apache.felix.service.command.Descriptor;

@Component(immediate = true)
// Provide the component class as a service
@Provides(specifications = ListComponentsCommand.class)
// Instantiate the component automatically
@Instantiate
public class ListComponentsCommand {

    // Gogo scope/namespace
    @ServiceProperty(name = "osgi.command.scope", value = "test")
    String scope;

    // Gogo commands provided by this class (must be methods names)
    @ServiceProperty(name = "osgi.command.function", value = "{}")
    String[] function = new String[] { "test" };

    // Method description
    @Descriptor("test")
    public void test() {
        System.out.println("test!");
    }
}

VI. Config Admin with iPOJO

See Combining iPOJO and Configuration Admin

VII. Event Admin with iPOJO

1. Create a new bundle, with iPOJO annotations to send events on jobs status changes

VIII. Extender with iPOJO

??

Guidelines & Good Practices ("OSGi Zen")

  • Always separate API and implementations
  • Always release services and resources once you're done or when their bundle is stopping
  • Always indicate requested/provided packages versions
  • Avoid public static class members
    • private static are allowed
    • Use interfaces to declare constants