Quick-start

Eager to get started? This page gives a good introduction to iPOPO. It assumes you already have iPOPO installed. If you do not, head over to the Installation section.

Play with the shell

The easiest way to see how iPOPO works is by playing with the builtin shell.

To start the shell locally, you can run the following command:

bash$ python -m pelix.shell
** Pelix Shell prompt **
$

Survival Kit

As always, the life-saving command is help:

$ help
=== Name space 'default' ===
- ? [<command>]
                Prints the available methods and their documentation,
                or the documentation of the given command.
- bd <bundle_id>
                Prints the details of the bundle with the given ID
                or name
- bl [<name>]
                Lists the bundles in the framework and their state.
                Possibility to filter on the bundle name.
...
$

The must-be-known shell commands of iPOPO are the following:

Command Description
help Shows the help
loglevel Prints/Changes the log level
exit Quits the shell (and stops the framework in console UI)
threads Prints the stack trace of all threads
run Runs a Pelix shell script

Bundle commands

The following commands can be used to handle bundles in the framework:

Command Description
install Installs a module as a bundle
start Starts the given bundle
update Updates the given bundle (restarts it if necessary)
uninstall Uninstalls the given bundle (stops it if necessary)
bl Lists the installed bundles and their state
bd Prints the details of a bundle

In the following example, we install the pelix.shell.remote bundle, and play a little with it:

$ install pelix.shell.remote
Bundle ID: 14
$ start 14
Starting bundle 14 (pelix.shell.remote)...
$ bl
+----+----------------------------------------+--------+---------+
| ID |                  Name                  | State  | Version |
+====+========================================+========+=========+
| 0  | pelix.framework                        | ACTIVE | 1.0.0   |
+----+----------------------------------------+--------+---------+
...
+----+----------------------------------------+--------+---------+
| 14 | pelix.shell.remote                     | ACTIVE | 1.0.0   |
+----+----------------------------------------+--------+---------+
15 bundles installed
$ update 14
Updating bundle 14 (pelix.shell.remote)...
$ stop 14
Stopping bundle 14 (pelix.shell.remote)...
$ uninstall 14
Uninstalling bundle 14 (pelix.shell.remote)...
$

While the install command requires the name of a module as argument, all other commands accepts a bundle ID as argument.

Service Commands

Services are handles by bundles and can’t be modified using the shell. The following commands can be used to check the state of the service registry:

Command Description
sl Lists the registered services
sd Prints the details of a services

This sample prints the details about the iPOPO core service:

$ sl
+----+---------------------------+----------------------------------------------------+---------+
| ID |      Specifications       |                       Bundle                       | Ranking |
+====+===========================+====================================================+=========+
| 1  | ['ipopo.handler.factory'] | Bundle(ID=5, Name=pelix.ipopo.handlers.properties) | 0       |
+----+---------------------------+----------------------------------------------------+---------+
...
+----+---------------------------+----------------------------------------------------+---------+
| 8  | ['pelix.ipopo.core']      | Bundle(ID=1, Name=pelix.ipopo.core)                | 0       |
+----+---------------------------+----------------------------------------------------+---------+
...
16 services registered
$ sd 8
ID............: 8
Rank..........: 0
Specifications: ['pelix.ipopo.core']
Bundle........: Bundle(ID=1, Name=pelix.ipopo.core)
Properties....:
        objectClass = ['pelix.ipopo.core']
        service.id = 8
        service.ranking = 0
Bundles using this service:
        Bundle(ID=4, Name=pelix.shell.ipopo)
$

iPOPO Commands

iPOPO provides a set of commands to handle the components and their factories:

Command Description
factories Lists registered component factories
factory Prints the details of a factory
instances Lists components instances
instance Prints the details of a component
waiting Lists the components waiting for an handler
instantiate Starts a new component instance
kill Kills a component
retry Retry the validation of an erroneous component

This snippets installs the pelix.shell.remote bundle and instantiate a new remote shell component:

