Table of Contents Previous Next
Logo
Ice Properties and Configuration : 26.8 Using Properties Programmatically
Copyright © 2003-2008 ZeroC, Inc.

26.8 Using Properties Programmatically

The Ice property mechanism is useful not only to configure Ice, but you can also use it as the configuration mechanism for your own applications. You can use the same configuration file and command-line mechanism to set application-specific properties. For example, we could introduce a property to control the maximum file size for our file system application:
# Configuration file for file system application

Filesystem.MaxFileSize=1024    # Max file size in kB
The Ice run time stores the Filesystem.MaxFileSize property like any other property and makes it accessible via the Properties interface.
To access property values from within your program, you need to acquire the communicator’s properties by calling getProperties:
module Ice {

    local interface Properties; // Forward declaration

    local interface Communicator {

        Properties getProperties();

        // ...
    };
};
The Properties interface provides methods to read and write property settings:
module Ice {
    local dictionary<string, string> PropertyDict;

    local interface Properties {

        string getProperty(string key);
        string getPropertyWithDefault(string key, string value);
        int getPropertyAsInt(string key);
        int getPropertyAsIntWithDefault(string key, int value);
        PropertyDict getPropertiesForPrefix(string prefix);

        void setProperty(string key, string value);

        StringSeq getCommandLineOptions();
        StringSeq parseCommandLineOptions(string prefix,
                                          StringSeq options);
        StringSeq parseIceCommandLineOptions(StringSeq options);

        void load(string file);

        Properties clone();
    };
};

26.8.1 Reading Properties

The operations to read property values behave as follows:
• getProperty
This operation returns the value of the specified property. If the property is not set, the operation returns the empty string.
• getPropertyWithDefault
This operation returns the value of the specified property. If the property is not set, the operation returns the supplied default value.
• getPropertyAsInt
This operation returns the value of the specified property as an integer. If the property is not set or contains a string that does not parse as an integer, the operation returns zero.
• getPropertyAsIntWithDefault
This operation returns the value of the specified property as an integer. If the property is not set or contains a string that does not parse as an integer, the operation returns the supplied default value.
• getPropertiesForPrefix
This operation returns all properties that begin with the specified prefix as a dictionary of type PropertyDict. This operation is useful if you want to extract the properties for a specific subsystem. For example,
getPropertiesForPrefix("Filesystem")
returns all properties that start with the prefix Filesystem, such as Filesystem.MaxFileSize. You can then use the usual dictionary lookup operations to extract the properties of interest from the returned dictionary.
With these lookup operations, using application-specific properties now becomes the simple matter of initializing a communicator as usual, getting access to the communicator’s properties, and examining the desired property value. For example (in C++):
// ...

Ice::CommunicatorPtr ic;

// ...

ic = Ice::initialize(argc, argv);

// Get the maximum file size.
//
Ice::PropertiesPtr props = ic>getProperties();
Ice::Int maxSize
    = props>getPropertyAsIntWithDefault("Filesystem.MaxFileSize",
                                         1024);

// ...
Assuming that you have created a configuration file that sets the Filesystem.MaxFileSize property (and set the ICE_CONFIG variable or the Ice.Config option accordingly), your application will pick up the configured value of the property.

26.8.2 Setting Properties

The setProperty operation sets a property to the specified value. (You can clear a property by setting it to the empty string.) For properties that control the Ice run time and its services (that is, properties that start with one of the reserved prefixes, such as Ice, Glacier2, etc.), this operation is useful only if you call it before you call initialize. This is because property values are usually read by the Ice run time only once, when you call initialize, so the Ice run time does not pay attention to a property value that is changed after you have initialized a communicator. Of course, this begs the question of how you can set a property value and have it also recognized by a communicator.
To permit you to set properties before initializing a communicator, the Ice run time provides an overloaded helper function called createProperties that creates a property set. In C++, the function is in the Ice namespace:
namespace Ice {

PropertiesPtr createProperties(const StringConverterPtr& = 0);
PropertiesPtr createProperties(StringSeq&,
                               const PropertiesPtr& = 0,
                               const StringConverterPtr& = 0);
PropertiesPtr createProperties(int&, char*[],
                               const PropertiesPtr& = 0,
                               const StringConverterPtr& = 0);

}
The StringConverter parameter allows you to parse properties whose values contain non-ASCII characters and to correctly convert this character into the native codeset. (See Section 28.23 for details.) The converter that is passed to createProperties remains attached to the returned property set for the life time of the property set.
The function is overloaded to accept either an argc/argv pair, or a StringSeq (see Section 26.8.3 for details.)
In C#, the Util class in the Ice namespace supplies equivalent methods:
namespace Ice {
    public sealed class Util {
        public static Properties createProperties();
        public static Properties
                createProperties(ref string[] args);
        public static Properties
                createProperties(ref string[] args,
                                 Properties defaults);
    }
}
The Python and Ruby methods reside in the Ice module:
def createProperties(args=[], defaults=None)
In Java, the functions are static methods of the Util class inside the Ice package:
package Ice;

