Configuring IceBox Services
9 min read
8 min read
8 min read
7 min read
7 min read
7 min read
7 min read
7 min read
7 min read
Installing an IceBox Service
A service is configured into an IceBox server using a single IceBox.Service property. This property serves several purposes: it defines the name of the service, it provides the server with the service entry point, and it defines properties and arguments for the service.
The format of the property is shown below:
IceBox.Service.name=entry_point [args]The name component of the property key is the service name. This name is passed to the service's start operation, and must be unique among all services configured in the same IceBox server. It is possible, though rarely necessary, to load two or more instances of the same service under different names.
The first argument in the property value is the entry point specification. IceBox converts an argument of the form --name=value that follows the entry point specification into a property of the communicator passed to the service's start operation when name starts with a reserved prefix, such as Ice., or with the service's name followed by a period. It passes the remaining arguments to the start operation in the args parameter.
IceBox Service Configuration
For a C++ service, the entry point must have the form library[,version]:symbol, where library is the simple name of the service's shared library or DLL, and symbol is the name of the entry point function. A "simple name" is one without any platform-specific prefixes or extensions; the server adds appropriate decorations depending on the platform. The simple name may include a leading path, and the version is optional. If specified, the version is embedded in the library name.
As an example, here is how we could configure IceStorm, which is implemented as an IceBox service in C++:
IceBox.Service.IceStorm=IceStormService,38:createIceStormIceBox uses the information provided in the entry point specification to compose a library name. For the IceStorm example shown above, IceBox on Windows would compose the library name IceStormService38.dll. If the Ice library is a Windows debug build, it appends a d to the version, so the name becomes IceStormService38d.dll instead.
If the simple name does not include a leading path, the shared library or DLL must reside in a directory that appears in PATH on Windows or the shared library search path (such as LD_LIBRARY_PATH) on POSIX systems.
The entry point function, symbol, must have the signature that we originally presented in our example:
IceBox::Service* factoryFunction(const Ice::CommunicatorPtr&)The communicator instance passed to this function is the IceBox server's communicator and should only be used for administrative purposes. For example, the entry point function could use this communicator's logger to display log messages. For a service's normal operations, it must use the communicator that it receives as an argument to its start method.
Here is a sample configuration for our C++ service:
IceBox.Service.Greeter=GreeterService:create --Ice.Trace.Network=1 hello thereThis configuration results in the creation of a service named Greeter. The service is expected to reside in GreeterService.dll on Windows or libGreeterService.so on Linux, and the entry point function create is invoked to create an instance of the service. The IceBox server converts --Ice.Trace.Network=1 into a property of the service's communicator and passes hello and there to start in args.
The entry point of a C# service has the form assembly:class. The assembly component can be a partially or fully qualified assembly name, or an assembly path name.
The assembly component accepts these forms:
| Form | Examples |
|---|---|
Assembly name | GreeterService,Version=...,Culture=neutral,publicKeyToken=... or GreeterService |
Assembly path | GreeterService.dll, services\GreeterService.dll or C:\services\GreeterService.dll |
IceBox resolves relative assembly paths against the iceboxnet process's current working directory.
The class component is the complete class name of the service implementation class, which must define a public constructor.
To instantiate the service, the IceBox server first checks to see if the service defines a constructor taking an argument of type Ice.Communicator. If so, the service calls this constructor and passes the server's communicator, which should only be used for administrative purposes. For example, the constructor could use this communicator's logger to display log messages. For a service's normal operations, it must use the communicator that it receives as an argument to its start method.
If the service does not define a constructor taking an Ice.Communicator argument, the server invokes the service's parameterless constructor.
Here is a sample configuration for our C# service:
IceBox.Service.Greeter=GreeterService.dll:Service.GreeterService --Ice.Trace.Network=1 hello thereThis configuration creates a service named Greeter, implemented by Service.GreeterService in GreeterService.dll. The IceBox server converts --Ice.Trace.Network=1 into a property of the service's communicator and passes hello and there to start in args.
For a Java service, the entry point is typically the class name (including any package) of the service implementation class, but may also include a leading path to a class directory or JAR file. The class must define a public constructor.
To create the service, the IceBox server first checks to see if the service defines a constructor taking an argument of type com.zeroc.Ice.Communicator. If so, the server invokes this constructor and passes the server's communicator, which should only be used for administrative purposes. For example, the constructor could use this communicator's logger to display log messages. For a service's normal operations, it must use the communicator that it receives as an argument to its start method.
If the service does not define a constructor taking a com.zeroc.Ice.Communicator argument, the server invokes the service's default constructor.
Here is a sample configuration for our Java example:
IceBox.Service.Greeter=com.example.icebox.greeter.service.GreeterService --Ice.Trace.Network=1 hello thereThis configuration results in the creation of a service named Greeter. The service is expected to reside in the class com.example.icebox.greeter.service.GreeterService. The IceBox server converts --Ice.Trace.Network=1 into a property of the service's communicator and passes hello and there to start in args.
You can use a configuration file to configure your IceBox service by specifying --Ice.Config. For example:
IceBox.Service.Greeter=<entry point> --Ice.Config=/etc/greeter.cfgLoad Order for IceBox Services
By default, the server loads the configured services in an undefined order, meaning services in the same IceBox server should not depend on one another. If services must be loaded in a particular order, the IceBox.LoadOrder property can be used:
IceBox.LoadOrder=Service1,Service2In this example, Service1 is loaded first, followed by Service2. Any remaining services are loaded after Service2, in an undefined order. Each service mentioned in IceBox.LoadOrder must have a matching IceBox.Service property.
During shutdown, services are stopped in the reverse of the order in which they were loaded.
Using a Shared Communicator
IceBox creates a separate communicator instance for each service by default in order to minimize the chances of accidental conflicts among services. You can optionally specify that certain services use a shared communicator instead by setting IceBox.UseSharedCommunicator.name properties in the server's configuration:
IceBox.Service.Hello=...IceBox.Service.Printer=...IceBox.UseSharedCommunicator.Hello=1IceBox.UseSharedCommunicator.Printer=1IceBox prepares the property set of this shared communicator as follows:
If services inherit the server's properties, the property set initially contains the IceBox server's properties, except those whose names start with
IceBox.orIce.Admin.; otherwise the property set starts out empty.For each service that uses the shared communicator:
- Merge its properties into the shared property set, overwriting any existing properties with the same names
- Remove any properties from the shared property set that the service explicitly clears. For example, the definition
Ice.Trace.Network=clears any existing setting ofIce.Trace.Networkin the shared property set. - Translate service-specific command-line settings into properties (e.g.,
--Hello.Debug=1)
Service properties are merged in the same order as the services are loaded. As a result, the final value of a property that is defined by multiple services depends on the order in which those services are loaded. Let's expand our example to demonstrate this behavior:
# File: server.cfgIceBox.Service.Hello=... --Ice.Config=hello.cfg --Hello.Debug=1IceBox.Service.Printer=... --Ice.Config=printer.cfgIceBox.UseSharedCommunicator.Hello=1IceBox.UseSharedCommunicator.Printer=1IceBox.InheritProperties=1IceBox.LoadOrder=Hello,PrinterIce.Trace.Network=1
# File: hello.cfgIce.Trace.Network=2
# File: printer.cfgIce.Trace.Network=3The two services Hello and Printer use the shared communicator, and all services inherit the server's properties. As IceBox prepares the shared property set, the initial value of Ice.Trace.Network is 1 as defined in the (inherited) server's configuration. The IceBox.LoadOrder property specifies that the Hello service should be loaded first, therefore the Ice.Trace.Network property momentarily has the value 2 but ultimately has the value 3 after the properties for the Printer service are merged.
If we change the value of IceBox.LoadOrder so that IceBox loads Printer first, the value for Ice.Trace.Network in the shared communicator will be 2 instead because the setting in hello.cfg overrides all previous values.
Inheriting Properties from the IceBox Server
By default, a service does not inherit the IceBox server's configuration properties. For example, consider the following server configuration:
IceBox.Service.Weather=... --Ice.Config=svc.cfgIce.Trace.Network=1The Weather service only receives the properties that are defined in its IceBox.Service property. In the example above, the service's communicator is initialized with the properties from the file svc.cfg.
If services need to inherit the IceBox server's configuration properties, define the IceBox.InheritProperties property in the IceBox server's configuration:
IceBox.Service.Weather=... --Ice.Config=svc.cfgIce.Trace.Network=1IceBox.InheritProperties=1All services inherit the server's properties when IceBox.InheritProperties is set to a non-zero value. The service inherits all the properties of the IceBox server, except those whose names start with IceBox. or Ice.Admin..
Logging Considerations for IceBox Services
IceBox sets Ice.ProgramName in each service communicator to the service name, or to SharedCommunicator for the shared communicator. When the IceBox server's Ice.ProgramName is not empty, IceBox prefixes this name with the server's Ice.ProgramName and a hyphen.
The IceBox server only configures a logger for a service if that service has not already specified its own logger via the Ice.LogFile or Ice.UseSyslog properties.