iPOPO is a Python-based Service-Oriented Component Model (SOCM) based on Pelix, a dynamic service platform. They are inspired on two popular Java technologies for the development of long-lived applications: the iPOJO component model and the OSGi Service Platform. iPOPO enables to conceive long-running and modular IT services.
See https://ipopo.coderxpress.net for documentation and more information.
Contents
iPOPO is available on PyPI and can be
installed using pip
:
# Install system-wide
$ sudo pip install iPOPO
# ... or user-wide installation
$ pip install --user iPOPO
$ git clone https://github.com/tcalmant/ipopo.git
$ cd ipopo
$ python setup.py install
To check if Pelix is installed correctly, run the following command:
$ python -m pelix.shell --version
Pelix 0.6.3 from /home/tcalmant/git/ipopo/pelix/framework.py
Pelix brings the concept of bundle in Python. A bundle is a module with a life cycle: it can be installed, started, stopped, updated and uninstalled.
A bundle can declare a class acting as bundle activator, using the
@BundleActivator
decorator.
This class will be instantiated by the framework and its start()
and
stop()
method will be called to notify the bundle about its activation and
deactivation.
When it is active, a bundle can register services. A service is an object implementing a specification and associated to a set of properties. A component will then be able to select and consume a service according to the specification(s) it provides and to its properties.
The components are a concept brought by iPOPO. A component, or component instance, is an object managed by a container. The container handles the interactions between the component and the Pelix framework. That way, the component contains only the code required for its task, not for its bindings with the framework. A component is an instance of a component factory, a class manipulated by iPOPO decorators.
For more information, see the concepts page on the wiki.
This sample gives a quick overview of the usage of iPOPO. For more information, take a look at iPOPO in 10 minutes.
The following code defines a component factory (a class) which instances will
provide a sample.hello
service.
# iPOPO decorators
from pelix.ipopo.decorators import ComponentFactory, Provides, Instantiate
# Manipulates the class and sets its (unique) factory name
@ComponentFactory("hello-provider-factory")
# Indicate that the components will provide a service
@Provides("sample.hello")
# Tell iPOPO to instantiate a component instance as soon as the file is loaded
@Instantiate("hello-provider-auto")
# A component class must always inherit from object (new-style class)
class HelloProvider(object):
"""
A sample service provider
"""
def hello(self, name="world"):
"""
Says hello
"""
print("Hello,", name, "!")
def bye(self, name="cruel world"):
"""
Says bye
"""
print("Bye,", name, "!")
When the bundle providing this component factory will be started, iPOPO will
automatically instantiate a component, due to the @Instantiate
decorator.
It is also possible to instantiate a component using shell commands.
Each component instance will provide a sample.hello
service, which can be
consumed by any bundle or any other component.
The following code defines a component factory (a class) which instances will
consume a sample.hello
service. If multiple services are available, iPOPO
will select the one with the highest rank and the lowest service ID
(i.e. the oldest service).
# iPOPO decorators
from pelix.ipopo.decorators import ComponentFactory, Requires, Instantiate, \
Validate, Invalidate
# Manipulates the class and sets its (unique) factory name
@ComponentFactory("hello-consumer-factory")
# Indicate that the components require a sample.hello service to work
# and to inject the found service in the _svc field
@Requires('_svc', "sample.hello")
# Tell iPOPO to instantiate a component instance as soon as the file is loaded
@Instantiate("hello-consumer-auto")
# A component class must always inherit from object (new-style class)
class HelloConsumer(object):
"""
A sample service consumer
"""
def __init__(self):
"""
Defines (injected) members
"""
self._svc = None
@Validate
def validate(self, context):
"""
Component validated: all its requirements have been injected
"""
self._svc.hello("Consumer")
@Invalidate
def invalidate(self, context):
"""
Component invalidated: one of its requirements is going away
"""
self._svc.bye("Consumer")
When the bundle providing this component factory will be started, iPOPO will
automatically instantiate a component, due to the @Instantiate
decorator.
Each component instance will require a sample.hello
service. Once iPOPO
has injected all the required services (here, a single sample.hello
service)
in a component instance, this instance will be considered valid and iPOPO
will call its method decorated by @Validate
.
There, the component can consume its dependencies, start threads, etc..
It is recommended for this method to start threads and to return quickly, as it
blocks iPOPO and the Pelix framework.
When a required service is unregistered by its provider, the component instances
consuming it are invalidated.
When the method decorated by @Invalidate
is called, the service is still
injected and should be usable (except for special cases, like remote services).
To run this sample, you'll need to copy the snippets above in different files:
- copy the Service provider snippet in a file called provider.py
- copy the Service consumer snippet in a file called consumer.py
Then, run a Pelix shell in the same folder as those files, and execute the commands listed in this trace:
$ python -m pelix.shell
** Pelix Shell prompt **
$ # Install the bundles
$ install provider
Bundle ID: 11
$ install consumer
Bundle ID: 12
$ # Start the bundles (the order isn't important here)
$ start 11 12
Starting bundle 11 (provider)...
Starting bundle 12 (consumer)...
Hello, Consumer !
$ # View iPOPO instances
$ instances
+----------------------+------------------------------+-------+
| Name | Factory | State |
+======================+==============================+=======+
| hello-consumer-auto | hello-consumer-factory | VALID |
+----------------------+------------------------------+-------+
| hello-provider-auto | hello-provider-factory | VALID |
+----------------------+------------------------------+-------+
| ipopo-shell-commands | ipopo-shell-commands-factory | VALID |
+----------------------+------------------------------+-------+
3 components running
$ # View details about the consumer
$ instance hello-consumer-auto
Name.....: hello-consumer-auto
Factory..: hello-consumer-factory
Bundle ID: 12
State....: VALID
Services.:
Dependencies:
Field: _svc
Specification: sample.hello
Filter......: None
Optional.....: False
Aggregate....: False
Handler......: SimpleDependency
Bindings:
ServiceReference(ID=11, Bundle=11, Specs=['sample.hello'])
Properties:
+---------------+---------------------+
| Key | Value |
+===============+=====================+
| instance.name | hello-consumer-auto |
+---------------+---------------------+
$ # Modify the provider file (e.g. change the 'Hello' string by 'Hi')
$ # Update the provider bundle (ID: 11)
$ update 11
Updating bundle 11 (provider)...
Bye, Consumer !
Hi, Consumer !
$ # Play with other commands (see help)
First, the install
commands are used to install the bundle: they will be
imported but their activator won't be called. If this command fails, the bundle
is not installed and is not referenced by the framework.
If the installation succeeded, the bundle can be started: it's activator is
called (if any). Then, iPOPO detects the component factories provided by the
bundle and instantiates the components declared using the @Instantiate
decorator.
The instances
and instance
commands can be use to print the state and
bindings of the components. Some other commands are very useful, like sl
and sd
to list the registered services and print their details. Use the
help
command to see which ones can be used.
The last part of the trace shows what happens when updating a bundle.
First, update the source code of the provider bundle, e.g. by changing the
string it prints in the hello()
method.
Then, tell the framework to update the bundle using the update
command.
This command requires a bundle ID, which has been given as a result of the
install
command and can be found using bl
.
When updating a bundle, the framework stops it and reloads it (using imp.reload). If the update fails, the old version is kept. If the bundle was active before the update, it is restarted by the framework.
Stopping a bundle causes iPOPO to kill the component instance(s) of the
factories it provided.
Therefore, no one provides the sample.hello
service, which causes the
consumer component to be invalidated.
When the provider bundle is restarted, a new provider component is instantiated
and its service is injected in the consumer, which becomes valid again.
Pelix/iPOPO comes with some useful services:
Pelix Shell: a simple shell to control the framework (manage bundles, show the state of components, ...). The shell is split in 4 parts:
- the parser: a shell interpreter class, which can be reused to create other shells (with a basic support of variables);
- the shell core service: callable from any bundle, it executes the given command lines;
- the UIs: text UI (console) and remote shell (TCP, XMPP)
- the commands providers: iPOPO commands, report, EventAdmin, ...
See the shell tutorial and the shell reference card for more information.
An HTTP service, based on the HTTP server from the standard library. It provides the concept of servlet, borrowed from Java.
See the HTTP service tutorial for more information.
Remote Services: export and import services to/from other Pelix framework or event Java OSGi frameworks!
See the remote services tutorial and the remote services reference card for more information.
Pelix also provides an implementation of the EventAdmin service, inspired from the OSGi specification.
Feel free to send feedback on your experience of Pelix/iPOPO, via the mailing lists:
- User list: https://groups.google.com/group/ipopo-users
- Development list: https://groups.google.com/group/ipopo-dev
Bugs and features requests can be submitted using the Issue Tracker on GitHub.
All contributions are welcome!
- Create an issue to discuss about your idea or the problem you encounter
- Fork the project
- Develop your changes
- Check your code with pylint and pep8
- If necessary, write some unit tests
- Commit your changes, indicating in each commit a reference to the issue you're working on
- Push the commits on your repository
- Create a Pull Request
- Enjoy!
Please note that your contributions will be released under the project's license, which is the Apache Software License 2.0.
Pelix and iPOPO are tested using Tox and Travis-CI with Pypy 2.5.0 and Python 2.7, 3.3, 3.4 and 3.5.
iPOPO doesn't support Python 2.6 anymore.
iPOPO is released under the Apache Software License 2.0.
- iPOPO now has a logo ! (thanks to @debbabi)
- README file has been rewritten
- Better PEP-8 compliance
- Updated jsonrpclib requirement version to 0.2.6
- Optimization of the service registry (less dictionaries, use of sets, ...)
- Added the
hide_bundle_services()
to the service registry. It is by the framework to hide the services of a stopping bundle fromget_service_reference
methods, and before those services will be unregistered. - Removed the deprecated
ServiceEvent.get_type()
method
- Optimization of StoredInstance (handlers, use of sets, ...)
- Added a
is_header_set()
method to the HTTPServletResponse bean. - Response headers are now sent on
end_headers()
, not onset_header()
, to avoid duplicate headers. - The request queue size of the basic HTTP server can now be set as a component
property (
pelix.http.request_queue_size
)
- The
pelix.shell.console
module can now be run as a main script - Added the report shell command
- Added the name of varargs in the signature of commands
- Corrected the signature shown in the help description for static methods
- Corrected the thread and threads shell commands for pypy
- Added support for keyword arguments in most of remote services transports (all except XML-RPC)
- Added support for
pelix.remote.export.only
andpelix.remote.export.none
service properties...only
tells the exporter to export the given specifications only, while..none
forbids the export of the service.
- Updated the MQTT client to follow the new API of Eclipse Paho MQTT Client
- Travis-CI: Added Python 3.5 and Pypy3 targets
- Better configuration of coverage
- Added tests for the remote shell
- Added tests for the MQTT client and for MQTT-RPC
See the CHANGELOG.rst file to see what changed in previous releases.