$ install pelix.shell.remote
Bundle ID: 15
$ start 15
Starting bundle 15 (pelix.shell.remote)...
$ factories
+------------------------------+----------------------------------------+
|           Factory            |                 Bundle                 |
+==============================+========================================+
| ipopo-remote-shell-factory   | Bundle(ID=15, Name=pelix.shell.remote) |
+------------------------------+----------------------------------------+
| ipopo-shell-commands-factory | Bundle(ID=4, Name=pelix.shell.ipopo)   |
+------------------------------+----------------------------------------+
2 factories available
$ instantiate ipopo-remote-shell-factory rshell pelix.shell.address=0.0.0.0 pelix.shell.port=9000
Component 'rshell' instantiated.

A remote shell as been started on port 9000 and can be accessed using Netcat:

bash$ nc localhost 9000
------------------------------------------------------------------------
** Pelix Shell prompt **

iPOPO Remote Shell
------------------------------------------------------------------------
$

The remote shell gives access to the same commands as the console UI. Note that an XMPP version of the shell also exists.

To stop the remote shell, you have to kill the component:

$ kill rshell
Component 'rshell' killed.

Finally, to stop the shell, simply run the exit command or press Ctrl+D.

Hello World!

In this section, we will create a service provider and its consumer using iPOPO. The consumer will use the provider to print a greeting message as soon as it is bound to it. To simplify this first sample, the consumer can only be bound to a single service and its life-cycle is highly tied to the availability of this service.

Here is the code of the provider component, which should be store in the provider module (provider.py). The component will provide a service with of the hello.world specification.

from pelix.ipopo.decorators import ComponentFactory, Provides, Instantiate

# Define the component factory, with a given name
@ComponentFactory("service-provider-factory")
# Defines the service to provide when the component is active
@Provides("hello.world")
# A component must be instantiated as soon as the bundle is active
@Instantiate("provider")
# Don't forget to inherit from object, for Python 2.x compatibility
class Greetings(object):
      def hello(self, name="World"):
          print("Hello,", name, "!")

Start a Pelix shell like shown in the previous section, then install and start the provider bundle:

** Pelix Shell prompt **
$ install provider
Bundle ID: 14
$ start 14
Starting bundle 14 (provider)...
$

The consumer will require the hello.world service and use it when it is validated, i.e. once this service has been injected. Here is the code of this component, which should be store in the consumer module (consumer.py).

from pelix.ipopo.decorators import ComponentFactory, Requires, Instantiate, \
     Validate, Invalidate

# Define the component factory, with a given name
@ComponentFactory("service-consumer-factory")
# Defines the service required by the component to be active
# The service will be injected in the '_svc' field
@Requires("_svc", "hello.world")
# A component must be instantiated as soon as the bundle is active
@Instantiate("consumer")
# Don't forget to inherit from object, for Python 2.x compatibility
class Consumer(object):
      @Validate
      def validate(self, context):
          print("Component validated, calling the service...")
          self._svc.hello("World")
          print("Done.")

      @Invalidate
      def invalidate(self, context):
         print("Component invalidated, the service is gone")

Install and start the consumer bundle in the active Pelix shell and play with the various commands described in the previous section:

$ install consumer
Bundle ID: 15
$ start 15
Starting bundle 15 (consumer)...
Component validated, calling the service...
Hello, World !
Done.
$ update 14
Updating bundle 14 (provider)...
Component invalidated, the service is gone
Component validated, calling the service...
Hello, World !
Done.
$ uninstall 14
Uninstalling bundle 14 (provider)...
Component invalidated, the service is gone

Hello from somewhere else!

This section reuses the bundles written in the Hello World sample and starts them into two distinct frameworks. The consumer will use the service provided from the other framework.

To achieve that, we will use the Pelix Remote Services, a set of bundles intending to share services across multiple Pelix frameworks. A reference card provides more information about this feature.

Core bundles

First, we must install the core bundles of the remote services implementation: the Imports Registry (pelix.remote.registry) and the Exports Dispatcher (pelix.remote.dispatcher). Both handle the description of the shared services, not their link with the framework: this will be the job of the discovery and transport providers. The discovery provider we will use requires to access the content of the Exports Dispatcher of the frameworks it finds, through HTTP requests. A component, the dispatcher servlet, must be instantiated to answer to those requests:

