Series MapLesson 07 / 50
Focus mode active/Press Alt+Shift+R to toggle/Esc to exit
Start HereOrdered learning track

Application Bootstrap

Application, ResourceConfig, Features, and Component Registration

Memahami bootstrap Jakarta REST, configuration model, component registration, Jersey ResourceConfig, feature activation, package scanning, properties, dan startup determinism.

30 min read5886 words
PrevNext
Lesson 0750 lesson track01–09 Start Here
#application#resourceconfig#jersey#feature+3 more

Part 007 — Application, ResourceConfig, Features, and Component Registration

Sebuah class berisi @Path belum otomatis menjadi endpoint. Runtime harus mengetahui application model, menemukan atau menerima registrasi resource dan provider, menggabungkan configuration, mengaktifkan features, membangun dependency graph, memvalidasi model, lalu memublikasikan application pada transport tertentu. Bootstrap yang tidak eksplisit menghasilkan sistem yang “berjalan karena classpath”, sulit direproduksi, dan berisiko berubah hanya karena dependency baru ditambahkan.

Daftar Isi

  1. Target kompetensi
  2. Scope dan baseline
  3. Mental model: source code bukan runtime model
  4. Bootstrap lifecycle end-to-end
  5. Standard Application
  6. @ApplicationPath
  7. getClasses() dan getSingletons()
  8. getProperties() dan configuration properties
  9. Class registration versus instance registration
  10. Portable discovery versus implementation discovery
  11. Standard Configurable dan Configuration
  12. Standard Feature
  13. FeatureContext
  14. DynamicFeature dan per-resource binding
  15. Jersey ResourceConfig
  16. Explicit registration dengan ResourceConfig
  17. Package scanning
  18. Jersey server properties
  19. Auto-discovery dan classpath-driven behavior
  20. Programmatic resource model
  21. Conditional registration dan profiles
  22. Configuration precedence
  23. Bootstrap, dependency injection, dan ownership
  24. Provider and feature ordering
  25. Configuration freeze dan runtime mutation
  26. Model validation dan fail-fast startup
  27. Component inventory sebagai operational artifact
  28. Health, readiness, dan bootstrap completion
  29. Graceful shutdown dan symmetric lifecycle
  30. Testing bootstrap dan registration
  31. Design pattern: explicit composition root
  32. Anti-patterns
  33. Failure-model matrix
  34. Debugging playbook
  35. PR review checklist
  36. Trade-off yang harus dipahami senior engineer
  37. Standard versus Jersey-specific behavior
  38. Internal verification checklist
  39. Latihan verifikasi
  40. Ringkasan
  41. Referensi resmi

Target kompetensi

Setelah menyelesaikan part ini, Anda harus mampu:

  • menjelaskan mengapa annotation pada resource tidak cukup untuk memublikasikan endpoint;
  • membedakan Jakarta REST Application dari Jersey ResourceConfig;
  • menjelaskan perbedaan registrasi component class dan component instance;
  • menentukan kapan explicit registration lebih aman daripada package scanning;
  • menjelaskan peran Feature, FeatureContext, dan DynamicFeature;
  • membaca bootstrap code untuk menemukan resource, provider, binder, feature, property, dan profile aktif;
  • mengenali perilaku yang muncul karena auto-discovery atau classpath, bukan karena code yang eksplisit;
  • membangun configuration precedence yang deterministik dan dapat diaudit;
  • mendesain startup agar gagal cepat ketika route, provider, secret, dependency, atau invariant tidak valid;
  • menghasilkan component inventory yang membantu debugging dan production readiness;
  • mereview perubahan bootstrap dari sisi portability, lifecycle, dependency ownership, security, dan rollout risk.

Scope dan baseline

Part ini menggunakan Jakarta RESTful Web Services 4.0 sebagai baseline standard dan Jersey 3.x sebagai contoh implementation family. Codebase internal dapat menggunakan:

  • Jakarta REST 4.0, 3.1, 3.0, atau versi lebih lama;
  • namespace jakarta.ws.rs atau legacy javax.ws.rs;
  • Jersey 2.x atau 3.x;
  • GlassFish, Grizzly, Tomcat, Jetty, atau runtime lain;
  • HK2, CDI, manual wiring, atau framework internal;
  • bootstrap yang dibungkus oleh platform library internal.

Karena itu, setiap konsep diklasifikasikan:

Jakarta REST standard
Jersey-specific extension
Runtime/container-specific deployment
Application-specific convention
Internal platform behavior

Part ini tidak membahas detail:

  • Jersey internal lifecycle: Part 009;
  • HK2: Part 010;
  • CDI integration: Part 011;
  • GlassFish/Grizzly: Part 012;
  • Servlet/Tomcat/Jetty: Part 013;
  • configuration dan secret lifecycle mendalam: Part 018.

Fokus Part 007 adalah bagaimana application model dibentuk dan divalidasi saat bootstrap.


Mental model: source code bukan runtime model

Source tree dapat berisi puluhan class berikut:

@Path("/quotes")
public class QuoteResource {
}

@Provider
public class ProblemExceptionMapper
        implements ExceptionMapper<Throwable> {
}

Namun keberadaan class tidak menjamin:

  • class ditemukan;
  • class diregistrasikan;
  • class diinstansiasi;
  • dependency berhasil di-inject;
  • provider dipilih;
  • feature aktif;
  • route valid;
  • application dipublikasikan pada listener yang dapat diakses.

Gunakan model berikut:

Source classes
  + dependencies
  + discovery rules
  + explicit registrations
  + runtime properties
  + DI bindings
  + deployment metadata
  = effective runtime application model

Core invariant

The effective application model must be:
deterministic,
observable,
valid before traffic,
and reproducible across environments.

Jika model berbeda antara local, test, staging, dan production hanya karena classpath atau scanning order, maka application tidak memiliki bootstrap contract yang kuat.


Bootstrap lifecycle end-to-end

Bootstrap server-side dapat dimodelkan sebagai state machine:

stateDiagram-v2 [*] --> ProcessStarting ProcessStarting --> ConfigurationLoaded ConfigurationLoaded --> ApplicationLocated ApplicationLocated --> ComponentsDiscovered ComponentsDiscovered --> ComponentsRegistered ComponentsRegistered --> DependencyGraphBuilt DependencyGraphBuilt --> ResourceModelValidated ResourceModelValidated --> ExternalResourcesInitialized ExternalResourcesInitialized --> ListenerBound ListenerBound --> Ready Ready --> Draining Draining --> ResourcesClosed ResourcesClosed --> [*] ConfigurationLoaded --> Failed ApplicationLocated --> Failed ComponentsDiscovered --> Failed ComponentsRegistered --> Failed DependencyGraphBuilt --> Failed ResourceModelValidated --> Failed ExternalResourcesInitialized --> Failed ListenerBound --> Failed Failed --> [*]

