Singleton Resource

../../_images/example-singleton.png

Singleton Resource Service

Introduction

A module service is useful to convert an existing multi-threaded, blocking module into a loosely coupled service. When the existing pre-service module is a blocking module, it needs multiple workers to support concurrent requests.

The module pattern can be used in an existing deployment, for example in a servlet web-app. Baratine’s service creates a loosely-coupled boundary around the service.

Used for:

  • Singleton resources
  • Servlet web-app, Baratine Server, Embedded Manager

Benefits:

  • Context Boundary
  • Synchronization
  • Queued requests
  • Loose coupling
  • Async callers
  • Testability
  • Isolation
  • CPU affinity
  • Batching

Examples:

  • Email service
  • Robotics
  • TCP connections (TCP, REST or WebSocket)
  • File systems
  • Logging
  • Singleton components
  • Registries and Naming

Baratine uses the singleton-resource pattern in many places internally. Clustering uses several singletons to manage server heartbeats and pod changes. Deployment uses a singleton to expand and manage a deployed node. Each table in Baratine’s internal database is a singleton resource that manages its btree pages. The filesystem is also managed by a singleton resource.

Blocking Proxy with Existing Source Code

We’ll start with the existing source code for the resource, showing how Baratine can turn existing applications into services.

For this example, we’ll show how to deploy a resource service in a servlet web-app. The resource is a robot, chosen as an example because a robot cannot have multiple simultaneous commands. In practice, any resource that requires a synchronization block could be helped with this pattern.

For the example, we’ll show both a blocking version of the code and a non-blocking. The non-blocking version is more flexible because it allows for async callbs, but the blocking version can be used with existing classes and interfaces.

Existing resource (MyRobot)

For this example, we’ll assume that the resource is already implemented with an existing API. In this case, a robot. For this example, we’ll show how Baratine can use an existing implementation and interface to create a service.

The singleton service has a single worker, which processes requests from its inbox in order. Because the service has a single thread, it does not need any other synchronization; the synchronization is provided by the Baratine service.

MyRobotImpl.java:

public class MyRobotImpl implements MyRobot
{
  public String moveForward(String location) { ... }

  public String sit() { ... }

  public String getStatus() { ... };
}

Existing Sync API (MyRobot)

MyRobot (sync example):

public interface MyRobot
{
  String moveForward(String location);

  String sit();

  String getStatus();
}

Web-App Programmatic Deployment

To deploy in a servlet web-app, add the baratine.jar to WEB-INF/lib, include the application classes as normal and build the .war. When the web-app starts, Baratine will create a ServiceManager, which we’ll use to create the robot service in a servlet init method. We could also use a ServletContextListener to create the service.

The following creates a new service using the MyRobotImpl implementation class, optionally binds the service to a name, and creates a thread-safe service proxy.

Servlet init:

private MyRobot _myRobot;

public void init(ServletContext cxt)
{
  super.init(cxt);

  ServiceManager manager = ServiceManager.current();

  MyRobotImpl myRobot = new MyRobotImpl();

  _myRobot = manager.newService()
                   .service(myRobot)
                   .address("/my-robot")
                   .build()
                   .as(MyRobot.class);

  _myRobot.moveForward("to bed");
  _myRobot.sit();
}

Client Code

The application can use the MyRobot service proxy directly. Since it’s thread-safe, all requesting threads can use it.

Because we gave the robot an address at “/my-robot”, another client can lookup the service without needing to be passed the proxy.

Looking up the service and generating a proxy looks like the following:

ServiceManager manager = ServiceManager.current();

MyRobot myRobot = manager.lookup("/my-robot")
                         .as(MyRobot.class);

Non Web-App Programmatic Deployment

The service module pattern can be used outside of a web-app (and outside of a Baratine container.) The ServiceManager interface includes a newManager method, which creates a new ServiceManager that can be used to create and lookup the service module.

ServiceManager manager = ServiceManager.newManager().build();

MyRobotImpl myRobot = new MyRobotImpl();

manager.newService()
       .address("/my-robot")
       .service(myRobotImpl)
       .build();

MyRobot myRobot = manager.lookup("/my-robot")
                         .as(MyRobot.class);