2017-11-25

Tamaya OSGI Support

Tamaya OSGI is an extension module. Refer to the extensions documentation for further details.

What functionality this module provides ?

Tamaya OSGI provides support for integration with OSGI. Hereby Tamaya does actively override or extend the OSGI ConfigAdmin based configuration with entries stored and managed by Tamaya. Tamaya provides also shell extensions to enable/perform configuration loading and restoring actions. Optionally Tamaya also provides extension for automatically trigger configuration updates, when configuration has been changed and configuration injection using Tamaya’s injection API.

Compatibility

All module described are based on Java 8, so it will run on Java 8 and beyond. The modules are built against OSGI Compendium version 5.0. Tamaya OSGI support is tested against the following OSGI runtimes:

  • Apache Karaf, version 4.0.7

  • Apache Felix, version 5.6.1

  • Eclipse Equinox, version x.x.x.

Installation

To benefit from Tamaya in an OSGI context you must deploy at least the following modules to your OSGI runtime environment:

# Runtime with OSGI ConfigAdmin support, e.g.
org.apache.felix:org.apache.felix.configadmin:{felix_version}
# API and core
org.apache.geronimo.specs:geronimo-annotation_1.2_spec:1.0
org.apache.tamaya:tamaya-api:{tamaya_version}
org.apache.tamaya:tamaya-spisupport:{tamaya_version}
org.apache.tamaya:tamaya-core:{tamaya_version}
# Required extensions
org.apache.tamaya.ext:tamaya-functions:{tamaya_version}
org.apache.tamaya.ext:tamaya-osgi:{tamaya_version}

Tamaya Service Loading in OSGI

Important to know is that within OSGI class- and resource loading is not compatible with standard Java SE. Also in OSGI, bundles can be loaded or unloaded at any time, so Tamaya’s logic must cope with this as well. These constraints are handled by Tamaya (implemented in tamaya-core and tamaya-osgi) as follows:

  • Tamaya registers a OSGIServiceContext which reads all java.util.ServiceLoader configurations and registers them as OSGI services. Hereby integration is two-way: The core module contains an OSGI Activator that replaces Tamaya’s default ServiceContext with an OSGI based implementation that will consume all services from the OSGI service API. Consequently you can also register Tamaya extensions as OSGI services using standard OSGI tooling (e.g. your own PropertySource instances). Tamaya hereby also does not store any service references, so the dynamic nature of OSGI is fully honored.

  • Tamaya’s ServiceContext SPI does additionally provide functionality for loading of (classpath) resources using the bundle’s getEntry(String) method.

  • Tamaya similarly checks the classpath of all bundles for Tamaya SPI services to be registered thus implementing the ServiceLoader logic in OSGI. Hereby Tamaya will only register services with the org.apache.tamaya as root package.

Note
Tamaya actually does not replace any existing ConfigAdmin component, Tamaya modifies any existing OSGI configuration on changes detected and stores backups of any OSGI configuration before applying any changes.

Configuring Bundles

Mapping of pids and factoryPids

When accessing configuration from the OSGI ConfigAdmin a pid and an optional location can be provided. Tamaya requires all configuration for a PID to be located in keys starting [PID]:

OSGI pid mapping
# OSGI settings
pid=myBundle
key=common.net.port

# Corresponding key in Tamaya configuration
[myBundle]key=common.net.port

Enabling/Disabling Tamaya

By default, Tamaya doesn’t do anything, unless it is told to so so. So having installed the Tamaya OSGI plugin, you will see the bundles are loaded, but your OSGI environment still works the same. This is not accidentally, since configuration is a crucial part. This means Tamaya, by default, is disabled for all bundles. You have now several options to enabled Tamaya:

  • you can enable Tamaya for all bundles by default by

    • setting the -Dtamaya-enabled=true system property.

    • by setting tamaya-enabled=true in the OSGI Configuration for the PID TamayaConfigPlugin.

  • you can enable Tamaya for a single bundle by

    • by setting tamaya-enabled=true in the OSGI Configuration for the given bundle.

    • by adding Tamaya-Enabled: true to the bundle’s MANIFEST.