Urutan konkret dapat berbeda per runtime. Namun pertanyaan ownership tetap sama:

StagePertanyaan utama
configuration loadedsiapa membaca environment, file, property, dan secret?
application locatedbagaimana runtime menemukan Application?
discoveryapakah menggunakan annotation scanning, service loader, atau explicit list?
registrationcomponent mana yang efektif aktif?
DI graphcontainer mana yang membuat object?
model validationapakah route/provider conflict menghentikan startup?
external initializationkapan DB pool, Kafka producer, dan HTTP client dibuat?
listener bindingapakah socket dibuka sebelum dependency siap?
readinessbukti apa yang menyatakan aplikasi siap menerima traffic?
shutdownsiapa menghentikan listener, menunggu in-flight request, dan menutup resource?

Startup ordering risk

Dua pola yang berbeda:

Bad:
bind port
-> report healthy
-> asynchronously initialize database/client/schema
-> fail after traffic arrives

Safer:
load and validate configuration
-> initialize required dependencies
-> validate resource model
-> bind or enable traffic
-> report ready

Tidak semua dependency harus tersambung saat startup. Namun keputusan lazy versus eager initialization harus eksplisit dan memiliki failure semantics.


Standard Application

jakarta.ws.rs.core.Application adalah configuration abstraction standar Jakarta REST untuk server-side application.

Contoh paling sederhana:

import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;

@ApplicationPath("/api")
public class QuoteOrderApplication extends Application {
}

Implementasi container dapat menemukan subclass ini dan membangun application model.

Tanggung jawab Application

Secara konseptual, Application menyatakan:

  • root resource classes;
  • provider classes;
  • singleton component instances;
  • application properties.

Application bukan:

  • HTTP server;
  • thread pool;
  • DI container;
  • database lifecycle manager;
  • servlet container;
  • configuration framework lengkap;
  • service registry.

Empty subclass

Subclass tanpa override sering berarti container melakukan discovery sesuai deployment environment:

@ApplicationPath("/api")
public class QuoteOrderApplication extends Application {
}

Kenyamanan ini memiliki trade-off:

less bootstrap code
but
more implicit discovery behavior

Dalam codebase enterprise, jangan berasumsi semua @Path dan @Provider otomatis ditemukan. Periksa deployment mode dan implementation rules.


@ApplicationPath

@ApplicationPath mendefinisikan base URI path application dalam deployment environment yang mendukung annotation tersebut.

@ApplicationPath("/api")
public final class ApiApplication extends Application {
}

Resource:

@Path("/quotes")
public final class QuoteResource {
}

Effective application path secara konseptual:

deployment context path
+ application path
+ resource path
= effective endpoint path

Contoh:

context path     = /quote-order
application path = /api
resource path    = /quotes/{id}

effective path   = /quote-order/api/quotes/{id}

Namun reverse proxy atau ingress dapat menambah, menghapus, atau menulis ulang prefix.

Failure modes

  • application path berbeda dari OpenAPI server URL;
  • duplicate slash atau prefix;
  • ingress rewrite menghapus path yang salah;
  • dua Application memiliki overlapping mappings;
  • servlet mapping dan @ApplicationPath bertabrakan;
  • health endpoint berada di application lain;
  • generated client memakai public prefix yang berbeda dari internal route.

Review invariant

Every externally visible path must be derivable from:
gateway routing
+ deployment context
+ application mapping
+ resource template.

getClasses() dan getSingletons()

Application menyediakan dua cara deklarasi component set.

Class registration

public final class ApiApplication extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        return Set.of(
                QuoteResource.class,
                OrderResource.class,
                ProblemExceptionMapper.class
        );
    }
}

Dengan class registration, runtime/container biasanya bertanggung jawab membuat instance dan menerapkan lifecycle serta injection yang didukung.

Singleton instance registration

public final class ApiApplication extends Application {

    private final Set<Object> singletons = Set.of(
            new HealthResource()
    );

    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }
}

Instance tersebut sudah dibuat oleh application code. Konsekuensinya harus dipahami:

  • constructor sudah dijalankan sebelum runtime menerima instance;
  • DI container mungkin tidak membuat object tersebut;
  • lifecycle callback dan proxy/interceptor dapat berbeda;
  • instance digunakan lintas request;
  • mutable fields menjadi shared state;
  • cleanup menjadi tanggung jawab yang harus jelas.

Jangan campurkan tanpa alasan

getClasses()
  cocok ketika runtime/DI container harus mengelola instance.

getSingletons()
  cocok hanya ketika ownership, thread safety,
  injection, and cleanup semantics are deliberate.

getProperties() dan configuration properties

Application#getProperties() dapat menyediakan property map untuk runtime.

@Override
public Map<String, Object> getProperties() {
    return Map.of(
            "application.name", "quote-order-api",
            "application.environment", "development"
    );
}

Namun property key dan maknanya dapat berasal dari:

  • Jakarta REST standard;
  • Jersey;
  • servlet initialization parameter;
  • platform library;
  • application code;
  • observability agent.

Property risk

String-keyed properties mudah menghasilkan:

  • typo yang tidak terdeteksi;
  • silent fallback;
  • type mismatch;
  • duplicate source;
  • unclear precedence;
  • secret tercetak saat startup;
  • configuration yang berbeda antar-environment.

Better boundary

Gunakan typed configuration untuk domain/application settings:

public record HttpServerSettings(
        int port,
        Duration idleTimeout,
        int maxRequestBytes
) {
    public HttpServerSettings {
        if (port < 1 || port > 65_535) {
            throw new IllegalArgumentException("invalid port");
        }
        if (maxRequestBytes <= 0) {
            throw new IllegalArgumentException("maxRequestBytes must be positive");
        }
    }
}

Kemudian terjemahkan hanya runtime-specific settings menjadi properties pada composition root.

Property classification

Property typeExampleOwner
application behaviorpage-size limitapplication configuration
Jersey runtimeprovider scanning settingJersey bootstrap
servlet init parameterapplication class nameservlet container
transportport, TLS, idle timeoutHTTP runtime
observabilityservice name, exporter endpointtelemetry bootstrap
secretpassword/tokensecret provider, bukan generic map

Class registration versus instance registration

Perbedaan ini bukan hanya syntax.

flowchart LR A[Register Class] --> B[Runtime or DI Creates Instance] B --> C[Injection and Lifecycle] C --> D[Managed Use] D --> E[Managed Disposal] F[Register Existing Instance] --> G[Object Already Constructed] G --> H[Limited or Different Injection] H --> I[Shared Use] I --> J[Cleanup Ownership Must Be Explicit]

