From dbe7418f134b118ac30868a8fd0cd5f5c14dce89 Mon Sep 17 00:00:00 2001 From: vaishnavbhosale Date: Wed, 7 Jan 2026 09:41:09 +0530 Subject: [PATCH 1/6] Add /health endpoint and standardize /version response --- .../controller/health/HealthController.java | 14 +++ .../controller/version/VersionController.java | 90 ++++++++----------- .../controller/version/VersionInfo.java | 20 +++++ 3 files changed, 73 insertions(+), 51 deletions(-) create mode 100644 src/main/java/com/iemr/common/controller/health/HealthController.java create mode 100644 src/main/java/com/iemr/common/controller/version/VersionInfo.java diff --git a/src/main/java/com/iemr/common/controller/health/HealthController.java b/src/main/java/com/iemr/common/controller/health/HealthController.java new file mode 100644 index 00000000..e61700f5 --- /dev/null +++ b/src/main/java/com/iemr/common/controller/health/HealthController.java @@ -0,0 +1,14 @@ +package com.iemr.common.controller.health; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import java.util.Map; + +@RestController +public class HealthController { + + @GetMapping("/health") + public Map health() { + return Map.of("status", "UP"); + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/controller/version/VersionController.java b/src/main/java/com/iemr/common/controller/version/VersionController.java index 705fccdc..e153f8d8 100644 --- a/src/main/java/com/iemr/common/controller/version/VersionController.java +++ b/src/main/java/com/iemr/common/controller/version/VersionController.java @@ -1,30 +1,29 @@ /* -* AMRIT – Accessible Medical Records via Integrated Technology -* Integrated EHR (Electronic Health Records) Solution -* -* Copyright (C) "Piramal Swasthya Management and Research Institute" -* -* This file is part of AMRIT. -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see https://www.gnu.org/licenses/. -*/ + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + package com.iemr.common.controller.version; -import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; +import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,46 +31,35 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import com.iemr.common.utils.response.OutputResponse; - import io.swagger.v3.oas.annotations.Operation; - @RestController public class VersionController { - private Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName()); + private final Logger logger = + LoggerFactory.getLogger(this.getClass().getSimpleName()); @Operation(summary = "Get version") @RequestMapping(value = "/version", method = { RequestMethod.GET }) - public String versionInformation() { - OutputResponse output = new OutputResponse(); - try { - logger.info("version Controller Start"); - output.setResponse(readGitProperties()); - } catch (Exception e) { - output.setError(e); - } + public VersionInfo versionInformation() { - logger.info("version Controller End"); - return output.toString(); - } + Properties properties = new Properties(); - private String readGitProperties() throws Exception { - ClassLoader classLoader = getClass().getClassLoader(); - InputStream inputStream = classLoader.getResourceAsStream("git.properties"); + try (InputStream is = getClass() + .getClassLoader() + .getResourceAsStream("git.properties")) { - return readFromInputStream(inputStream); - } - - private String readFromInputStream(InputStream inputStream) throws IOException { - StringBuilder resultStringBuilder = new StringBuilder(); - try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { - String line; - while ((line = br.readLine()) != null) { - resultStringBuilder.append(line).append("\n"); + if (is != null) { + properties.load(is); } + + } catch (Exception e) { + logger.error("Error reading git.properties", e); } - return resultStringBuilder.toString(); + + return new VersionInfo( + properties.getProperty("git.commit.id.abbrev", "unknown"), + properties.getProperty("git.build.time", "unknown") + ); } } diff --git a/src/main/java/com/iemr/common/controller/version/VersionInfo.java b/src/main/java/com/iemr/common/controller/version/VersionInfo.java new file mode 100644 index 00000000..4e8898d6 --- /dev/null +++ b/src/main/java/com/iemr/common/controller/version/VersionInfo.java @@ -0,0 +1,20 @@ +package com.iemr.common.controller.version; + +public class VersionInfo { + + private String commitHash; + private String buildTime; + + public VersionInfo(String commitHash, String buildTime) { + this.commitHash = commitHash; + this.buildTime = buildTime; + } + + public String getCommitHash() { + return commitHash; + } + + public String getBuildTime() { + return buildTime; + } +} From 2a3678c4fee11d4c6b7535218d5a8e351d7bea7b Mon Sep 17 00:00:00 2001 From: vaishnavbhosale Date: Wed, 7 Jan 2026 09:53:42 +0530 Subject: [PATCH 2/6] Add license headers and Javadocs to health and version controllers --- .../controller/health/HealthController.java | 29 ++++++++++++++++++- .../controller/version/VersionController.java | 10 ++++++- .../controller/version/VersionInfo.java | 26 +++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/controller/health/HealthController.java b/src/main/java/com/iemr/common/controller/health/HealthController.java index e61700f5..b0e81561 100644 --- a/src/main/java/com/iemr/common/controller/health/HealthController.java +++ b/src/main/java/com/iemr/common/controller/health/HealthController.java @@ -1,3 +1,29 @@ +/* + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ +/** + * Health check controller for Common-API. + * + * @author vaishnavbhosale + */ package com.iemr.common.controller.health; import org.springframework.web.bind.annotation.GetMapping; @@ -11,4 +37,5 @@ public class HealthController { public Map health() { return Map.of("status", "UP"); } -} \ No newline at end of file +} + diff --git a/src/main/java/com/iemr/common/controller/version/VersionController.java b/src/main/java/com/iemr/common/controller/version/VersionController.java index e153f8d8..10645866 100644 --- a/src/main/java/com/iemr/common/controller/version/VersionController.java +++ b/src/main/java/com/iemr/common/controller/version/VersionController.java @@ -19,7 +19,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/. */ - +/** + * REST controller exposing application version and build metadata. + *

+ * Provides the /version endpoint which returns the + * Git commit hash and build timestamp in a standardized JSON format. + *

+ * + * @author Vaishnav Bhosale + */ package com.iemr.common.controller.version; import java.io.InputStream; diff --git a/src/main/java/com/iemr/common/controller/version/VersionInfo.java b/src/main/java/com/iemr/common/controller/version/VersionInfo.java index 4e8898d6..20f560a1 100644 --- a/src/main/java/com/iemr/common/controller/version/VersionInfo.java +++ b/src/main/java/com/iemr/common/controller/version/VersionInfo.java @@ -1,3 +1,29 @@ +/* + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ +/** + * DTO for exposing build and version metadata. + * + * @author vaishnavbhosale + */ package com.iemr.common.controller.version; public class VersionInfo { From 4c019597aeaf342cd04b30937d6b6b660bd9db3b Mon Sep 17 00:00:00 2001 From: vaishnavbhosale Date: Thu, 8 Jan 2026 11:47:11 +0530 Subject: [PATCH 3/6] Enhance /health endpoint to check Database and Redis connectivity --- .../controller/health/HealthController.java | 67 +++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/iemr/common/controller/health/HealthController.java b/src/main/java/com/iemr/common/controller/health/HealthController.java index b0e81561..5d2a0f5d 100644 --- a/src/main/java/com/iemr/common/controller/health/HealthController.java +++ b/src/main/java/com/iemr/common/controller/health/HealthController.java @@ -19,23 +19,82 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see https://www.gnu.org/licenses/. */ + /** * Health check controller for Common-API. + *

+ * Verifies application liveness and connectivity to underlying + * runtime dependencies such as Database and Redis. + *

* * @author vaishnavbhosale */ package com.iemr.common.controller.health; +import java.sql.Connection; +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.Map; @RestController public class HealthController { + @Autowired(required = false) + private DataSource dataSource; + + @Autowired(required = false) + private RedisConnectionFactory redisConnectionFactory; + @GetMapping("/health") - public Map health() { - return Map.of("status", "UP"); + public Map health() { + + Map response = new HashMap<>(); + Map components = new HashMap<>(); + + boolean dbUp = checkDatabase(components); + boolean redisUp = checkRedis(components); + + response.put("status", (dbUp && redisUp) ? "UP" : "DOWN"); + response.put("components", components); + + return response; } -} + private boolean checkDatabase(Map components) { + if (dataSource == null) { + components.put("database", "NOT_CONFIGURED"); + return true; + } + + try (Connection connection = dataSource.getConnection()) { + components.put("database", "UP"); + return true; + } catch (Exception e) { + components.put("database", "DOWN"); + return false; + } + } + + private boolean checkRedis(Map components) { + if (redisConnectionFactory == null) { + components.put("redis", "NOT_CONFIGURED"); + return true; + } + + try (RedisConnection connection = redisConnectionFactory.getConnection()) { + connection.ping(); + components.put("redis", "UP"); + return true; + } catch (Exception e) { + components.put("redis", "DOWN"); + return false; + } + } +} From 13db52aaac12959ee425a86cf6c73833dd09ae18 Mon Sep 17 00:00:00 2001 From: vaishnavbhosale Date: Thu, 8 Jan 2026 12:30:31 +0530 Subject: [PATCH 4/6] Improve /health endpoint HTTP status handling and logging --- .../controller/health/HealthController.java | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/iemr/common/controller/health/HealthController.java b/src/main/java/com/iemr/common/controller/health/HealthController.java index 5d2a0f5d..d2240b91 100644 --- a/src/main/java/com/iemr/common/controller/health/HealthController.java +++ b/src/main/java/com/iemr/common/controller/health/HealthController.java @@ -20,15 +20,6 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ -/** - * Health check controller for Common-API. - *

- * Verifies application liveness and connectivity to underlying - * runtime dependencies such as Database and Redis. - *

- * - * @author vaishnavbhosale - */ package com.iemr.common.controller.health; import java.sql.Connection; @@ -37,15 +28,27 @@ import javax.sql.DataSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; +/** + * Health check controller for Common-API. + * Verifies application liveness and dependency health (DB, Redis). + * + * @author vaishnavbhosale + */ @RestController public class HealthController { + private static final Logger logger = + LoggerFactory.getLogger(HealthController.class); + @Autowired(required = false) private DataSource dataSource; @@ -53,7 +56,7 @@ public class HealthController { private RedisConnectionFactory redisConnectionFactory; @GetMapping("/health") - public Map health() { + public ResponseEntity> health() { Map response = new HashMap<>(); Map components = new HashMap<>(); @@ -61,10 +64,14 @@ public Map health() { boolean dbUp = checkDatabase(components); boolean redisUp = checkRedis(components); - response.put("status", (dbUp && redisUp) ? "UP" : "DOWN"); + boolean overallUp = dbUp && redisUp; + + response.put("status", overallUp ? "UP" : "DOWN"); response.put("components", components); - return response; + return overallUp + ? ResponseEntity.ok(response) + : ResponseEntity.status(503).body(response); } private boolean checkDatabase(Map components) { @@ -77,6 +84,7 @@ private boolean checkDatabase(Map components) { components.put("database", "UP"); return true; } catch (Exception e) { + logger.error("Database health check failed", e); components.put("database", "DOWN"); return false; } @@ -93,6 +101,7 @@ private boolean checkRedis(Map components) { components.put("redis", "UP"); return true; } catch (Exception e) { + logger.error("Redis health check failed", e); components.put("redis", "DOWN"); return false; } From 49edb56d37c9c88cb47ce477cf232e985bedd46e Mon Sep 17 00:00:00 2001 From: vaishnavbhosale Date: Thu, 8 Jan 2026 12:32:19 +0530 Subject: [PATCH 5/6] Enhance database health check with validation query --- .../iemr/common/controller/health/HealthController.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/iemr/common/controller/health/HealthController.java b/src/main/java/com/iemr/common/controller/health/HealthController.java index d2240b91..ee3afdde 100644 --- a/src/main/java/com/iemr/common/controller/health/HealthController.java +++ b/src/main/java/com/iemr/common/controller/health/HealthController.java @@ -79,17 +79,20 @@ private boolean checkDatabase(Map components) { components.put("database", "NOT_CONFIGURED"); return true; } - - try (Connection connection = dataSource.getConnection()) { + try (Connection connection = dataSource.getConnection(); + var statement = connection.createStatement()) { + statement.execute("SELECT 1"); components.put("database", "UP"); return true; - } catch (Exception e) { + } + catch (Exception e) { logger.error("Database health check failed", e); components.put("database", "DOWN"); return false; } } + private boolean checkRedis(Map components) { if (redisConnectionFactory == null) { components.put("redis", "NOT_CONFIGURED"); From 25baf2fc1b73744dcdcdd5bed8bf59c659d7c8b9 Mon Sep 17 00:00:00 2001 From: vaishnavbhosale Date: Thu, 8 Jan 2026 12:43:52 +0530 Subject: [PATCH 6/6] Refactor health controller to constructor injection and constants --- .../controller/health/HealthController.java | 41 +++++++++++-------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/iemr/common/controller/health/HealthController.java b/src/main/java/com/iemr/common/controller/health/HealthController.java index ee3afdde..e340b905 100644 --- a/src/main/java/com/iemr/common/controller/health/HealthController.java +++ b/src/main/java/com/iemr/common/controller/health/HealthController.java @@ -25,12 +25,9 @@ import java.sql.Connection; import java.util.HashMap; import java.util.Map; - import javax.sql.DataSource; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.http.ResponseEntity; @@ -49,11 +46,18 @@ public class HealthController { private static final Logger logger = LoggerFactory.getLogger(HealthController.class); - @Autowired(required = false) - private DataSource dataSource; + private static final String COMPONENT_DATABASE = "database"; + private static final String COMPONENT_REDIS = "redis"; + + private final DataSource dataSource; + private final RedisConnectionFactory redisConnectionFactory; - @Autowired(required = false) - private RedisConnectionFactory redisConnectionFactory; + public HealthController( + DataSource dataSource, + RedisConnectionFactory redisConnectionFactory) { + this.dataSource = dataSource; + this.redisConnectionFactory = redisConnectionFactory; + } @GetMapping("/health") public ResponseEntity> health() { @@ -76,37 +80,40 @@ public ResponseEntity> health() { private boolean checkDatabase(Map components) { if (dataSource == null) { - components.put("database", "NOT_CONFIGURED"); + components.put(COMPONENT_DATABASE, "NOT_CONFIGURED"); return true; } + try (Connection connection = dataSource.getConnection(); - var statement = connection.createStatement()) { + var statement = connection.createStatement()) { + statement.execute("SELECT 1"); - components.put("database", "UP"); + components.put(COMPONENT_DATABASE, "UP"); return true; - } - catch (Exception e) { + + } catch (Exception e) { logger.error("Database health check failed", e); - components.put("database", "DOWN"); + components.put(COMPONENT_DATABASE, "DOWN"); return false; } } - private boolean checkRedis(Map components) { if (redisConnectionFactory == null) { - components.put("redis", "NOT_CONFIGURED"); + components.put(COMPONENT_REDIS, "NOT_CONFIGURED"); return true; } try (RedisConnection connection = redisConnectionFactory.getConnection()) { connection.ping(); - components.put("redis", "UP"); + components.put(COMPONENT_REDIS, "UP"); return true; + } catch (Exception e) { logger.error("Redis health check failed", e); - components.put("redis", "DOWN"); + components.put(COMPONENT_REDIS, "DOWN"); return false; } } } +