Service Graph
Overview
- Designed to be used in combination with ActiveInject and Launcher as a means to start/stop application services according to their dependency graph
- It starts services by following the multithreaded graph traversal algorithm: leaf services first and so on
- It stops services in the opposite direction
- Services dependency graph is automatically built upon ActiveInject dependencies graph, but can be customized by user-specified dependencies.
- Supports multiple standard services like ThreadPool, Closeables, DataSource as well as Active-specific services like eventloops, async servers and async services.
- Can be configured to support other services as well with user-provided adapters
To get a basic understanding of ServiceGraph’s role, let’s have a look at a very simple example of an HTTP Server:
public final class HttpHelloWorldExample extends HttpServerLauncher {
@Provides
AsyncServlet servlet() {
return request -> HttpResponse.ok200().withPlainText("Hello World");
}
public static void main(String[] args) throws Exception {
Launcher launcher = new HttpHelloWorldExample();
launcher.launch(args);
}
}
- This application extends predefined HttpServerLauncher which features ServiceGraphModule.
- HttpServerLauncher uses two services: AsyncHttpServer and Eventloop.

- According to this graph, Service Graph starts Eventloop first. The dependent AsyncHttpServer is started afterwards.
- When the application stops, the services will be stopped in the opposite direction: AsyncHttpServer first and Eventloop next.
Examples
$ git clone https://github.com/activej/activej
And import it as a Maven project. Check out tag v4.0-beta1. Before running the example, build the project.
These examples are located at activej -> examples -> core -> boot
SimpleServiceExample
In this example we create an application that extends Launcher and has a simple custom service which basically only starts and stops:
public class SimpleServiceExample extends Launcher {
public static void main(String[] args) throws Exception {
SimpleServiceExample example = new SimpleServiceExample();
example.launch(args);
}
@Inject CustomService customService;
@Override
protected Module getModule() {
return ServiceGraphModule.create();
}
@Inject
private static class CustomService implements Service {
@Override
public CompletableFuture<?> start() {
System.out.println("|SERVICE STARTING|");
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<?> stop() {
System.out.println("|SERVICE STOPPING|");
return CompletableFuture.completedFuture(null);
}
}
@Override
protected void run() {
System.out.println("|RUNNING|");
}
}
EventloopServiceExample
Service Graph is also able to start and stop your custom services:
public class EventloopServiceExample extends Launcher {
@Provides
Eventloop eventloop() {
return Eventloop.create();
}
@Provides
@Eager
CustomEventloopService customEventloopService(Eventloop eventloop) {
return new CustomEventloopService(eventloop);
}
@Override
protected Module getModule() {
return ServiceGraphModule.create();
}
@Override
protected void run() {
System.out.println("|RUNNING|");
}
private static final class CustomEventloopService implements EventloopService {
private final Eventloop eventloop;
public CustomEventloopService(Eventloop eventloop) {
this.eventloop = eventloop;
}
@Override
public @NotNull Eventloop getEventloop() {
return eventloop;
}
@Override
public @NotNull Promise<?> start() {
System.out.println("|CUSTOM EVENTLOOP SERVICE STARTING|");
return Promises.delay(Duration.ofMillis(10))
.whenResult(() -> System.out.println("|CUSTOM EVENTLOOP SERVICE STARTED|"));
}
@Override
public @NotNull Promise<?> stop() {
System.out.println("|CUSTOM EVENTLOOP SERVICE STOPPING|");
return Promises.delay(Duration.ofMillis(10))
.whenResult(() -> System.out.println("|CUSTOM EVENTLOOP SERVICE STOPPED|"));
}
}
public static void main(String[] args) throws Exception {
EventloopServiceExample example = new EventloopServiceExample();
example.launch(args);
}
}
AdvancedServiceExample
Service Graph can manage more complex service dependencies. For example, let’s assume we have an e-mail service prototype. To work properly, it requires two services - an authorization service and a database service. The authorization service also requires a database service, along with Eventloop and Executor. As a result, we have the following service graph:

And ServiceGraphModule will start and stop all those services in the proper order:
=== STARTING APPLICATION
Started java.util.concurrent.Executor
Started io.activej.eventloop.Eventloop
Started AdvancedServiceExample$DBService
Started AdvancedServiceExample$AuthService
Started AdvancedServiceExample$EmailService
=== STOPPING APPLICATION
Stopped AdvancedServiceExample$EmailService
Stopped AdvancedServiceExample$AuthService
Stopped java.util.concurrent.Executor
Stopped io.activej.eventloop.Eventloop
Stopped AdvancedServiceExample$DBService
This application looks as follows:
public class AdvancedServiceExample extends Launcher {
@Provides
@Eager
DBService dbService() {
return new DBService();
}
@Provides
@Eager
EmailService emailService(DBService dbService, AuthService authService) {
return new EmailService(dbService, authService);
}
@Provides
@Eager
AuthService authService(Eventloop eventloop, Executor executor, DBService dbService) {
return new AuthService(eventloop, executor, dbService);
}
@Provides
Eventloop eventloop() {
return Eventloop.create().withCurrentThread();
}
@Provides
Executor executor() {
return Executors.newCachedThreadPool();
}
@Override
protected Module getModule() {
return ServiceGraphModule.create();
}
@SuppressWarnings("FieldCanBeLocal")
private static class AuthService implements EventloopService {
private final Eventloop eventloop;
private final Executor executor;
private final DBService dbService;
public AuthService(Eventloop eventloop, Executor executor, DBService dbService) {
this.executor = executor;
this.eventloop = eventloop;
this.dbService = dbService;
}
@Override
public @NotNull Eventloop getEventloop() {
return eventloop;
}
@Override
public @NotNull Promise<?> start() {
System.out.println("AuthService starting");
return Promise.ofBlockingRunnable(executor,
() -> System.out.println("AuthService started"));
}
@Override
public @NotNull Promise<?> stop() {
return Promise.ofBlockingRunnable(executor,
() -> System.out.println("AuthService stopped"));
}
}
private static class DBService implements Service {
@Override
public CompletableFuture<?> start() {
System.out.println("DBService is starting");
return CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("DBService is started");
});
}
@Override
public CompletableFuture<?> stop() {
System.out.println("DBService is stopping");
return CompletableFuture.runAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("DBService is stopped");
});
}
}
@SuppressWarnings("FieldCanBeLocal")
private static class EmailService implements Service {
private final DBService service;
private final AuthService authService;
public EmailService(DBService service, AuthService authService) {
this.service = service;
this.authService = authService;
}
@Override
public CompletableFuture<?> start() {
System.out.println("EmailService is starting");
return CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("EmailService is started");
});
}
@Override
public CompletableFuture<?> stop() {
System.out.println("EmailService is stopping");
return CompletableFuture.runAsync(() -> System.out.println("EmailService is stopped"));
}
}
@Override
protected void run() {
}
public static void main(String[] args) throws Exception {
AdvancedServiceExample example = new AdvancedServiceExample();
example.launch(args);
}
}