Class registration advantages

  • lifecycle lebih mudah dikelola runtime;
  • injection lebih mungkin diterapkan konsisten;
  • proxy/interceptor dapat dipasang;
  • test dapat mengganti binding;
  • object graph lebih mudah diinspeksi.

Instance registration advantages

  • dapat memasukkan object yang sudah dikonfigurasi;
  • berguna untuk object immutable sederhana;
  • berguna pada manual composition root;
  • memudahkan embedding tertentu.

Instance registration risks

var resource = new QuoteResource(repository);
resourceConfig.register(resource);

Pertanyaan wajib:

  • apakah QuoteResource thread-safe?
  • apakah satu instance dipakai untuk semua request?
  • apakah @Context atau @Inject masih bekerja?
  • siapa menutup repository?
  • apakah proxy security/transaction/metrics dilewati?
  • bagaimana test memastikan lifecycle yang sama dengan production?

Senior review rule

Registering an instance is an ownership decision,
not a convenience shortcut.

Portable discovery versus implementation discovery

Jakarta REST mendefinisikan application model dan provider annotations, tetapi concrete discovery behavior bergantung pada deployment model dan implementation.

Possible mechanisms:

Application#getClasses()
Application#getSingletons()
container scanning
Jersey package scanning
servlet init parameters
service loader metadata
framework-generated registry
build-time indexing
manual registration

Portable assumption yang aman

  • resource/provider dapat diregistrasikan secara eksplisit;
  • standard Application dapat menyatakan classes/singletons/properties;
  • @Provider menandai provider class untuk discovery dalam environment yang melakukan scanning;
  • @ApplicationPath memberi application path dalam supported deployment model.

Asumsi yang harus diverifikasi

  • seluruh classpath discan;
  • scanning recursive;
  • nested JAR discan;
  • test runtime dan production runtime memiliki scanning rules sama;
  • package rename tidak menghapus endpoint;
  • provider dari dependency otomatis aktif;
  • generated classes ditemukan;
  • duplicate Application ditangani dengan cara tertentu.

Standard Configurable dan Configuration

Jakarta REST menyediakan configuration API untuk client dan server contexts.

Secara mental:

Configurable
  = mutable registration/configuration surface during bootstrap

Configuration
  = read-only view of effective registered components and properties

Typical operations:

configurable
        .property("key", value)
        .register(MyProvider.class)
        .register(new MyFeature());

Registrable component contracts

Sebuah component dapat memiliki satu atau lebih contracts:

  • provider contract;
  • filter contract;
  • interceptor contract;
  • feature contract;
  • implementation-specific contract.

Registration tidak berarti semua interface pada class otomatis selalu diperlakukan sama di setiap custom scenario. Bila code memakai contract map atau binding priority, review contract yang benar-benar aktif.

Configuration as evidence

Pada startup/test, configuration view dapat digunakan untuk memeriksa:

  • component terdaftar;
  • property aktif;
  • feature terpasang;
  • duplicate atau missing component;
  • profile-specific behavior.

Jangan mengandalkan logging seluruh configuration secara mentah karena dapat mengandung secret atau sensitive endpoint.


Standard Feature

jakarta.ws.rs.core.Feature adalah unit configuration yang dapat mengaktifkan sekumpulan component/property.

import jakarta.ws.rs.core.Feature;
import jakarta.ws.rs.core.FeatureContext;

public final class ApiErrorFeature implements Feature {

    @Override
    public boolean configure(FeatureContext context) {
        context.register(ProblemExceptionMapper.class);
        context.register(ErrorResponseFilter.class);
        return true;
    }
}

Feature mental model

Feature
  is a bootstrap-time module,
  not a per-request handler.

Feature dapat digunakan untuk:

  • mendaftarkan filters/interceptors;
  • mendaftarkan provider;
  • memasang property;
  • mengelompokkan capability;
  • mengaktifkan integration tertentu.

Good feature characteristics

  • cohesive;
  • deterministic;
  • idempotent dalam maksud configuration;
  • tidak melakukan domain I/O berat;
  • tidak membuka thread tersembunyi tanpa lifecycle;
  • memvalidasi prerequisite;
  • mencatat activation secara aman;
  • dapat diuji sebagai unit configuration.

Bad feature

public boolean configure(FeatureContext context) {
    var token = callRemoteConfigService();
    startUnboundedBackgroundThread();
    mutateGlobalStaticState();
    return true;
}

Feature bukan tempat ideal untuk arbitrary startup orchestration.


FeatureContext

FeatureContext memberi registration surface kepada feature.

public final class ObservabilityFeature implements Feature {

    private final TraceFilter traceFilter;

    public ObservabilityFeature(TraceFilter traceFilter) {
        this.traceFilter = traceFilter;
    }

    @Override
    public boolean configure(FeatureContext context) {
        context.register(traceFilter);
        context.property("telemetry.enabled", true);
        return true;
    }
}

Pertanyaan lifecycle muncul karena traceFilter adalah instance:

  • apakah immutable?
  • apakah thread-safe?
  • apakah dependency sudah final?
  • apakah runtime akan menutupnya?
  • apakah instance registration melewati DI?

Jika container injection dibutuhkan, class registration sering lebih aman:

context.register(TraceFilter.class);

DynamicFeature dan per-resource binding

DynamicFeature memungkinkan provider/filter diikat berdasarkan metadata resource class/method saat application model dibangun.

@Provider
public final class AuditDynamicFeature implements DynamicFeature {

    @Override
    public void configure(
            ResourceInfo resourceInfo,
            FeatureContext context) {

        if (resourceInfo.getResourceMethod()
                .isAnnotationPresent(Audited.class)) {
            context.register(AuditFilter.class);
        }
    }
}

Perbedaannya:

Feature
  configures application capability globally.

DynamicFeature
  configures components based on each resource method/class.

Risks

  • annotation inheritance tidak dipahami;
  • binding diam-diam hilang setelah refactor;
  • method metadata berbeda pada proxy/interface;
  • duplicate registration;
  • security behavior bergantung pada optional annotation;
  • difficult-to-see effective policy.

Untuk security-critical behavior, gunakan deny-by-default dan startup validation terhadap endpoint yang tidak memiliki policy.


Jersey ResourceConfig

org.glassfish.jersey.server.ResourceConfig adalah Jersey-specific application configuration utility. Ia memperluas konsep Application dan menyediakan API programmatic yang lebih kaya.

Contoh:

import org.glassfish.jersey.server.ResourceConfig;