Similarly you can also combine these options the other way round:

  • You can enable Tamaya by default as shown above.

  • You can disable Tamaya for bundles by

    • by setting tamaya-enabled=false in the OSGI Configuration for the given bundle.

    • by adding Tamaya-Enabled: false to the bundle’s MANIFEST.

Controlling How Tamaya changes your OSGI Configuration

Tamaya supports different policies that define how Tamaya is changing the OSGI configuration:

  • EXTEND: Only add properties not existing in the OSGI configuration, but never override or remove existing properties.

  • OVERRIDE: Override existing properties and also add new properties.

  • UPDATE_ONLY: Only override existing properties but do not add any properties.

By default, Tamaya uses the OVERRIDE policy. Also this policy can be configured in several ways and with different scopes:

  • You can define the default policy applied, by

    • setting the -Dtamaya-policy=POLICY system property.

    • by setting tamaya-policy=POLICY in the OSGI Configuration for the PID TamayaConfigPlugin.

Hereby, POLICY must be one of OVERRIDE, EXTEND, UPDATE_ONLY.

  • You can also configure the policy individually for a bundle by

    • by setting tamaya-policy=POLICY in the OSGI Configuration for the given bundle.

    • by adding Tamaya-Policy: POLICY to the bundle’s MANIFEST.

Mapping OSGI PIDs to Tamaya Configuration

Tamaya configuration is a single Map<String,String> with String keys and String values. Whereas OSGI configuration are multiple +Dictionary<String,?> (for several PIDs). The Tamaya OSGI extension implements the following mapping:

As an example refer to the followinf Tamaya configuration entries:

Tamaya configuration for PID 'MyPlugin'
[MyPlugin]ch.base.pack.Main.customer=Native Inc
[MyPlugin]ch.base.pack.Main.use=234
[MyPlugin]ch.base.pack.Main.encoding=UTF-8

The OSGI Configuration Plugin now provides the following configuration for PID:

OSGI configuration for PID 'MyPlugin'
ch.base.pack.Main.use=100        (Integer)
ch.base.pack.Main.switch=on      (Boolean)
ch.base.pack.Main.customer=NONE  (String)

Now using Policy.OVERRIDE (as desribed in the previous section), Tamaya will change the OSGI configuration as follows:

OSGI configuration after Tamaya update for PID 'MyPlugin'
ch.base.pack.Main.use=234                   (Integer)
ch.base.pack.Main.switch=on                 (Boolean)
ch.base.pack.Main.customer=Native Inc       (String)
[MyPlugin]ch.base.pack.Main.encoding=UTF-8  (String)

So Tamaya configuration mapping can be summarized as follows:

  • The OSGI PID is mapped to a Tamaya prefix [PID].

  • The OSGI keys are the exact same keys as from Tamaya with the [PID] prefix removed.

  • New entries are added (depending on the Policy) as String values.

  • Types of existing entries are preserved on update (this requires the Tamaya entries to be convertable into the required target types. Refer to Tamaya’s core documentation for supported types and how to add custom converters).

Finally, the mapping of the OSGI PID to the Tamaya [PID] prefix also can be customized by

  • adding tamaya-config-root as an OSGI configuration property to the OSGI configuration.

  • adding Tamaya-Config-Root as a MANIFEST entry to the bundle.

The root will replace the default [PID] prefix with the value configured.

OSGI Configuration Backup

Before Tamaya changes any OSGI configuration it creates a Backup of the existing OSGI configuration dictionary and stores it in serialized form in the plugin’s OSGI configuration. This allows you to restore the original OSGI configuration in case of problems. Hereby Tamaya automatically sets the tamaya-enabled=false property to disable Tamaya for the given configuration (bundle).

The history can be accessed from the Tamaya Configuration Plugin Service (shown later).

OSGI Configuration Change Log

All changes applied by Tamaya are logged as well using ConfigHistory entry items. The history can be accessed from the Tamaya Configuration Plugin Service (shown later):

OSGI ConfigHistory Entry
public final class ConfigHistory implements Serializable{

    [...]