bash$ python -m pelix.shell
** Pelix Shell prompt **
$ install pelix.remote.registry
Bundle ID: 14
$ start 14
Starting bundle 14 (pelix.remote.registry)...
$ install pelix.remote.dispatcher
Bundle ID: 15
$ start 15
Starting bundle 15 (pelix.remote.dispatcher)...
$ instantiate pelix-remote-dispatcher-servlet-factory dispatcher-servlet
Component 'dispatcher-servlet' instantiated.

The protocols we will use for discovery and transport depends on an HTTP server. As we are using two framework on the same machine, don’t forget to use different HTTP ports for each framework:

$ install pelix.http.basic
Bundle ID: 16
$ start 16
Starting bundle 16 (pelix.http.basic)...
$ instantiate pelix.http.service.basic.factory httpd pelix.http.port=8000
INFO:httpd:Starting HTTP server: [0.0.0.0]:8000 ...
INFO:httpd:HTTP server started: [0.0.0.0]:8000
Component 'httpd' instantiated.

The dispatcher servlet will be discovered by the newly started HTTP server and will be able to answer to clients.

Discovery and Transport

Next, it is necessary to setup the remote service discovery layer. Here, we’ll use a Pelix-specific protocol based on UDP multicast packets. By default, this protocol uses the UDP port 42000, which must therefore be accessible on any machine providing or consuming a remote service.

Start two Pelix frameworks with their shell and, in each one, install the pelix.remote.discovery.multicast bundle then instantiate the discovery component:

$ install pelix.remote.discovery.multicast
Bundle ID: 17
$ start 17
Starting bundle 17 (pelix.remote.discovery.multicast)...
$ instantiate pelix-remote-discovery-multicast-factory discovery
Component 'discovery' instantiated.

Finally, you will have to install the transport layer that will be used to send requests and to wait for their responses. Here, we’ll use the JSON-RPC protocol (pelix.remote.json_rpc), which is the easiest to use (e.g. XML-RPC has problems handling dictionaries of complex types). Transport providers often require to instantiate two components: one handling the export of services and one handling their import. This allows to instantiate the export part only, avoiding every single framework to know about all available services:

$ install pelix.remote.json_rpc
Bundle ID: 18
$ start 18
Starting bundle 18 (pelix.remote.json_rpc)...
$ instantiate pelix-jsonrpc-importer-factory importer
Component 'importer' instantiated.
$ instantiate pelix-jsonrpc-exporter-factory exporter
Component 'exporter' instantiated.

Now, the frameworks you ran have all the necessary bundles and services to detect and use the services of their peers.

Export a service

Exporting a service is as simple as providing it: just add the service.exported.interfaces property while registering it and will be exported automatically. To avoid typos, this property is defined in the pelix.remote.PROP_EXPORTED_INTERFACES constant. This property can contain either a list of names of interfaces/contracts or a star (*) to indicate that all services interfaces are exported.

Here is the new version of the hello world provider, with the export property:

from pelix.ipopo.decorators import ComponentFactory, Provides, \
    Instantiate, Property
from pelix.remote import PROP_EXPORTED_INTERFACES

@ComponentFactory("service-provider-factory")
@Provides("hello.world")
# Here is the new property, to authorize the export
@Property('_export_itfs', PROP_EXPORTED_INTERFACES, '*')
@Instantiate("provider")
class Greetings(object):
      def hello(self, name="World"):
          print("Hello,", name, "!")

That’s all!

Now you can install this provider in a framework, using:

$ install provider
Bundle ID: 19
$ start 19
Starting bundle 19 (provider)...

When installing a consumer in another framework, it will see the provider and use it:

$ install consumer
Bundle ID: 19
$ start 19
Component validated, calling the service...
Done.

You should then see the greeting message (Hello, World !) in the shell of the provider that has been used by the consumer.

You can now continue to the Tutorials