public final class QuoteOrderResourceConfig extends ResourceConfig {

    public QuoteOrderResourceConfig() {
        register(QuoteResource.class);
        register(OrderResource.class);
        register(ProblemExceptionMapper.class);
        register(ApiErrorFeature.class);

        property("application.name", "quote-order-api");
    }
}

Important classification

Application
  = Jakarta REST standard.

ResourceConfig
  = Jersey-specific.

Class yang extends ResourceConfig mengikat bootstrap code pada Jersey. Itu dapat menjadi keputusan yang benar, tetapi harus sadar portability cost.

Why ResourceConfig exists

Ia mempermudah:

  • explicit component registration;
  • package scanning;
  • Jersey properties;
  • features;
  • binder registration;
  • programmatic resource model;
  • Jersey runtime integration.

Do not infer internal runtime

Menemukan ResourceConfig membuktikan penggunaan Jersey server API. Itu belum membuktikan:

  • Grizzly;
  • GlassFish;
  • Tomcat;
  • Jetty;
  • HK2 direct usage;
  • CDI;
  • executable JAR atau WAR.

Explicit registration dengan ResourceConfig

Contoh composition root:

public final class ApiResourceConfig extends ResourceConfig {

    public ApiResourceConfig(
            ApiSettings settings,
            QuoteService quoteService) {

        register(QuoteResource.class);
        register(OrderResource.class);

        register(ProblemExceptionMapper.class);
        register(CorrelationFilter.class);
        register(JsonFeature.class);

        register(new ApplicationBindings(settings, quoteService));

        property("application.environment", settings.environment());
    }
}

Catatan: bentuk binder aktual bergantung pada HK2/CDI/manual wiring dan dibahas pada part berikutnya.

Explicit registration advantages

  • component set terlihat dalam code review;
  • startup lebih deterministik;
  • dead code lebih mudah ditemukan;
  • package rename tidak diam-diam menghapus component;
  • provider dari dependency tidak otomatis aktif tanpa keputusan;
  • security review lebih mudah;
  • component inventory lebih mudah dibangun.

Cost

  • daftar dapat panjang;
  • developer dapat lupa mendaftarkan component;
  • modularity membutuhkan composition strategy;
  • generated modules membutuhkan registry integration;
  • tests perlu memastikan expected component set.

Mitigation

Bagi registration berdasarkan cohesive module:

public final class QuoteModuleFeature implements Feature {
    @Override
    public boolean configure(FeatureContext context) {
        context.register(QuoteResource.class);
        context.register(QuoteExceptionMapper.class);
        return true;
    }
}

Kemudian composition root tetap eksplisit:

register(QuoteModuleFeature.class);
register(OrderModuleFeature.class);

Package scanning

Jersey menyediakan package scanning convenience:

public final class ApiResourceConfig extends ResourceConfig {

    public ApiResourceConfig() {
        packages(
                "com.example.quote.api",
                "com.example.order.api"
        );
    }
}

Benefits

  • bootstrap code lebih kecil;
  • module baru dapat ditemukan tanpa central list;
  • cocok untuk prototype atau bounded package structure.

Risks

  • endpoint/provider aktif karena lokasi package, bukan keputusan eksplisit;
  • relocation atau shading mengubah behavior;
  • scanning cost;
  • nested dependency behavior;
  • test class ikut ditemukan;
  • provider dari package luas aktif tanpa review;
  • duplicate registration sulit dilacak;
  • production image minimization menghapus metadata/class;
  • native/build-time optimizations memerlukan index.

Safer scanning rules

Jika scanning dipakai:

  1. scan package sempit;
  2. jangan scan root organization package;
  3. jangan campur test packages;
  4. catat component inventory saat startup;
  5. test expected route/provider set;
  6. fail startup pada ambiguous resource model;
  7. dokumentasikan recursive behavior;
  8. jangan mengandalkan package scanning untuk security policy.

Trade-off

Explicit registration optimizes for governance.
Package scanning optimizes for local convenience.

Enterprise system sering memilih hybrid:

explicit module features
+ limited internal scanning inside each module
+ startup inventory and tests

Jersey server properties

Jersey memiliki server-specific properties untuk mengontrol behavior seperti scanning, validation, response processing, tracing, dan lainnya. Nama property serta availability bergantung pada Jersey version.

Contoh konseptual:

property("jersey.config.server.provider.packages",
         "com.example.api");

Hindari menyalin property dari artikel lama tanpa memeriksa:

  • Jersey major/minor version;
  • namespace generation;
  • default value;
  • deprecation;
  • replacement property;
  • production impact;
  • security impact.

Property governance

Buat registry internal:

PropertyVersionDefaultEnvironment overrideOwnerSensitive
package scanningruntime-specificdocumentednoplatformno
tracing levelruntime-specificofftemporarySREpotentially
validation moderuntime-specificstrictnoAPI platformno

Tracing caution

Runtime tracing dapat membocorkan:

  • route internals;
  • provider names;
  • timing detail;
  • headers;
  • application structure.

Aktifkan hanya secara terkontrol dan jangan mengekspos debug output kepada client production.


Auto-discovery dan classpath-driven behavior

Jersey modules atau third-party libraries dapat mendaftarkan capability melalui auto-discovery/service metadata.

Classpath-driven behavior berarti:

adding a dependency
may change runtime behavior
without changing bootstrap code.

Contoh kategori:

  • JSON provider;
  • multipart support;
  • injection bridge;
  • tracing feature;
  • entity filtering;
  • validation integration;
  • monitoring listeners.

Supply-chain implication

Dependency update dapat:

  • menambah provider;
  • mengubah provider priority;
  • mengganti serialization default;
  • mengaktifkan feature;
  • menambah reflection;
  • mengubah startup time;
  • menambah transitive implementation.

Governance controls

  • lock dependency versions;
  • inspect dependency tree;
  • maintain allowlist component;
  • disable unwanted auto-discovery bila didukung;
  • run bootstrap inventory tests;
  • compare effective configuration across upgrade;
  • use contract/golden serialization tests;
  • require architecture review untuk runtime module changes.

Programmatic resource model

Jersey mendukung resource model yang dibangun secara programmatic, selain annotation-based resources.

Use cases:

  • generated endpoints;
  • dynamic integration adapters;
  • compatibility facade;
  • framework-generated resources;
  • test fixtures.

Conceptual example:

Resource resource = Resource.builder("/health")
        .addMethod("GET")
        .handledBy((request) -> Response.ok("UP").build())
        .build();

resourceConfig.registerResources(resource);

API detail harus disesuaikan dengan Jersey version.