    public enum TaskType{
        PROPERTY,
        BEGIN,
        END,
    }

    // ***
    // Entry = attributes
    // ***

    public TaskType getType(){...}

    public String getPid() {... }

    public Object getPreviousValue() {... }

    public ConfigHistory setPreviousValue(Object previousValue) {... }

    public Object getValue() {...}

    public ConfigHistory setValue(Object value) {...}

    public String getKey() {...}

    public ConfigHistory setKey(String key) {...}

}

The Tamaya OSGI Configuration Service

As mentioned Tamaya exposes it’s OSGI functionality, allowing programmatic access to Tamaya configuration logic with the TamayaConfigService OSGI service:

The exposed TamayaConfigService
public interface TamayaConfigService{
    /** The system/config property to set Tamaya's {@link Policy}. */
    String TAMAYA_POLICY_PROP = "tamaya-policy";
    /** The MANIFEST property to set Tamaya's {@link Policy}. */
    String TAMAYA_POLICY_MANIFEST = "Tamaya-Policy";
    /** The system/config property to define a customized Tamaya's configuration root, replacing the {@code [PID]} default
     * prefix used. */
    String TAMAYA_CUSTOM_ROOT_PROP = "tamaya-config-root";
    /** The MANIFEST property to define a customized Tamaya's configuration root, replacing the {@code [PID]} default
     * prefix used. */
    String TAMAYA_CUSTOM_ROOT_MANIFEST = "Tamaya-Config-Root";
    /** The system/config property to enable Tamaya. */
    String TAMAYA_ENABLED_PROP = "tamaya-enabled";
    /** The MANIFEST property to enable Tamaya. */
    String TAMAYA_ENABLED_MANIFEST = "Tamaya-Enabled";
    /** The system/config property to enable Tamaya automatic updates (requires Tamaya's Updater plugin to be loaded as well). */
    String TAMAYA_AUTO_UPDATE_ENABLED_PROP = "tamaya-update-enabled";
    /** The MANIFEST property to enable Tamaya automatic updates (requires Tamaya's Updater plugin to be loaded as well). */
    String TAMAYA_AUTO_UPDATE_ENABLED_MANIFEST = "Tamaya-Update-Enabled";

    /**
     * Enables/disables automatic updates (requires Tamaya's Updater plugin to be loaded as well).
     * @param enabled set to true to enable updates.
     */
    void setAutoUpdateEnabled(boolean enabled);

    /**
     * Enables/disables Tamaya config by default.
     * @param enabled set to true to enable Tamaya for all bundles by default.
     */
    void setTamayaEnabledByDefault(boolean enabled);

    /**
     * Get the flag, if Tamaya is enabled by default for all bundles.
     * @return true if Tamaya is enabled.
     */
    boolean isTamayaEnabledByDefault();

    /**
     * Get the default policy Tamaya is using for adapting OSGI configuration.
     * @return the default policy, never null.
     */
    Policy getDefaultPolicy();

    /**
     * Set the default policy Tamaya is using for adapting OSGI configuration.
     * @param policy the policy, not null.
     */
    void setDefaultPolicy(Policy policy);

    /**
     * Updates the given OSGI configuration with Tamaya configuration.
     * @param pid the target PID, not null.
     * @return the new configuration.
     */
    Dictionary<String,Object> updateConfig(String pid);

    /**
     * Updates the given OSGI configuration with Tamaya configuration.
     * @param pid the target PID, not null.
     * @param dryRun if true, the changes will not be applied to the OSGI configuration.
     * @return the configuration that would be applied, has been applied.
     */
    Dictionary<String,Object> updateConfig(String pid, boolean dryRun);

    /**
     * Updates the given OSGI configuration with Tamaya configuration.
     * @param pid the target PID, not null.
     * @param policy the updating policy to be used, by default.
     * @param forcePolicy if set to true, the given policy will be used, even if an alternate policy is configured
     *                    for the given PID.
     * @param dryRun if true, the changes will not be applied to the OSGI configuration.
     * @return the configuration that would be applied, has been applied.
     */
    Dictionary<String,Object> updateConfig(String pid, Policy policy, boolean forcePolicy, boolean dryRun);

