Belt is the Iotinga library for microservices systems written in Java. The goal of Belt is to improve code reuse providing a lightweight architecture structure and a common set of interfaces to help developers on effective usage of its architecture.
Reuse is not intented only at software-level between projects, but at use-case level. For example, supporting the creation of customer service tools reusing the same code of the production microservices.
Belt's architecture is based on a few concepts: decoupling, composability, distribution. All of them are part of the basic Belt building-brick, the Gadget. Writing software in Gadgets allows the developer to inherit Belt's features and properties.
Belt is not a framework. This means that creating a good architecture is still a Developer's duty.
A Belt's Gadget is, like a Batman's belt gadget, a specific-purpose tool that can be used into multiple belts, even combined with other gadgets. De-facto, a Gadget is a Dependency Injection Module
(we use Google's Guice as framework) with some additional attributes, like a name, which provides an implementation for the GadgetCommandExecutor
interface. The GadgetCommandExecutor
's public CompletableFuture<Integer> submit(C command)
method has to be considered as the public static void main(String[] args);
method in a classic Java software.
A Belt is a way to run one or multiple Gadget's. De-facto, each Belt is a different way to make three operations before the public CompletableFuture<Integer> submit(C command)
method is called:
- Collect basic configuration as a Properties object;
- Collect user input as a Command object;
- Build the
GadgetCommandExecutor
dependency injector as described from the Gadget;
The operations order can be changed from the Belt definition
The Cli Belt is intented to make the Gadget usable through a command line interface, suitable for advanced users or for Iotinga's Customer Service agents.
At this time, the Cli Belt is not interactive
The Cli Belt loads Properties from a belt.properties file. Passing the -p option to the command, it is possible to specify a different filename and a different path for the file to be used as configuration. Another feature is the -h option which allows to print the command usage guide.
- tries to build the command object parsing the
string[] args
passed from the user; - sets the path of the properies file (if needed, using the default instead);
- asks the Gadget to build the
GadgetCommandExectuor
's modules passing properties and command objects; - obtains the
GadgetCommandExecutor
instance from the new child injector; - invokes the
public CompletableFuture<Integer> submit(C command)
System.exit(result.join());
on the submit method result.
The Runtime Belt is intended to run multiple Gadgets from the same process. It is useful for Docker microservices use-cases, allowing to decide what gadgets has to run into the docker even after the docker image creation.
As for bauer and slf4j limitations, all the Gadgets will use the same bauer and slf4j endpoints
The Runtime Belt loads properties from the runtime environment variables using the Dotenv library. It isn't possible to pass any other property or command to the Gadget. It passes a null
command to the submit method.
The lifecycle is a little more complex as it must create and run multiple gadgets, but we can solve this complexity splitting the lifecycle description in three phases: initialization, submission, shutdown
- tries to build the Runtime belt command object parsing the
string[] args
passed from the user; - asks the Runtime Gadget to build the
GadgetCommandExectuor
's modules passing properties and command objects; - obtains the
GadgetCommandExecutor
instance from the new child injector; The module looks at the command to choose what kind ofCompletionService
as to be created to obtain a sequential or parallel execution of Gadgets; - invokes the
public CompletableFuture<Integer> submit(C command)
on the Runtime Gadget; System.exit(result.join());
on the submit method result.
At this point, the main process is waiting that the Runtime stops to terminate. The runtime is now in the submission phase. The command execution iterates on each Gadget canonical name passed and:
- tries to build the Gadget module using reflection from the runtime's
GadgetCommandExectuor
's dependency injector. This means that the gadget inherits thePropertiesProvider
of the Runtime Gadget. - asks the Gadget to build the
GadgetCommandExectuor
's modules passing properties and null command; - obtains the
GadgetCommandExecutor
instance from the new child injector; - wraps into a Callable the
public CompletableFuture<Integer> submit(C command)
invocation on the Runtime Gadget passing null and waiting for the result; - sends the Callable to the Runtime Belt's
CompletionService
.
At this point, if any (important) error occours or if all the Gadget command execution are completed the runtime enters the third phase, shutdown:
- tries to gracefully shutdown all the running Gadgets
- waits for 5 seconds for gracefull shutdown
- terminates with 0 in any case
Using the ignore option of the Runtime Belt command it is possible to decide if an inner Gadget Error leads to an overall Runtime termination (default behaviour) or if Runtime shall keep up and running the other Gadgets.
To correcly initialize Bauer we suggest to use the TopicFactory.getAsModule(Properties properties)
into the Gadget's public Module[] buildExecutorModules(Properties properties, EntityGadgetCommand command)
method implementation. This allows to be sure you are using the correct properties object for bauer setup.