SPI Fly :: Apache Aries (original) (raw)

To use SPI Fly you need to decide whether to use the dynamic weaving bundle, dynamic weaving framework extension or static weaving version. More information about this can be found in the usage section.

To use the dynamic weaving bundle

<dependency>
    <groupId>org.apache.aries.spifly</groupId>
    <artifactId>org.apache.aries.spifly.dynamic.bundle</artifactId>
    <version>${spifly.version}</version>
</dependency>
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm</artifactId>
    <version>${asm.version}</version>
</dependency>
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm-commons</artifactId>
    <version>${asm.version}</version>
</dependency>
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm-util</artifactId>
    <version>${asm.version}</version>
</dependency>

To use the dynamic weaving framework extension

<dependency>
    <groupId>org.apache.aries.spifly</groupId>
    <artifactId>org.apache.aries.spifly.dynamic.framework.extension</artifactId>
    <version>${spifly.version}</version>
</dependency>

To use the static weaving bundle

<dependency>
    <groupId>org.apache.aries.spifly</groupId>
    <artifactId>org.apache.aries.spifly.static.bundle</artifactId>
    <version>${spifly.version}</version>
</dependency>

Building the code

To build, use Maven 3.x and run mvn install

Configuration: OSGi Spec-compliant

All the details surrounding this type of configuration are covered in the OSGi Enterprise Specification, chapter 133. This section provides a short overview.

Providers

SPI provider bundles opt in to being registered by specifying a requirement on the osgi.serviceloader.registrar extender. This is done by adding the following Bundle Manifest header. Without this they will not be considered by SPI Fly:

Require-Capability: \
  osgi.extender; \
    filter:="(osgi.extender=osgi.serviceloader.registrar)"

Additionally, they need to provide capabilities for all the APIs that are exposed through this mechanism, for example:

Provide-Capability: \
  osgi.serviceloader; \
    osgi.serviceloader=org.apache.aries.spifly.mysvc.MySPIProvider

While this example omits it, it is advisable to specify uses constraints on the Provide-Capability header, to ensure consistent class spaces.

See the spi-fly-example-provider2-bundle for an example of a spec-compliant provider.

Consumers

An SPI consumer (i.e. a bundle using the java.util.ServiceLoader.load() API) needs to specify required capabilities in the Required-Capability header. Two different types of requirements must be specified:

  osgi.extender; \  
    filter:="(osgi.extender=osgi.serviceloader.processor)"  

Without this requirement the bundle will not be considered for processing.

  osgi.serviceloader; \  
    filter:="(osgi.serviceloader=org.apache.aries.spifly.mysvc.MySPIProvider)";cardinality:=multiple  

Note that the cardinality directive is specified to allow multiple bundles to provide the requested capability, allowing provided services to come from more than one provider bundle.

All requirements are combined into a single Require-Capability header:

Require-Capability: \
  osgi.serviceloader; \
    filter:="(osgi.serviceloader=org.apache.aries.spifly.mysvc.MySPIProvider)"; \
    cardinality:=multiple, \
  osgi.extender; \
    filter:="(osgi.extender=osgi.serviceloader.processor)"

See the spi-fly-example-client2-bundle for an example of a spec-compliant consumer.

Configuration: SPI Fly-specific

This section describes how to use SPI Fly’s proprietary configuration mechanism. It provides more features, but doesn’t provide the portability that spec-compliance configuration gives. If you are only using SPI Fly with java.util.ServiceLoader or you are only using the provided services through the OSGi Service Registry, then consider using the spec-compliant configuration for portability.

Providers

First for all, SPI Fly needs to be made aware of any bundles that provide the services. These bundles are made visible through the TCCL for the duration of the ServiceLoader.load() (or similar) call.

To mark a bundle as a Provider, set the SPI-Provider manifest header:

Additionally services found in META-INF/services are registered in the OSGi Service Registry.

The SPI-Provider header can either be set in the providing bundle itself or in a wrapper bundle that holds the original unmodified jar containing the provider internally as a on the Bundle-ClassPath.

See the spi-fly-example-provider1-bundle for an example of a provider using this type of configuration.

Consumers

Service consumers also need to opt in to the process.

To specify a consumer, add the SPI-Consumer manifest header to the client bundle. This header will opt-in the bundle to the weaving process where for the duration of the specified call the TCCL will be set to the matching provider bundle(s).

Some example SPI-Consumer headers are:

See the spi-fly-example-client1-bundle for an example of a consumer using this type of configuration.