public final class Util
{
    public static Properties
    createProperties();

    public static Properties
    createProperties(StringSeqHolder args);

    public static Properties
    createProperties(StringSeqHolder args, Properties defaults);

    public static Properties
    createProperties(String[] args);

    public static Properties
    createProperties(String[] args, Properties defaults);

    // ...
}
As for initialize (see Section 28.3), createProperties strips Ice-related command-line options from the passed argument vector. (For Java, only the versions that accept a StringSeqHolder do this.)
Because Java cannot access environment variables, the Java implementation of createProperties always ignores the setting of the ICE_CONFIG environment variable. For the other languages, the functions behave as follows:
• The parameter-less version of createProperties simply creates an empty property set. It does not check ICE_CONFIG for a configuration file to parse.
• The other overloads of createProperties accept an argument vector and a default property set. The returned property set contains all the property settings that are passed as the default, plus any property settings in the argument vector. If the argument vector sets a property that is also set in the passed default property set, the setting in the argument vector overrides the default.
The overloads that accept an argument vector also look for the Ice.Config option; if the argument vector specifies a configuration file, the configuration file is parsed. The order of precedence of property settings, from lowest to highest, is:
Property settings passed in the default parameter
Property settings set in the configuration file
Property settings in the argument vector.
The overloads that accept an argument vector also look for the setting of the ICE_CONFIG environment variable (except for Java) and, if that variable specifies a configuration file, parse that file. (However, an explicit Ice.Config option in the argument vector or the defaults parameter overrides any setting of the ICE_CONFIG environment variable.)
createProperties is useful if you want to ensure that a property is set to a particular value, regardless of any setting of that property in a configuration file or in the argument vector. For example:
// Get the initialized property set.
//
Ice::PropertiesPtr props = Ice::createProperties(argc, argv);

// Make sure that network and protocol tracing are off.
//
props>setProperty("Ice.Trace.Network", "0");
props>setProperty("Ice.Trace.Protocol", "0");

// Initialize a communicator with these properties.
//
Ice::InitializationData id;
id.properties = props;
Ice::CommunicatorPtr ic = Ice::initialize(id);

// ...
The equivalent Python code is shown next:
props = Ice.createProperties(sys.argv)
props.setProperty("Ice.Trace.Network", "0")
props.setProperty("Ice.Trace.Protocol", "0")
id = Ice.InitializationData()
id.properties = props
ic = Ice.initialize(id)
This is the equivalent code in Ruby:
props = Ice::createProperties(ARGV)
props.setProperty("Ice.Trace.Network", "0")
props.setProperty("Ice.Trace.Protocol", "0")
id = Ice::InitializationData.new
id.properties = props
ic = Ice::initialize(id)
The equivalent Java code looks as follows:
Ice.StringSeqHolder argsH = new Ice.StringSeqHolder(args);
Ice.Properties properties = Ice.Util.createProperties(argsH);
properties.setProperty("Ice.Warn.Connections", "0");
properties.setProperty("Ice.Trace.Protocol", "0");
Ice.InitializationData id = new Ice.InitializationData();
id.properties = properties;
communicator = Ice.Util.initialize(id);
We first convert the argument array to an initialized StringSeqHolder. This is necessary so createProperties can strip Ice-specific settings. In that way, we first obtain an initialized property set, then override the settings for the two tracing properties, and then set the properties in the InitializationData structure.

26.8.3 Parsing Properties

