Table of Contents Previous Next
Logo
The Ice Run Time in Detail : 28.24 Developing a Plugin
Copyright © 2003-2008 ZeroC, Inc.

28.24 Developing a Plugin

Ice supports a plugin facility that allows you to add new features and install application-specific customizations. Plugins are defined using configuration properties and loaded dynamically by the Ice run time, making it possible to install a plugin into an existing program without modification.
Ice uses the plugin facility to implement some of its own features. Most well-known is IceSSL, a plugin that adds a secure transport for Ice communication (see Chapter 38). Other examples include the logger plugin (see Section 28.19.4) and the string converter plugin (see Section 28.23.6).
This section describes the plugin facility in more detail and demonstrates how to implement an Ice plugin.

28.24.1 Plugin API

The plugin facility defines a local Slice interface that all plugins must implement:
module Ice {
local interface Plugin {
    void initialize();
    void destroy();
};
};
The lifecycle of an Ice plugin is structured to accommodate dependencies between plugins, such as when a logger plugin needs to use IceSSL for its logging activities. Consequently, a plugin object’s lifecycle consists of four phases:
• Construction
The Ice run time uses a language-specific factory API for instantiating plugins. During construction, a plugin can acquire resources but must not spawn new threads or perform activities that depend on other plugins.
• Initialization
After all plugins have been constructed, the Ice run time invokes initialize on each plugin. The order in which plugins are initialized may be specified using a configuration property (see Section 28.24.3), otherwise the order is undefined. If a plugin has a dependency on another plugin, you must configure the Ice run time so that initialization occurs in the proper order. In this phase it is safe for a plugin to spawn new threads; it is also safe for a plugin to interact with other plugins and use their services, as long as those plugins have already been initialized.
If initialize raises an exception, the Ice run time invokes destroy on all plugins that were successfully initialized (in the reverse order of initialization) and raises the original exception to the application.
• Active
The active phase spans the time between initialization and destruction. Plugins must be designed to operate safely in the context of multiple threads.
• Destruction
The Ice run time invokes destroy on each plugin in the reverse order of initialization.
This lifecycle is repeated for each new communicator that an application creates and destroys.

C++ Factory

In C++, the plugin factory is an exported function with C linkage having the following signature:
extern "C"
{
ICE_DECLSPEC_EXPORT Ice::Plugin*
functionName(const Ice::CommunicatorPtr& communicator,
             const std::string& name,
             const Ice::StringSeq& args);
}
You can define the function with any name you wish. We recommend that you use the ICE_DECLSPEC_EXPORT macro to ensure that the function is exported correctly on all platforms. Since the function uses C linkage, it must return the plugin object as a regular C++ pointer and not as an Ice smart pointer. Furthermore, the function must not raise C++ exceptions; if an error occurs, the function must return zero.
The arguments to the function consist of the communicator that is in the process of being initialized, the name assigned to the plugin, and any arguments that were specified in the plugin’s configuration.

Java Factory

In Java, a plugin factory must implement the Ice.PluginFactory interface:
package Ice;

public interface PluginFactory {
    Plugin create(Communicator communicator,
                  String name,
                  String[] args);
}
The arguments to the create method consist of the communicator that is in the process of being initialized, the name assigned to the plugin, and any arguments that were specified in the plugin’s configuration.
The create method can return null to indicate that a general error occurred, or it can raise PluginInitializationException to provide more detailed information. If any other exception is raised, the Ice run time wraps it inside an instance of PluginInitializationException.

.NET Factory

In .NET, a plugin factory must implement the Ice.PluginFactory interface:
namespace Ice {
    public interface PluginFactory
    {
        Plugin create(Communicator communicator,
                      string name,
                      string[] args);
    }
}
The arguments to the create method consist of the communicator that is in the process of being initialized, the name assigned to the plugin, and any arguments that were specified in the plugin’s configuration.
The create method can return null to indicate that a general error occurred, or it can raise PluginInitializationException to provide more detailed information. If any other exception is raised, the Ice run time wraps it inside an instance of PluginInitializationException.

28.24.2 Plugin Configuration

