The Process Facet

6 min read

6 min read

6 min read

3 min read

3 min read

3 min read

5 min read

3 min read

5 min read

The Process facet lets an activation service, such as an IceGrid node, request a graceful shutdown of a server. The server registers a proxy for this facet with the activation service by calling setServerProcessProxy on the locator registry, as described in Integrating the Process Facet with an Activation Service.

The Slice interface Ice::Process allows an activation service to request a graceful shutdown of the program:

Slice
module Ice
{
interface Process
{
void shutdown();
void writeMessage(string message, int fd);
}
}

When shutdown is invoked, the object implementing this interface is expected to initiate the termination of its process. The activation service may expect the program to terminate within a certain period of time, after which it may terminate the program abruptly.

The writeMessage operation allows remote clients to print a message to the program's standard output (fd == 1) or standard error (fd == 2) channels.

findAdminFacet returns the servant that implements the Process facet of your communicator:

C++
// It's nullptr when the facet is not enabled
Ice::ProcessPtr process =
communicator->findAdminFacet<Ice::Process>("Process");

The default implementation of the Process facet requires cooperation from an application in order to successfully terminate a process. Specifically, the facet invokes shutdown on its communicator and assumes that the application uses this event as a signal to commence its termination procedure. For example, an application typically uses a thread (often the main thread) to call the communicator operation waitForShutdown, which blocks the calling thread until the communicator is shut down or destroyed. After waitForShutdown returns, the calling thread can initiate a graceful shutdown of its process.

You can replace the default Process facet if your application requires a different scheme for gracefully shutting itself down. To define your own facet, create a servant that implements the Ice::Process interface. As an example, the C++ servant definition shown below duplicates the functionality of the default Process facet:

C++
class MyProcess final : public Ice::Process
{
public:
MyProcess(Ice::CommunicatorPtr communicator) :
_communicator{std::move(communicator)}
{
}
void shutdown(const Ice::Current&) final
{
_communicator->shutdown();
}
void writeMessage(
std::string message,
std::int32_t fd,
const Ice::Current&) final
{
switch (fd)
{
case 1:
{
cout << message << endl;
break;
}
case 2:
{
cerr << message << endl;
break;
}
}
}
private:
const Ice::CommunicatorPtr _communicator;
};

As you can see, the default implementation of shutdown simply shuts down the communicator, which initiates an orderly termination of the Ice runtime's server-side components and prevents object adapters from dispatching any new requests. You can add your own application-specific behavior to the shutdown method to ensure that your program terminates in a timely manner.

To avoid the risk of a race condition, the recommended strategy for replacing the Process facet is to delay the creation of the admin object until after communicator initialization, so that your application can replace the facet before the admin object exposes it:

Properties
# Delay admin object creation for admin object hosted in the Ice.Admin
# object adapter
Ice.Admin.DelayCreation=1

With Ice.Admin.DelayCreation enabled, the application can safely remove the default Process facet and install its own:

C++
Ice::CommunicatorPtr communicator = ...;
communicator->removeAdminFacet("Process");
auto myProcessFacet = make_shared<MyProcess>(...);
communicator->addAdminFacet(myProcessFacet, "Process");

The final step is to create the admin object by calling getAdmin on the communicator.

findAdminFacet returns the servant that implements the Process facet of your communicator:

C#
if (communicator.findAdminFacet("Process") is Ice.Process process)
{
...
}
// else, the facet is not enabled

The default implementation of the Process facet requires cooperation from an application in order to successfully terminate a process. Specifically, the facet invokes shutdown on its communicator and assumes that the application uses this event as a signal to commence its termination procedure. For example, an application typically uses a thread (often the main thread) to call the communicator operation waitForShutdown, which blocks the calling thread until the communicator is shut down or destroyed. After waitForShutdown returns, the calling thread can initiate a graceful shutdown of its process.

You can replace the default Process facet if your application requires a different scheme for gracefully shutting itself down. To define your own facet, create a servant that implements the Ice::Process interface. As an example, the C# servant definition shown below duplicates the functionality of the default Process facet:

