Guide: Writing an Admin Command for Mongoose server¶
This guide explains how to add operational/admin commands to your Mongoose server application. Admin commands are lightweight functions that you register at runtime and can invoke via a CLI or programmatically to inspect or control the system (list services, stop processors, flush caches, custom health checks, etc.).
You’ll learn:
- What the AdminCommand API is and how it works
- How to register commands from processors and services using AdminCommandRegistry
- Command function signature, arguments, and output/error channels
- How commands are dispatched through the event flow (asynchronously) or executed directly
- How to invoke commands from a CLI or programmatically
- Tips, patterns, and references in this repository
Key types¶
- AdminCommandRegistry — central registry to add and invoke commands
- package:
com.fluxtion.server.service.admin
- package:
- AdminFunction
— your command function signature void processAdminCommand(List<String> args, Consumer<OUT> out, Consumer<ERR> err)
- AdminCommandRequest — DTO for programmatic invocation (name, args, output consumers)
- AdminCommandProcessor — service that routes commands through the event flow and hosts built-ins ( help/commands/eventSources)
- CliAdminCommandProcessor — optional interactive console to type commands
- FluxtionServerAdmin — sample “server control” commands (list services/processors, stop processors)
References:
- AdminCommandRegistry.java
- AdminFunction.java
- AdminCommandRequest.java
- AdminCommandProcessor.java
- CliAdminCommandProcessor.java
- FluxtionServerAdmin.java
How it works¶
At startup you register an AdminCommandProcessor as a service. Other services and processors can inject the AdminCommandRegistry (via @ServiceRegistered) and register commands. When a command is invoked:
- If the registering component was an event processor (i.e., currentProcessor present during registration), the command is wired to publish into that processor’s input queue as an AdminCommand event, which is executed by AdminCommandInvoker on the processor’s agent thread (asynchronous, back‑pressure aware).
- If registered outside a processor context (e.g., a plain service at init/start time), the command executes directly in the caller thread.
This lets you choose between async delivery into a processor’s single-threaded context, or immediate synchronous execution.
Registering a command¶
Register from a processor or a service using @ServiceRegistered to obtain the registry.
Example (from a processor):
package com.mycompany.ops;
import com.fluxtion.runtime.annotations.runtime.ServiceRegistered;
import com.fluxtion.runtime.node.ObjectEventHandlerNode;
import com.fluxtion.server.service.admin.AdminCommandRegistry;
import java.util.List;
import java.util.function.Consumer;
public class OpsHandler extends ObjectEventHandlerNode {
@ServiceRegistered
public void registerAdmin(AdminCommandRegistry admin, String name) {
// name is the AdminCommandRegistry service name
admin.registerCommand("ops.ping", this::ping);
admin.registerCommand("ops.echo", this::echo);
}
private void ping(List<String> args, Consumer<Object> out, Consumer<Object> err) {
out.accept("pong");
}
private void echo(List<String> args, Consumer<Object> out, Consumer<Object> err) {
// args[0] is the command name, args[1..] are user args
out.accept(String.join(" ", args));
}
}
Example (from a service):
public class OpsService implements com.fluxtion.runtime.lifecycle.Lifecycle {
private com.fluxtion.server.service.admin.AdminCommandRegistry registry;
@com.fluxtion.runtime.annotations.runtime.ServiceRegistered
public void admin(AdminCommandRegistry registry) { this.registry = registry; }
@Override public void start() {
registry.registerCommand("ops.time", (args, out, err) -> out.accept(java.time.Instant.now().toString()));
}
@Override public void init() {}
@Override public void stop() {}
@Override public void tearDown() {}
}
Wiring admin infrastructure in AppConfig¶
You need to register the admin services in your application.
import com.fluxtion.server.config.AppConfig;
import com.fluxtion.server.config.ServiceConfig;
import com.fluxtion.server.service.admin.AdminCommandRegistry;
import com.fluxtion.server.service.admin.impl.AdminCommandProcessor;
import com.fluxtion.server.service.admin.impl.CliAdminCommandProcessor;
// Admin registry/dispatcher service
ServiceConfig<AdminCommandRegistry> adminSvc = ServiceConfig.<AdminCommandRegistry>builder()
.service(new AdminCommandProcessor())
.serviceClass(AdminCommandRegistry.class)
.name("adminService")
.build();
// Optional: interactive CLI running on the JVM stdin/stdout
ServiceConfig<?> cliSvc = ServiceConfig.builder()
.service(new CliAdminCommandProcessor())
.name("adminCli")
.build();
AppConfig app = AppConfig.builder()
// add your processors and other services...
.addService(adminSvc)
.addService(cliSvc) // optional
.build();
Notes:
- AdminCommandProcessor exposes default commands:
help
,?
,commands
,eventSources
. - FluxtionServerAdmin registers higher-level server operations (list services/processors, stop processors). To use it:
ServiceConfig<?> serverAdmin = ServiceConfig.builder()
.service(new FluxtionServerAdmin())
.name("serverAdmin")
.build();
app = AppConfig.builder()
// ...
.addService(adminSvc)
.addService(serverAdmin)
.build();
Invoking a command¶
There are two common ways:
1) From the CLI (if CliAdminCommandProcessor is registered)
- At runtime type:
commands
to list available commands - Example:
server.processors.list
- Example:
ops.echo hello world
2) Programmatically using AdminCommandRegistry
import com.fluxtion.server.service.admin.AdminCommandRequest;
var request = new AdminCommandRequest();
request.setCommand("ops.echo");
request.setArguments(java.util.List.of("hello", "world"));
request.setOutput(System.out::println);
request.setErrOutput(System.err::println);
// obtain the registry from FluxtionServer.registeredServices()
Service<?> svc = server.registeredServices().get("adminService");
AdminCommandRegistry registry = (AdminCommandRegistry) svc.instance();
registry.processAdminCommandRequest(request);
What happens under the hood:
- The AdminCommandProcessor looks up your registered command. If it was registered inside a processor context, it publishes an AdminCommand event into that processor’s input queue and the AdminCommandInvoker executes it on the processor’s agent thread. Otherwise, it executes immediately in the caller thread.
Command function signature and args¶
Your command implements:
void processAdminCommand(List<String> args, Consumer<OUT> out, Consumer<ERR> err)
- args contains the full tokenized input including the command name at index 0 (e.g., ["ops.echo", "hello", "world"]).
- Use
out.accept(...)
for normal output,err.accept(...)
for warnings/errors. - Prefer short, dash‑separated names (e.g.,
cache.clear
,server.service.list
).
Tips and patterns¶
- Keep commands small and fast. If you need to run in a processor context, the infrastructure will deliver your command asynchronously to that single‑threaded agent.
- Validate args and produce helpful
err
messages; don’t throw unless exceptional. - For long operations, consider returning a quick acknowledgement and performing the work asynchronously; stream
progress to
out
if appropriate. - Use
commands
andhelp
to explore what’s registered at runtime. - Combine with FluxtionServerAdmin for common operational tasks.
Example end‑to‑end¶
See:
- BroadcastCallbackTest.java
- FluxtionServerAdmin.java These show wiring the admin registry, adding commands, and optional CLI usage.