Risks

  • endpoint tidak terlihat melalui source annotation search;
  • OpenAPI generator mungkin tidak melihat route;
  • security annotation tidak berlaku;
  • model berubah berdasarkan runtime configuration;
  • generated handler memiliki DI/lifecycle berbeda;
  • route compatibility sulit direview.

Rule

Jika programmatic resources digunakan, wajib memiliki:

  • generated route manifest;
  • explicit authorization policy;
  • contract generation/verification;
  • deterministic input;
  • startup model validation;
  • integration tests;
  • ownership dan deprecation policy.

Conditional registration dan profiles

Registration kadang bergantung pada environment:

if (settings.enableInternalDiagnostics()) {
    register(InternalDiagnosticsResource.class);
}

Ini bukan otomatis salah. Namun profile-specific application model harus diperlakukan sebagai contract.

Risks

  • endpoint hanya ada di production atau hanya local;
  • security filter tidak terpasang pada profile tertentu;
  • test tidak mencakup production model;
  • disabled feature meninggalkan route yang referensinya masih ada;
  • configuration typo mengaktifkan diagnostics;
  • two environments expose different schemas.

Safer approach

public enum DeploymentProfile {
    LOCAL,
    TEST,
    STAGING,
    PRODUCTION
}

Gunakan typed profile dan exhaustive decision:

switch (settings.profile()) {
    case LOCAL -> register(LocalDevelopmentFeature.class);
    case TEST -> register(TestSupportFeature.class);
    case STAGING, PRODUCTION -> register(ProductionSecurityFeature.class);
}

Tetap validasi:

  • production tidak memiliki test endpoints;
  • required production features aktif;
  • profile dikenal;
  • default tidak permissive;
  • OpenAPI sesuai model.

Configuration precedence

Bootstrap sering menggabungkan banyak source:

code defaults
< packaged configuration
< environment-specific file
< environment variables
< secret references
< command-line/runtime overrides

Urutan aktual harus didokumentasikan dan diuji.

Precedence invariant

Untuk setiap effective value, engineer harus dapat menjawab:

what is the value?
where did it come from?
who can change it?
when does it take effect?
is it sensitive?
how is drift detected?

Dangerous ambiguity

APP_PORT=8080
server.port=9090
JERSEY_PORT=8081
container connector port=8443

Satu property name tidak cukup bila terdapat beberapa listener atau layer.

Startup report

Log aman:

configuration.profile=production
configuration.source.count=4
http.listener.name=public-api
http.listener.port=8443
http.tls.enabled=true
resource.count=42
provider.count=18

Jangan log:

  • secret value;
  • raw connection string dengan password;
  • full token;
  • private key;
  • tenant-specific sensitive configuration.

Bootstrap, dependency injection, dan ownership

Registration dan DI saling terkait.

Question sequence

Untuk setiap component:

  1. siapa membuat instance?
  2. kapan dibuat?
  3. scope apa?
  4. dependency dari container mana?
  5. apakah proxy/interceptor dipasang?
  6. apakah digunakan concurrently?
  7. siapa menutupnya?
  8. apa yang terjadi jika initialization gagal?

Example ambiguity

register(QuoteResource.class);

Ini memberi runtime class, tetapi actual constructor selection dan injection bergantung pada Jersey/HK2/CDI integration.

register(new QuoteResource(new QuoteService(...)));

Ini memberi instance yang sudah jadi, tetapi lifecycle/proxy semantics berbeda.

Composition root rule

Pilih satu tempat yang menjelaskan object graph boundary:

Configuration parsing
-> typed settings
-> infrastructure clients
-> repositories
-> services
-> resources/providers
-> runtime registration

Jangan membiarkan resources membuat infrastructure sendiri:

@Path("/quotes")
public class QuoteResource {
    private final DataSource dataSource =
            createDataSourceFromEnvironment(); // anti-pattern
}

Provider and feature ordering

Registration order tidak boleh dianggap otomatis sama dengan request execution order.

Ordering dapat ditentukan oleh:

  • @Priority;
  • built-in priority bands;
  • name binding;
  • dynamic binding;
  • implementation contracts;
  • explicit contract priority;
  • auto-discovery rules.

Wrong assumption

register(AuthenticationFilter.class);
register(AuditFilter.class);

Tidak otomatis membuktikan authentication selalu berjalan sebelum audit.

Correct approach

  • declare priority;
  • test order;
  • document invariants;
  • avoid equal priority for order-dependent components;
  • validate required security ordering;
  • expose effective chain dalam diagnostics aman.

Feature dependency

Feature A dapat mengharapkan component dari Feature B. Jangan bergantung pada incidental registration order.

Better:

public final class SecurityFeature implements Feature {
    @Override
    public boolean configure(FeatureContext context) {
        requireConfiguration(context);
        context.register(AuthenticationFilter.class);
        context.register(AuthorizationFilter.class);
        return true;
    }
}

Atau gabungkan capability yang memiliki hard dependency.


Configuration freeze dan runtime mutation

Configuration API biasanya digunakan saat bootstrap. Setelah runtime terbentuk, mutasi application model saat traffic berjalan harus dianggap berisiko dan implementation-specific.

Why runtime mutation is hard

  • concurrent request melihat model berbeda;
  • route cache perlu rebuild;
  • provider chain berubah;
  • OpenAPI tidak sinkron;
  • metrics cardinality berubah;
  • security policy dapat memiliki gap;
  • in-flight request memakai old model;
  • rollback semantics tidak jelas.

Prefer

Untuk perubahan capability:

configuration change
-> build new deployment
-> validate
-> progressive rollout
-> replace old instances

Untuk tenant/product settings yang memang dynamic, gunakan typed configuration service pada application layer, bukan mutation resource model.

Exception

Dynamic resource registration dapat legitimate untuk platform tertentu, tetapi memerlukan:

  • atomic model switch;
  • versioning;
  • consistency guarantee;
  • audit;
  • rollback;
  • compatibility policy;
  • operational tooling.

Model validation dan fail-fast startup

Runtime model validation harus menangkap sebanyak mungkin error sebelum traffic:

  • duplicate/ambiguous routes;
  • invalid path templates;
  • unsupported injection point;
  • missing provider;
  • conflicting provider;
  • invalid feature dependency;
  • duplicate application mapping;
  • missing required property;
  • malformed URI;
  • unsupported media type setup;
  • security policy gap.

Fail-fast versus degraded startup

FailureRecommended default
required database configuration missingfail startup
required JWT issuer missingfail startup
optional metrics exporter unavailablepossibly start degraded
optional feature disabledstart if explicitly configured
ambiguous routefail startup
missing required serializerfail startup
remote dependency temporarily unavailabledepends on readiness/lazy strategy