    /**
     * Checks if a bundle is enabled for Tamaya configuration.
     * @param bundle the bundle, not null.
     * @return true, if the bundle is enabled.
     */
    boolean isBundleEnabled(Bundle bundle);

    /**
     * Get the flag if automatic updates for config changes are enabled.
     * @return true, if automatic updates for config changes are enabled.
     */
    boolean isAutoUpdateEnabled();

    /**
     * Get the backup written for a PID.
     * @param pid the pid, not null.
     * @return the backup, or null, if no backup is present.
     */
    Dictionary<String,?> getBackup(String pid);

    /**
     * Get all current known PIDs for which backups are registered.
     * @return all known PIDs for which backups are registered.
     */
    Set<String> getBackupPids();

    /**
     * Restores a backup, replacing the current OSGI configuration with the backup and
     * disabling Tamaya for this PID.
     * @param pid the PID, not null.
     * @return true, if a backup has been restored successfully.
     */
    boolean restoreBackup(String pid);

    /**
     * Stores the current OSGI configuration as a backup (only if no backup is existing).
     * @param pid the target PID, not null.
     * @return true, if a backup has been stored successfully.
     */
    boolean createBackup(String pid);

    /**
     * Deletes a backup, if existing.
     * @param pid the target PID, not null.
     * @return true, if a backup has been restored successfully.
     */
    boolean deleteBackup(String pid);

    /**
     * Sets the maximum getHistory size.
     * @param maxHistory the max getHistory size. {@code 0} disables the getHistory function.
     */
    void setMaxHistorySize(int maxHistory);

    /**
     * Get the max getHistory size.
     * @return the max getHistory size. {@code 0} means the getHistory function is disabled.
     */
    int getMaxHistorySize();

    /**
     * Access the current (full) change getHistory.
     * @return the current getHistory, never null.
     */
    List<ConfigHistory> getHistory();

    /**
     * Clears the getHistory.
     */
    void clearHistory();

    /**
     * Clears the getHistory for a PID.
     * @param pid the target PID, not null.
     */
    void clearHistory(String pid);

    /**
     * Get the getHistory for a PID.
     * @param pid the target PID, not null.
     * @return the PID's getHistory, never null.
     */
    List<ConfigHistory> getHistory(String pid);

    /**
     * Access the current OSGI configuration for a PID.
     * @param pid the target PID, not null.
     * @param section a subsection to be filter (using startsWith).
     * @return the (optionally filtered) OSGI configuration.
     */
    Dictionary<String,Object> getOSGIConfiguration(String pid, String section);

    /**
     * Checks if a backup exists.
     * @param pid the target PID, not null.
     * @return true, if a backup exists.
     */
    boolean containsBackup(String pid);
}

The Tamaya OSGI Configuration Service

Finally Tamaya also provides support for using Tamaya’s injection API with your OSGI project. To enable injection you must install a few additional bundles:

<dependency>
   <groupId>org.apache.tamaya.ext</groupId>
   <artifactId>tamaya-osgi-injection</artifactId>
   <version>${tamaya.version}</version>
</dependency>
<dependency>
   <groupId>org.apache.tamaya.ext</groupId>
   <artifactId>tamaya-injection</artifactId>
   <version>${tamaya.version}</version>
</dependency>
<dependency>
   <groupId>org.apache.tamaya.ext</groupId>
   <artifactId>injection-api</artifactId>
   <version>${tamaya.version}</version>
</dependency>

Given that you can inject configuration entries

  • on your services by

    • setting tamaya-config-inject=true in your service properties.

    • setting Tamaya-Config-Inject: true in your bundle’s manifest.

  • or by using the registered ConfigInjectionService:

public interface ConfigInjectionService {
    /** The manifest entry to enable Tamaya injection. */
    String TAMAYA_INJECTION_ENABLED_MANIFEST = "Tamaya-Config-Inject";
    /** The OSGI config entry to enable Tamaya injection. */
    String TAMAYA_INJECTION_ENABLED_PROP = "tamaya-config-inject";

