Developing IceBox Services
5 min read
4 min read
4 min read
3 min read
3 min read
3 min read
3 min read
3 min read
3 min read
The IceBox Service Interface
Service InterfaceWriting an IceBox service requires implementing the IceBox ServiceIceBox ServiceIceBox ServiceIceBox Service interface or abstract base class.
Your service implements two methods, start and stop. IceBox calls start after loading the service and stop when shutting down a running service. An administrator can also stop and restart the service through the service manager. IceBox reuses the same service object, communicator, and arguments for each restart.
The start method initializes the service, typically by creating an object adapter and servants. The name and args parameters supply information from the service's configuration, and the communicator parameter supplies a communicator that IceBox creates for the service. Services that share a communicator must use distinct names for their object adapters.
start must return once the service is initialized, because IceBox waits for it before starting the next service and before activating its admin object.
The stop method must release the resources owned by the service and destroy the object adapters it created.
IceBox owns the communicator it passes to start and destroys it when the server shuts down.
IceBox Service Example
The example we present here is taken from the IceBox/greeter demo program.
The class definition for our service is quite straightforward:
#include <IceBox/IceBox.h>
namespace Service{ class GreeterService final : public IceBox::Service { public: void start( const std::string& name, const Ice::CommunicatorPtr& communicator, const Ice::StringSeq& args) final;
void stop() final;
private: Ice::ObjectAdapterPtr _adapter; };}The implementation is equally straightforward:
voidService::GreeterService::start( [[maybe_unused]] const string& name, const Ice::CommunicatorPtr& communicator, [[maybe_unused]] const Ice::StringSeq& args){ assert(!_adapter); _adapter = communicator->createObjectAdapterWithEndpoints( "GreeterAdapter", "tcp -p 4061");
_adapter->add( make_shared<GreeterServer::Chatbot>("Syd"), Ice::Identity{"greeter"});
_adapter->activate(); cout << "Listening on port 4061..." << endl;}
voidService::GreeterService::stop(){ cout << "Shutting down..." << endl;
assert(_adapter); _adapter->destroy(); _adapter = nullptr;}The start method creates an object adapter “GreeterAdapter”, activates a single servant of type Chatbot (not shown), and activates the object adapter. The stop method simply destroys the object adapter.
C++ Service Entry Point
The last piece of the puzzle is the entry point function, which the IceBox server calls to create an instance of the IceBox service:
extern "C"{ ICE_DECLSPEC_EXPORT IceBox::Service* create(const Ice::CommunicatorPtr&) { return new Service::GreeterService; }}In this example, the create function returns a new instance of the GreeterService service. The name of the function is not important, but it must have the signature shown above. In particular, the function must have C linkage, accept a single const Ice::CommunicatorPtr& parameter and return an IceBox::Service*.
The example we present here is taken from the IceBox/Greeter demo program.
Add a reference to the ZeroC.IceBox NuGet package, version 3.8.x, to your service project. This package provides the IceBox.Service interface.
The class definition for our service is quite straightforward:
public class GreeterService : IceBox.Service{ private Ice.ObjectAdapter? _adapter;
public void start(string name, Ice.Communicator communicator, string[] args) { Debug.Assert(_adapter is null); _adapter = communicator.createObjectAdapterWithEndpoints( "GreeterAdapter", "tcp -p 4061");
_adapter.add(new Chatbot("Syd"), new Ice.Identity { name = "greeter" }); _adapter.activate(); Console.WriteLine("Listening on port 4061..."); }
public void stop() { Console.WriteLine("Shutting down...");
Debug.Assert(_adapter is not null); _adapter.destroy(); _adapter = null; }}The start method creates an object adapter “GreeterAdapter”, activates a single servant of type Chatbot (not shown), and activates the object adapter. The stop method simply destroys the object adapter.
C# Service Entry Point
The last piece of the puzzle is the entry point, which the IceBox server calls to create an instance of the service.
IceBox requires a service implementation to have a public parameterless constructor or a public constructor with a single Communicator parameter. This is the C# entry point for IceBox: the IceBox server dynamically loads the service implementation class from an assembly and calls this public constructor to create an instance of the service.
The example we present here is taken from the IceBox/greeter demo program.
The class definition for our service is quite straightforward:
package com.example.icebox.greeter.service;
import com.zeroc.Ice.Communicator;import com.zeroc.Ice.Identity;import com.zeroc.Ice.ObjectAdapter;import com.zeroc.IceBox.Service;
public class GreeterService implements Service { private ObjectAdapter _adapter;
@Override public void start(String name, Communicator communicator, String[] args) { _adapter = communicator.createObjectAdapterWithEndpoints( "GreeterAdapter", "tcp -p 4061");
_adapter.add(new Chatbot(), new Identity("greeter", "")); _adapter.activate(); System.out.println("Listening on port 4061..."); }
@Override public void stop() { System.out.println("Shutting down...");
assert _adapter != null; _adapter.destroy(); _adapter = null; }}The start method creates an object adapter “GreeterAdapter”, activates a single servant of type Chatbot (not shown), and activates the object adapter. The stop method simply destroys the object adapter.
Java Service Entry Point
The last piece of the puzzle is the entry point, which the IceBox server calls to create an instance of the service.
IceBox requires a service implementation to have a public parameterless constructor or a public constructor with a single Communicator parameter. This is the Java entry point for IceBox: the IceBox server dynamically loads the service implementation class and calls this public constructor to create an instance of the service.
Configuring IceBox Services provides more information on entry points and describes how to configure your service into an IceBox server.
IceBox Service Failures
A service implementation can indicate a failure by throwing an exception. IceBox handles the exception according to when it occurs:
- Initial startup: If the service's entry point or initial
startcall throws, the server terminates as described in Starting the IceBox Server. - Administrative start: If
startthrows duringServiceManager.startService, IceBox logs a warning, records the service as stopped, and returns normally fromstartService. - Administrative stop: If
stopthrows duringServiceManager.stopService, IceBox logs a warning, records the service as started, and returns normally fromstopService. - Server shutdown: If
stopthrows, IceBox logs a warning and continues stopping the remaining services and destroying their communicators.
Because startService and stopService return normally in these cases, use ServiceManager.isServiceRunning to check whether the service started or stopped.
If start fails, the service must release resources it acquired during that attempt before propagating the exception. IceBox calls stop only for services it records as started.