Startup error quality

Bad:

IllegalStateException

Better:

APPLICATION_BOOTSTRAP_FAILED
component=QuoteModuleFeature
reason=missing required configuration
property=quote.validation.max-lines
source=environment

Error message tidak boleh mencetak secret.


Component inventory sebagai operational artifact

Effective component inventory dapat memuat:

  • application class;
  • base application path;
  • root resources;
  • resource method count;
  • providers by contract;
  • filters and priorities;
  • exception mappers;
  • features;
  • DI modules;
  • serialization provider;
  • runtime version;
  • disabled auto-discovery settings.

Uses

  • compare local versus production;
  • detect accidental provider activation;
  • upgrade regression;
  • incident debugging;
  • audit runtime capability;
  • onboarding;
  • architecture documentation.

Security boundary

Jangan expose full inventory melalui unauthenticated public endpoint. Pilihan:

  • startup log dengan redaction;
  • build artifact manifest;
  • internal admin endpoint;
  • test snapshot;
  • observability metadata restricted to operators.

Example manifest

{
  "application": "QuoteOrderResourceConfig",
  "applicationPath": "/api",
  "resources": [
    "QuoteResource",
    "OrderResource"
  ],
  "providers": {
    "exceptionMappers": [
      "ProblemExceptionMapper"
    ],
    "requestFilters": [
      "CorrelationFilter",
      "AuthenticationFilter"
    ]
  }
}

Health, readiness, dan bootstrap completion

Health bukan satu boolean universal.

liveness
  process can continue making progress.

readiness
  instance should receive traffic.

startup completion
  initialization has reached required state.

Bootstrap relation

Readiness harus false sampai:

  • application model valid;
  • required configuration valid;
  • critical lifecycle components initialized;
  • listener and routing usable;
  • required security material loaded;
  • required migration compatibility verified.

Avoid

  • readiness selalu 200;
  • liveness bergantung pada transient downstream;
  • startup probe memanggil seluruh dependency secara agresif;
  • health endpoint memakai component graph yang sama berat dengan business endpoint;
  • listener menerima external traffic sebelum readiness enforced.

Runtime integration detail dibahas dalam container/Kubernetes parts, tetapi application bootstrap harus menyediakan state yang benar.


Graceful shutdown dan symmetric lifecycle

Bootstrap dan shutdown harus simetris:

flowchart LR A[Create Config] --> B[Create Clients] B --> C[Register Components] C --> D[Start Runtime] D --> E[Ready] E --> F[Stop Readiness] F --> G[Drain Requests] G --> H[Stop Consumers and Jobs] H --> I[Close Runtime] I --> J[Close Clients]

Ownership stack

Jika resource dibuat di composition root, composition root harus tahu cara menutupnya atau menyerahkan ownership secara eksplisit.

Gunakan AutoCloseable aggregate:

public final class ApplicationRuntime implements AutoCloseable {

    private final Server server;
    private final List<AutoCloseable> resources;

    @Override
    public void close() {
        stopAcceptingTraffic(server);
        drainInFlightRequests(server);

        for (int i = resources.size() - 1; i >= 0; i--) {
            closeSafely(resources.get(i));
        }
    }
}

Shutdown failure

  • close order salah;
  • Kafka consumer berhenti setelah DB pool ditutup;
  • executor ditutup sebelum callback selesai;
  • port ditutup tetapi readiness masih true;
  • request masih berjalan ketika dependency ditutup;
  • hook tidak dijalankan pada hard kill;
  • shutdown timeout lebih panjang dari platform grace period.

Testing bootstrap dan registration

Bootstrap harus diuji sebagai unit arsitektur, bukan hanya melalui endpoint happy path.

Test categories

1. Component set test

@Test
void registersRequiredResources() {
    var config = new ApiResourceConfig(testSettings());

    assertThat(config.getClasses())
            .contains(
                    QuoteResource.class,
                    OrderResource.class
            );
}

API introspection aktual dapat berbeda menurut registration style.

2. Application model startup test

Start runtime dan pastikan:

  • no model validation error;
  • routes expected tersedia;
  • duplicate route gagal;
  • required providers tersedia;
  • OpenAPI generation sukses.

3. Profile matrix test

LOCAL
TEST
STAGING
PRODUCTION

Untuk setiap profile, snapshot:

  • resources;
  • providers;
  • diagnostics endpoints;
  • security features;
  • serialization behavior.

4. Dependency mutation test

Upgrade/add dependency dan compare effective component manifest.

5. Packaging test

Jalankan packaged artifact, bukan hanya test classpath:

mvn verify
-> build final JAR/WAR
-> start artifact
-> query route inventory/contract

Design pattern: explicit composition root

Struktur yang dapat direview:

public final class Bootstrap {

    public static ApplicationRuntime start(
            String[] args,
            Environment environment) {

        var settings = SettingsLoader.load(environment);
        SettingsValidator.validate(settings);

        var infrastructure = Infrastructure.create(settings);
        var services = DomainServices.create(
                infrastructure,
                settings
        );

        var resourceConfig = new ApiResourceConfig(
                settings,
                services
        );

        var server = HttpRuntime.start(
                settings.http(),
                resourceConfig
        );

        return new ApplicationRuntime(
                server,
                infrastructure.closeables()
        );
    }
}

Benefits

  • object ownership terlihat;
  • startup order terlihat;
  • failure boundary terlihat;
  • test dapat mengganti environment;
  • runtime-specific code terisolasi;
  • application service tidak tahu container;
  • shutdown reverse order dapat dibangun.

Boundary

main/bootstrap package
  may depend on all modules.

domain/application modules
  must not depend on bootstrap/runtime implementation.

Anti-patterns

1. Scan everything

packages("com.company");

Risiko: provider/resource internal atau test ikut aktif.

2. Static mutable ResourceConfig

public static final ResourceConfig CONFIG = new ResourceConfig();

Risiko: shared mutation, test pollution, initialization order.

3. Hidden registration in static initializer

static {
    GlobalRegistry.register(...);
}

Risiko: classloading-dependent behavior.

4. Register preconstructed mutable resource singleton

register(new QuoteResource());

Resource menyimpan request state dalam fields.

5. Environment string spread everywhere

if ("prod".equals(System.getenv("ENV"))) {
    // ...
}

Risiko: inconsistent profile and unsafe defaults.

6. Catch and continue on bootstrap invariant failure

try {
    registerSecurity();
} catch (Exception ignored) {
}

Risiko: service berjalan tanpa security control.

7. Remote I/O inside feature configuration

Menjadikan feature execution unpredictable dan sulit di-timeout.

8. Log all properties

Membocorkan secret/token/endpoint internal.