The Properties interface provides three operations to convert and parse properties:
• getCommandLineOptions
This operation converts an initialized set of properties into a sequence of equivalent command-line options. For example, if you have set the Filesystem.MaxFileSize property to 1024 and call getCommandLineOptions, the setting is returned as the string "Filesystem.MaxFileSize=1024". This operation is useful for diagnostic purposes, for example, to dump the setting of all properties to a logging facility (see Section 28.19), or if you want to fork a new process with the same property settings as the current process.
• parseCommandLineOptions
This operation examines the passed argument vector for command-line options that have the specified prefix. Any options that match the prefix are converted to property settings (that is, they initialize the corresponding properties). The operation returns an argument vector that contains all those options that were not converted (that is, those options that did not match the prefix).
Because parseCommandLineOptions expects a sequence of strings, but C++ programs are used to dealing with argc and argv, Ice provides two utility functions that convert an argc/argv vector into a sequence of strings and vice-versa:
namespace Ice {

    StringSeq argsToStringSeq(int argc, char* argv[]);

    void stringSeqToArgs(const StringSeq& args,
                         int& argc, char* argv[]);

}
You need to use parseCommandLineOptions (and the utility functions) if you want to permit application-specific properties to be set from the command line. For example, to permit the Filesystem.MaxFileSize option to be used on the command line, we need to initialize our program as follows:
int
main(int argc, char* argv[])
{
    // Create an empty property set.
    //
    Ice::PropertiesPtr props = Ice::createProperties();

    // Convert argc/argv to a string sequence.
    //
    Ice::StringSeq args = Ice::argsToStringSeq(argc, argv);

    // Strip out all options beginning with Filesystem.
    //
    args = props>parseCommandLineOptions("Filesystem", args);

    // args now contains only those options that were not
    // stripped. Any options beginning with Filesystem have
    // been converted to properties.

    // Convert remaining arguments back to argc/argv vector.
    //
    Ice::stringSeqToArgs(args, argc, argv);

    // Initialize communicator.
    //
    Ice::InitializationData id;
    id.props = props;
    Ice::CommunicatorPtr ic = Ice::initialize(argc, argv, id);

    // At this point, argc/argv only contain options that
    // set neither an Ice property nor a Filesystem property,
    // so we can parse these options as usual.
    //
    // ...
}
Using this code, any options beginning with Filesystem are converted to properties and are available via the property lookup operations as usual. The call to initialize then removes any Ice-specific command-line options so, once the communicator is created, argc/argv only contains options and arguments that are not related to setting either a filesystem or an Ice property.
An easier way to achieve the same thing is to use the overload of Ice::initialize that accepts a string sequence, instead of an argc/argv pair:
int
main(int argc, char* argv[])
{
    // Create an empty property set.
    //
    Ice::PropertiesPtr props = Ice::createProperties();

    // Convert argc/argv to a string sequence.
    //
    Ice::StringSeq args = Ice::argsToStringSeq(argc, argv);

    // Strip out all options beginning with Filesystem.
    //
    args = props>parseCommandLineOptions("Filesystem", args);

    // args now contains only those options that were not
    // stripped. Any options beginning with Filesystem have
    // been converted to properties.

    // Initialize communicator.
    //
    Ice::InitializationData id;
    id.props = props;
    Ice::CommunicatorPtr ic = Ice::initialize(args, id);

    // At this point, args only contains options that
    // set neither an Ice property nor a Filesystem property,
    // so we can parse these options as usual.
    //
    // ...
}
This version of the code avoids having to convert the string sequence back into an argc/argv pair before calling Ice::initialize.
• parseIceCommandLineOptions
This operation behaves like parseCommandLineOptions, but removes the reserved Ice-specific options from the argument vector (see Section 26.2.2). It is used internally by the Ice run time to parse Ice-specific options in initialize.

26.8.4 Utility Operations

The Properties interface provides two utility operations:
• clone
This operation makes a copy of an existing property set. The copy contains exactly the same properties and values as the original.
• load
This operation accepts a pathname to a configuration file and initializes the property set from that file. If the specified file cannot be read (for example, because it does not exist or the caller does not have read permission), the operation throws a FileException.
These operations are useful if you need to work with multiple communicators that use different property sets.
Table of Contents Previous Next
Logo