    /**
     * Checks if injection is enabled on the given service.
     * @param reference the service reference, not null.
     * @return true, if enjection is enabled.
     */
    boolean isInjectionEnabled(ServiceReference reference);

    /**
     * Checks if injection is enabled on the given service.
     * @param bundle the bundle, not null.
     * @return true, if enjection is enabled.
     */
    boolean isInjectionEnabled(Bundle bundle);

    /**
     * Configures the passed instance.
     * @param instance the instance, not null.
     * @param <T> the input and return type.
     * @param pid the target PID, not null.
     * @param location the optional location
     * @return the configured instance.
     */
    <T> T configure(String pid, String location, T instance);

    /**
     * Creates a suzpplier, which supplies events as created by the basic supplier, which are
     * automatically configured, when supplying.
     * @param supplier the base supplier, not null.
     * @param pid the target PID, not null.
     * @param location the optional location
     * @param <T> the type
     * @return a configuring supplier.
     */
    <T> Supplier<T> getConfiguredSupplier(String pid, String location, java.util.function.Supplier<T> supplier);

    /**
     * Creates a template implementing the annotated methods based on current configuration data.
     *
     * @param <T> the type of the template.
     * @param templateType the type of the template to be created.
     * @param pid the target PID, not null.
     * @param location the optional location
     * @return the configured template.
     */
    <T> T createTemplate(String pid, String location, Class<T> templateType);

    /**
     * Configures the passed instance.
     * @param instance the instance, not null.
     * @param <T> the input and return type.
     * @param bundle the target bundle, not null.
     * @return the configured instance.
     */
    <T> T configure(Bundle bundle, T instance);

    /**
     * Creates a suzpplier, which supplies events as created by the basic supplier, which are
     * automatically configured, when supplying.
     * @param supplier the base supplier, not null.
     * @param bundle the target bundle, not null.
     * @param <T> the type
     * @return a configuring supplier.
     */
    <T> Supplier<T> getConfiguredSupplier(Bundle bundle, java.util.function.Supplier<T> supplier);

    /**
     * Creates a template implementing the annotated methods based on current configuration data.
     *
     * @param <T> the type of the template.
     * @param templateType the type of the template to be created.
     * @param bundle the target bundle, not null.
     * @return the configured template.
     */
    <T> T createTemplate(Bundle bundle, Class<T> templateType);
}
Note
Injection hereby is based on the OSGI ConfigAdmin values only. To use Tamaya configuration you have to additionally install the Tamaya common OSGI support as described in the previous sections.

More details on Tamaya’s injection API can be found in the corresponding API documentation.

Special OSGI Platform support

Apache Karaf Shell

Apache Tamaya provides a Karaf Shell Extension providing commands for performing several actions related to Tamaya configuration. To use them, simply add the org.apache.tamaya.ext:tamaya-osgi-karaf-shell bundle to your OSGI runtime. The extension will add the following commands to your Karaf conaole (with prefix tamaya):

Artifact Description Options

tm_apply_config

Show the current Tamaya configuration.

SYNTAX
tamaya:tm_apply_config [options] pid
ARGUMENTS
pid  The target OSGI component PID.
OPTIONS
operationMode, -m, --opmode Explicitly set (override) the operation mode to use.
dryRun, -d, --dryrun If set to true no OSGI configuration gets changed.

tm_backup_create

Creates a backup of a current OSGI configuration.

SYNTAX
tamaya:tm_backup_create [options] pid
ARGUMENTS
pid  The target pid to backup.
OPTIONS
--force, -f  Forces to (over)write a backup, even if one already exists.

tm_backup_delete

Deletes the OSGI configuration backup of Tamya.

SYNTAX
tamaya:tm_backup_delete pid
ARGUMENTS
pid  Allows to filter on the given PID. '*' removes all backups.

tm_backup_list

List the backed-up OSGI configuration before Tamya applied changes.

SYNTAX
tamaya:tm_backup_list [pid]
ARGUMENTS
pid  Allows to filter on the given PID.

tm_backup_restore

Restores the OSGI configuration backup of Tamya and disabled the PID for Tamaya configuration.

