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:
The Ice run time stores the Filesystem.MaxFileSize property like any other property and makes it accessible via the
Properties interface.
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();
};
};
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.
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.
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);
}
}
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.
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);
// ...
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)
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)
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.
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.
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.
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 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.
The Properties interface provides two utility operations: