GlassFish, Grizzly, and Standalone Runtime
GlassFish Grizzly and Standalone Runtime
Runtime alternatif untuk Jersey/JAX-RS: GlassFish server model, Grizzly HTTP server, embedded runtime, startup lifecycle, thread model, deployment implications, dan internal verification
Part 018 — GlassFish, Grizzly, and Standalone Runtime
Fokus part ini: memahami runtime alternatif untuk JAX-RS/Jersey selain Tomcat/Jetty Servlet container, terutama GlassFish, Grizzly, dan standalone/embedded runtime.
Part sebelumnya membahas Tomcat/Jetty sebagai Servlet container runtime.
Part ini memperluas model: JAX-RS/Jersey bisa berjalan di full application server, embedded HTTP server, atau custom standalone launcher.
Pertanyaan utamanya:
What changes when JAX-RS runs in GlassFish instead of Tomcat/Jetty?
What is Grizzly and why does it often appear with Jersey?
What does standalone runtime mean?
Who owns startup, shutdown, thread pools, ports, and configuration?
What should be verified before assuming runtime behavior?
Untuk konteks CSG Quote & Order, jangan mengasumsikan GlassFish atau Grizzly dipakai hanya karena ada dependency Jersey atau package org.glassfish.*. org.glassfish.jersey adalah package Jersey; itu tidak otomatis berarti server GlassFish dipakai.
1. Runtime Taxonomy
Ada beberapa cara JAX-RS service dapat dijalankan.
1. Servlet container deployment
Example mental model: Tomcat/Jetty + Jersey ServletContainer
2. Full Jakarta EE application server
Example mental model: GlassFish/Payara-like server with Jakarta EE services
3. Embedded HTTP runtime
Example mental model: Java main starts Grizzly/Jetty/Tomcat programmatically
4. Standalone framework runtime
Example mental model: executable JAR owns server lifecycle behind framework abstraction
Yang penting bukan nama servernya, tetapi ownership lifecycle-nya.
Tanyakan:
Who starts the HTTP listener?
Who registers resources/providers?
Who owns dependency injection?
Who owns transactions?
Who owns configuration?
Who owns graceful shutdown?
Who owns metrics/access logs?
Jawaban pertanyaan ini menentukan cara debugging dan PR review.
2. Do Not Confuse org.glassfish.jersey with GlassFish Server
Jersey package sering memakai namespace:
org.glassfish.jersey.*
Ini berarti library Jersey berasal dari project Jersey, bukan bukti bahwa aplikasi berjalan di GlassFish server.
Contoh:
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
Bisa berjalan di:
Tomcat
Jetty
GlassFish
Grizzly embedded
custom standalone runtime
Kesalahan umum:
I saw org.glassfish.jersey, therefore runtime is GlassFish.
Kesimpulan yang benar:
I saw Jersey. Runtime still needs to be verified from bootstrap/deployment artifact/logs.
Internal verification:
[ ] Apakah ada GlassFish server image/runtime?
[ ] Apakah ada Grizzly bootstrap class?
[ ] Apakah ada Tomcat/Jetty dependency?
[ ] Apakah startup log menyebut actual server connector?
[ ] Apakah Dockerfile menjalankan server atau executable app?
3. GlassFish Server Model
GlassFish adalah application server Jakarta EE. Dalam full application server, container menyediakan lebih banyak layanan dibanding Servlet container minimal.
Konsep yang mungkin relevan:
domain/server instance
deployment unit
web container
EJB/CDI/JTA/JMS/JPA services if enabled/used
resource adapters
datasource managed by server
security realm/config
admin config
Dalam runtime seperti ini, JAX-RS tidak selalu hanya library yang kamu bootstrap sendiri. Sebagian lifecycle dapat dikelola server.
Simplified flow:
GlassFish starts
-> initializes server services
-> deploys application artifact
-> initializes web/JAX-RS/CDI components
-> exposes endpoints
-> manages server resources
Implikasi:
some resources may be configured outside application code
some services may be container-managed
startup failure may come from server config, not app code
deployment descriptor may matter
classloading can be server-controlled
4. GlassFish vs Plain Servlet Container
Perbandingan mental model:
Tomcat/Jetty style:
Mostly web container + application-provided libraries.
GlassFish style:
Jakarta EE application server + container-provided services.
Kemungkinan perbedaan:
CDI may be integrated by server
JTA may be available
JPA datasource may be server-managed
security realm may exist
JAX-RS may be part of server runtime
classloading may differ from embedded/executable app
admin config may affect app behavior
Tetapi jangan mengasumsikan semua fitur dipakai.
Rule:
Available in server does not mean used by application.
Used by application must be proven from code/config/deployment.
Internal verification checklist:
[ ] Apakah artifact dideploy ke full application server?
[ ] Apakah datasource dikelola server atau app/pool library?
[ ] Apakah transaksi memakai JTA atau manual/local transaction?
[ ] Apakah CDI dikelola server?
[ ] Apakah security realm/server auth dipakai?
[ ] Apakah ada deployment descriptor khusus server?
5. Grizzly Mental Model
Grizzly adalah HTTP/network framework yang sering dipakai bersama Jersey untuk embedded JAX-RS runtime.
Simplified model:
Java main
-> create ResourceConfig
-> create Grizzly HTTP server using ResourceConfig
-> start server on host/port
-> Jersey handles JAX-RS lifecycle
Contoh konseptual:
public final class Main {
public static void main(String[] args) throws Exception {
ResourceConfig config = new ResourceConfig()
.packages("com.company.quoteorder.api");
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(
URI.create("http://0.0.0.0:8080/api/"),
config
);
Runtime.getRuntime().addShutdownHook(new Thread(server::shutdownNow));
Thread.currentThread().join();
}
}
Ini bukan Servlet deployment tradisional.
Implikasi:
there may be no web.xml
there may be no Servlet filter chain
server mapping is defined in bootstrap URI/config
application owns server lifecycle
JAX-RS ResourceConfig is central
thread pool and network settings may be configured programmatically
6. Grizzly vs Servlet Runtime
Jika Jersey berjalan di Grizzly standalone:
Client
-> Grizzly HTTP server
-> Jersey runtime
-> JAX-RS request matching
-> resource method
Jika Jersey berjalan di Tomcat/Jetty Servlet:
Client
-> Servlet container connector
-> Servlet filter chain
-> Jersey ServletContainer
-> JAX-RS request matching
-> resource method
Perbedaan utama:
Servlet-specific APIs may not be available or may behave differently.
Servlet filters may not exist.
web.xml may not exist.
context path/mapping model differs.
bootstrap is usually code-driven.
container-level config may live in Java config rather than server config.
PR review question:
Does this change assume Servlet APIs are available?
Jika aplikasi harus portable across runtimes, hindari direct dependency ke HttpServletRequest kecuali benar-benar diperlukan.
7. Standalone Runtime Ownership
Standalone runtime berarti aplikasi Java memiliki lebih banyak tanggung jawab.
Aplikasi biasanya memiliki:
main method
server startup
port binding
resource registration
config loading
dependency wiring
thread pool setup
shutdown hook
dependency cleanup
readiness lifecycle
Dalam external application server, banyak hal ini dimiliki server.
Trade-off standalone runtime:
Pros:
explicit startup path
easier containerization
single artifact owns its runtime
less server magic
easier local execution
Cons:
app must implement lifecycle carefully
more responsibility for shutdown and resource cleanup
thread/connector tuning may be missed
observability/access log may need explicit setup
runtime conventions may vary per service
Senior engineer harus mencari main() atau launcher class dan membaca startup path sebagai production-critical code.
8. Startup Lifecycle in Standalone Jersey/Grizzly
Typical standalone startup:
1. process starts
2. config is loaded
3. logging initialized
4. DI/container initialized
5. ResourceConfig/Application created
6. resources/providers/features registered
7. DB/Kafka/Redis/HTTP clients initialized or lazily prepared
8. HTTP server created
9. port bound
10. readiness exposed
Bad startup pattern:
server port opens before dependencies are ready
readiness returns OK before resources are initialized
lazy startup hides missing binding until first production request
expensive initialization blocks without timeout
startup failure logs are unstructured
Preferred pattern:
fail fast for required config
validate required dependency wiring
separate liveness from readiness
bind server only after critical bootstrap succeeds, or keep readiness false
log startup phases clearly
expose build/version/runtime metadata safely
Internal verification checklist:
[ ] Di mana config dimuat?
[ ] Kapan ResourceConfig dibuat?
[ ] Kapan dependency penting diinisialisasi?
[ ] Kapan server mulai listen?
[ ] Kapan readiness true?
[ ] Apakah startup failure terlihat jelas di logs?
9. Shutdown Lifecycle in Standalone Runtime
Shutdown bukan hanya System.exit.
Production shutdown harus mempertimbangkan:
stop accepting new requests
drain in-flight requests
stop background consumers/workers
flush logs/metrics/traces
close DB pools
close HTTP clients
close Kafka producers/consumers
release file handles
exit within Kubernetes termination grace period
Bad shutdown pattern:
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
server.shutdownNow();
}));
Ini mungkin terlalu kasar jika langsung membunuh request.
Better mental model:
readiness false
server graceful shutdown
workers stop polling
in-flight work drains with deadline
resources close in dependency order
force exit only after deadline
Internal verification checklist:
[ ] Apakah SIGTERM ditangani?
[ ] Apakah server punya graceful shutdown?
[ ] Apakah Kafka/worker/background jobs berhenti rapi?
[ ] Apakah DB pool ditutup?
[ ] Apakah shutdown order terdokumentasi?
[ ] Apakah termination grace period cukup?
10. Thread Model in Grizzly/Standalone Runtime
Standalone runtime tetap memiliki finite processing capacity.
Thread pools dapat meliputi:
HTTP acceptor / selector threads
worker threads
application executor
scheduled executor
DB connection pool
Kafka consumer threads
HTTP client connection pool threads
background job threads
Masalah umum:
HTTP worker blocked by DB/downstream call
small thread pool causing low concurrency
unbounded executor causing memory pressure
scheduled task blocks shared executor
shutdown hook waits forever
Senior review harus melihat:
which executor runs request code
which executor runs background code
which calls are blocking
which queues are bounded
which timeout protects each wait
Internal verification checklist:
[ ] Di mana thread pool HTTP dikonfigurasi?
[ ] Apakah ada custom executor?
[ ] Apakah queue bounded?
[ ] Apakah blocking work dipisah?
[ ] Apakah metrics thread pool tersedia?
11. Port Binding and Base URI
Standalone runtime sering menentukan base URI di code/config.
Contoh konseptual:
host = 0.0.0.0
port = 8080
basePath = /api
baseUri = http://0.0.0.0:8080/api/
Failure mode:
binds to localhost inside container, not reachable from outside
port mismatch with Kubernetes Service targetPort
base URI includes /api but ingress also adds /api
base URI missing trailing slash causing route issues
hardcoded port conflicts with platform standard
Debugging checklist:
[ ] Process listens on expected host/port?
[ ] Container exposes correct port?
[ ] Kubernetes Service targetPort matches container port?
[ ] Readiness probe path includes correct base path?
[ ] Ingress path rewrite matches runtime base path?
12. Configuration Source in Standalone Runtime
Standalone runtime often controls configuration loading directly.
Possible sources:
system properties
environment variables
property files
YAML files
Kubernetes ConfigMap/Secret
cloud config store
command-line arguments
internal config service
Important questions:
What is precedence order?
Are required configs validated?
Are secrets separated from ordinary config?
Are defaults safe?
Can config reload at runtime?
Is config snapshot logged safely?
Failure mode:
local default accidentally used in production
secret logged during startup
wrong profile selected
config drift across pods
runtime reload changes behavior unexpectedly
Internal verification checklist:
[ ] Di mana config loader berada?
[ ] Apa precedence order?
[ ] Bagaimana profile/environment dipilih?
[ ] Apakah missing config fail fast?
[ ] Apakah secret redacted?
13. Resource Registration in Standalone Jersey
Standalone Jersey usually uses ResourceConfig heavily.
Examples:
ResourceConfig config = new ResourceConfig()
.register(QuoteResource.class)
.register(GlobalExceptionMapper.class)
.register(CorrelationFilter.class)
.packages("com.company.quoteorder.api");
Registration modes:
explicit class registration
package scanning
feature registration
binder registration
provider auto-discovery
Trade-off:
explicit registration is clear but verbose
package scanning is convenient but can hide accidental inclusion/exclusion
auto-discovery reduces boilerplate but makes runtime behavior less obvious
Failure mode:
resource not registered
provider registered twice
wrong package scanned
test runtime differs from production runtime
auto-discovered provider changes behavior after dependency upgrade
Internal verification checklist:
[ ] Apakah resource registration explicit atau scanning?
[ ] Apakah test memakai ResourceConfig yang sama?
[ ] Apakah provider registration deterministic?
[ ] Apakah auto-discovery aktif?
[ ] Apakah startup log mencetak registered resources/providers?
14. GlassFish/Grizzly/Jersey Classloading Concerns
Classloading dapat berbeda antar runtime.
External server:
server classloader
application classloader
shared libraries
provided dependencies
Standalone app:
application classpath owns most libraries
fat jar/shaded jar may relocate classes
container image includes runtime dependencies
Failure mode:
works locally but fails in server due to provided dependency mismatch
NoSuchMethodError due to version conflict
ClassNotFoundException for provider
duplicate provider because dependency brought multiple modules
JAXB/Jackson provider mismatch
Senior review:
Do not only check source code. Check dependency tree and runtime packaging.
Internal verification checklist:
[ ] Apakah dependency Servlet/Jakarta/Jersey scope `provided` atau bundled?
[ ] Apakah ada dependency conflict Jakarta vs javax?
[ ] Apakah shaded/fat jar digunakan?
[ ] Apakah server menyediakan library yang juga dibundled app?
[ ] Apakah dependency tree diverifikasi di CI?
15. Access Logs and Observability in Standalone Runtime
External container sering punya access log built-in.
Standalone runtime mungkin perlu setup eksplisit.
Observability minimum:
access log or equivalent request log
application structured log
correlation ID
request latency metric
HTTP status metric
in-flight request metric
thread pool metric
error metric
startup/shutdown events
Failure mode:
no access log, cannot confirm request reached process
all 404s look like application errors
thread pool saturation invisible
startup failures unstructured
shutdown kills request without trace
Internal verification checklist:
[ ] Apakah standalone runtime punya access log?
[ ] Apakah request log punya method/path/status/latency?
[ ] Apakah correlation ID masuk access log?
[ ] Apakah thread pool metrics tersedia?
[ ] Apakah startup/shutdown event tercatat?
16. Health, Liveness, and Readiness
Standalone runtime harus sangat jelas soal health endpoints.
liveness = process should be restarted if false
readiness = process should receive traffic if true
startup = process is still starting and should not be killed too early
Common mistakes:
liveness checks DB and causes restart storm
readiness returns true before server/dependencies ready
health endpoint goes through same auth filter as public API
health endpoint depends on slow downstream call
readiness does not turn false during shutdown
Better model:
liveness: minimal internal process health
readiness: dependency/config/runtime readiness with bounded timeout
startup: large initialization window if needed
Internal verification checklist:
[ ] Apa path liveness/readiness/startup?
[ ] Apakah health endpoint JAX-RS resource atau container/platform endpoint?
[ ] Apakah readiness menguji dependency penting?
[ ] Apakah check punya timeout?
[ ] Apakah readiness false saat shutdown?
17. Embedded Runtime Testing Strategy
Standalone/embedded runtime dapat diuji lebih realistis dibanding pure resource test.
Test levels:
ResourceConfig-only test
-> tests JAX-RS resources/providers without real network
Embedded HTTP test
-> starts Grizzly/Jetty/Tomcat on random port
-> sends real HTTP request
Container integration test
-> starts service artifact in Docker/Testcontainers
-> tests packaging/runtime/config behavior
Kapan pakai embedded HTTP test:
path mapping
provider registration
serialization
filter ordering
ExceptionMapper
real client behavior
Kapan perlu container integration test:
Dockerfile
JVM flags
port binding
config/secret mounting
Kubernetes-style env vars
startup/shutdown behavior
Internal verification checklist:
[ ] Apakah test memakai ResourceConfig production?
[ ] Apakah embedded test memakai random port?
[ ] Apakah container image diuji sebelum deploy?
[ ] Apakah health endpoint diuji?
[ ] Apakah startup failure path diuji?
18. Common Runtime Failure Modes
18.1 Jersey resources not found in Grizzly
Possible causes:
wrong base URI
wrong package scanning
resource class not registered
provider registration fails at startup
classloader/dependency issue
18.2 Service starts but Kubernetes readiness fails
Possible causes:
server binds wrong port
readiness path missing base path
binds to localhost instead of 0.0.0.0
startup time exceeds probe threshold
health endpoint protected by auth filter
18.3 Works in embedded test but fails in production
Possible causes:
different ResourceConfig
different config profile
different classpath
container image missing resource file
gateway path rewrite not represented in test
18.4 Shutdown causes duplicate processing
Possible causes:
server kills in-flight request
client retries unknown outcome
Kafka producer not flushed
DB transaction committed but response lost
outbox publisher interrupted mid-batch
18.5 Runtime library upgrade changes behavior
Possible causes:
provider auto-discovery changed
dependency conflict resolved differently
JSON provider changed priority
default thread pool changed
security defaults changed
19. Debugging Playbook
For GlassFish/Grizzly/standalone runtime, debug in this order:
1. Runtime identity
What server/runtime is actually running?
2. Artifact type
WAR, JAR, fat JAR, container image, external server deployment?
3. Startup path
Who creates server, ResourceConfig, DI container, dependencies?
4. Port/base path
What host/port/base URI is bound?
5. Resource registration
Are resources/providers registered as expected?
6. Request path
Does external path match runtime base path and @Path?
7. Thread/timeout/resource limits
Are threads, pools, and timeouts sufficient?
8. Health/readiness
Does platform route only when ready?
9. Shutdown
Are in-flight requests and background workers drained?
This prevents guessing based only on source annotations.
20. PR Review Checklist
For changes touching GlassFish/Grizzly/standalone runtime:
[ ] Does the change alter startup order?
[ ] Does it alter ResourceConfig/Application registration?
[ ] Does it assume Servlet APIs in a non-Servlet runtime?
[ ] Does it change base URI, port, context path, or routing prefix?
[ ] Does it introduce lazy initialization that may fail on first request?
[ ] Does it add a background worker without shutdown handling?
[ ] Does it change thread pool, queue, timeout, or connector settings?
[ ] Does it expose health/readiness correctly?
[ ] Does it preserve access logging and correlation?
[ ] Does it affect local/test runtime differently from production runtime?
[ ] Does dependency scope match actual deployment model?
21. Internal Verification Checklist — CSG Quote & Order
Use this checklist before making runtime assumptions:
Runtime
[ ] Does the service run in GlassFish, Grizzly, Tomcat, Jetty, or another runtime?
[ ] Does `org.glassfish.jersey` only indicate Jersey library, or actual GlassFish server?
[ ] Is runtime external or embedded?
Artifact
[ ] Is the deployment artifact WAR, executable JAR, or container image?
[ ] Is there a `main()` launcher?
[ ] Is there server bootstrap code?
JAX-RS registration
[ ] Is `Application` or `ResourceConfig` used?
[ ] Are resources registered explicitly or by package scan?
[ ] Are providers/features/binders registered deterministically?
Lifecycle
[ ] What happens at startup?
[ ] What happens at shutdown?
[ ] Are readiness/liveness/startup probes aligned with runtime lifecycle?
Threading
[ ] Where are HTTP worker threads configured?
[ ] Are background workers separate?
[ ] Are executors bounded and instrumented?
Operations
[ ] Are access logs available?
[ ] Are runtime metrics available?
[ ] Are startup/shutdown events logged?
[ ] Are runtime limits documented?
Dependencies
[ ] Are Jakarta/Jersey/Servlet dependencies bundled or provided?
[ ] Are there javax/jakarta conflicts?
[ ] Is dependency tree stable across local/test/prod?
22. Senior Mental Model
The runtime is part of the architecture.
A JAX-RS service is not just:
@Path class + service method + repository
It is:
process lifecycle
+ runtime bootstrap
+ HTTP server/container
+ resource registration
+ dependency wiring
+ thread pools
+ config/secret loading
+ health/readiness
+ shutdown behavior
+ observability
+ JAX-RS resource methods
GlassFish, Grizzly, Tomcat, Jetty, and standalone launchers differ in where these responsibilities live.
Senior engineering skill is not memorizing every runtime API. It is being able to locate ownership boundaries and reason about failure.
23. Key Takeaways
`org.glassfish.jersey` means Jersey library, not necessarily GlassFish server.
GlassFish is a Jakarta EE application server with more container-managed services.
Grizzly is often used as an embedded HTTP runtime for Jersey.
Standalone runtime means the application owns startup, shutdown, config, and server lifecycle.
Servlet APIs should not be assumed available in non-Servlet runtime.
ResourceConfig is often the central artifact in standalone Jersey.
Readiness, shutdown, thread pools, access logs, and dependency scope must be verified.
Runtime identity must be proven from artifact, bootstrap code, deployment config, and logs.
Part berikutnya akan membahas Internal Runtime Verification Map secara sistematis: cara membaca repo, dependency tree, Dockerfile, Kubernetes manifest, pipeline, dan startup logs untuk membuktikan runtime aktual yang dipakai.
You just completed lesson 18 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.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.