Plugins are installed using a configuration property of the following form:
Ice.Plugin.Name=entry_point [arg ...]
In most cases you can assign an arbitrary name to a plugin. In the case of IceSSL, however, the plugin requires that its name be IceSSL.
The value of entry_point is a language-specific representation of the plugin’s factory. In C++, it consists of the name of the shared library or DLL containing the factory function, along with the name of the factory function. In Java, the entry point is the name of the factory class, while in .NET the entry point also includes the assembly.
The language-specific nature of plugin properties can present a problem when applications that are written in multiple implementation languages attempt to share a configuration file. Ice supports an alternate syntax for plugin properties that alleviates this issue:
Ice.Plugin.Name.cpp=...    # C++ plugin
Ice.Plugin.Name.java=...   # Java plugin
Ice.Plugin.Name.clr=...    # .NET (Common Language Runtime) plugin
Plugin properties having a suffix of .cpp, .java, or .clr are loaded only by the appropriate Ice run time and ignored by others.
Refer to Appendix C for more information on these properties.

28.24.3 Advanced Topics

This section discusses additional aspects of the Ice plugin facility that may be of use to applications with special requirements.

Plugin Dependencies

If a plugin has a dependency on another plugin, you must ensure that Ice initializes the plugins in the proper order. Suppose that a custom logger implementation depends on IceSSL; for example, the logger may need to transmit log messages securely to another server. We start with the following C++ configuration:
Ice.Plugin.IceSSL=IceSSL:createIceSSL
Ice.Plugin.MyLogger=MyLogger:createMyLogger
The problem with this configuration is that it does not specify the order in which the plugins should be loaded and initialized. If the Ice run time happens to initialize MyLogger first, the plugin’s initialize method will fail if it attempts to use the services of the uninitialized IceSSL plugin.
To remedy the situation, we need to add one more property:
Ice.Plugin.IceSSL=IceSSL:createIceSSL
Ice.Plugin.MyLogger=MyLogger:createMyLogger
Ice.PluginLoadOrder=IceSSL, MyLogger
Using the Ice.PluginLoadOrder property we can guarantee that the plugins are loaded in the correct order. Appendix C describes this property in more detail.

The Plugin Manager

PluginManager is the name of an internal Ice object that is responsible for managing all aspects of Ice plugins. This object supports a Slice interface of the same name, and an application can obtain a reference to this object using the following communicator operation:
module Ice {
local interface Communicator {
    PluginManager getPluginManager();
    // ...
};
};
The PluginManager interface offers three operations:
module Ice {
local interface PluginManager {
    void initializePlugins();
    Plugin getPlugin(string name);
    void addPlugin(string name, Plugin pi);
};
};
The initializePlugins operation is used in special cases when an application needs to manually initialize one or more plugins, as discussed in the next section.
The getPlugin operation returns a reference to a specific plugin. The name argument must match an installed plugin, otherwise the operation raises NotRegisteredException. This operation is useful when a plugin exports an interface that an application can use to query or customize its attributes or behavior.
Finally, addPlugin provides a way for an application to install a plugin directly, without the use of a configuration property.

Delayed Initialization

It is sometimes necessary for an application to manually configure a plugin prior to its initialization. For example, SSL keys are often protected by a passphrase, but a developer may be understandably reluctant to specify that passphrase in a configuration file because it would be exposed in clear text. The developer would likely prefer to configure the IceSSL plugin with a password callback instead; however, this must be done before the plugin is initialized and attempts to load the SSL key. The solution is to configure the Ice run time so that it postpones the initialization of its plugins:
Ice.InitPlugins=0
When this property is set to zero, initializing plugins becomes the application’s responsibility. The example below demonstrates how to perform this initialization:
// C++
Ice::CommunicatorPtr ic = ...
Ice::PluginManagerPtr pm = ic>getPluginManager();
IceSSL::PluginPtr ssl = pm>getPlugin("IceSSL");
ssl>setPasswordPrompt(...);
pm>initializePlugins();
After obtaining the IceSSL plugin and establishing the password callback, the application invokes initializePlugins on the plugin manager object to commence plugin initialization.
Table of Contents Previous Next
Logo