Service Properties

When a provider is published as an OSGi service it will gain some automatic service properties.

Table 1. Default Service Properties

Property Value
serviceloader.mediator The bundle Id of the SPI bundle (SPI Fly)
.org.apache.aries.spifly.provider.implclass The implementation class of the registered service
.org.apache.aries.spifly.provider.discovery.mode The mode which was used to discover the service; SPI_PROVIDER_HEADER AUTO_PROVIDERS_PROPERTY or SERVICELOADER_CAPABILITIES

Any additional attributes found on the SPI-Provider header, or the capability will be used as service properties allowing great flexibility for identifying registered services.

Prototype Scope Service

By default services provided are registered using an org.osgi.framework.ServiceFactory. This means that by default services are bundle scoped. However, it is possible to provide prototype scoped services by adding a service property service.scope=prototype which will cause SPI Fly to register the service using a org.osgi.framework.PrototypeServiceFactory.

Special Cases

SPI Fly can be used for most SPI provider/lookup systems that use the TCCL pattern to obtain implementations. However, in some cases special treatment is needed. This special treatment is often needed when the API itself does not match the name of the resources in META-INF/services, java.util.ServiceLoader is such a case, however SPI-Fly has built-in knowledge of ServiceLoader. Known APIs that require special treatment are listed below:

Usage: There are currently two ways to use the SPI Fly component.

  1. If you have an OSGi 4.3 (or higher) compliant framework that supports WeavingHooks you can use the dynamic weaving approach for which you have 3 options:
    1. dynamic weaving bundle
    2. dynamic weaving framework extension
    3. dynamic weaving auto properties
  2. If you have an pre-4.3 OSGi framework or don’t want to use bytecode weaving at runtime you can use the static weaving approach.

Dynamic Weaving Bundle

Install and start the org.apache.aries.spifly.dynamic.bundle into the system (see dependencies).

g! lb START LEVEL 1 ID|State |Level|Name 0|Active | 0|System Bundle ... 5|Active | 1|ASM all classes 7|Active | 1|Apache Aries SPI Fly Dynamic Weaving Bundle

Note that, as with any OSGi Bundle that uses the OSGi 4.3 WeavingHooks, the weaver bundle (org.apache.aries.spifly.dynamic.bundle in the SPI Fly case) needs to be active before any bundles that need to be dynamically woven. OSGi Start Levels can provide a mechanism to control this.

Dynamic Weaving Framework Extension

Install and start the org.apache.aries.spifly.dynamic.framework.extension into the system (see dependencies).

g! lb START LEVEL 1 ID|State |Level|Name 0|Active | 0|System Bundle ... 7|Active | 1|Apache Aries SPI Fly Dynamic Framework Extension

Note that, the framework extension bundle (org.apache.aries.spifly.dynamic.framework.extension in the SPI Fly case) attaches to and extends the system.bundle. As such it will always be active before any consumer or provider bundles that have a requirement on it (via the osgi.serviceloader.processor or osgi.serviceloader.registrar requirements.)

Dynamic Weaving by auto properties

Install and start either the bundle or framework.extension as described above, then provide the following framework properties:

org.apache.aries.spifly.auto.consumers=jakarta.*  
org.apache.aries.spifly.auto.providers=" \  
    com.sun.*;vendor=Oracle, \  
    my.provider;vendor=Me;service.scope=prototype"  

Note: The syntax for these two properties matches the Common Header Syntax defined by the OSGi Core specification.

Use with Static Weaving

For static use, you need to weave the client bundle before installing it into the system. The modification changes the byte code around java.util.ServiceLoader.load() or other calls in the bundle and inserts calls to set the correct ThreadContextClassLoader around it. Provider bundles are still handled dynamically.

To statically weave a bundle

The easiest way to invoke the static weaver is to take the org.apache.aries.spifly.static.tool jar with dependencies. Then run the static tool on any bundle that needs processing:

java -jar org.apache.aries.spifly.static.tool-1.0.2-jar-with-dependencies.jar mybundle.jar

This will produce a second bundle with the same name with the _spifly suffix appended, so in this case the generated bundle will be called mybundle_spifly.jar.

At runtime, install the org.apache.aries.spifly.static.bundle into the system:

g! lb START LEVEL 1 ID|State |Level|Name 0|Active | 0|System Bundle ... 6|Active | 1|Apache Aries SPI Fly Static Weaving Bundle

Then install and start the statically woven bundle into the system.