SYNTAX
tamaya:tm_backup_restore pid
ARGUMENTS
pid The target PID. '*' restores all backups.

tm_config

Show the current Tamaya configuration.

SYNTAX
tamaya:tm_config [options]
OPTIONS
pi, -p, --pid Apply filtering for the given OSGI component PID.
section, -s, --section A starting expression selecting the section to be filtered.

tm_enable

Enables or disable Tamaya by default for all bundles/services (default: enabled=false). Disabling still allows to explicitly enable bundles using 'tamaya-enable' manifest or OSGI config entries.

SYNTAX
tamaya:tm_enable enabled
ARGUMENTS
enabled The boolean value to enabled/disable Tamaya by default.

tm_enabled

Check if Tamaya is currently by default enabled for all bundles/services (default: enabled=false). If disabled still Tamaya allows to explicitly enable bundles using 'tamaya-enable' manifest or OSGI config entries.

SYNTAX
tamaya:tm_enabled

tm_history

Gets the getHistory of changes Tamaya applied to the OSGI configuration.

SYNTAX
tamaya:tm_history [options] [pid]
ARGUMENTS
pid Allows to filter on the given PID.
--type, -t Allows to filter the events types shown.

tm_history_delete

Deletes the getHistory of changes Tamaya applied to the OSGI configuration.

SYNTAX
tamaya:tm_history_delete pid
ARGUMENTS
pid Allows to filter on the given PID.

tm_history_delete_all

Deletes the full getHistory of changes Tamaya applied to the OSGI configuration.

SYNTAX
tamaya:tm_history_delete_all

tm_history_maxsize

Gets the maximal size of stored getHistory entries.

SYNTAX
tamaya:tm_history_maxsize

tm_history_maxsize_set

Sets the maximal size of Tamaya getHistory entries.

SYNTAX
tamaya:tm_history_maxsize_set size
ARGUMENTS
size: The maximum number of entries in the getHistory.

tm_info

Show he current Tamaya status.

SYNTAX
tamaya:tm_info

tm_osgi_config

Show the current OSGI configuration.

SYNTAX
tamaya:tm_osgi_config [options] pid
ARGUMENTS
pid The target OSGI component PID.
OPTIONS
section, -s, --section: A starting expression selecting the keys to be filtered.

tm_policy

Get the current Tamaya overriding policy.

SYNTAX
tamaya:tm_policy

tm_policy_set

Sets the current Tamaya operation policy.

SYNTAX
tamaya:tm_policy_set tm_policy_set
ARGUMENTS
tm_policy_set: The operation policy how Tamaya intercepts OSGI configuration.

tm_propagate_updates

Flag if Tamaya is automatically triggering OSGI config updates, when according Tamaya configuration changes.

SYNTAX
tm_propagate_updates

tm_propagate_updates_set

Configure if Tamaya is automatically triggering OSGI config updates, when according Tamaya configuration changes.

SYNTAX
tamaya:tm_propagate_updates_set enabled
ARGUMENTS
enabled: Set to true to enable Tamaya's updating trigger.

tm_property

Get a Tamaya property.

SYNTAX
tamaya:tm_property [options] [key]
ARGUMENTS
key: The target property source id.
OPTIONS
extended,e: Also print extended property value attributes.
propertysource, ps: The target property source id.

tm_propertysource

Show the current Tamaya entries of a propertysource.

SYNTAX
tamaya:tm_propertysource [propertysource]
ARGUMENTS
propertysource: The target property source id.

tm_propertysources

Get a list of currently registered propertysources.

SYNTAX
tamaya:tm_propertysources

Apache Karaf Ferature

Apache Tamaya provides a Karaf feature with all required dependencies as org.apache.tamaya.ext:tamaya-karaf-features:{tamaya-version}:features:xml.

Apache Felix Gogo Console

Apache Tamaya also provides the same commands as described for Karaf, but executable in plaing Gogo console as used by Apache Felix and Equinox as org.apache.tamaya.ext:tamaya-gogo-shell:{tamaya-version}. Refer to the previous sections for a detailed command description.