{"id":23546818,"url":"https://github.com/summerbootframework/jexpress","last_synced_at":"2026-05-29T02:04:43.567Z","repository":{"id":37797023,"uuid":"409689969","full_name":"SummerBootFramework/jExpress","owner":"SummerBootFramework","description":"Summer Boot Framework was initiated by a group of developers in 2004 to provide a high performance, free customizable but also lightweight Netty JAX-RS Restful, WebSocket and gRPC service with powerful reusable non-functional features, and was adopted by several Toronto law firms in 2011 to customize their back-end services.","archived":false,"fork":false,"pushed_at":"2025-08-18T23:35:57.000Z","size":1678,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-08-19T01:14:22.622Z","etag":null,"topics":["grpc","http","http-server","java","jax-rs","jpa-hibernate","netty","netty-grpc","netty-http","netty-jax-rs","netty-jaxrs","netty-restful","netty-rpc","netty-rs","netty-server","netty-websrvice","nio","restful-api","restful-webservices","websocket"],"latest_commit_sha":null,"homepage":"https://summerboot.org/, https://jexpress.org","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SummerBootFramework.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-09-23T17:45:00.000Z","updated_at":"2025-08-18T23:32:43.000Z","dependencies_parsed_at":"2023-02-18T13:16:37.195Z","dependency_job_id":"f4123fa8-48ea-47ef-a99a-0af9c63ca17a","html_url":"https://github.com/SummerBootFramework/jExpress","commit_stats":{"total_commits":198,"total_committers":2,"mean_commits":99.0,"dds":0.04040404040404044,"last_synced_commit":"29e14f5582c0bc053ba5608eeda5252116fdc0d8"},"previous_names":["summerbootframework/core"],"tags_count":45,"template":false,"template_full_name":null,"purl":"pkg:github/SummerBootFramework/jExpress","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SummerBootFramework%2FjExpress","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SummerBootFramework%2FjExpress/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SummerBootFramework%2FjExpress/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SummerBootFramework%2FjExpress/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SummerBootFramework","download_url":"https://codeload.github.com/SummerBootFramework/jExpress/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SummerBootFramework%2FjExpress/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273467424,"owners_count":25111129,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-03T02:00:09.631Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["grpc","http","http-server","java","jax-rs","jpa-hibernate","netty","netty-grpc","netty-http","netty-jax-rs","netty-jaxrs","netty-restful","netty-rpc","netty-rs","netty-server","netty-websrvice","nio","restful-api","restful-webservices","websocket"],"created_at":"2024-12-26T09:11:56.331Z","updated_at":"2026-05-29T02:04:43.560Z","avatar_url":"https://github.com/SummerBootFramework.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jExpress focuses on addressing non-functional and operational maintainability needs, some of which Spring Boot may not yet provide.\u003c/description\u003e\r\n\r\n* **[Apache Central Repository][1]**\r\n* **[Maven Central Repository][2]**\r\n* **[mvnrepository.com][3]**\r\n\r\n[1]: https://repo.maven.apache.org/maven2/org/summerboot/jexpress/2.6.10\r\n\r\n[2]: https://central.sonatype.com/artifact/org.summerboot/jexpress/2.6.10\r\n\r\n[3]: https://mvnrepository.com/artifact/org.summerboot/jexpress/2.6.10\r\n\r\n[View Changelog (CHANGES)](CHANGES.md)\r\n\r\n\u003e **Java 21+ · Netty 4.2 · Guice 7 · Jakarta EE · Virtual Threads**\r\n\r\nSummer Boot Framework was initiated by a group of developers in 2004 to provide a high-performance, free, customizable, and lightweight Netty JAX-RS RESTful, WebSocket, and gRPC\r\nservice with JPA and other powerful reusable non-functional features. Since 2011, it has been adopted by several Toronto law firms to customize their back-end services.\r\n\r\nIts sub-project, **jExpress** (a.k.a. Summer Boot Framework Core), focuses on solving the following non-functional and operational maintainability requirements.\r\n\r\n![Summer Boot Overview](SummerBootOverview.png)\r\n\r\n**Open Source History:** jExpress was initially open-sourced on MS MySpace in Sep 2006. Due to the shutdown of MySpace, this framework was migrated to a server sponsored by one of\r\nthe law firms in October 2011, then to GitLab in Dec 2016, and eventually to GitHub in Sep 2021.\r\n\r\n\u003e Disclaimer: We really had a great time with GitLab until 2021 when we realized one of the contributor's employers was also using GitLab at that time. We decided to move to GitHub\r\n\u003e instead to avoid incurring unnecessary hassles.\r\n\r\n---\r\n\r\n## Maven Dependency\r\n\r\n```xml\r\n\r\n\u003cdependency\u003e\r\n    \u003cgroupId\u003eorg.summerboot\u003c/groupId\u003e\r\n    \u003cartifactId\u003ejexpress\u003c/artifactId\u003e\r\n    \u003cversion\u003e2.6.6\u003c/version\u003e\r\n\u003c/dependency\u003e\r\n```\r\n\r\nSNAPSHOT repository:\r\n\r\n```xml\r\n\r\n\u003crepositories\u003e\r\n    \u003crepository\u003e\r\n        \u003cid\u003emaven.snapshots\u003c/id\u003e\r\n        \u003cname\u003eMaven Snapshot Repository\u003c/name\u003e\r\n        \u003curl\u003ehttps://s01.oss.sonatype.org/content/repositories/snapshots/\u003c/url\u003e\r\n        \u003creleases\u003e\r\n            \u003cenabled\u003efalse\u003c/enabled\u003e\r\n        \u003c/releases\u003e\r\n        \u003csnapshots\u003e\r\n            \u003cenabled\u003etrue\u003c/enabled\u003e\r\n        \u003c/snapshots\u003e\r\n    \u003c/repository\u003e\r\n\u003c/repositories\u003e\r\n```\r\n\r\n- **Apache Central:** https://repo.maven.apache.org/maven2/org/summerboot/jexpress/2.6.6\r\n- **Sonatype Central:** https://central.sonatype.com/artifact/org.summerboot/jexpress/2.6.6\r\n- **mvnrepository.com:** https://mvnrepository.com/artifact/org.summerboot/jexpress/2.6.6\r\n\r\n---\r\n\r\n## 1. Performance: RESTful Web Services (JAX-RS) with Non-blocking I/O (powered by Netty Reactor)\r\n\r\n### 1.1 Intent\r\n\r\n* Solve the performance bottleneck of traditional multi-threading at the I/O layer.\r\n* Quickly develop a RESTful Web Service with JAX-RS with minimal code.\r\n\r\n### 1.2 Motivation\r\n\r\n* Application servers are always heavy, and some are not free (IBM WebSphere, Oracle Glassfish, Payara, Red Hat JBoss, Tomcat).\r\n* Netty Reactor's *multiplexing* approach provides incredible power for socket-level custom communication protocols.\r\n* **Virtual Thread** support (Java 21): `VirtualThread`, `CPU`, `IO`, and `Mixed` modes are configurable for HTTP server, HTTP client, gRPC server, and BackOffice.\r\n\r\n### 1.3 Sample Code\r\n\r\n**step 1 — main class:**\r\n\r\n```java\r\nimport org.summerboot.jexpress.boot.SummerApplication;\r\n\r\npublic class Main {\r\n    public static void main(String... args) {\r\n        SummerApplication.run();\r\n    }\r\n}\r\n```\r\n\r\n**step 2 — lifecycle hooks (replaces deprecated `SummerRunner`):**\r\n\r\nImplement `AppLifecycleListener` or extend `AppLifecycleHandler` (recommended):\r\n\r\n```java\r\nimport com.google.inject.Singleton;\r\nimport org.summerboot.jexpress.boot.SummerApplication;\r\nimport org.summerboot.jexpress.boot.SummerInitializer;\r\nimport org.summerboot.jexpress.annotation.Order;\r\nimport org.summerboot.jexpress.boot.lifecycle.AppLifecycleHandler;\r\nimport org.apache.commons.cli.Options;\r\nimport org.apache.logging.log4j.LogManager;\r\nimport org.apache.logging.log4j.Logger;\r\n\r\nimport java.io.File;\r\n\r\n@Singleton\r\n@Order(1)\r\npublic class MainLifecycle extends AppLifecycleHandler implements SummerInitializer {\r\n\r\n    private static final Logger log = LogManager.getLogger(MainLifecycle.class);\r\n\r\n    @Override\r\n    public void initCLI(Options options) {\r\n        log.info(\"CLI options initialized\");\r\n    }\r\n\r\n    @Override\r\n    public void initAppBeforeIoC(File configDir) {\r\n        log.info(\"before IoC: {}\", configDir);\r\n    }\r\n\r\n    @Override\r\n    public void initAppAfterIoC(File configDir, com.google.inject.Injector guiceInjector) {\r\n        log.info(\"after IoC: {}\", configDir);\r\n    }\r\n\r\n    @Override\r\n    public void beforeApplicationStart(SummerApplication.AppContext context) throws Exception {\r\n        log.debug(\"application about to start\");\r\n    }\r\n}\r\n```\r\n\r\n\u003e **Migration note (from \u003c 2.6.5):**  \r\n\u003e `SummerRunner` has been removed. Move your `run()` logic to `AppLifecycleListener.beforeApplicationStart()`.\r\n\r\n**step 3 — a RESTful controller:**\r\n\r\n```java\r\nimport com.google.inject.Singleton;\r\nimport jakarta.validation.constraints.NotNull;\r\nimport jakarta.ws.rs.*;\r\nimport jakarta.ws.rs.core.MediaType;\r\n\r\nimport java.util.List;\r\n\r\nimport org.summerboot.jexpress.annotation.Controller;\r\nimport org.summerboot.jexpress.annotation.restful.Log;\r\nimport org.summerboot.jexpress.controller.SessionContext;\r\nimport io.netty.handler.codec.http.HttpResponseStatus;\r\n\r\n@Singleton\r\n@Controller\r\n@Path(\"/hellosummer\")\r\npublic class MyController {\r\n\r\n    @GET\r\n    @Path(\"/account/{name}\")\r\n    @Produces(MediaType.TEXT_PLAIN)\r\n    public String hello(@NotNull @PathParam(\"name\") String myName) {\r\n        return \"Hello \" + myName;\r\n    }\r\n\r\n    @POST\r\n    @Path(\"/account/{name}\")\r\n    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})\r\n    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})\r\n    public ResponseDto hello_no_validation(@PathParam(\"name\") String myName, RequestDto request) {\r\n        return new ResponseDto();\r\n    }\r\n\r\n    /**\r\n     * Three features:\r\n     * 1. auto-validate JSON request via Bean Validation (enabled by default in v2.6+, no @Valid needed)\r\n     * 2. mask sensitive fields in log via @Log(maskDataFields)\r\n     * 3. mark performance POI via context.poi(key)\r\n     */\r\n    @POST\r\n    @Path(\"/hello/{name}\")\r\n    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})\r\n    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})\r\n    @Log(maskDataFields = {\"creditCardNumber\", \"clientPrivacy\", \"secretList\"})\r\n    public ResponseDto hello(@NotNull @PathParam(\"name\") String myName,\r\n                             @NotNull RequestDto request,          // @Valid not required since v2.6.0\r\n                             final SessionContext context) {\r\n        context.poi(\"DB begin\");\r\n        // ... DB access ...\r\n        context.poi(\"DB end\");\r\n\r\n        context.status(HttpResponseStatus.CREATED);\r\n        return new ResponseDto();\r\n    }\r\n\r\n    public static class RequestDto {\r\n        @NotNull\r\n        private String creditCardNumber;\r\n        @NotNull\r\n        private List\u003cString\u003e shoppingList;\r\n    }\r\n\r\n    public static class ResponseDto {\r\n        private String clientPrivacy;\r\n        private final List\u003cString\u003e secretList = List.of(\"aa\", \"bb\");\r\n    }\r\n}\r\n```\r\n\r\n\u003e **v2.6.0+:** Bean Validation is **enabled by default** for all `@Controller` methods — no need to add `@Valid` on request body parameters.\r\n\r\n### 1.4 Use `AlternativeName`\r\n\r\nThe controller is only activated when the app is launched with `-use RoleBased`:\r\n\r\n```java\r\n@Controller(AlternativeName = \"RoleBased\")\r\n```\r\n\r\n### 1.5 PING endpoint\r\n\r\n```java\r\nimport org.summerboot.jexpress.controller.restful.PingController;\r\n\r\n@Controller\r\n@Path(\"/hellosummer\")\r\npublic class MyController extends PingController {\r\n    // GET /hellosummer/ping is auto-registered\r\n}\r\n```\r\n\r\nor use `@Ping` directly:\r\n\r\n```java\r\nimport org.summerboot.jexpress.annotation.restful.Ping;\r\n\r\n@Controller\r\n@Path(\"/hellosummer\")\r\npublic class MyController {\r\n    @GET\r\n    @Path(\"/ping\")\r\n    @Ping\r\n    public void ping() {\r\n    }\r\n}\r\n```\r\n\r\n### 1.6 Role-Based Access Control\r\n\r\n**step 1 — annotate the controller:**\r\n\r\n```java\r\nimport jakarta.annotation.security.PermitAll;\r\nimport jakarta.annotation.security.RolesAllowed;\r\nimport org.summerboot.jexpress.controller.restful.BootController;\r\n\r\n@Controller\r\n@Path(\"/hellosummer\")\r\npublic class MyController extends BootController {\r\n\r\n    @GET\r\n    @Path(\"/hello/anonymous\")\r\n    public void anonymous() {\r\n    }\r\n\r\n    @GET\r\n    @Path(\"/helloAdmin/user\")\r\n    @PermitAll\r\n    public void loginedUserOnly() {\r\n    }\r\n\r\n    @GET\r\n    @Path(\"/helloAdmin/admin\")\r\n    @RolesAllowed({\"AppAdmin\"})\r\n    public void adminOnly() {\r\n    }\r\n\r\n    @GET\r\n    @Path(\"/helloAdmin/employee\")\r\n    @RolesAllowed({\"Employee\"})\r\n    public void employeeOnly() {\r\n    }\r\n}\r\n```\r\n\r\n**step 2 — implement an Authenticator:**\r\n\r\n```java\r\nimport com.google.inject.Singleton;\r\nimport io.netty.handler.codec.http.HttpHeaders;\r\n\r\nimport javax.naming.NamingException;\r\n\r\nimport org.summerboot.jexpress.annotation.Service;\r\nimport org.summerboot.jexpress.controller.SessionContext;\r\nimport org.summerboot.jexpress.controller.authenticate.Authenticator;\r\nimport org.summerboot.jexpress.controller.authenticate.AuthenticatorListener;\r\nimport org.summerboot.jexpress.controller.authenticate.BootAuthenticator;\r\nimport org.summerboot.jexpress.controller.authenticate.Caller;\r\nimport org.summerboot.jexpress.controller.authenticate.User;\r\nimport org.summerboot.jexpress.webserver.netty.RequestProcessor;\r\n\r\n@Singleton\r\n@Service(binding = Authenticator.class)\r\npublic class MyAuthenticator extends BootAuthenticator\u003cLong\u003e {\r\n\r\n    @Override\r\n    protected Caller authenticate(String username, String password, Long metaData,\r\n                                  AuthenticatorListener listener, SessionContext context) throws NamingException {\r\n        if (\"wrongpwd\".equals(password)) return null;\r\n        long tenantId = 1;\r\n        String tenantName = \"jExpress Org\";\r\n        long userId = 456;\r\n        User user = new User(tenantId, tenantName, userId, username);\r\n        user.addGroup(\"AdminGroup\");\r\n        user.addGroup(\"EmployeeGroup\");\r\n        return user;\r\n    }\r\n\r\n    @Override\r\n    public boolean customizedAuthorizationCheck(RequestProcessor processor,\r\n                                                HttpHeaders httpRequestHeaders,\r\n                                                String httpRequestPath,\r\n                                                SessionContext context) throws Exception {\r\n        return true;\r\n    }\r\n}\r\n```\r\n\r\n\u003e **v2.6+:** `BootAuthenticator` also implements `ServerInterceptor` for unified JWT auth across both HTTP and gRPC.\r\n\r\n**step 3 — cfg_auth.properties:**\r\n\r\n```properties\r\nroles.AppAdmin.groups=AdminGroup\r\n#roles.AppAdmin.users=admin1, admin2\r\nroles.Employee.groups=EmployeeGroup\r\n#roles.Employee.users=employee1, employee2\r\n```\r\n\r\n### 1.7 Request Log Sample (v2.6.6)\r\n\r\n```\r\n[411043] 2025-08-17T11:50:58,429 WARN org.summerboot.jexpress.controller.restful.BootHttpRequestHandler.() [Netty-HTTP.Biz-5-vt-1]\r\n[411043-2 /127.0.0.1:8311] [200 OK, error=0, queuing=1ms, process=5798ms, response=5801ms]\r\nHTTP/1.1 GET /hellosummer/services/appname/v1/aaa/111\r\nPOI.t0=2025-08-17T11:50:52.627-04:00 service.begin=0ms, process.begin=1ms, biz.begin=6ms, biz.end=5795ms, process.end=5798ms, service.end=5801ms,\r\n1.client_req.headers=...\r\n2.client_req.body(0 bytes)=null\r\n3.server_resp.headers=...\r\n4.server_resp.body(158 bytes)={\"name\":\"...\",\"value\":\"...\"}\r\n```\r\n\r\n\u003e **v2.6.6 API change:** `SessionContext.uri()` renamed to `SessionContext.uriRawDecoded()`.  \r\n\u003e `SessionContext.uriRawDecoded()` = raw URI from `FullHttpRequest.uri()`  \r\n\u003e `ServiceRequest.getHttpRequestPath()` = decoded path from `QueryStringDecoder.path()`\r\n\r\n---\r\n\r\n## 2. Auto-Generated Configuration Files\r\n\r\n### 2.1 Intent\r\n\r\n* Keep configuration files clean and in sync with your code.\r\n\r\n### 2.2 Auto-generated configs — all applications\r\n\r\n| File                  | Purpose                                                      |\r\n|-----------------------|--------------------------------------------------------------|\r\n| `log4j2.xml`          | Async logging via Log4j2 + Disruptor                         |\r\n| `cfg_smtp.properties` | SMTP / email alert settings                                  |\r\n| `cfg_auth.properties` | JWT signing, role/group mapping                              |\r\n| `etc/boot.ini`        | Master security algorithms, keystore type, thread pool modes |\r\n\r\n\u003e `log4j2.xml` requires JVM arg: `-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector`\r\n\r\n### 2.3 Auto-generated configs — type-based\r\n\r\n| File                         | Condition                                                                              |\r\n|------------------------------|----------------------------------------------------------------------------------------|\r\n| `cfg_nio.properties`         | Application contains `@Controller`                                                     |\r\n| `cfg_grpc.properties`        | Application contains a gRPC service                                                    |\r\n| Any `@ImportResource` config | Annotated with `@ImportResource`, extends `BootConfig`, or implements `JExpressConfig` |\r\n\r\n### 2.4 etc/boot.ini — new in v2.6.0\r\n\r\nKey new sections in `etc/boot.ini`:\r\n\r\n```properties\r\n#######################\r\n# 3. Default Settings #\r\n#######################\r\n#default.ConfigChangeMonitor.Throttle.Milliseconds=100\r\n#####################################################\r\n# 5.1 Security Settings: keystore type and provider #\r\n#####################################################\r\n## PKCS12 (default), PKCS11, JCEKS, JKS, BCFKS\r\n#keystore.type=PKCS12\r\n#keystore.provider=\r\n#########################################\r\n# 5.2 Security Settings: message digest #\r\n#########################################\r\n## SHA3-256 (default), SHA3-384, SHA3-512, SHA-256, SHA-384, SHA-512\r\n#algorithm.Messagedigest=SHA3-256\r\n###################################################################\r\n# 5.3 Security Settings: asymmetric key                          #\r\n###################################################################\r\n#algorithm.Asymmetric=RSA\r\n#transformation.Asymmetric=RSA/None/OAEPWithSHA-256AndMGF1Padding\r\n######################################################\r\n# 5.4 Security Settings: symmetric key               #\r\n######################################################\r\n#algorithm.Symmetric=AES\r\n#length.SymmetricKey.Bits=256\r\n#transformation.Symmetric=AES/GCM/NoPadding\r\n#length.SymmetricKey.AuthenticationTag.Bits=128\r\n#length.symmetricKey.InitializationVector.Bytes=12\r\n#####################################################\r\n# 5.5 Security Settings: secret key (with password) #\r\n#####################################################\r\n#algorithm.SecretKey=PBKDF2WithHmacSHA256\r\n#length.algorithm.SecretKey.Bits=256\r\n#length.algorithm.SecretKey.Salt.Bits=16\r\n#count.algorithm.SecretKey.iteration=310000\r\n```\r\n\r\n---\r\n\r\n## 3. Hot Configuration\r\n\r\n### 3.1 Intent\r\n\r\n* Guarantee service continuity when configuration changes (3rd-party tokens, license keys, etc.).\r\n\r\n### 3.2 Sample Code\r\n\r\n```java\r\nimport com.fasterxml.jackson.annotation.JsonIgnore;\r\n\r\nimport java.io.File;\r\nimport java.util.Properties;\r\n\r\nimport org.summerboot.jexpress.annotation.config.ConfigFilename;\r\nimport org.summerboot.jexpress.boot.config.BootConfig;\r\nimport org.summerboot.jexpress.boot.config.ConfigUtil;\r\nimport org.summerboot.jexpress.annotation.config.Config;\r\nimport org.summerboot.jexpress.annotation.config.ConfigHeader;\r\n\r\n@ConfigFilename(\"cfg_app.properties\")\r\npublic class MyConfig extends BootConfig {\r\n\r\n    public static final MyConfig cfg = new MyConfig();\r\n\r\n    private MyConfig() {\r\n    }\r\n\r\n    @ConfigHeader(title = \"My Header description\")\r\n    @JsonIgnore\r\n    @Config(key = \"my.licenseKey\", validate = Config.Validate.Encrypted, required = true)\r\n    protected volatile String licenseKey;\r\n\r\n    @Override\r\n    protected void loadCustomizedConfigs(File cfgFile, boolean isNotMock, ConfigUtil helper, Properties props) throws Exception {\r\n    }\r\n\r\n    @Override\r\n    public void shutdown() {\r\n    }\r\n\r\n    public String getLicenseKey() {\r\n        return licenseKey;\r\n    }\r\n}\r\n```\r\n\r\nGenerate the template at any time:\r\n\r\n```java\r\npublic static void main(String[] args) {\r\n    String template = MyConfig.generateTemplate(MyConfig.class);\r\n    System.out.println(template);\r\n}\r\n```\r\n\r\nGenerated `cfg_app.properties`:\r\n\r\n```properties\r\n#########################\r\n# My Header description #\r\n#########################\r\nmy.licenseKey=DEC(plain password)\r\n```\r\n\r\n---\r\n\r\n## 4. Protected Configuration\r\n\r\n### 4.1 Intent\r\n\r\n* Sensitive data (passwords, license keys, JWT signing keys, 3rd-party tokens) must not be plain text.\r\n* **Two-Level Protection:** root admin controls the encryption key; app admin manages the config values.\r\n\r\n### 4.2 How It Works\r\n\r\n| Level   | Role              | Capability                                             |\r\n|---------|-------------------|--------------------------------------------------------|\r\n| Level 1 | Application Admin | Writes plain `DEC(...)` values; app auto-encrypts them |\r\n| Level 2 | Root (OS) Admin   | Holds the root password file used to encrypt/decrypt   |\r\n\r\nLaunch the app with:\r\n\r\n```bash\r\njava -jar jExpressApp.jar -authfile /etc/security/my-service.root_pwd\r\n```\r\n\r\nRoot password file format:\r\n\r\n```bash\r\nAPP_ROOT_PASSWORD=\u003cbase64 encoded root password\u003e\r\n```\r\n\r\n\u003e **v2.6.0+:** The default master password is no longer hardcoded. It is loaded from `etc/master.password` (auto-created if absent) when `-authfile` is not provided.\r\n\r\n### 4.3 Operations\r\n\r\n**Auto-encrypt:** Wrap plain text with `DEC()` and save. The app encrypts it within 5 seconds:\r\n\r\n```properties\r\ndatasource.password=DEC(plain password)\r\n# becomes →\r\ndatasource.password=ENC(encrypted password)\r\n```\r\n\r\n**Manual batch encrypt:**\r\n\r\n```bash\r\njava -jar my-service.jar -cfgdir \u003cconfig folder\u003e -encrypt -authfile \u003croot pwd file\u003e\r\njava -jar my-service.jar -cfgdir \u003cconfig folder\u003e -encrypt\r\n```\r\n\r\n**Manual batch decrypt (root password required):**\r\n\r\n```bash\r\njava -jar my-service.jar -cfgdir \u003cconfig folder\u003e -decrypt\r\n```\r\n\r\n\u003e Default `\u003capp root password\u003e` is `changeit` when `-authfile` is provided.\r\n\r\n---\r\n\r\n## 5. Ping with Load Balancer\r\n\r\n### 5.1 Sample Code\r\n\r\nEnable `GET /hellosummer/ping` without polluting your application log:\r\n\r\n```java\r\nimport org.summerboot.jexpress.annotation.restful.Ping;\r\nimport jakarta.ws.rs.GET;\r\nimport jakarta.ws.rs.Path;\r\nimport org.summerboot.jexpress.annotation.Controller;\r\nimport org.summerboot.jexpress.controller.restful.BootController;\r\n\r\n@Controller\r\n@Path(\"/hellosummer\")\r\npublic class WebController extends BootController {\r\n\r\n    @GET\r\n    @Ping\r\n    @Path(\"/ping\")\r\n    public void ping() {\r\n    }\r\n\r\n    @GET\r\n    @Path(\"/hello/{name}\")\r\n    public String hello(@PathParam(\"name\") String name) {\r\n        return \"Hello \" + name;\r\n    }\r\n}\r\n```\r\n\r\n### 5.2 cfg_nio.properties — ping-related new items (v2.6.0)\r\n\r\n```properties\r\nping.sync.HealthStatus.requiredHealthChecks=\r\nping.sync.PauseStatus=\r\nping.sync.showRootCause=\r\n```\r\n\r\n---\r\n\r\n## 6. Ping with Health Check / Auto-Shutdown\r\n\r\n### 6.1 Intent\r\n\r\n* Automatically respond with an error to the load balancer when a dependency (DB, 3rd-party service) is down.\r\n\r\n### 6.2 Sample Code\r\n\r\n```java\r\n\r\nimport org.summerboot.jexpress.annotation.Service;\r\nimport org.summerboot.jexpress.integration.HealthChecker;\r\nimport domain.org.summerboot.jexpress.webserver.netty.Err;\r\n\r\nimport java.util.List;\r\n\r\n@org.summerboot.jexpress.annotation.integration.HealthCheck(name = \"myDB\")\r\n@Service(binding = HealthChecker.class)\r\npublic class MyHealthInspector implements HealthChecker\u003cVoid\u003e {\r\n\r\n    @Override\r\n    public List\u003cErr\u003e ping(Void... param) {\r\n        List\u003cErr\u003e errors = new java.util.ArrayList\u003c\u003e();\r\n        // check DB connectivity, add Err on failure\r\n        // errors.add(new Err(123, \"DB_DOWN\", \"Database is unreachable\", null));\r\n        return errors;\r\n    }\r\n}\r\n```\r\n\r\n\u003e **Daemon mode:** Annotate a `@Controller` class or method with `@Daemon` to keep it accessible even when the service is paused or health-check fails:\r\n\u003e\r\n\u003e ```java\r\n\u003e @Daemon(requiredHealthChecks = {\"myDB\"})\r\n\u003e @GET @Path(\"/admin/status\")\r\n\u003e public void adminStatus() {}\r\n\u003e ```\r\n\r\n---\r\n\r\n## 7. Auto-Alert (SMTP)\r\n\r\n### 7.1 Intent\r\n\r\n* Get notified before someone knocks on your door. Debounce repeated alerts.\r\n\r\n### 7.2 cfg_smtp.properties\r\n\r\n```properties\r\n####################\r\n# 1. SMTP Settings #\r\n####################\r\nmail.smtp.host=smtpserver\r\nmail.smtp.user=abc_service@email.addr\r\nmail.smtp.user.displayname=ABC Service\r\nmail.smtp.user.password=DEC(changeit)\r\n###########################################\r\n# 2. Alert Recipients (CSV format)        #\r\n###########################################\r\n#email.to.AppSupport=\r\n#email.to.Development=\r\n#email.to.ReportViewer=\r\n## Same-title alerts suppressed within this many minutes\r\ndebouncing.emailalert_minute=30\r\n```\r\n\r\nNo code changes required — just update `cfg_smtp.properties`.\r\n\r\n---\r\n\r\n## 8. Log After Response Sent\r\n\r\n### 8.1 Features\r\n\r\n* Request and response logged together in a single log entry.\r\n* Client receives response without waiting for logging.\r\n* Log file auto-rotated and named with the server hostname.\r\n\r\n### 8.2 POI (Point of Interest)\r\n\r\n```java\r\ncontext.poi(\"DB begin\");\r\n// ... DB access ...\r\ncontext.\r\n\r\npoi(\"DB end\");\r\n```\r\n\r\nSample POI output:\r\n\r\n```\r\nPOI: service.begin=4ms, auth.begin=4ms, process.begin=4ms, biz.begin=4ms,\r\n     biz.end=18ms, process.end=18ms, service.end=18ms\r\n```\r\n\r\n### 8.3 Two Log Types\r\n\r\n1. **Request log** — Security, performance, full client↔server conversation.\r\n2. **App status/event log** — Version, start/stop events, config-change events, TPS counters.\r\n\r\n---\r\n\r\n## 9. Application Lifecycle — `AppLifecycleListener`\r\n\r\n### 9.1 Interface (v2.6.5+)\r\n\r\n`SummerRunner` and `IdleEventMonitor.IdleEventListener` have been consolidated into:\r\n\r\n```java\r\npublic interface AppLifecycleListener extends IdleEventMonitor.IdleEventListener {\r\n    void beforeApplicationStart(SummerApplication.AppContext context) throws Exception;\r\n\r\n    void onApplicationStart(SummerApplication.AppContext context, String appVersion, String fullConfigInfo) throws Exception;\r\n\r\n    void onApplicationStop(SummerApplication.AppContext context, String appVersion);\r\n\r\n    void onApplicationStatusUpdated(SummerApplication.AppContext context, boolean healthOk, boolean paused,\r\n                                    boolean serviceStatusChanged, String reason) throws Exception;\r\n\r\n    void onHealthInspectionFailed(SummerApplication.AppContext context, boolean healthOk, boolean paused,\r\n                                  long retryIndex, int nextInspectionIntervalSeconds) throws Exception;\r\n\r\n    void onConfigChangeBefore(File configFile, JExpressConfig cfg);\r\n\r\n    void onConfigChangedAfter(File configFile, JExpressConfig cfg, Throwable ex);\r\n\r\n    // from IdleEventMonitor.IdleEventListener:\r\n    void onIdle(IdleEventMonitor idleEventMonitor) throws Exception;\r\n}\r\n```\r\n\r\nExtend the default adapter `AppLifecycleHandler` and override only what you need.\r\n\r\n### 9.2 Idle Event Monitoring (v2.6.5+)\r\n\r\nConfigure idle thresholds in configuration files:\r\n\r\n```properties\r\n# cfg_nio.properties\r\nnio.server.idle.threshold.second=60\r\n# cfg_grpc.properties\r\ngRpc.server.idle.threshold.second=60\r\n```\r\n\r\n---\r\n\r\n## 10. CLI — A/B/Mock Mode\r\n\r\n### 10.1 Sample Code\r\n\r\n```java\r\n\r\n@Service                           // default implementation\r\npublic class MyServiceImpl implements MyService { ...\r\n}\r\n\r\n@Service(AlternativeName = \"impl1\")\r\npublic class MyServiceImpl_1 implements MyService { ...\r\n}\r\n\r\n@Service(AlternativeName = \"impl2\")\r\npublic class MyServiceImpl_2 implements MyService { ...\r\n}\r\n```\r\n\r\n```bash\r\njava -jar my-service.jar -?\r\n# shows: -use \u003citems\u003e  launch application in mock mode, valid values \u003cimpl1, impl2\u003e\r\n\r\njava -jar my-service.jar -use impl1\r\n```\r\n\r\n---\r\n\r\n## 11. CLI — List and Check Duplicate Error Codes\r\n\r\n### 11.1 Sample Code\r\n\r\n```java\r\nimport org.summerboot.jexpress.boot.BootErrorCode;\r\nimport org.summerboot.jexpress.annotation.validation.Unique;\r\nimport org.summerboot.jexpress.annotation.validation.UniqueIgnore;\r\n\r\n@Unique(name = \"ErrorCode\", type = int.class)\r\npublic interface AppErrorCode extends BootErrorCode {\r\n    int APP_UNEXPECTED_FAILURE = 1001;\r\n    int BAD_REQUEST = 1002;\r\n    int AUTH_CUSTOMER_NOT_FOUND = 1003;\r\n    int DB_SP_ERROR = 1004;\r\n\r\n    // suppress false-positive duplicate alert:\r\n    @UniqueIgnore\r\n    int ALIAS_BAD_REQUEST = BAD_REQUEST;\r\n}\r\n\r\n@Unique(name = \"POI\", type = String.class)\r\npublic interface AppPOI extends BootPOI {\r\n    String FILE_BEGIN = \"file.begin\";\r\n    String FILE_END = \"file.end\";\r\n}\r\n```\r\n\r\n```bash\r\njava -jar my-service.jar -unique ErrorCode\r\njava -jar my-service.jar -unique POI\r\n```\r\n\r\n---\r\n\r\n## 12. Scheduled Tasks — `@Scheduled`\r\n\r\n```java\r\nimport org.summerboot.jexpress.annotation.Scheduled;\r\n\r\npublic class MyJob {\r\n\r\n    @Scheduled(cron = \"0 15 10 ? * 6L 2024-2030\")  // every last Friday at 10:15am\r\n    public void monthlyReport() { ...}\r\n\r\n    @Scheduled(hour = 2, minute = 0)               // daily at 2:00am\r\n    public void dailyMaintenance() { ...}\r\n\r\n    @Scheduled(fixedRateMs = 10_000, initialDelayMs = 5_000)  // every 10s\r\n    public void polling() { ...}\r\n\r\n    @Scheduled(fixedDelayMs = 10_000)              // 10s after completion\r\n    public void delayedPolling() { ...}\r\n}\r\n```\r\n\r\nDynamic cron from a static field:\r\n\r\n```java\r\nprivate static String MY_CRON = \"0 0 * * * ?\";\r\n\r\n@Scheduled(cronField = \"MY_CRON\")\r\npublic void dynamicJob() { ...}\r\n```\r\n\r\n---\r\n\r\n## 13. gRPC Service\r\n\r\n### 13.1 Server-side\r\n\r\nAnnotate your gRPC service implementation with `@GrpcService`:\r\n\r\n```java\r\nimport org.summerboot.jexpress.annotation.GrpcController;\r\nimport org.summerboot.jexpress.boot.annotation.GrpcService;\r\n\r\n@GrpcController\r\npublic class MyGrpcServiceImpl extends MyGrpcService.MyGrpcServiceImplBase {\r\n    // implement gRPC methods\r\n}\r\n```\r\n\r\nConfigure in **cfg_grpc.properties**.\r\n\r\n### 13.2 Client-side\r\n\r\n```java\r\nimport org.summerboot.jexpress.controller.grpc.GRPCClient;\r\nimport org.summerboot.jexpress.controller.grpc.GRPCClientConfig;\r\n\r\nGRPCClient client = new GRPCClient(GRPCClientConfig.cfg);\r\n```\r\n\r\nSupports:\r\n\r\n- 2-way TLS (mutual authentication)\r\n- Client-side load balancing via `BootLoadBalancerProvider`\r\n- Bearer token authentication via `BearerAuthCredential`\r\n- Dynamic configuration reload\r\n\r\n### 13.3 gRPC Test Helper\r\n\r\n```java\r\n\r\n// see https://github.com/SummerBootFramework/jExpressDemo-HelloSummer\r\n```\r\n\r\n---\r\n\r\n## 14. HTTP Client\r\n\r\n```java\r\nimport org.summerboot.jexpress.integration.httpclient.RPCDelegate;\r\nimport org.summerboot.jexpress.integration.httpclient.RPCResult;\r\n\r\n// inject or create an RPCDelegate instance\r\nRPCResult\u003cMyResponseDto\u003e result = rpcDelegate.get(url, MyResponseDto.class, context);\r\nif(result.\r\n\r\n        hasError()){\r\n        // handle errors\r\n        }\r\n        MyResponseDto dto = result.getResult();\r\n```\r\n\r\nConfigure in **cfg_httpclient.properties** (proxy, TLS, timeout, thread pool mode, etc.).\r\n\r\n---\r\n\r\n## 15. JPA / Database\r\n\r\n```java\r\nimport org.summerboot.jexpress.integration.jpa.JPAHibernateConfig;\r\nimport org.summerboot.jexpress.integration.jpa.AbstractEntity;\r\n```\r\n\r\nConfigure in a JPA config file that extends `JPAHibernateConfig`. DB credentials use `DEC()`/`ENC()`:\r\n\r\n```properties\r\njakarta.persistence.jdbc.url=jdbc:mysql://localhost:3306/mydb\r\njakarta.persistence.jdbc.user=myuser\r\njakarta.persistence.jdbc.password=DEC(changeit)\r\njakarta.persistence.jdbc.driver=com.mysql.jdbc.Driver\r\n```\r\n\r\n---\r\n\r\n## 16. MQTT Client\r\n\r\n```java\r\nimport org.summerboot.jexpress.integration.mqtt.MqttClientConfig;\r\n```\r\n\r\nConfigure in a MQTT config file. TLS settings follow the same pattern as HTTP/gRPC clients.\r\n\r\n---\r\n\r\n## 17. Cache (Redis)\r\n\r\n```java\r\nimport org.summerboot.jexpress.integration.cache.AuthTokenCache;\r\n```\r\n\r\nJedis (Redis) is a provided dependency. Add it to your app's dependencies if needed:\r\n\r\n```xml\r\n\r\n\u003cdependency\u003e\r\n    \u003cgroupId\u003eredis.clients\u003c/groupId\u003e\r\n    \u003cartifactId\u003ejedis\u003c/artifactId\u003e\r\n    \u003cversion\u003e7.2.1\u003c/version\u003e\r\n\u003c/dependency\u003e\r\n```\r\n\r\n---\r\n\r\n## 18. Security Enhancements (v2.6.x)\r\n\r\n### 18.1 URL Sanitizer\r\n\r\nRequests with illegal characters or path traversal in the URL are automatically rejected with **400 Bad Request**:\r\n\r\n```\r\n/../../../../windows/win.ini?q=\u003cscript\u003ealert(1)\u003c/script\u003e   → 400\r\n/?action:%{(new java.lang.ProcessBuilder(...)).start()}    → 400\r\n```\r\n\r\n`UrlSanitizer.cleanUrl(String url)` returns:\r\n\r\n```java\r\npublic record UrlSanitized(String cleanPath, String cleanQuery, String cleanedURL, boolean isPathTraversal) {\r\n}\r\n```\r\n\r\n### 18.2 Caller Address Filter\r\n\r\nWhitelist/blacklist with regex support (no prefix required since v2.6.1):\r\n\r\n```properties\r\n# cfg_nio.properties\r\nCallerAddressFilter.option=String   # String, Regex, or HostName\r\n# cfg_grpc.properties\r\ngRpc.server.CallerAddressFilter.option=String\r\n```\r\n\r\n### 18.3 File Download Security\r\n\r\n```java\r\nimport org.summerboot.jexpress.security.SecurityUtil;\r\n\r\nString safeFilename = SecurityUtil.escape4Filename(userInput);\r\n```\r\n\r\n### 18.4 Keystore \u0026 Cipher Defaults\r\n\r\n| Setting        | Default                                |\r\n|----------------|----------------------------------------|\r\n| Keystore type  | PKCS12                                 |\r\n| Message digest | SHA3-256                               |\r\n| Asymmetric     | RSA/None/OAEPWithSHA-256AndMGF1Padding |\r\n| Symmetric      | AES/GCM/NoPadding                      |\r\n| Secret key     | PBKDF2WithHmacSHA256                   |\r\n| EC curve       | secp256r1                              |\r\n\r\n---\r\n\r\n## 19. Plugin — External JAR Files\r\n\r\nPlace plugin JARs in the `plugin/` folder. The framework picks them up at startup, allowing you to:\r\n\r\n* Add new features without modifying the core application.\r\n* Override existing logic via the Visitor pattern.\r\n* Deploy logic developed by separate teams as independent plugins.\r\n\r\n---\r\n\r\n## 20. CLI Reference\r\n\r\n| Option             | Description                                                     |\r\n|--------------------|-----------------------------------------------------------------|\r\n| `-?`               | Show help                                                       |\r\n| `-cfgdir \u003cpath\u003e`   | Config folder path                                              |\r\n| `-authfile \u003cpath\u003e` | Root password file                                              |\r\n| `-encrypt`         | Batch encrypt all `DEC(...)` values                             |\r\n| `-decrypt`         | Batch decrypt all `ENC(...)` values                             |\r\n| `-use \u003cname\u003e`      | Launch with alternative service implementation                  |\r\n| `-unique \u003cname\u003e`   | List/validate unique codes                                      |\r\n| `-domain \u003cname\u003e`   | Domain name                                                     |\r\n| `-psv \u003cenvId\u003e`     | Print service version for environment                           |\r\n| `-debug`           | Enable debug mode (logs all requests/responses, ignores `@Log`) |\r\n\r\n---\r\n\r\n## 21. Key Dependencies (v2.6.6)\r\n\r\n| Library             | Version      |\r\n|---------------------|--------------|\r\n| Java                | 21           |\r\n| Netty               | 4.2.10.Final |\r\n| gRPC                | 1.79.0       |\r\n| Guice (IoC)         | 7.0.0        |\r\n| Jackson             | 2.21.0       |\r\n| Hibernate ORM       | 7.2.4.Final  |\r\n| HikariCP            | 7.0.2        |\r\n| Log4j2              | 2.25.3       |\r\n| BouncyCastle        | 1.83         |\r\n| jjwt                | 0.13.0       |\r\n| Hibernate Validator | 9.1.0.Final  |\r\n| Quartz              | 2.5.2        |\r\n| PDFBox              | 3.0.6        |\r\n| openhtmltopdf       | 1.1.37       |\r\n| iText               | 9.5.0        |\r\n| Apache Tika         | 3.2.3        |\r\n| ZXing (barcode)     | 3.5.4        |\r\n| Jedis (Redis)       | 7.2.1        |\r\n| Freemarker          | 2.3.34       |\r\n\r\n---\r\n\r\n## Migration Guides\r\n\r\n### From \u003c 2.6.5 (SummerRunner removed)\r\n\r\n1. Delete classes implementing `SummerRunner` or `IdleEventMonitor.IdleEventListener`.\r\n2. Implement `AppLifecycleListener` **or** extend `AppLifecycleHandler`.\r\n3. Move `SummerRunner.run()` logic → `AppLifecycleListener.beforeApplicationStart()`.\r\n4. Move idle listener logic → `AppLifecycleListener.onIdle()`.\r\n5. Set `nio.server.idle.threshold.second` / `gRpc.server.idle.threshold.second` in config.\r\n\r\n### From \u003c 2.6.6 (URI method rename)\r\n\r\n- `SessionContext.uri()` → `SessionContext.uriRawDecoded()`\r\n\r\n### From \u003c 2.6.0 (encryption format change)\r\n\r\nThe encryption format changed in 2.6.0. Before upgrading, either:\r\n\r\n```bash\r\njava -jar your-app.jar -decrypt\r\n```\r\n\r\nor redeploy with `DEC(...)` values and let the new version re-encrypt them.\r\n\r\n### From \u003c 2.6.0 (API renames)\r\n\r\n| Old                                                                          | New                                  |\r\n|------------------------------------------------------------------------------|--------------------------------------|\r\n| `@Controller.implTag`                                                        | `@Controller.AlternativeName`        |\r\n| `@Service.implTag`                                                           | `@Service.AlternativeName`           |\r\n| `@Log.hideJsonStringFields` / `hideJsonNumberFields` / `hideJsonArrayFields` | `@Log.maskDataFields`                |\r\n| `ServiceContext`                                                             | `SessionContext`                     |\r\n| `@ImportResource.checkImplTagUsed`                                           | `@ImportResource.whenUseAlternative` |\r\n| `@ImportResource.loadWhenImplTagUsed`                                        | `@ImportResource.thenLoadConfig`     |\r\n| `BootHealthInspectorImpl`                                                    | `@DefaultHealthInspector`            |\r\n| `ServiceContext.reset()`                                                     | `SessionContext.resetResponseData()` |\r\n| DB config `hibernate.*`                                                      | `jakarta.persistence.*`              |","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsummerbootframework%2Fjexpress","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsummerbootframework%2Fjexpress","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsummerbootframework%2Fjexpress/lists"}