C#
internal sealed class MyProcess : Ice.ProcessDisp_
{
private readonly Ice.Communicator _communicator;
public MyProcess(Ice.Communicator communicator) => _communicator = communicator;
public override void shutdown(Ice.Current current) => _communicator.shutdown();
public override void writeMessage(string message, int fd, Ice.Current current)
{
switch (fd)
{
case 1:
{
Console.Out.WriteLine(message);
break;
}
case 2:
{
Console.Error.WriteLine(message);
break;
}
}
}
}

As you can see, the default implementation of shutdown simply shuts down the communicator, which initiates an orderly termination of the Ice runtime's server-side components and prevents object adapters from dispatching any new requests. You can add your own application-specific behavior to the shutdown method to ensure that your program terminates in a timely manner.

To avoid the risk of a race condition, the recommended strategy for replacing the Process facet is to delay the creation of the admin object until after communicator initialization, so that your application can replace the facet before the admin object exposes it:

Properties
# Delay admin object creation for admin object hosted in the Ice.Admin
# object adapter
Ice.Admin.DelayCreation=1

With Ice.Admin.DelayCreation enabled, the application can safely remove the default Process facet and install its own:

C#
Ice.Communicator communicator = ...;
communicator.removeAdminFacet("Process");
var myProcessFacet = new MyProcess(...);
communicator.addAdminFacet(myProcessFacet, "Process");

The final step is to create the admin object by calling getAdmin on the communicator.

findAdminFacet returns the servant that implements the Process facet of your communicator:

Java
com.zeroc.Ice.Object obj = communicator.findAdminFacet("Process");
if (obj != null) { // It's null when the facet is not enabled
var process = (com.zeroc.Ice.Process)obj;
...
}

The default implementation of the Process facet requires cooperation from an application in order to successfully terminate a process. Specifically, the facet invokes shutdown on its communicator and assumes that the application uses this event as a signal to commence its termination procedure. For example, an application typically uses a thread (often the main thread) to call the communicator operation waitForShutdown, which blocks the calling thread until the communicator is shut down or destroyed. After waitForShutdown returns, the calling thread can initiate a graceful shutdown of its process.

You can replace the default Process facet if your application requires a different scheme for gracefully shutting itself down. To define your own facet, create a servant that implements the Ice::Process interface. As an example, the Java servant definition shown below duplicates the functionality of the default Process facet:

Java
class MyProcess implements com.zeroc.Ice.Process {
private final com.zeroc.Ice.Communicator _communicator;
public MyProcess(com.zeroc.Ice.Communicator communicator) {
_communicator = communicator;
}
@Override
public void shutdown(com.zeroc.Ice.Current current) {
_communicator.shutdown();
}
@Override
public void writeMessage(String message, int fd, com.zeroc.Ice.Current current) {
switch (fd) {
case 1 -> System.out.println(message);
case 2 -> System.err.println(message);
}
}
}

As you can see, the default implementation of shutdown simply shuts down the communicator, which initiates an orderly termination of the Ice runtime's server-side components and prevents object adapters from dispatching any new requests. You can add your own application-specific behavior to the shutdown method to ensure that your program terminates in a timely manner.

To avoid the risk of a race condition, the recommended strategy for replacing the Process facet is to delay the creation of the admin object until after communicator initialization, so that your application can replace the facet before the admin object exposes it:

Properties
# Delay admin object creation for admin object hosted in the Ice.Admin
# object adapter
Ice.Admin.DelayCreation=1

With Ice.Admin.DelayCreation enabled, the application can safely remove the default Process facet and install its own:

Java
Communicator communicator = ...;
communicator.removeAdminFacet("Process");
var myProcessFacet = new MyProcess(...);
communicator.addAdminFacet(myProcessFacet, "Process");

The final step is to create the admin object by calling getAdmin on the communicator.

Ice for Python does not expose the built-in Process facet as a local object: communicator.findAdminFacet("Process") returns None. To interact with the Process facet of your own communicator, use a proxy, as shown for a remote facet.

The default implementation of the Process facet requires cooperation from an application in order to successfully terminate a process. Specifically, the facet invokes shutdown on its communicator and assumes that the application uses this event as a signal to commence its termination procedure. For example, an application typically uses a thread (often the main thread) to call the communicator operation waitForShutdown, which blocks the calling thread until the communicator is shut down or destroyed. After waitForShutdown returns, the calling thread can initiate a graceful shutdown of its process.