9. Runtime route mutation without versioning

Menghasilkan split behavior antar-request/instance.

10. Assume registration order equals execution order

Menghasilkan security dan observability ordering bug.


Failure-model matrix

FailureStageObservable symptomDetectionLikely causeCorrective direction
resource missingdiscovery/registration404 semua route moduleroute inventorypackage tidak discan atau class tidak registeredexplicit registration/test
provider missingregistration415, 500, raw exceptionstartup/integration testmodule dependency atau feature tidak aktifregister provider/feature
duplicate routemodel validationstartup failure atau ambiguous behaviorstartup logsoverlapping resource templatesremove ambiguity
DI binding missingdependency graphstartup/invocation failurestack tracewrong container/binderfix ownership/binding
invalid propertyconfigurationsilent fallback atau startup failuretyped validationtypo/type mismatchtyped config/fail fast
auto-discovered provider changes JSONdiscoverypayload driftgolden contract testsnew transitive modulecontrol auto-discovery
singleton raceruntime useintermittent corrupt responseconcurrency testinstance registration + mutable fieldsmanaged scope/immutability
test endpoint in productionprofile registrationsecurity exposureroute manifestunsafe default/profile typodeny-by-default profile
startup reports ready too earlyreadinessearly request failuresdeployment timelineasync initializationgate readiness
shutdown leakteardownprocess hangsthread dumpunclosed client/executorsymmetric lifecycle
classpath scan slowdiscoverylong startupstartup timingbroad recursive scannarrow/explicit registration
feature partially configuredfeature activationmissing filter/providercomponent inventoryswallowed exception/conditional logicatomic fail-fast feature
wrong base pathdeployment mappingsystematic 404access logcontext/application/gateway mismatchderive route chain
config differs by nodeprecedence/driftinconsistent behaviorconfig fingerprintmutable external configversioned config/drift alert
debug tracing exposedruntime propertyinformation disclosuresecurity scanunsafe Jersey propertyrestricted diagnostics

Debugging playbook

Step 1 — Establish the effective artifact

Record:

commit SHA
artifact version
container digest
Java version
Jakarta REST API version
Jersey version
runtime/container version
active profile

Jangan debug source branch yang berbeda dari artifact.

Step 2 — Find composition root

Search:

