Components & lifecycle
Every GGCommons component is built around one runtime object: GGCommons (Rust spells it
GgCommons). You construct it once at startup from the standard CLI args, hold it for the lifetime
of the process, and reach every subsystem through it — messaging, config, metrics, heartbeat,
health, and the opt-in credentials, parameters, and streaming services. Teardown is owned by the
library, not by your code.
This guide covers the three things you do with that object: build it, use its accessors, and let it shut down cleanly. For the full per-method API surface, see the API reference: entry point.
Build the runtime
Section titled “Build the runtime”Always construct the runtime through the fluent builder — GGCommonsBuilder
(GgCommonsBuilder in Rust). The pattern is the same everywhere: name the component, hand it the
CLI args, then build().
import com.mbreissi.ggcommons.GGCommons;import com.mbreissi.ggcommons.GGCommonsBuilder;
public static void main(String[] args) { // Use the FULL component name; pass argv straight through (already app-stripped). GGCommons gg = GGCommonsBuilder.create("com.mbreissi.greengrass.JavaComponentSkeleton") .withArgs(args) .build();
// ... use gg ...}from ggcommons.ggcommons_builder import GGCommonsBuilder
gg = ( GGCommonsBuilder.create("ggcommons_python") .with_args([ "-c", "FILE", "config.json", "--platform", "HOST", "--transport", "MQTT", "messaging.json", "-t", "my-thing", ]) .build())use ggcommons::prelude::*;
#[tokio::main]async fn main() -> anyhow::Result<()> { let gg = GgCommonsBuilder::new("com.example.SkeletonComponent") .args(std::env::args_os()) // argv INCLUDES the program name .build() .await?;
// ... use gg ... Ok(())}import { GGCommonsBuilder } from "ggcommons";
const gg = await new GGCommonsBuilder("com.example.Lc") .args([ "--platform", "HOST", "--transport", "MQTT", "messaging.json", "-c", "ENV", "GGC_LIFECYCLE_CFG", "-t", "lc-thing", ]) // argv WITHOUT the node/script prefix (process.argv.slice(2)) .build();A few builder details worth knowing:
build()is async only in Rust and TypeScript (it connects messaging before returning). Java and Pythonbuild()are synchronous.- Construction is builder-first in every language. Java additionally keeps three deprecated
public
GGCommons(...)constructors for back-compat, and Python keeps a publicGGCommons(...)constructor — but the builder is the recommended path and Rust/TS are builder-only. There is noinit()facade in any language. - A null component name is rejected — Java
build()throwsIllegalStateException; Python additionally rejects an empty string (create()/the constructor raiseValueErrorfor both empty andNone). withAppOptions/with_app_optionslet you register extra CLI options your component needs;receiveOwnMessagesis covered below.
Reach subsystems through accessors
Section titled “Reach subsystems through accessors”Once built, the runtime exposes a typed accessor per subsystem. The names follow each language’s
convention — Java uses getX(), Python uses get_x(), Rust and TypeScript use bare x():
| Subsystem | Java | Python | Rust | TypeScript |
|---|---|---|---|---|
| Config | getConfigManager() |
get_config_manager() |
config() |
config() |
| Messaging | getMessaging() |
get_messaging() |
messaging() |
messaging() |
| Metrics | getMetrics() |
get_metrics() |
metrics() |
metrics() |
| Streaming | getStreams() |
get_streams() |
streams() |
streams() |
| Credentials | getCredentials() |
get_credentials() |
credentials() |
credentials() |
| Parameters | getParameters() |
get_parameters() |
parameters() |
parameters() |
The always-on subsystems (config, messaging, metrics) return their service directly:
var config = gg.getConfigManager(); // ConfigManagervar messaging = gg.getMessaging(); // MessagingClientvar metrics = gg.getMetrics(); // MetricEmitterconfig = gg.get_config_manager() # ConfigManagermessaging = gg.get_messaging() # the MessagingClient class (its ops are static)metrics = gg.get_metrics() # the MetricEmitter classlet cfg = gg.config(); // Arc<Config> snapshotlet metrics = gg.metrics(); // Arc<dyn MetricService>if let Ok(messaging) = gg.messaging() { // Result, not Option // ... publish / subscribe ...}const config = gg.config(); // Config snapshotconst messaging = gg.messaging(); // IMessagingService (throws if no transport wired)const metrics = gg.metrics(); // MetricServiceOpt-in subsystems: the null contract
Section titled “Opt-in subsystems: the null contract”Credentials, parameters, and streaming are opt-in. The accessor returns “nothing” unless the matching section is present in your config — so a component that never configures credentials never pays for them. What “nothing” means follows each language’s idiom:
- Java returns
null. - Python returns
None. - TypeScript returns
undefined. - Rust returns
Option(None) for credentials and parameters — butstreams()is NOT anOption. It always returns anArc<dyn StreamService>; that service is simply empty (no streams) when there is nostreamingsection. Checkstreams().stream_names(), not forNone.
var streams = gg.getStreams(); // null if no `streaming` sectionvar credentials = gg.getCredentials(); // null if no `credentials` sectionvar parameters = gg.getParameters(); // null if no `parameters` section
if (credentials != null) { // ... use the vault ...}streams = gg.get_streams() # None if no `streaming` sectioncredentials = gg.get_credentials() # None if no `credentials` sectionparameters = gg.get_parameters() # None if no `parameters` section
if credentials is not None: ... # use the vault// Each accessor exists ONLY when the matching cargo feature is built// (`streaming`, `credentials`, `parameters` — all off by default).
#[cfg(feature = "streaming")]{ let streams = gg.streams(); // Arc<dyn StreamService>, NOT Option let _ = streams.stream_names(); // empty when no `streaming` section}
#[cfg(feature = "credentials")]if let Some(creds) = gg.credentials() { // Option<Arc<dyn CredentialService>> let _ = creds; // None when no `credentials` section}
#[cfg(feature = "parameters")]if let Some(params) = gg.parameters() { // Option<Arc<dyn ParameterService>> let _ = params;}const streams = gg.streams(); // undefined if no `streaming` sectionconst credentials = gg.credentials(); // undefined if no `credentials` sectionconst parameters = gg.parameters(); // undefined if no `parameters` section
if (credentials !== undefined) { // ... use the vault ...}See the per-subsystem guides for what each service does: Credentials, Parameters, and Streaming.
Readiness gates /readyz
Section titled “Readiness gates /readyz”The runtime exposes an HTTP health endpoint (default-on for Kubernetes, opt-in elsewhere). Its
/readyz probe answers a single predicate: messagingConnected && readyFlag && !shuttingDown. The
readyFlag defaults to true, so a component is reported ready as soon as messaging connects.
If your component must not receive traffic until its own subscriptions (or other startup work)
are established, call setReady(false) early and flip it back to true once you are ready. The flag
only gates readiness — it cannot force a component ready while messaging is disconnected or during
shutdown.
gg.setReady(false); // probe returns 503 while we set up// ... subscribe to required topics ...gg.setReady(true); // now /readyz can report readygg.set_ready(False) # probe returns 503 while we set up# ... subscribe to required topics ...gg.set_ready(True) # now /readyz can report readygg.set_ready(false); // probe returns 503 while we set up// ... subscribe to required topics ...gg.set_ready(true); // now /readyz can report ready
// Rust also exposes a readiness/shutdown getter:if gg.is_shutting_down() { // drain in-flight work}gg.setReady(false); // probe returns 503 while we set up// ... subscribe to required topics ...gg.setReady(true); // now /readyz can report ready
if (gg.ready()) { // ... readiness getter is public in TS ...}Lifecycle & graceful shutdown
Section titled “Lifecycle & graceful shutdown”The most important rule on this page: the library owns the termination signal. When the
Greengrass Nucleus or the kubelet stops your component, GGCommons catches the signal, flips
/readyz to 503, unsubscribes every tracked subscription, and bounded-closes
messaging/streams/heartbeat/vault before the process exits.
How teardown is wired differs by language:
// The library installs a JVM shutdown hook on SIGTERM + SIGINT.// You do NOT add your own hook. An explicit call is optional and idempotent:gg.shutdown(); // idempotent (guarded by an atomic CAS)# The library installs a SIGTERM handler (main thread only) that runs shutdown()# then sys.exit(0). Prefer the context manager so teardown is automatic:with GGCommonsBuilder.create("ggcommons_python").with_args(args).build() as gg: ... # business logic; gg.shutdown() runs on exit
# Or call it explicitly (also idempotent):gg.shutdown()// Rust has NO close()/shutdown(): teardown is pure RAII — dropping `gg`// aborts the heartbeat, hot-reload, signal-watcher and health-server tasks.// The library's signal watcher ONLY flips readiness to "shutting down"; it does// NOT exit the process, so YOUR app must await the signal and then return/drop.
let gg = GgCommonsBuilder::new("com.example.SkeletonComponent") .args(std::env::args_os()) .build() .await?;
tokio::select! { // ... your run loops ... _ = shutdown_signal() => tracing::info!("shutdown signal received"),}// falling out of scope drops `gg` -> all resources released (RAII)Ok(())// The library installs BOTH SIGTERM and SIGINT handlers; on signal it flips// readiness to 503, runs close(), then process.exit(0). You do NOT add your own.// An explicit call is optional and idempotent (TS has no RAII):await gg.close(); // idempotentReceiving your own messages
Section titled “Receiving your own messages”receiveOwnMessages controls whether a component receives messages it published itself (relevant
when a component both publishes and subscribes to the same topic). It defaults to true and is set
on the builder.
GGCommons gg = GGCommonsBuilder.create("com.example.MyComponent") .withArgs(args) .receiveOwnMessages(false) // honored on the IPC transport .build();gg = ( GGCommonsBuilder.create("com.example.MyComponent") .with_args(args) .receive_own_messages(False) # honored on the IPC transport .build())let gg = GgCommonsBuilder::new("com.example.MyComponent") .args(std::env::args_os()) .receive_own_messages(false) // NO-OP in Rust: the GG Rust SDK has no IPC .build() // ReceiveMode, so build() logs a warning and .await?; // proceeds as `true`.const gg = await new GGCommonsBuilder("com.example.MyComponent") .args(process.argv.slice(2)) .receiveOwnMessages(false) // honored on IPC; MQTT uses local-broker semantics .build();Related
Section titled “Related”- API reference: entry point — every builder method and accessor, with exact signatures.
- Messaging guide — publish, subscribe, and request/reply.
- Configuration guide — config sources and hot reload.