You can replace the default Process facet if your application requires a different scheme for gracefully shutting itself down. To define your own facet, create a servant that implements the Ice::Process interface. As an example, the Python servant definition shown below duplicates the functionality of the default Process facet:

Python
class MyProcess(Ice.Process):
def __init__(self, communicator: Ice.Communicator):
self._communicator = communicator
def shutdown(self, current: Ice.Current) -> None:
self._communicator.shutdown()
def writeMessage(self, message: str, fd: int, current: Ice.Current) -> None:
if fd == 1:
print(message)
elif fd == 2:
print(message, file=sys.stderr)

As you can see, the default implementation of shutdown simply shuts down the communicator, which initiates an orderly termination of the Ice runtime's server-side components and prevents object adapters from dispatching any new requests. You can add your own application-specific behavior to the shutdown method to ensure that your program terminates in a timely manner.

To avoid the risk of a race condition, the recommended strategy for replacing the Process facet is to delay the creation of the admin object until after communicator initialization, so that your application can replace the facet before the admin object exposes it:

Properties
# Delay admin object creation for admin object hosted in the Ice.Admin
# object adapter
Ice.Admin.DelayCreation=1

With Ice.Admin.DelayCreation enabled, the application can safely remove the default Process facet and install its own:

Python
communicator = ...
communicator.removeAdminFacet("Process")
myProcessFacet = MyProcess(...)
communicator.addAdminFacet(myProcessFacet, "Process")

The final step is to create the admin object by calling getAdmin on the communicator.

findAdminFacet returns the servant that implements the Process facet of your communicator:

Swift
if let process = communicator.findAdminFacet("Process") as? Process {
...
}

The default implementation of the Process facet requires cooperation from an application in order to successfully terminate a process. Specifically, the facet invokes shutdown on its communicator and assumes that the application uses this event as a signal to commence its termination procedure. For example, an application typically uses a thread (often the main thread) to call the communicator operation waitForShutdown, which blocks the calling thread until the communicator is shut down or destroyed. After waitForShutdown returns, the calling thread can initiate a graceful shutdown of its process.

You can replace the default Process facet if your application requires a different scheme for gracefully shutting itself down. To define your own facet, create a servant that implements the Ice::Process interface. As an example, the Swift servant definition shown below duplicates the functionality of the default Process facet:

Swift
final class MyProcess: Ice.Process {
private let communicator: Ice.Communicator
init(communicator: Ice.Communicator) {
self.communicator = communicator
}
func shutdown(current _: Ice.Current) {
communicator.shutdown()
}
func writeMessage(message: String, fd: Int32, current _: Ice.Current) {
switch fd {
case 1:
print(message)
case 2:
FileHandle.standardError.write(Data((message + "\n").utf8))
default:
break
}
}
}

As you can see, the default implementation of shutdown simply shuts down the communicator, which initiates an orderly termination of the Ice runtime's server-side components and prevents object adapters from dispatching any new requests. You can add your own application-specific behavior to the shutdown method to ensure that your program terminates in a timely manner.

To avoid the risk of a race condition, the recommended strategy for replacing the Process facet is to delay the creation of the admin object until after communicator initialization, so that your application can replace the facet before the admin object exposes it:

Properties
# Delay admin object creation for admin object hosted in the Ice.Admin
# object adapter
Ice.Admin.DelayCreation=1

With Ice.Admin.DelayCreation enabled, the application can safely remove the default Process facet and install its own:

Swift
let communicator = ...
try communicator.removeAdminFacet("Process")
try communicator.addAdminFacet(servant: MyProcess(...), facet: "Process")

The final step is to create the admin object by calling getAdmin on the communicator.

If Ice.Admin.ServerId is configured and the communicator has a default locator, the communicator performs the following steps after creating the admin object:

  • Obtains proxies for the Process facet and the default locator
  • Invokes getRegistry on the locator proxy to obtain a proxy for the locator registry
  • Invokes setServerProcessProxy on the locator registry and supplies the value of Ice.Admin.ServerId along with a proxy for the Process facet

The identifier specified by Ice.Admin.ServerId must uniquely identify the process within the locator registry.

In the case of IceGrid, IceGrid defines the Ice.Admin.ServerId and Ice.Default.Locator properties for each deployed server. IceGrid also supplies a value for Ice.Admin.Endpoints if neither this property nor Ice.Admin.Enabled are defined by the server.