extends Application
extends ResourceConfig
@ApplicationPath
new ResourceConfig
register(
packages(
ServletContainer
ApplicationHandler
GrizzlyHttpServerFactory

Tujuan: menemukan siapa yang membangun application model.

Step 3 — Identify discovery mode

Tentukan:

explicit classes
explicit instances
package scanning
annotation scanning
service loader
generated registry
auto-discovery

Step 4 — Build effective component inventory

Bandingkan expected versus actual:

  • resources;
  • providers;
  • filters;
  • mappers;
  • features;
  • properties;
  • DI bindings.

Step 5 — Verify path composition

gateway prefix
-> context path
-> application path
-> resource path

Gunakan access log pada setiap layer.

Step 6 — Check startup warnings/errors

Cari:

  • ambiguous resource;
  • ignored provider;
  • missing injection;
  • duplicate component;
  • unsupported contract;
  • invalid property;
  • scan failure;
  • class not found.

Step 7 — Disable incidental discovery in a reproduction

Buat minimal explicit ResourceConfig untuk menentukan apakah masalah berasal dari classpath scanning/auto-discovery.

Step 8 — Compare environment manifests

Compare:

local
integration test
staging
production

Bukan hanya config values, tetapi component set.

Step 9 — Confirm instance lifecycle

Jika component instance diregistrasikan, inspeksi:

  • mutable fields;
  • constructor side effects;
  • thread safety;
  • proxy/interceptor;
  • close semantics.

Step 10 — Prove with packaged-artifact test

Start artifact final dan jalankan:

  • route probes;
  • serialization golden tests;
  • security negative tests;
  • readiness transition;
  • shutdown test.

PR review checklist

Bootstrap boundary

  • Apakah composition root jelas?
  • Apakah runtime-specific code terisolasi?
  • Apakah application/domain modules bebas dari bootstrap global state?
  • Apakah startup order dan ownership dapat dipahami?

Application model

  • Resource/provider baru diregistrasikan secara deterministik?
  • Package scanning tetap sempit?
  • Tidak ada duplicate route/provider?
  • Programmatic resource memiliki contract dan security policy?
  • OpenAPI sesuai effective routes?

Class versus instance

  • Mengapa class atau instance registration dipilih?
  • Instance immutable/thread-safe?
  • Injection/proxy/lifecycle semantics dipahami?
  • Cleanup owner jelas?

Features

  • Feature cohesive?
  • Tidak melakukan unbounded I/O/thread creation?
  • Prerequisite divalidasi?
  • Failure tidak ditelan?
  • Dynamic binding aman terhadap annotation refactor?

Configuration

  • Property typed bila application-specific?
  • Precedence terdokumentasi?
  • Safe default?
  • Secret tidak masuk generic logging?
  • Unknown profile/property gagal cepat?
  • Runtime reload semantics jelas?

Discovery and dependencies

  • Dependency baru mengaktifkan provider otomatis?
  • Auto-discovery impact diuji?
  • Dependency tree dan BOM konsisten?
  • Component inventory snapshot diperbarui?

Operational readiness

  • Readiness menunggu bootstrap selesai?
  • Startup failure actionable?
  • Shutdown menutup resource reverse order?
  • Startup/shutdown timeout sesuai platform?
  • Diagnostic endpoint tidak public?

Trade-off yang harus dipahami senior engineer

Explicit registration versus scanning

DimensionExplicit registrationPackage scanning
determinismtinggibergantung rules/classpath
maintenancecentral list/module featureslebih otomatis
accidental activationrendahlebih tinggi
startup costpredictabledapat lebih besar
modularitybutuh composition disciplineconvenient
auditabilitytinggibutuh inventory tooling

Standard Application versus Jersey ResourceConfig

DimensionApplicationResourceConfig
portabilitytinggiJersey-specific
configuration richnessdasar/standardlebih luas
Jersey integrationsindirectdirect
migration costlebih rendahlebih tinggi
implementation controlterbataskuat

Class versus singleton instance

DimensionClassInstance
construction ownerruntime/DIapplication code
lifecycle integrationbiasanya lebih baikharus diverifikasi
thread safetyscope-dependentsering shared
test injectioncontainer-drivenmanual
cleanupcontainer/defined scopeexplicit owner required

Eager versus lazy initialization

DimensionEagerLazy
startup failure detectioncepatsaat traffic
startup timelebih tinggilebih rendah
transient dependency tolerancelebih rendahdapat lebih tinggi
first-request latencyrendahdapat tinggi
operational claritytinggilebih kompleks

Standard versus Jersey-specific behavior

AreaJakarta REST standardJersey-specific / runtime-specific
Applicationstandarddiscovery/deployment mechanism
@ApplicationPathstandard annotationintegration dengan servlet/runtime
getClasses()standardinstance creation/injection details
getSingletons()standardlifecycle integration details
getProperties()standard surfaceproperty keys and defaults
Configurablestandardextended contracts
Configurationstandarddiagnostics/extensions
FeaturestandardJersey-provided features
DynamicFeaturestandardmodel metadata nuances
ResourceConfigtidak standardJersey
packages(...)tidak standardJersey scanning
server propertiessebagian standard property surfacekeys Jersey-specific
auto-discoveryimplementation-specificJersey modules/service metadata
programmatic resource modeltidak portableJersey API
HK2 binder registrationtidak standardJersey/HK2
servlet init parametersdeployment-specificJersey servlet integration
startup diagnosticstidak distandardisasi penuhruntime/Jersey
runtime mutationtidak portableimplementation-specific

Rule

Use Jersey-specific APIs intentionally,
isolate them at the runtime boundary,
and verify internal version before copying configuration.

Internal verification checklist

Version and dependency graph

  • Identify jakarta.ws.rs-api or javax.ws.rs-api.
  • Identify Jersey server modules and exact version.
  • Identify BOM/parent dependency management.
  • Check duplicate Jakarta REST API artifacts.
  • Check auto-discovery-capable modules.
  • Check legacy Jersey 2 versus Jersey 3 namespace compatibility.

Application bootstrap

  • Find every subclass of Application.
  • Find every subclass/creation of ResourceConfig.
  • Find @ApplicationPath.
  • Find servlet init parameters/application class declarations.
  • Find standalone server factory/bootstrap.
  • Determine whether multiple applications exist in one artifact.

Registration

  • Inventory register(...), packages(...), property(...).
  • Inventory getClasses() and getSingletons().
  • Identify registered instances and their thread-safety.
  • Identify module-level features.
  • Identify DynamicFeature.
  • Identify programmatic resources.
  • Identify duplicate registration suppression behavior.

Discovery

  • Is package scanning enabled?
  • Which packages?
  • Is scanning recursive?
  • Are dependency JARs scanned?
  • Is auto-discovery enabled/disabled?
  • Are service-loader metadata files present?
  • Can adding a dependency activate a provider?

Configuration

  • List Jersey server properties.
  • List servlet init parameters.
  • Document property precedence.
  • Find environment/profile branching.
  • Identify unknown-property behavior.
  • Confirm sensitive properties are redacted.
  • Check runtime reload and drift behavior.

DI and lifecycle

  • Who constructs resources/providers?
  • HK2, CDI, manual, or hybrid?
  • Which components are singleton/request/per-lookup?
  • Are registered instances proxied or injected?
  • Who owns client/pool/executor shutdown?
  • Are lifecycle callbacks invoked?

Validation and operations

  • Does ambiguous resource model fail startup?
  • Is there an effective route/provider manifest?
  • Does readiness wait for initialization?
  • Are startup errors structured/actionable?
  • Are startup and shutdown durations measured?
  • Is there a shutdown drain policy?
  • Are runtime diagnostics access-controlled?

CI and governance

  • Test effective component set.
  • Test all deployment profiles.
  • Test packaged artifact.
  • Compare OpenAPI with runtime routes.
  • Detect accidental provider activation on dependency upgrade.
  • Review historical PRs for bootstrap/runtime changes.
  • Identify platform-standard bootstrap library used internally.

Latihan verifikasi

Latihan 1 — Standard application

Buat Application yang hanya menggunakan:

getClasses()
getSingletons()
getProperties()

Catat component mana yang dibuat runtime dan mana yang dibuat application.

Latihan 2 — Class versus instance

Register provider yang sama sebagai:

register(MyFilter.class);
register(new MyFilter());

Bandingkan:

  • constructor calls;
  • injection;
  • lifecycle callbacks;
  • thread usage;
  • cleanup.

Latihan 3 — Scanning blast radius

Mulai dengan package sempit, lalu perluas ke root package. Capture component inventory dan startup time. Identifikasi component yang tidak sengaja aktif.

Latihan 4 — Dependency activation

Tambahkan satu Jersey media/integration module. Bandingkan:

  • provider list;
  • serializer;
  • response payload;
  • startup logs;
  • dependency tree.

Latihan 5 — Profile matrix

Snapshot component manifest untuk local, test, staging, dan production. Buat assertion bahwa diagnostics/test resources tidak ada di production.

Latihan 6 — Fail-fast model

Tambahkan duplicate route dan missing required property. Pastikan startup gagal sebelum readiness.

Latihan 7 — Base path proof

Buktikan effective path melalui:

direct runtime
container context
local reverse proxy
development ingress

Dokumentasikan prefix transformations.

Latihan 8 — Graceful lifecycle

Buat instrumented closeables. Pastikan shutdown order adalah reverse dari initialization order dan tidak ada non-daemon thread tersisa.


Ringkasan

Mental model Part 007:

Annotated classes
do not equal
effective runtime application.

Effective application =
standard Application model
+ Jersey/runtime registration
+ discovery
+ properties
+ DI bindings
+ deployment metadata.

Invariant terpenting:

  1. Application adalah standard configuration abstraction, bukan HTTP server.
  2. ResourceConfig adalah Jersey-specific dan harus diisolasi pada runtime boundary.
  3. Class registration dan instance registration memiliki ownership serta lifecycle berbeda.
  4. Package scanning menukar explicit governance dengan convenience.
  5. Classpath dapat mengubah behavior melalui auto-discovery.
  6. Feature adalah bootstrap module, bukan tempat arbitrary background work.
  7. Configuration precedence harus dapat dijelaskan untuk setiap effective value.
  8. Model validation harus gagal sebelum traffic ketika invariant penting rusak.
  9. Readiness hanya boleh true setelah required bootstrap selesai.
  10. Bootstrap dan shutdown harus simetris serta memiliki owner yang jelas.
  11. Effective component inventory adalah artifact penting untuk audit dan debugging.
  12. Tidak ada detail runtime CSG yang boleh disimpulkan hanya dari annotation atau dependency tunggal.

Part berikutnya membahas taxonomy runtime: standalone Java SE process, embedded HTTP runtime, Servlet container, Jakarta EE server, packaging, thread/classloader ownership, dan bagaimana membuktikan runtime yang benar-benar digunakan.


Referensi resmi

Lesson Recap

You just completed lesson 07 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.