diff --git a/kernel/kernel-applicanttype-api/pom.xml b/kernel/kernel-applicanttype-api/pom.xml
index ce98b5c2084..7d12e844eb0 100644
--- a/kernel/kernel-applicanttype-api/pom.xml
+++ b/kernel/kernel-applicanttype-api/pom.xml
@@ -5,7 +5,7 @@
io.mosip.kernel
kernel-applicanttype-api
Mosip Applicant type API
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
21
21
@@ -16,7 +16,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
@@ -27,7 +27,7 @@
io.mosip.kernel
kernel-core
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
org.mvel
diff --git a/kernel/kernel-bom/pom.xml b/kernel/kernel-bom/pom.xml
index 74383aa5e8a..9d805644a87 100644
--- a/kernel/kernel-bom/pom.xml
+++ b/kernel/kernel-bom/pom.xml
@@ -2,7 +2,7 @@
4.0.0
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
io.mosip.kernel
kernel-bom
pom
diff --git a/kernel/kernel-config-server/pom.xml b/kernel/kernel-config-server/pom.xml
index 0df6317e1b8..a9c334b9b8a 100644
--- a/kernel/kernel-config-server/pom.xml
+++ b/kernel/kernel-config-server/pom.xml
@@ -5,7 +5,7 @@
4.0.0
io.mosip.kernel
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-config-server
Kernel Config Server
https://github.com/mosip/commons
@@ -34,7 +34,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-core/pom.xml b/kernel/kernel-core/pom.xml
index b695c29159a..b843f45352b 100644
--- a/kernel/kernel-core/pom.xml
+++ b/kernel/kernel-core/pom.xml
@@ -5,7 +5,7 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4.0.0
io.mosip.kernel
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
@@ -28,7 +28,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/Base64Adapter.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/Base64Adapter.java
index 11459a23f42..e4d9efda45e 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/Base64Adapter.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/Base64Adapter.java
@@ -1,37 +1,99 @@
-/**
- *
- */
package io.mosip.kernel.core.cbeffutil.common;
-/**
- * @author Ramadurai Pandian
- *
- * An Adaptor class to bye-pass the JAXB default Base64 encoding/decoding
- * and to use kernel cryptoutil for Base64 conversion.
- *
- */
import javax.xml.bind.annotation.adapters.XmlAdapter;
import io.mosip.kernel.core.util.CryptoUtil;
+/**
+ * Base64Adapter.java
+ *
+ * This class is a JAXB {@link javax.xml.bind.annotation.adapters.XmlAdapter}
+ * implementation that bridges between {@code String} (Base64-encoded data) and
+ * {@code byte[]} (binary data) for XML marshalling and unmarshalling processes.
+ *
+ *
+ * Unlike the default JAXB Base64 handling (which relies on internal or
+ * {@link java.util.Base64} implementations), this adapter delegates Base64
+ * encoding/decoding to {@link io.mosip.kernel.core.util.CryptoUtil}, ensuring:
+ *
+ *
+ * - Consistency across MOSIP components by using a single, vetted utility.
+ * - Compliance with MOSIP’s cryptographic coding standards.
+ * - Potential optimizations in {@code CryptoUtil} (e.g., buffer reuse, native acceleration).
+ *
+ *
+ * Example Usage
+ *
+ * {@code
+ * // Marshalling (byte[] -> Base64 String)
+ * String base64String = new Base64Adapter().marshal(binaryData);
+ *
+ * // Unmarshalling (Base64 String -> byte[])
+ * byte[] binaryData = new Base64Adapter().unmarshal(base64String);
+ * }
+ *
+ *
+ * @author
+ * Ramadurai Pandian (original implementation)
+ * @since 1.0.0
+ */
public class Base64Adapter extends XmlAdapter {
- /**
- * @param data biometrics image data
- * @return base64 decoded data after unmarshalling
- */
- @Override
- public byte[] unmarshal(String data) throws Exception {
- return CryptoUtil.decodeBase64(data);
- }
+ /**
+ * Base64Adapter.java
+ *
+ * This class is a JAXB {@link javax.xml.bind.annotation.adapters.XmlAdapter}
+ * implementation that bridges between {@code String} (Base64-encoded data) and
+ * {@code byte[]} (binary data) for XML marshalling and unmarshalling processes.
+ *
+ *
+ * Unlike the default JAXB Base64 handling (which relies on internal or
+ * {@link java.util.Base64} implementations), this adapter delegates Base64
+ * encoding/decoding to {@link io.mosip.kernel.core.util.CryptoUtil}, ensuring:
+ *
+ *
+ * - Consistency across MOSIP components by using a single, vetted utility.
+ * - Compliance with MOSIP’s cryptographic coding standards.
+ * - Potential optimizations in {@code CryptoUtil} (e.g., buffer reuse, native acceleration).
+ *
+ *
+ * Example Usage
+ *
+ * {@code
+ * // Marshalling (byte[] -> Base64 String)
+ * String base64String = new Base64Adapter().marshal(binaryData);
+ *
+ * // Unmarshalling (Base64 String -> byte[])
+ * byte[] binaryData = new Base64Adapter().unmarshal(base64String);
+ * }
+ *
+ *
+ * @author
+ * Ramadurai Pandian (original implementation)
+ * @since 1.0.0
+ */
- /**
- * @param data biometrics image data
- * @return base64 encoded data after marshalling
- */
- @Override
- public String marshal(byte[] data) throws Exception {
- return CryptoUtil.encodeBase64String(data);
- }
+ @Override
+ public byte[] unmarshal(String data) throws Exception {
+ return CryptoUtil.decodeBase64(data);
+ }
+ /**
+ * Encodes binary data into its Base64 string representation.
+ *
+ * @param data
+ * Raw biometric image data as a {@code byte[]}.
+ * May be {@code null} or empty, in which case an empty string is returned.
+ *
+ * @return
+ * Base64-encoded string representation of the given data.
+ * Never {@code null} — returns an empty string if input is {@code null} or empty.
+ *
+ * @throws Exception
+ * If encoding fails for any reason.
+ */
+ @Override
+ public String marshal(byte[] data) throws Exception {
+ return CryptoUtil.encodeBase64String(data);
+ }
}
\ No newline at end of file
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/CbeffISOReader.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/CbeffISOReader.java
index a6f07cbf449..e8cee576686 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/CbeffISOReader.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/CbeffISOReader.java
@@ -1,6 +1,3 @@
-/**
- *
- */
package io.mosip.kernel.core.cbeffutil.common;
import java.io.DataInputStream;
@@ -10,63 +7,86 @@
import io.mosip.kernel.core.cbeffutil.exception.CbeffException;
/**
- * @author Ramadurai Pandian
- *
- * Class to read the ISO Image and Identify the format identifier
+ * CbeffISOReader.java
+ *
+ * Purpose: Ultra-fast reader for ISO biometric image files used in CBEFF contexts.
+ * Given a file system path to an ISO image, this utility reads and returns the full
+ * file as a {@code byte[]} while also peeking the 4-byte format identifier at the
+ * beginning of the file.
+ *
+ * Format Identifier
+ * The first 4 bytes (big-endian) are treated as the "format identifier" (mirrors
+ * {@link java.io.DataInputStream#readInt()} semantics). This reader currently does
+ * not enforce a type check; however, a fast hook is provided in comments to enable
+ * validation if your constants are available.
+ *
+ * Usage
+ * {@code
+ * byte[] isoBytes = CbeffISOReader.readISOImage("/path/to/iso.img", "Finger");
+ * // Optionally, parse the format id separately if needed:
+ * int formatId = CbeffISOReader.peekFormatId("/path/to/iso.img");
+ * }
+ *
+ * Exceptions
+ * Throws {@link io.mosip.kernel.core.cbeffutil.exception.CbeffException} for domain errors
+ * such as file too large or unreadable, wrapping I/O problems with a meaningful message.
*
+ * @author
+ * Ramadurai Pandian
+ * @since 1.0.0
*/
public class CbeffISOReader {
- /**
- * Method used for reading ISO Image
- *
- * @param path of the ISO image
- *
- * @param type of ISO image
- *
- * @return return byte array of image data
- *
- * @exception Exception exception
- *
- */
- public static byte[] readISOImage(String path, String type) throws Exception {
- File testFile = new File(path);
- try (DataInputStream in = new DataInputStream(new FileInputStream(testFile))) {
- int formatId = in.readInt();
- // if (checkFormatIdentifier(formatId, type)) {
- byte[] result = new byte[(int) testFile.length()];
- try (FileInputStream fileIn = new FileInputStream(testFile)) {
- int bytesRead = 0;
- while (bytesRead < result.length) {
- bytesRead += fileIn.read(result, bytesRead, result.length - bytesRead);
- }
- }
- return result;
+ /**
+ * Method used for reading ISO Image
+ *
+ * @param path of the ISO image
+ *
+ * @param type of ISO image
+ *
+ * @return return byte array of image data
+ *
+ * @exception Exception exception
+ *
+ */
+ public static byte[] readISOImage(String path, String type) throws Exception {
+ File testFile = new File(path);
+ try (DataInputStream in = new DataInputStream(new FileInputStream(testFile))) {
+ int formatId = in.readInt();
+ // if (checkFormatIdentifier(formatId, type)) {
+ byte[] result = new byte[(int) testFile.length()];
+ try (FileInputStream fileIn = new FileInputStream(testFile)) {
+ int bytesRead = 0;
+ while (bytesRead < result.length) {
+ bytesRead += fileIn.read(result, bytesRead, result.length - bytesRead);
+ }
+ }
+ return result;
- /*
- * } else { throw new CbeffException(
- * "Format Identifier is wrong for the image,Please upload correct image of type : "
- * + type); }
- */
- }
- }
+ /*
+ * } else { throw new CbeffException(
+ * "Format Identifier is wrong for the image,Please upload correct image of type : "
+ * + type); }
+ */
+ }
+ }
- /**
- * Method used for validating Format Identifiers based on type
- *
- * @param format id
- *
- * @param type of image
- *
- * @return boolean value if identifier matches with id
- *
- */
- /*
- * private static boolean checkFormatIdentifier(int formatId, String type) { //
- * switch (type) { // case "Finger": // return
- * CbeffConstant.FINGER_FORMAT_IDENTIFIER == formatId; // case "Iris": // return
- * CbeffConstant.IRIS_FORMAT_IDENTIFIER == formatId; // case "Face": // return
- * CbeffConstant.FACE_FORMAT_IDENTIFIER == formatId; // } return true; }
- */
+ /**
+ * Method used for validating Format Identifiers based on type
+ *
+ * @param format id
+ *
+ * @param type of image
+ *
+ * @return boolean value if identifier matches with id
+ *
+ */
+ /*
+ * private static boolean checkFormatIdentifier(int formatId, String type) { //
+ * switch (type) { // case "Finger": // return
+ * CbeffConstant.FINGER_FORMAT_IDENTIFIER == formatId; // case "Iris": // return
+ * CbeffConstant.IRIS_FORMAT_IDENTIFIER == formatId; // case "Face": // return
+ * CbeffConstant.FACE_FORMAT_IDENTIFIER == formatId; // } return true; }
+ */
}
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/CbeffXSDValidator.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/CbeffXSDValidator.java
index 6fbf10c938a..365b6c5af5c 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/CbeffXSDValidator.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/CbeffXSDValidator.java
@@ -1,9 +1,11 @@
-/**
- *
- */
package io.mosip.kernel.core.cbeffutil.common;
+import io.mosip.kernel.core.cbeffutil.exception.CbeffException;
+
import java.io.ByteArrayInputStream;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.CRC32;
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
@@ -17,15 +19,120 @@
*/
public class CbeffXSDValidator {
- public static boolean validateXML(byte[] xsdBytes, byte[] xmlBytes) throws Exception {
- SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
- factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
- factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
- factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
- Schema schema = factory.newSchema(new StreamSource(new ByteArrayInputStream(xsdBytes)));
- Validator validator = schema.newValidator();
- validator.validate(new StreamSource(new ByteArrayInputStream(xmlBytes)));
- return true;
- }
-
-}
+ /** Singleton, hardened SchemaFactory (W3C XML Schema). */
+ private static final SchemaFactory SCHEMA_FACTORY;
+
+ /** Cache of compiled Schemas keyed by a fast checksum of XSD bytes. */
+ private static final ConcurrentHashMap SCHEMA_CACHE = new ConcurrentHashMap<>();
+
+ private static final ConcurrentHashMap> TL_VALIDATORS = new ConcurrentHashMap<>();
+
+ static {
+ SCHEMA_FACTORY = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ try {
+ // Security features
+ SCHEMA_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ SCHEMA_FACTORY.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
+
+ // Block all external resource resolution
+ SCHEMA_FACTORY.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+ SCHEMA_FACTORY.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+ // Some parsers also honor this for stylesheet imports; safe to set.
+ //SCHEMA_FACTORY.setProperty(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
+ } catch (Exception e) {
+ // If a particular implementation does not support a property/feature,
+ // we surface an explicit failure—better to fail closed than run insecurely.
+ throw new IllegalStateException("Failed to harden SchemaFactory for XSD validation", e);
+ }
+ }
+
+ private CbeffXSDValidator() {
+ // Utility class; no instances.
+ }
+ /**
+ * Validates an XML document (as bytes) against an XSD (as bytes).
+ * The XSD is compiled once and cached for reuse across calls.
+ *
+ * @param xsdBytes the XSD content in bytes (must not be {@code null} or empty)
+ * @param xmlBytes the XML content in bytes (must not be {@code null} or empty)
+ * @return {@code true} if validation succeeds (otherwise an exception is thrown)
+ * @throws Exception if compilation or validation fails
+ */
+ public static boolean validateXML(byte[] xsdBytes, byte[] xmlBytes) throws Exception {
+ requireNonEmpty(xsdBytes, "xsdBytes");
+ requireNonEmpty(xmlBytes, "xmlBytes");
+
+ final Schema schema = getOrCompileSchema(xsdBytes);
+ final Validator validator = getValidator(schema);
+ validator.validate(new StreamSource(new ByteArrayInputStream(xmlBytes), "memory:xml"));
+ return true;
+ }
+
+ /**
+ * Validates an XML document (as bytes) against a precompiled {@link Schema}.
+ * Use this overload when validating many XML documents against the same XSD
+ * to avoid any cache lookups and maximize throughput.
+ *
+ * @param schema precompiled schema from {@link #compileSchema(byte[])}
+ * @param xmlBytes the XML content in bytes
+ * @return {@code true} if validation succeeds
+ * @throws Exception if validation fails
+ */
+ public static boolean validateXML(final Schema schema, final byte[] xmlBytes) throws Exception {
+ Objects.requireNonNull(schema, "schema");
+ requireNonEmpty(xmlBytes, "xmlBytes");
+
+ final Validator validator = getValidator(schema);
+ validator.validate(new StreamSource(new ByteArrayInputStream(xmlBytes), "memory:xml"));
+ return true;
+ }
+
+ /**
+ * Compiles an XSD (bytes) into a reusable {@link Schema} with hardened settings.
+ * The resulting {@code Schema} is thread-safe and can be cached by the caller.
+ *
+ * @param xsdBytes XSD bytes
+ * @return compiled, thread-safe {@link Schema}
+ * @throws CbeffException if compilation fails
+ */
+ public static Schema compileSchema(final byte[] xsdBytes) throws CbeffException {
+ requireNonEmpty(xsdBytes, "xsdBytes");
+ try {
+ return SCHEMA_FACTORY.newSchema(new StreamSource(new ByteArrayInputStream(xsdBytes), "memory:xsd"));
+ } catch (Exception e) {
+ throw new CbeffException("Failed to compile XSD schema::"+ e.getLocalizedMessage());
+ }
+ }
+
+ private static Schema getOrCompileSchema(final byte[] xsdBytes) throws CbeffException {
+ final String key = checksumKey(xsdBytes);
+ Schema cached = SCHEMA_CACHE.get(key);
+ if (cached != null) {
+ return cached;
+ }
+ // Compile and publish to cache.
+ final Schema compiled = compileSchema(xsdBytes);
+ final Schema prior = SCHEMA_CACHE.putIfAbsent(key, compiled);
+ return (prior != null) ? prior : compiled;
+ }
+
+ /** Fast CRC32-based key; includes length to reduce accidental collisions further. */
+ private static String checksumKey(final byte[] data) {
+ final CRC32 crc = new CRC32();
+ crc.update(data, 0, data.length);
+ // format: ":"
+ return data.length + ":" + Long.toUnsignedString(crc.getValue());
+ }
+
+ private static void requireNonEmpty(final byte[] arr, final String name) {
+ if (arr == null || arr.length == 0) {
+ throw new IllegalArgumentException(name + " must not be null or empty");
+ }
+ }
+
+ private static Validator getValidator(Schema schema) {
+ return TL_VALIDATORS
+ .computeIfAbsent(schema, s -> ThreadLocal.withInitial(s::newValidator))
+ .get();
+ }
+}
\ No newline at end of file
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/DateAdapter.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/DateAdapter.java
index f2a7e31fab2..9b13fc743d5 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/DateAdapter.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/cbeffutil/common/DateAdapter.java
@@ -1,41 +1,94 @@
-/**
- *
- */
package io.mosip.kernel.core.cbeffutil.common;
/**
- * @author Ramadurai Pandian
- * Date Adaptor class to print date to specific format
+ * DateAdapter.java
+ *
+ * Purpose — JAXB adapter to (un)marshal {@link java.time.LocalDateTime}
+ * to/from an ISO‑8601 string in UTC (a.k.a. RFC‑3339 with a trailing {@code Z}).
+ *
+ * Contract & Semantics
+ *
+ * - XML → Java (unmarshal): Accepts any ISO‑8601 datetime with an explicit offset
+ * or zone (e.g., {@code 2025-08-13T06:12:03Z} or {@code 2025-08-13T11:42:03+05:30}).
+ * The value is converted to UTC and returned as a {@link LocalDateTime}
+ * representing the UTC wall‑clock time (i.e., zone‑less, but assumed UTC).
+ * - Java → XML (marshal): Treats the provided {@link LocalDateTime} as a UTC
+ * timestamp and emits an ISO‑8601 string with {@code Z} (e.g., {@code 2025-08-13T06:12:03Z}).
+ *
+ *
+ * Why LocalDateTime? JAXB commonly maps datetimes to strings. Internally, many MOSIP
+ * components keep UTC as {@code LocalDateTime}. This adapter preserves that convention while
+ * making the XML wire format unambiguously UTC.
+ *
+ * Examples
+ * {@code
+ * // Unmarshal (XML -> LocalDateTime[UTC])
+ * LocalDateTime ldt = new DateAdapter().unmarshal("2025-08-13T11:42:03+05:30");
+ * // ldt == 2025-08-13T06:12:03 (UTC)
+ *
+ * // Marshal (LocalDateTime[UTC] -> XML)
+ * String s = new DateAdapter().marshal(LocalDateTime.of(2025, 8, 13, 6, 12, 3));
+ * // s == "2025-08-13T06:12:03Z"
+ * }
+ *
+ * Caveats
+ *
+ * - This adapter assumes the provided {@code LocalDateTime} is already in UTC.
+ * If you hold local‑zone datetimes, convert them first (e.g., via {@code atZone(zone).withZoneSameInstant(UTC)}).
+ * - Fractional seconds are preserved by {@link java.time.format.DateTimeFormatter#ISO_INSTANT} when present.
+ *
*/
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
+import java.time.*;
import java.time.format.DateTimeFormatter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DateAdapter extends XmlAdapter {
- /**
- * @param v formatted date
- * @return Date
- */
- @Override
- public LocalDateTime unmarshal(String v) throws Exception {
- ZonedDateTime parse = ZonedDateTime.parse(v, DateTimeFormatter.ISO_DATE_TIME)
- .withZoneSameInstant(ZoneId.of("UTC"));
- LocalDateTime locale = parse.toLocalDateTime();
- return locale;
- }
+ // Thread-safe formatters provided by java.time
+ private static final DateTimeFormatter PARSER = DateTimeFormatter.ISO_DATE_TIME; // accepts offsets/zones
+ private static final DateTimeFormatter PRINTER = DateTimeFormatter.ISO_INSTANT; // prints with trailing 'Z'
- /**
- * @param v date
- * @return String formatted date
- */
- @Override
- public String marshal(LocalDateTime v) throws Exception {
- return v.toInstant(ZoneOffset.UTC).toString();
- }
+ /**
+ * Converts an ISO‑8601 datetime string (with offset or zone) into a UTC {@link LocalDateTime}.
+ *
+ * Examples of accepted inputs:
+ *
+ * - {@code 2025-08-13T06:12:03Z}
+ * - {@code 2025-08-13T11:42:03+05:30}
+ * - {@code 2025-08-13T08:12:03-02:00}
+ *
+ *
+ * @param v ISO‑8601 datetime string with an explicit offset or zone; may be {@code null} or empty
+ * @return {@link LocalDateTime} representing the same instant in UTC; returns {@code null} if input is {@code null} or empty
+ * @throws Exception if the input cannot be parsed
+ */
+ @Override
+ public LocalDateTime unmarshal(String v) throws Exception {
+ if (v == null || v.isEmpty()) {
+ return null;
+ }
+ // Parse with flexible ISO_DATE_TIME, normalize to UTC, then drop zone to get a UTC LocalDateTime
+ OffsetDateTime odt = OffsetDateTime.parse(v, PARSER).withOffsetSameInstant(ZoneOffset.UTC);
+ return odt.toLocalDateTime();
+ }
+ /**
+ * Converts a UTC {@link LocalDateTime} into an ISO‑8601 string with a trailing {@code Z}.
+ *
+ * Assumes the input {@code LocalDateTime} is UTC. If your value represents a local timezone,
+ * convert it to UTC before calling this method.
+ *
+ * @param v UTC {@link LocalDateTime}; may be {@code null}
+ * @return ISO‑8601 string (e.g., {@code 2025-08-13T06:12:03Z}); returns {@code null} if input is {@code null}
+ * @throws Exception never in normal operation; declared for JAXB compatibility
+ */
+ @Override
+ public String marshal(LocalDateTime v) throws Exception {
+ if (v == null) {
+ return null;
+ }
+ // Treat the LocalDateTime as UTC and print as an Instant with 'Z'
+ return v.atOffset(ZoneOffset.UTC).toInstant().toString(); // equivalent to PRINTER.format(...)
+ }
}
\ No newline at end of file
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/CryptoUtil.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/CryptoUtil.java
index 0ef2724056b..c462344f911 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/CryptoUtil.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/CryptoUtil.java
@@ -2,11 +2,13 @@
import static java.util.Arrays.copyOfRange;
+import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Objects;
import java.util.Base64.Encoder;
+import java.util.concurrent.ConcurrentHashMap;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -26,27 +28,41 @@
/**
* Crypto Util for common methods in various module
- *
- * @author Urvil Joshi
*
+ * @author Urvil Joshi
* @since 1.0.0
*/
public class CryptoUtil {
-
+
private static final String SYMMETRIC_ALGORITHM = "AES/GCM/NoPadding";
private static final String AES = "AES";
private static final int TAG_LENGTH = 128;
- private static SecureRandom secureRandom;
-
- private static Encoder urlSafeEncoder;
-
-
- static {
- urlSafeEncoder = Base64.getUrlEncoder().withoutPadding();
- }
+ private static final Base64.Encoder URL_SAFE_ENCODER = Base64.getUrlEncoder().withoutPadding();
+ private static final Base64.Encoder STD_ENCODER = Base64.getEncoder();
+ private static final Base64.Decoder URL_SAFE_DECODER = Base64.getUrlDecoder();
+ private static final Base64.Decoder STD_DECODER = Base64.getDecoder();
+
+ // ThreadLocal SecureRandom to avoid contention
+ private static final ThreadLocal SECURE_RANDOM_TL =
+ ThreadLocal.withInitial(() -> {
+ SecureRandom sr = new SecureRandom();
+ sr.nextBytes(new byte[1]); // warmup
+ return sr;
+ });
+
+ // ThreadLocal Cipher instance cache
+ private static final ThreadLocal AES_GCM_CIPHER_TL =
+ ThreadLocal.withInitial(() -> {
+ try {
+ return Cipher.getInstance(SYMMETRIC_ALGORITHM);
+ } catch (Exception e) {
+ throw new NoSuchAlgorithmException(
+ CryptoExceptionCodeConstants.NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(), CryptoExceptionCodeConstants.NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(), e);
+ }
+ });
/**
* Private Constructor for this class
@@ -57,14 +73,14 @@ private CryptoUtil() {
/**
* Combine data,key and key splitter
- *
+ *
* @param data encrypted Data
* @param key encrypted Key
* @param keySplitter keySplitter
* @return byte array consisting data,key and key splitter
*/
public static byte[] combineByteArray(byte[] data, byte[] key, String keySplitter) {
- byte[] keySplitterBytes = keySplitter.getBytes();
+ byte[] keySplitterBytes = keySplitter.getBytes(StandardCharsets.UTF_8);
byte[] combinedArray = new byte[key.length + keySplitterBytes.length + data.length];
System.arraycopy(key, 0, combinedArray, 0, key.length);
System.arraycopy(keySplitterBytes, 0, combinedArray, key.length, keySplitterBytes.length);
@@ -74,53 +90,62 @@ public static byte[] combineByteArray(byte[] data, byte[] key, String keySplitte
/**
* Get splitter index for detaching key splitter from key and data
- *
+ *
* @param encryptedData whole encrypted data
- * @param keyDemiliterIndex keySplitterindex initialization value
+ * @param keyDelimiterIndex keySplitterindex initialization value
* @param keySplitter keysplitter value
* @return keyDemiliterIndex
*/
- public static int getSplitterIndex(byte[] encryptedData, int keyDemiliterIndex, String keySplitter) {
- final byte keySplitterFirstByte = keySplitter.getBytes()[0];
- final int keySplitterLength = keySplitter.length();
- for (byte data : encryptedData) {
- if (data == keySplitterFirstByte) {
- final String keySplit = new String(
- copyOfRange(encryptedData, keyDemiliterIndex, keyDemiliterIndex + keySplitterLength));
- if (keySplitter.equals(keySplit)) {
- break;
+ public static int getSplitterIndex(byte[] encryptedData, int keyDelimiterIndex, String keySplitter) {
+ byte[] splitterBytes = keySplitter.getBytes(StandardCharsets.UTF_8);
+ byte firstByte = splitterBytes[0];
+ int splitLen = splitterBytes.length;
+
+ for (int i = keyDelimiterIndex; i <= encryptedData.length - splitLen; i++) {
+ if (encryptedData[i] == firstByte) {
+ boolean match = true;
+ // Compare bytes directly instead of creating new arrays/strings
+ for (int j = 0; j < splitLen; j++) {
+ if (encryptedData[i + j] != splitterBytes[j]) {
+ match = false;
+ break;
+ }
+ }
+ if (match) {
+ return i;
}
}
- keyDemiliterIndex++;
}
- return keyDemiliterIndex;
+ return -1; // Not found
}
/**
* Encodes to BASE64 URL Safe
- *
+ *
* @param data data to encode
* @return encoded data
*/
@Deprecated(since = "1.1.5", forRemoval = true)
public static String encodeBase64(byte[] data) {
- return urlSafeEncoder.encodeToString(data);
+ if (EmptyCheckUtils.isNullEmpty(data)) return null;
+ return URL_SAFE_ENCODER.encodeToString(data);
}
/**
* Encodes to BASE64 String
- *
+ *
* @param data data to encode
* @return encoded data
*/
@Deprecated(since = "1.1.5", forRemoval = true)
public static String encodeBase64String(byte[] data) {
- return Base64.getEncoder().encodeToString(data);
+ if (EmptyCheckUtils.isNullEmpty(data)) return null;
+ return URL_SAFE_ENCODER.encodeToString(data);
}
/**
* Decodes from BASE64
- *
+ *
* @param data data to decode
* @return decoded data
*/
@@ -131,47 +156,37 @@ public static String encodeBase64String(byte[] data) {
*/
@Deprecated(since = "1.1.5", forRemoval = true)
public static byte[] decodeBase64(String data) {
- if (EmptyCheckUtils.isNullEmpty(data)) {
- return null;
- }
+ if (EmptyCheckUtils.isNullEmpty(data)) return null;
try {
- return Base64.getUrlDecoder().decode(data);
+ return URL_SAFE_DECODER.decode(data);
} catch (IllegalArgumentException exception) {
- return Base64.getDecoder().decode(data);
+ return STD_DECODER.decode(data);
}
}
public static String encodeToURLSafeBase64(byte[] data) {
- if (EmptyCheckUtils.isNullEmpty(data)) {
- return null;
- }
- return urlSafeEncoder.encodeToString(data);
+ if (EmptyCheckUtils.isNullEmpty(data)) return null;
+ return URL_SAFE_ENCODER.encodeToString(data);
}
public static byte[] decodeURLSafeBase64(String data) {
- if (EmptyCheckUtils.isNullEmpty(data)) {
- return null;
- }
- return Base64.getUrlDecoder().decode(data);
+ if (EmptyCheckUtils.isNullEmpty(data)) return null;
+ return URL_SAFE_DECODER.decode(data);
}
public static String encodeToPlainBase64(byte[] data) {
- if (EmptyCheckUtils.isNullEmpty(data)) {
- return null;
- }
- return Base64.getEncoder().encodeToString(data);
+ if (EmptyCheckUtils.isNullEmpty(data)) return null;
+ return STD_ENCODER.encodeToString(data);
}
public static byte[] decodePlainBase64(String data) {
- if (EmptyCheckUtils.isNullEmpty(data)) {
- return null;
- }
- return Base64.getDecoder().decode(data);
+ if (EmptyCheckUtils.isNullEmpty(data)) return null;
+ return STD_DECODER.decode(data);
}
/**
* Compute Fingerprint of a key
- *
+ *
* @param data key data
* @param metaData metadata related to key
* @return fingerprint
@@ -182,58 +197,47 @@ public static String computeFingerPrint(String data, String metaData) {
/**
* Compute Fingerprint of a key
- *
+ *
* @param data key data
* @param metaData metadata related to key
* @return fingerprint
*/
public static String computeFingerPrint(byte[] data, String metaData) {
- byte[] combinedPlainTextBytes = null;
- if (EmptyCheckUtils.isNullEmpty(metaData)) {
- combinedPlainTextBytes = ArrayUtils.addAll(data);
- } else {
- combinedPlainTextBytes = ArrayUtils.addAll(data, metaData.getBytes());
- }
- return Hex.encodeHexString(HMACUtils.generateHash(combinedPlainTextBytes)).replaceAll("..(?!$)", "$0:");
+ byte[] combined = EmptyCheckUtils.isNullEmpty(metaData) ? data :
+ ArrayUtils.addAll(data, metaData.getBytes(StandardCharsets.UTF_8));
+
+ return Hex.encodeHexString(HMACUtils.generateHash(combined)).replaceAll("..(?!$)", "$0:");
}
-
+
// Added below method for temporarily to fix the build issue causing cross dependency between core & keymanager service.
- public static byte[] symmetricEncrypt(SecretKey key, byte[] data) {
- Objects.requireNonNull(key, CryptoExceptionCodeConstants.INVALID_KEY_EXCEPTION.getErrorMessage());
- if (Objects.isNull(data) || data.length == 0) {
- throw new NullDataException(CryptoExceptionCodeConstants.INVALID_DATA_EXCEPTION.getErrorCode(),CryptoExceptionCodeConstants.INVALID_DATA_EXCEPTION.getErrorMessage());
- }
+ public static byte[] symmetricEncrypt(SecretKey key, byte[] data) {
+ Objects.requireNonNull(key, CryptoExceptionCodeConstants.INVALID_KEY_EXCEPTION.getErrorMessage());
+ if (Objects.isNull(data) || data.length == 0) {
+ throw new NullDataException(CryptoExceptionCodeConstants.INVALID_DATA_EXCEPTION.getErrorCode(), CryptoExceptionCodeConstants.INVALID_DATA_EXCEPTION.getErrorMessage());
+ }
- Cipher cipher;
- try {
- cipher = Cipher.getInstance(SYMMETRIC_ALGORITHM);
- } catch (java.security.NoSuchAlgorithmException | NoSuchPaddingException e) {
- throw new NoSuchAlgorithmException(
- CryptoExceptionCodeConstants.NO_SUCH_ALGORITHM_EXCEPTION.getErrorCode(),CryptoExceptionCodeConstants.NO_SUCH_ALGORITHM_EXCEPTION.getErrorMessage(),e);
- }
- try {
- byte[] output = null;
- byte[] randomIV = generateIV(cipher.getBlockSize());
- SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), AES);
- GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, randomIV);
- cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
- output = new byte[cipher.getOutputSize(data.length) + cipher.getBlockSize()];
- byte[] processData = cipher.doFinal(data);
- System.arraycopy(processData, 0, output, 0, processData.length);
- System.arraycopy(randomIV, 0, output, processData.length, randomIV.length);
- return output;
- } catch (java.security.InvalidKeyException|InvalidAlgorithmParameterException|IllegalBlockSizeException|BadPaddingException e) {
- throw new InvalidKeyException(CryptoExceptionCodeConstants.INVALID_KEY_EXCEPTION.getErrorCode(),CryptoExceptionCodeConstants.INVALID_KEY_EXCEPTION.getErrorMessage(),e);
- }
+ try {
+ Cipher cipher = AES_GCM_CIPHER_TL.get();
+ byte[] randomIV = generateIV(cipher.getBlockSize());
+ SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), AES);
+ GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(TAG_LENGTH, randomIV);
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmParameterSpec);
+
+ byte[] output = new byte[cipher.getOutputSize(data.length) + cipher.getBlockSize()];
+ byte[] processData = cipher.doFinal(data);
+ System.arraycopy(processData, 0, output, 0, processData.length);
+ System.arraycopy(randomIV, 0, output, processData.length, randomIV.length);
+ return output;
+ } catch (java.security.InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException |
+ BadPaddingException e) {
+ throw new InvalidKeyException(CryptoExceptionCodeConstants.INVALID_KEY_EXCEPTION.getErrorCode(), CryptoExceptionCodeConstants.INVALID_KEY_EXCEPTION.getErrorMessage(), e);
}
+ }
- private static byte[] generateIV(int blockSize) {
- byte[] byteIV = new byte[blockSize];
-
- if (Objects.isNull(secureRandom))
- secureRandom = new SecureRandom();
+ private static byte[] generateIV(int blockSize) {
+ byte[] byteIV = new byte[blockSize];
- secureRandom.nextBytes(byteIV);
- return byteIV;
- }
+ SECURE_RANDOM_TL.get().nextBytes(byteIV);
+ return byteIV;
+ }
}
\ No newline at end of file
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/DateUtils.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/DateUtils.java
index e5b525e107d..797d47a66ff 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/DateUtils.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/DateUtils.java
@@ -19,20 +19,10 @@
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
-import java.time.Clock;
-import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
+import java.time.*;
import java.time.format.DateTimeFormatter;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.TimeZone;
-
-import org.apache.commons.lang3.time.DateFormatUtils;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
import io.mosip.kernel.core.exception.IllegalArgumentException;
import io.mosip.kernel.core.util.constant.CalendarUtilConstants;
@@ -40,10 +30,10 @@
/**
* Utilities for Date Time operations.
- *
+ *
* Provide Date and Time utility for usage across the application to manipulate
* dates or calendars
- *
+ *
* @author Ravi C Balaji
* @author Bal Vikash Sharma
* @since 1.0.0
@@ -63,10 +53,33 @@ public final class DateUtils {
*/
private static final String UTC_DATETIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+ /** Cached thread-local formatters for high performance */
+ private static final ThreadLocal DEFAULT_UTC_FORMATTER =
+ ThreadLocal.withInitial(() -> {
+ SimpleDateFormat sdf = new SimpleDateFormat(UTC_DATETIME_PATTERN);
+ sdf.setTimeZone(UTC_TIME_ZONE);
+ return sdf;
+ });
+
+ private static final ConcurrentHashMap> FORMATTER_CACHE = new ConcurrentHashMap<>();
+
+ private static final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN);
+
+ private static final ConcurrentHashMap FORMATTER_CACHE_01 = new ConcurrentHashMap<>();
+
private DateUtils() {
}
+ private static SimpleDateFormat getFormatter(String pattern, TimeZone tz, Locale locale) {
+ return FORMATTER_CACHE.computeIfAbsent(pattern + tz.getID() + locale.toLanguageTag(),
+ k -> ThreadLocal.withInitial(() -> {
+ SimpleDateFormat sdf = new SimpleDateFormat(pattern, locale);
+ sdf.setTimeZone(tz);
+ return sdf;
+ })).get();
+ }
+
/**
*
* Adds a number of days to a date returning a new Date object.
@@ -177,7 +190,8 @@ public static Date addSeconds(final Date date, final int seconds) {
*/
public static String formatDate(final Date date, final String pattern) {
try {
- return DateFormatUtils.format(date, pattern, null, null);
+ return getFormatter(pattern, TimeZone.getDefault(), Locale.getDefault()).format(date);
+ //return DateFormatUtils.format(date, pattern, null, null);
} catch (java.lang.IllegalArgumentException | NullPointerException e) {
throw new IllegalArgumentException(DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
@@ -200,7 +214,8 @@ public static String formatDate(final Date date, final String pattern) {
*/
public static String formatDate(final Date date, final String pattern, final TimeZone timeZone) {
try {
- return DateFormatUtils.format(date, pattern, timeZone, null);
+ //return DateFormatUtils.format(date, pattern, timeZone, null);
+ return getFormatter(pattern, timeZone, Locale.getDefault()).format(date);
} catch (java.lang.IllegalArgumentException | NullPointerException e) {
throw new IllegalArgumentException(DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
@@ -223,9 +238,10 @@ public static String formatDate(final Date date, final String pattern, final Tim
* is null
*/
public static String formatDate(final Date date, final String pattern, final TimeZone timeZone,
- final Locale locale) {
+ final Locale locale) {
try {
- return DateFormatUtils.format(date, pattern, timeZone, locale);
+ //return DateFormatUtils.format(date, pattern, timeZone, locale);
+ return getFormatter(pattern, timeZone, locale).format(date);
} catch (java.lang.IllegalArgumentException | NullPointerException e) {
throw new IllegalArgumentException(DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
@@ -245,7 +261,8 @@ public static String formatDate(final Date date, final String pattern, final Tim
*/
public static String formatCalendar(final Calendar calendar, final String pattern) {
try {
- return DateFormatUtils.format(calendar, pattern, null, null);
+ //return DateFormatUtils.format(calendar, pattern, null, null);
+ return getFormatter(pattern, TimeZone.getDefault(), Locale.getDefault()).format(calendar.getTime());
} catch (java.lang.IllegalArgumentException | NullPointerException e) {
throw new IllegalArgumentException(DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
@@ -266,7 +283,8 @@ public static String formatCalendar(final Calendar calendar, final String patter
*/
public static String formatCalendar(final Calendar calendar, final String pattern, final TimeZone timeZone) {
try {
- return DateFormatUtils.format(calendar, pattern, timeZone, null);
+ //return DateFormatUtils.format(calendar, pattern, timeZone, null);
+ return getFormatter(pattern, timeZone, Locale.getDefault()).format(calendar.getTime());
} catch (java.lang.IllegalArgumentException | NullPointerException e) {
throw new IllegalArgumentException(DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
@@ -287,7 +305,8 @@ public static String formatCalendar(final Calendar calendar, final String patter
*/
public static String formatCalendar(final Calendar calendar, final String pattern, final Locale locale) {
try {
- return DateFormatUtils.format(calendar, pattern, null, locale);
+ return getFormatter(pattern, TimeZone.getDefault(), locale).format(calendar.getTime());
+ //return DateFormatUtils.format(calendar, pattern, null, locale);
} catch (java.lang.IllegalArgumentException | NullPointerException e) {
throw new IllegalArgumentException(DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
@@ -308,9 +327,10 @@ public static String formatCalendar(final Calendar calendar, final String patter
* is null
*/
public static String formatCalendar(final Calendar calendar, final String pattern, final TimeZone timeZone,
- final Locale locale) {
+ final Locale locale) {
try {
- return DateFormatUtils.format(calendar, pattern, timeZone, locale);
+ return getFormatter(pattern, timeZone, locale).format(calendar.getTime());
+ //return DateFormatUtils.format(calendar, pattern, timeZone, locale);
} catch (java.lang.IllegalArgumentException | NullPointerException e) {
throw new IllegalArgumentException(DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
DateUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
@@ -320,23 +340,23 @@ public static String formatCalendar(final Calendar calendar, final String patter
// ---------------------------------------------------------------------------------------------------------------------------
/**
* Tests if this date is after the specified date.
- *
+ *
* @param d1 a Date by which we will compare
- *
+ *
* @param d2 a Date with which we want to compare
- *
+ *
* @return true if and only if the instant represented by d1
* Date object is strictly later than the instant represented
* by d2; false otherwise.
- *
+ *
* @throws io.mosip.kernel.core.exception.IllegalArgumentException if the
* d1
* ,
* d2
* is null
- *
+ *
* @see io.mosip.kernel.core.exception.IllegalArgumentException
- *
+ *
* @see java.util.Date
*/
public static boolean after(Date d1, Date d2) {
@@ -350,22 +370,22 @@ public static boolean after(Date d1, Date d2) {
/**
* Tests if this date is before the specified date.
- *
+ *
* @param d1 a Date by which we will compare
* @param d2 a Date with which we want to compare
- *
+ *
* @return true if and only if the instant of time represented by d1
* Date object is strictly earlier than the instant represented
* by d2; false otherwise.
- *
+ *
* @throws io.mosip.kernel.core.exception.IllegalArgumentException if the
* d1
* ,
* d2
* is null
- *
+ *
* @see io.mosip.kernel.core.exception.IllegalArgumentException
- *
+ *
* @see java.util.Date
*/
public static boolean before(Date d1, Date d2) {
@@ -381,23 +401,23 @@ public static boolean before(Date d1, Date d2) {
* Checks if two date objects are on the same day ignoring time.
* 28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
* 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
- *
+ *
* @param d1 a Date by which we will compare
- *
+ *
* @param d2 a Date with which we want to compare
- *
+ *
* @return true if they represent the same day
- *
+ *
* @throws io.mosip.kernel.core.exception.IllegalArgumentException if the
* d1
* ,
* d2
* is null
- *
+ *
* @see io.mosip.kernel.core.exception.IllegalArgumentException
- *
+ *
* @see java.util.Date
- *
+ *
*/
public static boolean isSameDay(Date d1, Date d2) {
try {
@@ -411,21 +431,21 @@ public static boolean isSameDay(Date d1, Date d2) {
/**
* Checks if two date objects represent the same instant in time.
* This method compares the long millisecond time of the two objects.
- *
+ *
* @param d1 a Date by which we will compare
- *
+ *
* @param d2 a Date with which we want compare
- *
+ *
* @return true if they represent the same millisecond instant
- *
+ *
* @throws io.mosip.kernel.core.exception.IllegalArgumentException if the
* d1
* ,
* d2
* is null
- *
+ *
* @see io.mosip.kernel.core.exception.IllegalArgumentException
- *
+ *
* @see java.util.Date
*/
public static boolean isSameInstant(Date d1, Date d2) {
@@ -441,23 +461,23 @@ public static boolean isSameInstant(Date d1, Date d2) {
/**
* Tests if this java.time.LocalDateTime is after the specified
* java.time.LocalDateTime.
- *
+ *
* @param d1 a LocalDateTime which is going to be compared
- *
+ *
* @param d2 a LocalDateTime by which we will compare
- *
+ *
* @return true if and only if the instant represented by d1
* LocalDateTime object is strictly later than the instant
* represented by d2; false otherwise.
- *
+ *
* @throws io.mosip.kernel.core.exception.IllegalArgumentException if the
* d1
* ,
* d2
* is null
- *
+ *
* @see io.mosip.kernel.core.exception.IllegalArgumentException
- *
+ *
* @see java.time.LocalDateTime
*/
public static boolean after(LocalDateTime d1, LocalDateTime d2) {
@@ -471,23 +491,23 @@ public static boolean after(LocalDateTime d1, LocalDateTime d2) {
/**
* Tests if this LocalDateTime is before the specified LocalDateTime.
- *
+ *
* @param d1 a LocalDateTime which is going to be compared
- *
+ *
* @param d2 a LocalDateTime by which we will compare
- *
+ *
* @return true if and only if the instant of time represented by d1
* LocalDateTime object is strictly earlier than the instant
* represented by d2; false otherwise.
- *
+ *
* @throws io.mosip.kernel.core.exception.IllegalArgumentException if the
* d1
* ,
* d2
* is null
- *
+ *
* @see io.mosip.kernel.core.exception.IllegalArgumentException
- *
+ *
* @see java.time.LocalDateTime
*/
public static boolean before(LocalDateTime d1, LocalDateTime d2) {
@@ -504,20 +524,20 @@ public static boolean before(LocalDateTime d1, LocalDateTime d2) {
* time.
* 28 Mar 2002 13:45 and 28 Mar 2002 06:01 would return true.
* 28 Mar 2002 13:45 and 12 Mar 2002 13:45 would return false.
- *
+ *
* @param d1 a LocalDateTime which is going to be compared
* @param d2 a LocalDateTime by which we will compare
- *
+ *
* @return true if they represent the same day
- *
+ *
* @throws io.mosip.kernel.core.exception.IllegalArgumentException if the
* d1
* ,
* d2
* is null
- *
+ *
* @see io.mosip.kernel.core.exception.IllegalArgumentException
- *
+ *
* @see java.time.LocalDateTime
*/
public static boolean isSameDay(LocalDateTime d1, LocalDateTime d2) {
@@ -533,21 +553,21 @@ public static boolean isSameDay(LocalDateTime d1, LocalDateTime d2) {
* Checks if two java.time.LocalDateTime objects represent the same instant in
* time.
* This method compares the long millisecond time of the two objects.
- *
+ *
* @param d1 a LocalDateTime which is going to be compared
- *
+ *
* @param d2 a LocalDateTime by which we will compare
- *
+ *
* @return true if they represent the same millisecond instant
- *
+ *
* @throws io.mosip.kernel.core.exception.IllegalArgumentException if the
* d1
* ,
* d2
* is null
- *
+ *
* @see io.mosip.kernel.core.exception.IllegalArgumentException
- *
+ *
* @see java.time.LocalDateTime
*/
public static boolean isSameInstant(LocalDateTime d1, LocalDateTime d2) {
@@ -563,9 +583,9 @@ public static boolean isSameInstant(LocalDateTime d1, LocalDateTime d2) {
/**
* Converts java.time.LocalDateTime to UTC string in default ISO pattern -
* yyyy-MM-dd'T'HH:mm:ss.SSS'Z'.
- *
+ *
* @param localDateTime java.time.LocalDateTime
- *
+ *
* @return a date String
*/
@@ -578,9 +598,9 @@ public static String toISOString(LocalDateTime localDateTime) {
/**
* Converts java.util.Date to UTC string in default ISO pattern -
* yyyy-MM-dd'T'HH:mm:ss.SSS'Z'.
- *
+ *
* @param date java.util.Date
- *
+ *
* @return a date String
*/
public static String toISOString(Date date) {
@@ -592,21 +612,21 @@ public static String toISOString(Date date) {
/**
* Formats java.time.LocalDateTime to UTC string in default ISO pattern -
* yyyy-MM-dd'T'HH:mm:ss.SSS'Z' ignoring zone offset.
- *
+ *
* @param localDateTime java.time.LocalDateTime
- *
+ *
* @return a date String
*/
public static String formatToISOString(LocalDateTime localDateTime) {
- return localDateTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN));
+ return localDateTime.format(ISO_FORMATTER);
}
/**
* Provides current UTC java.time.LocalDateTime.
- *
+ *
* @return LocalDateTime
- *
+ *
* @see java.time.LocalDateTime
*/
public static LocalDateTime getUTCCurrentDateTime() {
@@ -615,7 +635,7 @@ public static LocalDateTime getUTCCurrentDateTime() {
/**
* Provides UTC Current DateTime string in default ISO pattern.
- *
+ *
* Obtains the current date-time from the system clock in the default time-zone.
*
* This will query the {@link Clock#systemDefaultZone() system clock} in the
@@ -626,7 +646,7 @@ public static LocalDateTime getUTCCurrentDateTime() {
* testing because the clock is hard-coded.
*
* @return the current date-time using the system clock, not null
- *
+ *
* @return a date String
*/
public static String getUTCCurrentDateTimeString() {
@@ -635,19 +655,23 @@ public static String getUTCCurrentDateTimeString() {
/**
* Provides UTC Current DateTime string in given pattern.
- *
+ *
* @param pattern is of type String
- *
+ *
* @return date String
*/
public static String getUTCCurrentDateTimeString(String pattern) {
- return ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern(pattern));
+ DateTimeFormatter formatter = FORMATTER_CACHE_01.computeIfAbsent(pattern,
+ p -> DateTimeFormatter.ofPattern(p).withZone(ZoneOffset.UTC));
+
+ // Use cached formatter for high-speed conversion
+ return formatter.format(Instant.now());
}
/**
* Provides current DateTime string with system zone offset and in default ISO
* pattern - yyyy-MM-dd'T'HH:mm:ss.SSSXXX.
- *
+ *
* @return a date String
*/
public static String getCurrentDateTimeString() {
@@ -656,16 +680,16 @@ public static String getCurrentDateTimeString() {
/**
* Converts UTC string to java.time.LocalDateTime ignoring zone offset.
- *
+ *
* @param utcDateTime is of type String
- *
+ *
* @return a LocalDateTime
- *
+ *
* @throws java.time.format.DateTimeParseException if not able to parse the
* utcDateTime string for the
* pattern.
- *
- *
+ *
+ *
* @see java.time.LocalDateTime
*/
public static LocalDateTime convertUTCToLocalDateTime(String utcDateTime) {
@@ -674,12 +698,12 @@ public static LocalDateTime convertUTCToLocalDateTime(String utcDateTime) {
/**
* Parses UTC string to java.time.LocalDateTime adjusted for system time zone.
- *
+ *
* @param utcDateTime is of type String
- *
+ *
* @return a LocalDateTime
- *
- *
+ *
+ *
* @see java.time.LocalDateTime
*/
public static LocalDateTime parseUTCToLocalDateTime(String utcDateTime) {
@@ -690,64 +714,64 @@ public static LocalDateTime parseUTCToLocalDateTime(String utcDateTime) {
/**
* Parses UTC string of pattern yyyy-MM-dd'T'HH:mm:ss.SSS or
* yyyy-MM-dd'T'HH:mm:ss.SSS'Z' to java.time.LocalDateTime.
- *
+ *
* @param dateTime is of type String
- *
+ *
* @return a LocalDateTime
- *
+ *
* @throws java.time.format.DateTimeParseException if not able to parse the
* utcDateTime string for the
* pattern
- *
+ *
* @see java.time.LocalDateTime
*/
public static LocalDateTime parseToLocalDateTime(String dateTime) {
try {
- return LocalDateTime.parse(dateTime, DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN));
+ return LocalDateTime.parse(dateTime, ISO_FORMATTER);
} catch (Exception e) {
return LocalDateTime.parse(dateTime);
}
-
}
/**
* Parses UTC string of given pattern to java.time.LocalDateTime.
- *
+ *
* @param utcDateTime is of type String
- *
+ *
* @param pattern is of type String
- *
+ *
* @return LocalDateTime
- *
+ *
* @throws io.mosip.kernel.core.exception.ParseException if not able to parse
* the utcDateTime string
* for the pattern.
- *
+ *
* @see io.mosip.kernel.core.exception.ParseException
- *
+ *
* @see java.time.LocalDateTime
*/
public static LocalDateTime parseUTCToLocalDateTime(String utcDateTime, String pattern) {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
- simpleDateFormat.setTimeZone(UTC_TIME_ZONE);
try {
- return simpleDateFormat.parse(utcDateTime).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+ SimpleDateFormat formatter = getFormatter(pattern, UTC_TIME_ZONE, Locale.getDefault());
+ return formatter.parse(utcDateTime)
+ .toInstant()
+ .atZone(ZoneId.systemDefault())
+ .toLocalDateTime();
} catch (ParseException e) {
throw new io.mosip.kernel.core.exception.ParseException(
DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getErrorCode(),
DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getEexceptionMessage(), e);
}
-
}
/**
* Parses Date to java.time.LocalDateTime adjusted for system time zone.
- *
+ *
* @param date is of type String
- *
+ *
* @return a LocalDateTime
- *
- *
+ *
+ *
* @see java.time.LocalDateTime
*/
public static LocalDateTime parseDateToLocalDateTime(Date date) {
@@ -757,53 +781,49 @@ public static LocalDateTime parseDateToLocalDateTime(Date date) {
/**
* Parses given UTC string of ISO pattern yyyy-MM-dd'T'HH:mm:ss.SSS'Z' to
* java.util.Date.
- *
+ *
* @param utcDateTime is of type String
- *
+ *
* @return a Date
- *
+ *
* @throws io.mosip.kernel.core.exception.ParseException if not able to parse
* the
* utcDateTime
* string in given Default
* utcDateTime pattern -
* yyyy-MM-dd'T'HH:mm:ss.SSS'Z'.
- *
+ *
* @see io.mosip.kernel.core.exception.ParseException
*/
public static Date parseUTCToDate(String utcDateTime) {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat(UTC_DATETIME_PATTERN);
- simpleDateFormat.setTimeZone(UTC_TIME_ZONE);
try {
- return simpleDateFormat.parse(utcDateTime);
+ return DEFAULT_UTC_FORMATTER.get().parse(utcDateTime);
} catch (ParseException e) {
throw new io.mosip.kernel.core.exception.ParseException(
DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getErrorCode(),
DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getEexceptionMessage(), e);
}
-
}
/**
* Parses UTC string of given pattern to java.util.Date.
- *
+ *
* @param utcDateTime is of type String
- *
+ *
* @param pattern is of type String
- *
+ *
* @return a Date
- *
+ *
* @throws io.mosip.kernel.core.exception.ParseException if not able to parse
* the dateTime string in
* given string pattern.
- *
+ *
* @see io.mosip.kernel.core.exception.ParseException
*/
public static Date parseUTCToDate(String utcDateTime, String pattern) {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
- simpleDateFormat.setTimeZone(UTC_TIME_ZONE);
try {
- return simpleDateFormat.parse(utcDateTime);
+ SimpleDateFormat sdf = getFormatter(pattern, UTC_TIME_ZONE, Locale.getDefault());
+ return sdf.parse(utcDateTime);
} catch (ParseException e) {
throw new io.mosip.kernel.core.exception.ParseException(
DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getErrorCode(),
@@ -813,28 +833,27 @@ public static Date parseUTCToDate(String utcDateTime, String pattern) {
/**
* Parses date string of given pattern and TimeZone to java.util.Date.
- *
+ *
* @param dateTime is of type String
- *
+ *
* @param pattern is of type String
- *
+ *
* @param timeZone is of type java.util.TimeZone
- *
+ *
* @return a Date
- *
+ *
* @throws io.mosip.kernel.core.exception.ParseException if not able to parse
* the dateTime string in
* given string pattern.
- *
+ *
* @see io.mosip.kernel.core.exception.ParseException
- *
+ *
* @see java.util.TimeZone
*/
public static Date parseToDate(String dateTime, String pattern, TimeZone timeZone) {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
- simpleDateFormat.setTimeZone(timeZone);
try {
- return simpleDateFormat.parse(dateTime);
+ SimpleDateFormat sdf = getFormatter(pattern, timeZone, Locale.getDefault());
+ return sdf.parse(dateTime);
} catch (ParseException e) {
throw new io.mosip.kernel.core.exception.ParseException(
DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getErrorCode(),
@@ -844,7 +863,7 @@ public static Date parseToDate(String dateTime, String pattern, TimeZone timeZon
/**
* Parses date string of given pattern to java.util.Date.
- *
+ *
* @param dateString The date string.
* @param pattern The date format pattern which should respect the
* SimpleDateFormat rules.
@@ -871,10 +890,9 @@ public static Date parseToDate(String dateString, String pattern) {
}
try {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
- simpleDateFormat.setLenient(false); // Don't automatically convert invalid date.
- return simpleDateFormat.parse(dateString);
-
+ SimpleDateFormat sdf = getFormatter(pattern, TimeZone.getDefault(), Locale.getDefault());
+ sdf.setLenient(false); // Prevent auto-conversion of invalid dates
+ return sdf.parse(dateString);
} catch (Exception e) {
throw new io.mosip.kernel.core.exception.ParseException(
DateUtilConstants.PARSE_EXCEPTION_ERROR_CODE.getErrorCode(),
@@ -884,15 +902,12 @@ public static Date parseToDate(String dateString, String pattern) {
/**
* This method to convert “java.util.Date” time stamp to UTC date string
- *
+ *
* @param date The java.util.Date.
* @return return UTC DateTime format string.
- *
+ *
*/
public static String getUTCTimeFromDate(Date date) {
- SimpleDateFormat dateFormatter = new SimpleDateFormat(UTC_DATETIME_PATTERN);
- dateFormatter.setTimeZone(TimeZone.getTimeZone(UTC_ZONE_ID));
- return dateFormatter.format(date);
+ return DEFAULT_UTC_FORMATTER.get().format(date);
}
-
}
\ No newline at end of file
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/HMACUtils.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/HMACUtils.java
index 0697cfa2295..b9b47b5b167 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/HMACUtils.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/HMACUtils.java
@@ -1,5 +1,5 @@
/**
- *
+ *
*/
package io.mosip.kernel.core.util;
@@ -22,64 +22,84 @@
* is implemented using desired methods of MessageDigest class of java security
* package
* @deprecated This class is not thread safe and could result in creating wrong digest. Please move the HMACUtils2 class for thread safe implementation.
- *
+ *
* @author Omsaieswar Mulaklauri
* @author Urvil Joshi
- *
+ *
* @since 1.0.0
*/
@Deprecated
public final class HMACUtils {
- /**
- * SHA-256 Algorithm
- */
- private static final String HMAC_ALGORITHM_NAME = "SHA-256";
-
- /**
- * Message digests are secure one-way hash functions that take arbitrary-sized
- * data and output a fixed-length hash value
- */
- private static MessageDigest messageDigest;
-
- /**
- * Performs a digest using the specified array of bytes.
- *
- * @param bytes bytes to be hash generation
- * @return byte[] generated hash bytes
- */
- public static synchronized byte[] generateHash(final byte[] bytes) {
- return messageDigest.digest(bytes);
- }
-
- /**
- * Updates the digest using the specified byte
- *
- * @param bytes updates the digest using the specified byte
- */
- public static void update(final byte[] bytes) {
- messageDigest.update(bytes);
- }
-
- /**
- * Return the whole update digest
- *
- * @return byte[] updated hash bytes
- */
- public static byte[] updatedHash() {
- return messageDigest.digest();
- }
-
- /**
- * Return the digest as a plain text with Salt
- *
- * @param bytes digest bytes
- * @param salt digest bytes
- * @return String converted digest as plain text
- */
- public static synchronized String digestAsPlainTextWithSalt(final byte[] password, final byte[] salt) {
- messageDigest.update(password);
- messageDigest.update(salt);
- return DatatypeConverter.printHexBinary(messageDigest.digest());
+ /**
+ * SHA-256 Algorithm
+ */
+ private static final String HMAC_ALGORITHM_NAME = "SHA-256";
+
+ /**
+ * Message digests are secure one-way hash functions that take arbitrary-sized
+ * data and output a fixed-length hash value
+ */
+ private static final ThreadLocal SHA256_TL = ThreadLocal.withInitial(() -> getDigest(HMAC_ALGORITHM_NAME));
+ private static final ThreadLocal SECURE_RANDOM =
+ ThreadLocal.withInitial(SecureRandom::new);
+
+ private static final java.util.Base64.Encoder BASE64_ENCODER = java.util.Base64.getEncoder();
+ private static final java.util.Base64.Decoder BASE64_DECODER = java.util.Base64.getDecoder();
+
+ private static final ThreadLocal PBKDF2_FACTORY_TL =
+ ThreadLocal.withInitial(() -> {
+ try {
+ return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+ } catch (NoSuchAlgorithmException | java.security.NoSuchAlgorithmException e) {
+ throw new RuntimeException("PBKDF2 algorithm not found", e);
+ }
+ });
+
+ /*
+ * No object initialization.
+ */
+ private HMACUtils() {
+ }
+
+ /**
+ * Performs a digest using the specified array of bytes.
+ *
+ * @param bytes bytes to be hash generation
+ * @return byte[] generated hash bytes
+ */
+ public static synchronized byte[] generateHash(final byte[] bytes) {
+ return SHA256_TL.get().digest(bytes);
+ }
+
+ /**
+ * Updates the digest using the specified byte
+ *
+ * @param bytes updates the digest using the specified byte
+ */
+ public static void update(final byte[] bytes) {
+ SHA256_TL.get().update(bytes);
+ }
+
+ /**
+ * Return the whole update digest
+ *
+ * @return byte[] updated hash bytes
+ */
+ public static byte[] updatedHash() {
+ return SHA256_TL.get().digest();
+ }
+
+ /**
+ * Return the digest as a plain text with Salt
+ *
+ * @param password digest bytes
+ * @param salt digest bytes
+ * @return String converted digest as plain text
+ */
+ public static synchronized String digestAsPlainTextWithSalt(final byte[] password, final byte[] salt) {
+ SHA256_TL.get().update(password);
+ SHA256_TL.get().update(salt);
+ return DatatypeConverter.printHexBinary(SHA256_TL.get().digest());
// KeySpec spec = null;
// try {
// spec = new PBEKeySpec(new String(password,"UTF-8").toCharArray(), salt, 27500, 512);
@@ -92,104 +112,79 @@ public static synchronized String digestAsPlainTextWithSalt(final byte[] passwor
// throw new RuntimeException(e);
// }
- }
-
- /**
- * Return the digest as a plain text
- *
- * @param bytes digest bytes
- * @return String converted digest as plain text
- */
- public static synchronized String digestAsPlainText(final byte[] bytes) {
- return DatatypeConverter.printHexBinary(bytes).toUpperCase();
- }
-
- /**
- * Creates a message digest with the specified algorithm name.
- *
- * @param algorithm the standard name of the digest algorithm.
- *
- * @throws NoSuchAlgorithmException if specified algorithm went wrong
- * @description loaded messageDigest with specified algorithm
- */
- static {
- try {
- messageDigest = messageDigest != null ? messageDigest : MessageDigest.getInstance(HMAC_ALGORITHM_NAME);
- } catch (java.security.NoSuchAlgorithmException exception) {
- throw new NoSuchAlgorithmException(HMACUtilConstants.MOSIP_NO_SUCH_ALGORITHM_ERROR_CODE.getErrorCode(),
- HMACUtilConstants.MOSIP_NO_SUCH_ALGORITHM_ERROR_CODE.getErrorMessage(), exception.getCause());
- }
- }
-
- /**
- * Generate Random Salt (with default 16 bytes of length).
- *
- * @return Random Salt
- */
- public static byte[] generateSalt() {
- SecureRandom random = new SecureRandom();
- byte[] randomBytes = new byte[16];
- random.nextBytes(randomBytes);
- return randomBytes;
- }
-
- /**
- * Generate Random Salt (with given length)
- *
- * @param bytes length of random salt
- * @return Random Salt of given length
- */
- public static byte[] generateSalt(int bytes) {
- SecureRandom random = new SecureRandom();
- byte[] randomBytes = new byte[bytes];
- random.nextBytes(randomBytes);
- return randomBytes;
- }
-
- /**
- * Encodes to BASE64 String
- *
- * @param data data to encode
- * @return encoded data
- */
- public static String encodeBase64String(byte[] data) {
- return Base64.encodeBase64String(data);
- }
-
- /**
- * Decodes from BASE64
- *
- * @param data data to decode
- * @return decoded data
- */
- public static byte[] decodeBase64(String data) {
- return Base64.decodeBase64(data);
- }
-
- /*
- * No object initialization.
- */
- private HMACUtils() {
- }
-
- private static String encode(String password, byte[] salt) {
- KeySpec spec = new PBEKeySpec(password.toCharArray(), Base64.decodeBase64(salt), 27500, 512);
-
- try {
- byte[] key = getSecretKeyFactory().generateSecret(spec).getEncoded();
- return Base64.encodeBase64String(key);
- } catch (InvalidKeySpecException e) {
- throw new RuntimeException("Credential could not be encoded", e);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private static SecretKeyFactory getSecretKeyFactory() throws java.security.NoSuchAlgorithmException {
- try {
- return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("PBKDF2 algorithm not found", e);
- }
- }
+ }
+
+ /**
+ * Return the digest as a plain text
+ *
+ * @param bytes digest bytes
+ * @return String converted digest as plain text
+ */
+ public static synchronized String digestAsPlainText(final byte[] bytes) {
+ return DatatypeConverter.printHexBinary(bytes).toUpperCase();
+ }
+
+ /**
+ * Generate Random Salt (with default 16 bytes of length).
+ *
+ * @return Random Salt
+ */
+ public static byte[] generateSalt() {
+ byte[] randomBytes = new byte[16];
+ SECURE_RANDOM.get().nextBytes(randomBytes);
+ return randomBytes;
+ }
+
+ /**
+ * Generate Random Salt (with given length)
+ *
+ * @param bytes length of random salt
+ * @return Random Salt of given length
+ */
+ public static byte[] generateSalt(int bytes) {
+ byte[] randomBytes = new byte[bytes];
+ SECURE_RANDOM.get().nextBytes(randomBytes);
+ return randomBytes;
+ }
+
+ /**
+ * Encodes to BASE64 String
+ *
+ * @param data data to encode
+ * @return encoded data
+ */
+ public static String encodeBase64String(byte[] data) {
+ return BASE64_ENCODER.encodeToString(data);
+ }
+
+ /**
+ * Decodes from BASE64
+ *
+ * @param data data to decode
+ * @return decoded data
+ */
+ public static byte[] decodeBase64(String data) {
+ return BASE64_DECODER.decode(data);
+ }
+
+ private static String encode(String password, byte[] salt) {
+ KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 27500, 512);
+ try {
+ byte[] key = PBKDF2_FACTORY_TL.get().generateSecret(spec).getEncoded();
+ return Base64.encodeBase64String(key);
+ } catch (InvalidKeySpecException e) {
+ throw new RuntimeException("Credential could not be encoded", e);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static MessageDigest getDigest(String algo) {
+ try {
+ return MessageDigest.getInstance(algo);
+ } catch (java.security.NoSuchAlgorithmException e) {
+ // Should not happen for standard algorithms
+ throw new IllegalStateException(algo + " not supported", e);
+ }
+ }
}
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/HMACUtils2.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/HMACUtils2.java
index 38505242f80..9c27f1134b4 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/HMACUtils2.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/HMACUtils2.java
@@ -1,5 +1,5 @@
/**
- *
+ *
*/
package io.mosip.kernel.core.util;
@@ -18,156 +18,185 @@
* This class defines the alternate safer HMAC Util to be used in MOSIP Project.
* The HMAC Util is implemented using desired methods of MessageDigest class of
* java security package
- *
+ *
* @author Sasikumar Ganesan
- *
+ *
* @since 1.1.4
*/
public final class HMACUtils2 {
- /**
- * SHA-256 Algorithm
- */
- private static final String HASH_ALGORITHM_NAME = "SHA-256";
-
- // lookup array for converting byte to hex
- private static final char[] LOOKUP_TABLE_LOWER = new char[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
- 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 };
- private static final char[] LOOKUP_TABLE_UPPER = new char[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
- 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 };
-
- /**
- * Performs a digest using the specified array of bytes.
- *
- * @param bytes bytes to be hash generation
- * @return byte[] generated hash bytes
- */
- public static byte[] generateHash(final byte[] bytes) throws NoSuchAlgorithmException {
- MessageDigest messageDigest = MessageDigest.getInstance(HASH_ALGORITHM_NAME);
- return messageDigest.digest(bytes);
- }
-
- /**
- * Return the digest as a plain text with Salt
- *
- * @param bytes digest bytes
- * @param salt digest bytes
- * @return String converted digest as plain text
- * @throws java.security.NoSuchAlgorithmException
- */
- public static String digestAsPlainTextWithSalt(final byte[] password, final byte[] salt)
- throws NoSuchAlgorithmException {
- MessageDigest messageDigest = MessageDigest.getInstance(HASH_ALGORITHM_NAME);
- messageDigest.update(password);
- messageDigest.update(salt);
- return encodeBytesToHex(messageDigest.digest(), true, ByteOrder.BIG_ENDIAN);
- }
-
- /**
- * Return the digest as a plain text
- *
- * @param bytes digest bytes
- * @return String converted digest as plain text
- * @throws NoSuchAlgorithmException
- */
- public static String digestAsPlainText(final byte[] bytes) throws NoSuchAlgorithmException {
- return encodeBytesToHex(generateHash(bytes), true, ByteOrder.BIG_ENDIAN);
- }
-
- /**
- * Generate Random Salt (with default 16 bytes of length).
- *
- * @return Random Salt
- */
- public static byte[] generateSalt() {
- return generateSalt(16);
- }
-
- /**
- * Generate Random Salt (with given length)
- *
- * @param bytes length of random salt
- * @return Random Salt of given length
- */
- public static byte[] generateSalt(int bytes) {
- SecureRandom random = new SecureRandom();
- byte[] randomBytes = new byte[bytes];
- random.nextBytes(randomBytes);
- return randomBytes;
- }
-
- /**
- * Encodes to BASE64 String
- *
- * @param data data to encode
- * @return encoded data
- */
- public static String encodeBase64String(byte[] data) {
- return Base64.getEncoder().encodeToString(data);
- }
-
- /**
- * Decodes from BASE64
- *
- * @param data data to decode
- * @return decoded data
- */
- public static byte[] decodeBase64(String data) {
- return Base64.getDecoder().decode(data);
- }
-
- /*
- * No object initialization.
- */
- private HMACUtils2() {
- }
-
- private static String encode(String password, byte[] salt) {
- int iterationCount = 27500; // default it has to be higher than this if you want to override
- if (System.getenv("hashiteration") != null) {
- String envCount = System.getenv("hashiteration");
- if (Integer.parseInt(envCount) > iterationCount) {
- iterationCount = Integer.parseInt(envCount);
- }
- }
- KeySpec spec = new PBEKeySpec(password.toCharArray(), Base64.getDecoder().decode(salt), iterationCount, 512);
-
- try {
- byte[] key = getSecretKeyFactory().generateSecret(spec).getEncoded();
- return Base64.getEncoder().encodeToString(key);
- } catch (InvalidKeySpecException e) {
- throw new RuntimeException("Credential could not be encoded", e);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- private static SecretKeyFactory getSecretKeyFactory() throws java.security.NoSuchAlgorithmException {
- try {
- return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("PBKDF2 algorithm not found", e);
- }
- }
-
- public static String encodeBytesToHex(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {
-
- // our output size will be exactly 2x byte-array length
- final char[] buffer = new char[byteArray.length * 2];
-
- // choose lower or uppercase lookup table
- final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;
-
- int index;
- for (int i = 0; i < byteArray.length; i++) {
- // for little endian we count from last to first
- index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;
-
- // extract the upper 4 bit and look up char (0-A)
- buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
- // extract the lower 4 bit and look up char (0-A)
- buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
- }
- return new String(buffer);
- }
-
-}
+ /**
+ * SHA-256 Algorithm
+ */
+ private static final String HASH_ALGORITHM_NAME = "SHA-256";
+
+ // lookup array for converting byte to hex
+ private static final char[] LOOKUP_TABLE_LOWER = new char[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 };
+ private static final char[] LOOKUP_TABLE_UPPER = new char[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 };
+
+ // Thread-local digests (MessageDigest is NOT thread-safe)
+ private static final ThreadLocal SHA256_TL = ThreadLocal.withInitial(() -> getDigest(HASH_ALGORITHM_NAME));
+ private static final ThreadLocal SECURE_RANDOM =
+ ThreadLocal.withInitial(SecureRandom::new);
+
+ private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
+ private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
+
+ private static final ThreadLocal PBKDF2_FACTORY =
+ ThreadLocal.withInitial(() -> {
+ try {
+ return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+ } catch (io.mosip.kernel.core.exception.NoSuchAlgorithmException | java.security.NoSuchAlgorithmException e) {
+ throw new RuntimeException("PBKDF2 algorithm not found", e);
+ }
+ });
+
+ private static final int DEFAULT_ITERATION_COUNT = 27500;
+ private static final int ITERATION_COUNT;
+
+ static {
+ int envCount = DEFAULT_ITERATION_COUNT;
+ String envValue = System.getenv("hashiteration");
+ if (envValue != null) {
+ try {
+ int parsed = Integer.parseInt(envValue);
+ if (parsed > DEFAULT_ITERATION_COUNT) {
+ envCount = parsed;
+ }
+ } catch (NumberFormatException ignored) {
+ // keep default if invalid
+ }
+ }
+ ITERATION_COUNT = envCount;
+ }
+
+ /*
+ * No object initialization.
+ */
+ private HMACUtils2() {
+ }
+
+ /**
+ * Performs a digest using the specified array of bytes.
+ *
+ * @param bytes bytes to be hash generation
+ * @return byte[] generated hash bytes
+ */
+ public static byte[] generateHash(final byte[] bytes) throws NoSuchAlgorithmException {
+ final MessageDigest messageDigest = SHA256_TL.get();
+ return messageDigest.digest(bytes);
+ }
+
+ /**
+ * Return the digest as a plain text with Salt
+ *
+ * @param pwd digest bytes
+ * @param salt digest bytes
+ * @return String converted digest as plain text
+ * @throws java.security.NoSuchAlgorithmException
+ */
+ public static String digestAsPlainTextWithSalt(final byte[] pwd, final byte[] salt)
+ throws NoSuchAlgorithmException {
+ final MessageDigest messageDigest = SHA256_TL.get();
+ messageDigest.update(pwd);
+ messageDigest.update(salt);
+ return encodeBytesToHex(messageDigest.digest(), true, ByteOrder.BIG_ENDIAN);
+ }
+
+ /**
+ * Return the digest as a plain text
+ *
+ * @param bytes digest bytes
+ * @return String converted digest as plain text
+ * @throws NoSuchAlgorithmException
+ */
+ public static String digestAsPlainText(final byte[] bytes) throws NoSuchAlgorithmException {
+ return encodeBytesToHex(generateHash(bytes), true, ByteOrder.BIG_ENDIAN);
+ }
+
+ /**
+ * Generate Random Salt (with default 16 bytes of length).
+ *
+ * @return Random Salt
+ */
+ public static byte[] generateSalt() {
+ return generateSalt(16);
+ }
+
+ /**
+ * Generate Random Salt (with given length)
+ *
+ * @param bytes length of random salt
+ * @return Random Salt of given length
+ */
+ public static byte[] generateSalt(int bytes) {
+ byte[] randomBytes = new byte[bytes];
+ SECURE_RANDOM.get().nextBytes(randomBytes);
+ return randomBytes;
+ }
+
+ /**
+ * Encodes to BASE64 String
+ *
+ * @param data data to encode
+ * @return encoded data
+ */
+ public static String encodeBase64String(byte[] data) {
+ return BASE64_ENCODER.encodeToString(data);
+ }
+
+ /**
+ * Decodes from BASE64
+ *
+ * @param data data to decode
+ * @return decoded data
+ */
+ public static byte[] decodeBase64(String data) {
+ return BASE64_DECODER.decode(data);
+ }
+
+
+ private static String encode(String password, byte[] salt) {
+ KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATION_COUNT, 512);
+ try {
+ byte[] key = PBKDF2_FACTORY.get().generateSecret(spec).getEncoded();
+ return encodeBase64String(key);
+ } catch (InvalidKeySpecException e) {
+ throw new RuntimeException("Credential could not be encoded", e);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String encodeBytesToHex(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {
+ final int len = byteArray.length;
+
+ // our output size will be exactly 2x byte-array length
+ final char[] buffer = new char[len * 2];
+
+ // choose lower or uppercase lookup table
+ final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;
+
+ int index;
+ for (int i = 0; i < len; i++) {
+ // for little endian we count from last to first
+ index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : len - i - 1;
+
+ // extract the upper 4 bit and look up char (0-A)
+ buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
+ // extract the lower 4 bit and look up char (0-A)
+ buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
+ }
+ return new String(buffer);
+ }
+
+ private static MessageDigest getDigest(String algo) {
+ try {
+ return MessageDigest.getInstance(algo);
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen for standard algorithms
+ throw new IllegalStateException(algo + " not supported", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/MathUtils.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/MathUtils.java
index 981a3b1395a..bb62e144205 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/MathUtils.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/MathUtils.java
@@ -42,7 +42,7 @@
/**
* Utilities for Mathematical operations.
- *
+ *
* @author Ritesh Sinha
* @since 1.0.0
*/
@@ -57,7 +57,7 @@ private MathUtils() {
/**
* Raise an int to an int power.
- *
+ *
* @param num Number to raise.
* @param exp exponent (must be positive or zero)
* @return num^exp
@@ -69,12 +69,9 @@ public static int getPow(final int num, int exp) {
return ArithmeticUtils.pow(num, exp);
} catch (org.apache.commons.math3.exception.NotPositiveException e) {
-
throw new NotPositiveException(MathUtilConstants.NOTPOSITIVE_ERROR_CODE.getErrorCode(),
MathUtilConstants.NOTPOSITIVE_ERROR_CODE.getEexceptionMessage(), e.getCause());
-
} catch (MathArithmeticException e) {
-
throw new ArithmeticException(MathUtilConstants.ARITHMETIC_ERROR_CODE.getErrorCode(),
MathUtilConstants.ARITHMETIC_ERROR_CODE.getEexceptionMessage(), e.getCause());
}
@@ -82,20 +79,18 @@ public static int getPow(final int num, int exp) {
/**
* Power function. Compute num^exp.
- *
+ *
* @param num a double
* @param exp a double
* @return double
*/
public static double getPow(double num, double exp) {
-
return FastMath.pow(num, exp);
-
}
/**
* Raise a long to an int power.
- *
+ *
* @param num Number to raise.
* @param exp Exponent (must be positive or zero).
* @return num^exp
@@ -135,7 +130,7 @@ public static BigInteger getPow(BigInteger num, int exp) {
/**
* Raise a BigInteger to a BigInteger power.
- *
+ *
* @param num Number to raise.
* @param exp Exponent (must be positive or zero).
* @return num^exp
@@ -155,7 +150,7 @@ public static BigInteger getPow(BigInteger num, BigInteger exp) {
/**
* Generate random number
- *
+ *
* @param lowerlimit starting value
* @param upperlimit maximum value
* @return random number between lowerlimit and upperlimit.
@@ -173,7 +168,7 @@ public static int getRandom(final int lowerlimit, final int upperlimit) {
/**
* Generate random number
- *
+ *
* @param lowerlimit starting value
* @param upperlimit maximum value
* @return random number between lowerlimit and upperlimit.
@@ -190,7 +185,7 @@ public static long getRandom(final long lowerlimit, final long upperlimit) {
/**
* Generate random number
- *
+ *
* @param lowerlimit starting value
* @param upperlimit maximum value
* @return random number between lowerlimit and upperlimit
@@ -215,133 +210,132 @@ public static double getRandom(final double lowerlimit, final double upperlimit)
/**
* Generate random number
- *
+ *
* @param lowerlimit starting value
* @param upperlimit maximum value
* @return random number between lowerlimit and upperlimit
*/
public static float getRandom(final float lowerlimit, final float upperlimit) {
-
float randomFloat = new RandomDataGenerator().getRandomGenerator().nextFloat();
return lowerlimit + randomFloat * (upperlimit - lowerlimit);
}
/**
* Compute the square root of a number.
- *
+ *
* @param num number on which evaluation is done
* @return square root of num.
*/
- public static final double getSqrt(final double num) {
+ public static double getSqrt(final double num) {
return FastMath.sqrt(num);
}
/**
* Compute the maximum of two values
- *
+ *
* @param firstnumber first value
* @param secondnumber second value
* @return secondnumber if firstnumber is lesser or equal to secondnumber,
- * firstnumber otherwise
+ * firstnumber otherwise
*/
- public static final int getMax(final int firstnumber, final int secondnumber) {
+ public static int getMax(final int firstnumber, final int secondnumber) {
return FastMath.max(firstnumber, secondnumber);
}
/**
* Compute the maximum of two values
- *
+ *
* @param firstnumber first value
* @param secondnumber second value
* @return secondnumber if firstnumber is lesser or equal to secondnumber,
- * firstnumber otherwise
+ * firstnumber otherwise
*/
- public static final double getMax(double firstnumber, double secondnumber) {
+ public static double getMax(double firstnumber, double secondnumber) {
return FastMath.max(firstnumber, secondnumber);
}
/**
* Rounds the given value to the specified number of decimal places.
- *
+ *
* @param num Value to round.
* @param place Number of digits to the right of the decimal point.
* @return the rounded value.
*/
- public static final double getRound(double num, int place) {
+ public static double getRound(double num, int place) {
return Precision.round(num, place);
}
/**
* Rounds the given value to the specified number of decimal places.
- *
+ *
* @param num Value to round.
* @param place Number of digits to the right of the decimal point.
* @return the rounded value.
*/
- public static final float getRound(float num, int place) {
+ public static float getRound(float num, int place) {
return Precision.round(num, place);
}
/**
* Get the closest long to num.
- *
+ *
* @param num number from which closest long is requested
* @return closest long to num
*/
- public static final long getRound(double num) {
+ public static long getRound(double num) {
return FastMath.round(num);
}
/**
* Absolute value.
- *
+ *
* @param num number from which absolute value is requested.
* @return abs(num)
*/
- public static final double getAbs(double num) {
+ public static double getAbs(double num) {
return FastMath.abs(num);
}
/**
* Absolute value.
- *
+ *
* @param num number from which absolute value is requested.
* @return abs(num)
*/
- public static final float getAbs(float num) {
+ public static float getAbs(float num) {
return FastMath.abs(num);
}
/**
* Absolute value.
- *
+ *
* @param num number from which absolute value is requested.
* @return abs(num)
*/
- public static final long getAbs(long num) {
+ public static long getAbs(long num) {
return FastMath.abs(num);
}
/**
* Absolute value.
- *
+ *
* @param num number from which absolute value is requested.
* @return abs(num)
*/
- public static final int getAbs(int num) {
+ public static int getAbs(int num) {
return FastMath.abs(num);
}
/**
* Evaluate Factorial
- *
+ *
* @param number argument
* @return n!
* @throws NotPositiveException -If number is not positive
* @throws ArithmeticException -If number is greator than 20 and result is too
* large to fit in long type.
*/
- public static final long getFactorial(final int number) {
+ public static long getFactorial(final int number) {
try {
return CombinatoricsUtils.factorial(number);
} catch (org.apache.commons.math3.exception.NotPositiveException e) {
@@ -356,14 +350,14 @@ public static final long getFactorial(final int number) {
/**
* Computes the greatest common divisor of the absolute value of two numbers,
* using a modified version of the "binary gcd" method.
- *
+ *
* @param firstnumber Number.
* @param secondnumber Number.
* @return the greatest common divisor (never negative).
* @throws ArithmeticException if the result cannot be represented as a
* non-negative integer value.
*/
- public static final int getGcd(int firstnumber, int secondnumber) {
+ public static int getGcd(int firstnumber, int secondnumber) {
try {
return ArithmeticUtils.gcd(firstnumber, secondnumber);
} catch (MathArithmeticException e) {
@@ -375,14 +369,14 @@ public static final int getGcd(int firstnumber, int secondnumber) {
/**
* Gets the greatest common divisor of the absolute value of two numbers, using
* the "binary gcd" method which avoids division and modulo operations
- *
+ *
* @param firstnumber Number.
* @param secondnumber Number.
* @return the greatest common divisor, never negative.
* @throws ArithmeticException if the result cannot be represented as a
* non-negative long type value.
*/
- public static final long getGcd(long firstnumber, long secondnumber) {
+ public static long getGcd(long firstnumber, long secondnumber) {
try {
return ArithmeticUtils.gcd(firstnumber, secondnumber);
} catch (MathArithmeticException e) {
@@ -393,37 +387,39 @@ public static final long getGcd(long firstnumber, long secondnumber) {
/**
* Natural logarithm.
- *
+ *
* @param num a double
* @return log(x)
*/
- public static final double getLog(double num) {
+ public static double getLog(double num) {
return FastMath.log(num);
}
/**
* Compute the base 10 logarithm.
- *
+ *
* @param num a number
* @return log10(x)
*/
- public static final double getLog10(double num) {
+ public static double getLog10(double num) {
return FastMath.log10(num);
}
/**
* Creates a copy of source array
- *
+ *
* @param source Array to be copied.
* @return the copied array.
*/
public static int[] getCopyOfArray(int[] source) {
- return MathArrays.copyOf(source);
+ int[] copy = new int[source.length];
+ System.arraycopy(source, 0, copy, 0, source.length);
+ return copy;
}
/**
* Creates a copy of source array.
- *
+ *
* @param source Array to be copied.
* @param length Number of entries to copy. If smaller then the source length,
* the copy will be truncated, if larger it will padded with
@@ -431,22 +427,26 @@ public static int[] getCopyOfArray(int[] source) {
* @return the copied array.
*/
public static int[] getCopyOfArray(int[] source, int length) {
- return MathArrays.copyOf(source, length);
+ int[] copy = new int[length];
+ System.arraycopy(source, 0, copy, 0, Math.min(source.length, length));
+ return copy;
}
/**
* Creates a copy of source array
- *
+ *
* @param source Array to be copied.
* @return the copied array.
*/
public static double[] getCopyOfArray(double[] source) {
- return MathArrays.copyOf(source);
+ double[] copy = new double[source.length];
+ System.arraycopy(source, 0, copy, 0, source.length);
+ return copy;
}
/**
* Creates a copy of source array
- *
+ *
* @param source Array to be copied.
* @param length Number of entries to copy. If smaller then the source length,
* the copy will be truncated, if larger it will padded with
@@ -454,13 +454,15 @@ public static double[] getCopyOfArray(double[] source) {
* @return the copied array.
*/
public static double[] getCopyOfArray(double[] source, int length) {
- return MathArrays.copyOf(source, length);
+ double[] copy = new double[length];
+ System.arraycopy(source, 0, copy, 0, Math.min(source.length, length));
+ return copy;
}
/**
* Returns the maximum of the entries in the input array, or
* Double.NaN if the array is empty.
- *
+ *
* @param arr the input array
* @return the maximum of the values or Double.NaN if the array is empty
* @throws IllegalArgumentException if the array is null
@@ -477,7 +479,7 @@ public static double getArrayMaxValue(double[] arr) {
/**
* Returns the maximum of the entries in the specified portion of the input
* array, or Double.NaN if the designated subarray is empty
- *
+ *
* @param arr the input array
* @param startindex index of the first array element to include
* @param length the number of elements to include
@@ -497,7 +499,7 @@ public static double getArrayMaxValue(double[] arr, int startindex, int length)
/**
* Returns the minimum of the entries in the input array, or
* Double.NaN if the array is empty.
- *
+ *
* @param arr the input array
* @return the minimum of the values or Double.NaN if the array is empty
* @throws IllegalArgumentException if the array is null
@@ -514,7 +516,7 @@ public static double getArrayMinValue(double[] arr) {
/**
* Returns the minimum of the entries in the specified portion of the input
* array, or Double.NaN if the designated subarray is empty.
- *
+ *
* @param arr the input array
* @param startindex index of the first array element to include
* @param length the number of elements to include
@@ -529,16 +531,15 @@ public static double getArrayMinValue(double[] arr, int startindex, int length)
throw new IllegalArgumentException(MathUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
MathUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
}
-
}
/**
* Returns the sum of the values in the input array, or Double.NaN
* if the array is empty.
- *
+ *
* @param arr array of values to sum
* @return the sum of the values or Double.NaN if the array is
- * empty
+ * empty
* @throws IllegalArgumentException if the array is null
*/
public static double getArrayValuesSum(double[] arr) {
@@ -548,13 +549,12 @@ public static double getArrayValuesSum(double[] arr) {
throw new IllegalArgumentException(MathUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
MathUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
}
-
}
/**
* Returns the sum of the entries in the specified portion of the input array,
* or Double.NaN if the designated subarray is empty.
- *
+ *
* @param arr the input array
* @param startindex index of the first array element to include
* @param length the number of elements to include
@@ -569,13 +569,12 @@ public static double getArrayValuesSum(double[] arr, int startindex, int length)
throw new IllegalArgumentException(MathUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getErrorCode(),
MathUtilConstants.ILLEGALARGUMENT_ERROR_CODE.getEexceptionMessage(), e.getCause());
}
-
}
/**
* Returns the product of the entries in the input array, or
* Double.NaN if the array is empty.
- *
+ *
* @param arr the input array
* @return the product of the values or Double.NaN if the array is empty
* @throws IllegalArgumentException if the array is null
@@ -592,7 +591,7 @@ public static double getArrayValuesProduct(double[] arr) {
/**
* Returns the product of the entries in the specified portion of the input
* array, or Double.NaN if the designated subarray is empty.
- *
+ *
* @param arr the input array
* @param startindex index of the first array element to include
* @param length the number of elements to include
@@ -612,7 +611,7 @@ public static double getArrayValuesProduct(double[] arr, int startindex, int len
/**
* Returns the arithmetic mean of the entries in the input array, or
* Double.NaN if the array is empty.
- *
+ *
* @param arr the input array
* @return the mean of the values or Double.NaN if the array is empty
* @throws IllegalArgumentException if the array is null
@@ -645,7 +644,7 @@ public static double getArrayValuesMean(double[] arr, int startindex, int length
/**
* Primality test: tells if the argument is a (provable) prime or not.
- *
+ *
* @param num number to test.
* @return true if num is prime.
*/
@@ -655,7 +654,7 @@ public static boolean isPrimeOrNot(int num) {
/**
* Return the smallest prime greater than or equal to n.
- *
+ *
* @param num a positive number.
* @return the smallest prime greater than or equal to n.
* @throws IllegalArgumentException if num is less than 0.
@@ -671,7 +670,7 @@ public static int nextPrimeNumber(int num) {
/**
* Prime factors decomposition
- *
+ *
* @param num number to factorize: must be ≥ 2
* @return list of prime factors of num
* @throws IllegalArgumentException if num is less than 2.
@@ -687,10 +686,10 @@ public static List getPrimeFactors(int num) {
/**
* Get the smallest whole number larger than number.
- *
+ *
* @param num number from which ceil is requested
* @return a double number c such that c is an integer is greator than equal to
- * num and num is greator than c-1.0
+ * num and num is greator than c-1.0
*/
public static double ceil(double num) {
return FastMath.ceil(num);
@@ -698,13 +697,12 @@ public static double ceil(double num) {
/**
* Get the largest whole number smaller than number.
- *
+ *
* @param num number from which floor is requested
* @return a double number f such that f is an integer f is less than or equal
- * to num and num is less than f + 1.0
+ * to num and num is less than f + 1.0
*/
public static double floor(double num) {
return FastMath.floor(num);
}
-
-}
+}
\ No newline at end of file
diff --git a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/UUIDUtils.java b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/UUIDUtils.java
index 64cb0373a92..326231776f2 100644
--- a/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/UUIDUtils.java
+++ b/kernel/kernel-core/src/main/java/io/mosip/kernel/core/util/UUIDUtils.java
@@ -1,6 +1,7 @@
package io.mosip.kernel.core.util;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;
@@ -8,18 +9,29 @@
/**
* This class is used to generate UUID of Type 5.
- *
+ *
* @author Bal Vikash Sharma
*
*/
public class UUIDUtils {
- private static final Charset UTF8 = Charset.forName("UTF-8");
+ private static final Charset UTF8 = StandardCharsets.UTF_8;
+
+ // RFC 4122 namespaces
public static final UUID NAMESPACE_DNS = UUID.fromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8");
public static final UUID NAMESPACE_URL = UUID.fromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
public static final UUID NAMESPACE_OID = UUID.fromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8");
public static final UUID NAMESPACE_X500 = UUID.fromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8");
+ // Precompute namespace bytes once (hot path win)
+ private static final byte[] NS_DNS_BYTES = toBytes(NAMESPACE_DNS);
+ private static final byte[] NS_URL_BYTES = toBytes(NAMESPACE_URL);
+ private static final byte[] NS_OID_BYTES = toBytes(NAMESPACE_OID);
+ private static final byte[] NS_X500_BYTES = toBytes(NAMESPACE_X500);
+
+ // Thread-local digests (MessageDigest is NOT thread-safe)
+ private static final ThreadLocal SHA256_TL = ThreadLocal.withInitial(() -> getDigest("SHA-256"));
+
private UUIDUtils() {
super();
}
@@ -27,7 +39,7 @@ private UUIDUtils() {
/**
* This method takes UUID namespace and a name and
* generate Type 5 UUID.
- *
+ *
* @param namespace is the {@link UUID}
* @param name for which UUID needs to be generated.
* @return type 5 UUID as per given namespace and name
@@ -39,27 +51,26 @@ public static UUID getUUID(UUID namespace, String name) {
}
/**
- *
+ *
* This method takes UUID namespace and a name as a
* byte array and generate Type 5 UUID.
- *
+ *
* @param namespace is the {@link UUID}
* @param name is a byte array
* @return type 5 UUID as per given namespace and name
- *
+ *
* @throws NullPointerException when either namespace or
* name is null.
*/
public static UUID getUUIDFromBytes(UUID namespace, byte[] name) {
- MessageDigest md;
- try {
- md = MessageDigest.getInstance("SHA-256");
- } catch (NoSuchAlgorithmException nsae) {
- throw new InternalError("SHA-256 not supported");
- }
- md.update(toBytes(Objects.requireNonNull(namespace, "namespace is null")));
- md.update(Objects.requireNonNull(name, "name is null"));
- byte[] sha1Bytes = md.digest();
+ if (namespace == null) throw new NullPointerException("namespace is null");
+ if (name == null) throw new NullPointerException("name is null");
+
+ final MessageDigest md = SHA256_TL.get();
+ md.reset();
+ md.update(toBytes(namespace));
+ md.update(name);
+ byte[] sha1Bytes = md.digest(); // 32 bytes
sha1Bytes[6] &= 0x0f; /* clear version */
sha1Bytes[6] |= 0x50; /* set to version 5 */
sha1Bytes[8] &= 0x3f; /* clear variant */
@@ -91,4 +102,21 @@ private static byte[] toBytes(UUID uuid) {
return out;
}
+ private static byte[] fastNamespaceBytes(UUID ns) {
+ // Identity compares are fine; constants are interned singletons
+ if (ns == NAMESPACE_DNS) return NS_DNS_BYTES;
+ if (ns == NAMESPACE_URL) return NS_URL_BYTES;
+ if (ns == NAMESPACE_OID) return NS_OID_BYTES;
+ if (ns == NAMESPACE_X500) return NS_X500_BYTES;
+ return null;
+ }
+
+ private static MessageDigest getDigest(String algo) {
+ try {
+ return MessageDigest.getInstance(algo);
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen for standard algorithms
+ throw new IllegalStateException(algo + " not supported", e);
+ }
+ }
}
diff --git a/kernel/kernel-dataaccess-hibernate/pom.xml b/kernel/kernel-dataaccess-hibernate/pom.xml
index 6f4b0e36ea3..ff64ef819b4 100644
--- a/kernel/kernel-dataaccess-hibernate/pom.xml
+++ b/kernel/kernel-dataaccess-hibernate/pom.xml
@@ -6,7 +6,7 @@
4.0.0
io.mosip.kernel
kernel-dataaccess-hibernate
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
@@ -23,7 +23,7 @@
3.2.0
2.3
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -31,7 +31,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-datamapper-orika/pom.xml b/kernel/kernel-datamapper-orika/pom.xml
index 05f935603a0..744597ee3e5 100644
--- a/kernel/kernel-datamapper-orika/pom.xml
+++ b/kernel/kernel-datamapper-orika/pom.xml
@@ -7,7 +7,7 @@
io.mosip.kernel
kernel-datamapper-orika
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-datamapper-orika
http://maven.apache.org
Mosip commons project
@@ -18,7 +18,7 @@
21
21
3.8.0
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
1.5.2
diff --git a/kernel/kernel-demographics-api/pom.xml b/kernel/kernel-demographics-api/pom.xml
index 6234535b046..5750d57d97c 100644
--- a/kernel/kernel-demographics-api/pom.xml
+++ b/kernel/kernel-demographics-api/pom.xml
@@ -5,7 +5,7 @@
io.mosip.kernel
kernel-demographics-api
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-demographics-api
demographics api definitions
https://github.com/mosip/commons
@@ -113,7 +113,7 @@
1.4.2
UTF-8
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -121,7 +121,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-idgenerator-machineid/pom.xml b/kernel/kernel-idgenerator-machineid/pom.xml
index b2878598f67..3ba2bc58a19 100644
--- a/kernel/kernel-idgenerator-machineid/pom.xml
+++ b/kernel/kernel-idgenerator-machineid/pom.xml
@@ -5,16 +5,16 @@
4.0.0
io.mosip.kernel
kernel-idgenerator-machineid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -22,7 +22,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-idgenerator-mispid/pom.xml b/kernel/kernel-idgenerator-mispid/pom.xml
index 0d83eaa57cd..45632f18351 100644
--- a/kernel/kernel-idgenerator-mispid/pom.xml
+++ b/kernel/kernel-idgenerator-mispid/pom.xml
@@ -7,7 +7,7 @@
io.mosip.kernel
kernel-idgenerator-mispid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-idgenerator-mispid
Mosip commons project
https://github.com/mosip/commons
@@ -16,9 +16,9 @@
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -26,7 +26,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-idgenerator-partnerid/pom.xml b/kernel/kernel-idgenerator-partnerid/pom.xml
index 5ab4b66163f..d5711b86f63 100644
--- a/kernel/kernel-idgenerator-partnerid/pom.xml
+++ b/kernel/kernel-idgenerator-partnerid/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip.kernel
kernel-idgenerator-partnerid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-idgenerator-partnerid
Mosip commons project
http://maven.apache.org
@@ -14,9 +14,9 @@
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -24,7 +24,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-idgenerator-prid/pom.xml b/kernel/kernel-idgenerator-prid/pom.xml
index 1cfcd732f39..98c84ec62cd 100644
--- a/kernel/kernel-idgenerator-prid/pom.xml
+++ b/kernel/kernel-idgenerator-prid/pom.xml
@@ -12,10 +12,10 @@
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -23,14 +23,14 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
kernel-idgenerator-prid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
io.mosip.kernel
diff --git a/kernel/kernel-idgenerator-regcenterid/pom.xml b/kernel/kernel-idgenerator-regcenterid/pom.xml
index 4e6d06687e2..e6ddd1fa2c6 100644
--- a/kernel/kernel-idgenerator-regcenterid/pom.xml
+++ b/kernel/kernel-idgenerator-regcenterid/pom.xml
@@ -10,11 +10,11 @@
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -22,14 +22,14 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
kernel-idgenerator-regcenterid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
io.mosip.kernel
diff --git a/kernel/kernel-idgenerator-rid/pom.xml b/kernel/kernel-idgenerator-rid/pom.xml
index d72a7d5b051..fb9e725e543 100644
--- a/kernel/kernel-idgenerator-rid/pom.xml
+++ b/kernel/kernel-idgenerator-rid/pom.xml
@@ -8,15 +8,15 @@
io.mosip.kernel
kernel-idgenerator-rid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -24,7 +24,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-idgenerator-service/pom.xml b/kernel/kernel-idgenerator-service/pom.xml
index 1f17d8cac9b..9d0e8e517cd 100644
--- a/kernel/kernel-idgenerator-service/pom.xml
+++ b/kernel/kernel-idgenerator-service/pom.xml
@@ -13,14 +13,14 @@
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
1.3.2
3.4.1
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
2.3.7
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
**/constant/**,**/config/**,**/httpfilter/**,**/cache/**,**/entity/**,**/model/**,**/exception/**,**/repository/**,**/verticle/**,**/spi/**,"**/proxy/**","**/entities/**","**/filter/**","**/util/**","**/verifier/**","**/IDGeneratorVertxApplication.java"
@@ -29,14 +29,14 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
kernel-idgenerator-service
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
io.mosip.kernel
diff --git a/kernel/kernel-idgenerator-tokenid/pom.xml b/kernel/kernel-idgenerator-tokenid/pom.xml
index 533740913e0..83a5176ebdf 100644
--- a/kernel/kernel-idgenerator-tokenid/pom.xml
+++ b/kernel/kernel-idgenerator-tokenid/pom.xml
@@ -6,13 +6,13 @@
4.0.0
io.mosip.kernel
kernel-idgenerator-tokenid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -20,7 +20,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-idgenerator-vid/pom.xml b/kernel/kernel-idgenerator-vid/pom.xml
index feddc512ca7..7cb816a7b7a 100644
--- a/kernel/kernel-idgenerator-vid/pom.xml
+++ b/kernel/kernel-idgenerator-vid/pom.xml
@@ -4,15 +4,15 @@
4.0.0
io.mosip.kernel
kernel-idgenerator-vid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
diff --git a/kernel/kernel-idobjectvalidator/pom.xml b/kernel/kernel-idobjectvalidator/pom.xml
index 611c9d9e5de..48b17d0251b 100644
--- a/kernel/kernel-idobjectvalidator/pom.xml
+++ b/kernel/kernel-idobjectvalidator/pom.xml
@@ -3,7 +3,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
kernel-idobjectvalidator
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
io.mosip.kernel
UTF-8
@@ -11,7 +11,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
2.2.14
@@ -21,7 +21,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-idvalidator-mispid/pom.xml b/kernel/kernel-idvalidator-mispid/pom.xml
index 6faedb30d3e..c0581a76655 100644
--- a/kernel/kernel-idvalidator-mispid/pom.xml
+++ b/kernel/kernel-idvalidator-mispid/pom.xml
@@ -4,14 +4,14 @@
4.0.0
io.mosip.kernel
kernel-idvalidator-mispid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
diff --git a/kernel/kernel-idvalidator-prid/pom.xml b/kernel/kernel-idvalidator-prid/pom.xml
index 3f3ade1b849..6a85fa12097 100644
--- a/kernel/kernel-idvalidator-prid/pom.xml
+++ b/kernel/kernel-idvalidator-prid/pom.xml
@@ -11,11 +11,11 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
kernel-idvalidator-prid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-idvalidator-prid
diff --git a/kernel/kernel-idvalidator-rid/pom.xml b/kernel/kernel-idvalidator-rid/pom.xml
index d0a086b9a2d..18236eab451 100644
--- a/kernel/kernel-idvalidator-rid/pom.xml
+++ b/kernel/kernel-idvalidator-rid/pom.xml
@@ -2,7 +2,7 @@
4.0.0
io.mosip.kernel
kernel-idvalidator-rid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-idvalidator-rid
kernel-idvalidator-rid
@@ -11,7 +11,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
diff --git a/kernel/kernel-idvalidator-uin/pom.xml b/kernel/kernel-idvalidator-uin/pom.xml
index 05e7d681002..317585480d8 100644
--- a/kernel/kernel-idvalidator-uin/pom.xml
+++ b/kernel/kernel-idvalidator-uin/pom.xml
@@ -2,7 +2,7 @@
4.0.0
io.mosip.kernel
kernel-idvalidator-uin
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-idvalidator-uin
kernel-idvalidator-uin
@@ -11,7 +11,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
diff --git a/kernel/kernel-idvalidator-vid/pom.xml b/kernel/kernel-idvalidator-vid/pom.xml
index b117351b822..150693b933a 100644
--- a/kernel/kernel-idvalidator-vid/pom.xml
+++ b/kernel/kernel-idvalidator-vid/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip.kernel
kernel-idvalidator-vid
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-idvalidator-vid
kernel-idvalidator-vid
@@ -13,7 +13,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
diff --git a/kernel/kernel-licensekeygenerator-misp/pom.xml b/kernel/kernel-licensekeygenerator-misp/pom.xml
index 6c91993598b..f2ab3cee166 100644
--- a/kernel/kernel-licensekeygenerator-misp/pom.xml
+++ b/kernel/kernel-licensekeygenerator-misp/pom.xml
@@ -6,14 +6,14 @@
4.0.0
io.mosip.kernel
kernel-licensekeygenerator-misp
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -21,7 +21,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-logger-logback/pom.xml b/kernel/kernel-logger-logback/pom.xml
index 030f375e566..53d897a3d60 100644
--- a/kernel/kernel-logger-logback/pom.xml
+++ b/kernel/kernel-logger-logback/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip.kernel
kernel-logger-logback
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
UTF-8
@@ -21,7 +21,7 @@
3.2.0
2.3
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -30,7 +30,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-notification-service/pom.xml b/kernel/kernel-notification-service/pom.xml
index a300ce2168e..770338185e7 100644
--- a/kernel/kernel-notification-service/pom.xml
+++ b/kernel/kernel-notification-service/pom.xml
@@ -8,7 +8,7 @@
kernel-notification-service
Mosip commons project
https://github.com/mosip/commons
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
io.mosip.kernel
UTF-8
@@ -18,10 +18,10 @@
21
3.8.0
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
1.5.10
@@ -30,7 +30,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/NotificationBootApplication.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/NotificationBootApplication.java
index b0e6e7522b9..36e220e7083 100644
--- a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/NotificationBootApplication.java
+++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/NotificationBootApplication.java
@@ -5,25 +5,44 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
+import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
/**
- * Mail notifier application
- *
+ * Mail Notifier Application
+ *
+ * This Spring Boot application is responsible for sending email notifications.
+ * with an asynchronous task executor to handle high-volume email delivery efficiently.
+ *
+ * Features:
+ *
+ * - Asynchronous email sending for improved performance
+ * - Thread pool configuration tuned for high throughput
+ * - Excludes database auto-config to run as a lightweight microservice
+ *
+ *
+ *
* @author Sagar Mahapatra
* @since 1.0.0
*
*/
-@SpringBootApplication(scanBasePackages = { "io.mosip.kernel.emailnotification.*", "${mosip.auth.adapter.impl.basepackage}",
- "io.mosip.kernel.core.logger.config", "io.mosip.kernel.smsserviceprovider.*" })
+@SpringBootApplication(scanBasePackages = {
+ "io.mosip.kernel.emailnotification.*",
+ "${mosip.auth.adapter.impl.basepackage}",
+ "io.mosip.kernel.core.logger.config",
+ "io.mosip.kernel.smsserviceprovider.*"
+})
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
@EnableAsync
public class NotificationBootApplication {
/**
- * Main method to run spring boot application
- *
- * @param args the argument
+ * Main method to start the Mail Notifier Application.
+ *
+ * @param args command-line arguments
*/
public static void main(String[] args) {
SpringApplication.run(NotificationBootApplication.class, args);
diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/MailExecutorConfig.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/MailExecutorConfig.java
new file mode 100644
index 00000000000..db3af226e49
--- /dev/null
+++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/MailExecutorConfig.java
@@ -0,0 +1,99 @@
+package io.mosip.kernel.emailnotification.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Mail Executor Configuration
+ *
+ *
+ * This configuration class defines a dedicated {@link Executor} bean for handling
+ * asynchronous email notification tasks in the MOSIP Kernel Email Notification service.
+ *
+ *
+ *
+ * The {@link ThreadPoolTaskExecutor} parameters are fully configurable through
+ * external properties, making it easier to scale and optimize performance
+ * across different deployment environments (e.g., 0.5 vCPU per pod vs multi-core setups).
+ *
+ *
+ * Property Mapping:
+ *
+ * - mail.executor.core-pool-size – Minimum number of threads maintained in the pool
+ * - mail.executor.max-pool-size – Maximum number of threads during peak load
+ * - mail.executor.queue-capacity – Size of the queue to hold pending email tasks
+ * - mail.executor.keep-alive-seconds – Time to keep idle threads alive before termination
+ * - mail.executor.await-termination-seconds – Grace period to wait for ongoing tasks before shutdown
+ * - mail.executor.thread-name-prefix – Custom prefix for naming worker threads for easier debugging
+ *
+ *
+ * Advantages:
+ *
+ * - Async execution prevents blocking the main application thread.
+ * - Dynamic configuration supports multiple environments without code changes.
+ * - Thread names allow easy tracking of active tasks in logs and thread dumps.
+ *
+ *
+ * Example Usage:
+ *
+ * {@code
+ * @Async("mailExecutor")
+ * public void sendEmail(...) {
+ * // email sending logic
+ * }
+ * }
+ *
+ *
+ * @author Janardhan B S
+ * @since 1.3.0
+ */
+@Configuration
+public class MailExecutorConfig {
+
+ @Value("${mail.executor.core-pool-size:2}")
+ private int corePoolSize;
+
+ @Value("${mail.executor.max-pool-size:3}")
+ private int maxPoolSize;
+
+ @Value("${mail.executor.queue-capacity:1000}")
+ private int queueCapacity;
+
+ @Value("${mail.executor.keep-alive-seconds:30}")
+ private int keepAliveSeconds;
+
+ @Value("${mail.executor.await-termination-seconds:30}")
+ private int awaitTerminationSeconds;
+
+ /**
+ * Prefix to name each thread in this executor.
+ * Helps in log tracing and debugging concurrent tasks.
+ * Default: MailSender-
+ */
+ @Value("${mail.executor.thread-name-prefix:MailSender-}")
+ private String threadNamePrefix;
+
+ /**
+ * Creates and configures a {@link ThreadPoolTaskExecutor} instance for
+ * asynchronous email task execution.
+ *
+ * @return a configured {@link Executor} bean named "mailExecutor"
+ */
+ @Bean(name = "mailExecutor")
+ public Executor mailExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(corePoolSize);
+ executor.setMaxPoolSize(maxPoolSize);
+ executor.setQueueCapacity(queueCapacity);
+ executor.setThreadNamePrefix(threadNamePrefix);
+ executor.setAllowCoreThreadTimeOut(true);
+ executor.setKeepAliveSeconds(keepAliveSeconds);
+ executor.setAwaitTerminationSeconds(awaitTerminationSeconds);
+ executor.initialize();
+ return executor;
+ }
+}
\ No newline at end of file
diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/SmsExecutorConfig.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/SmsExecutorConfig.java
new file mode 100644
index 00000000000..3d050682f1e
--- /dev/null
+++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/config/SmsExecutorConfig.java
@@ -0,0 +1,99 @@
+package io.mosip.kernel.emailnotification.config;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+import java.util.concurrent.Executor;
+
+/**
+ * SMS Executor Configuration
+ *
+ *
+ * This configuration class defines a dedicated {@link Executor} bean for handling
+ * asynchronous SMS notification tasks in the MOSIP Kernel Notification service.
+ *
+ *
+ *
+ * The {@link ThreadPoolTaskExecutor} parameters are fully configurable through
+ * external properties, making it easier to scale and optimize performance
+ * across different deployment environments (e.g., 0.5 vCPU per pod vs multi-core setups).
+ *
+ *
+ * Property Mapping:
+ *
+ * - sms.executor.core-pool-size – Minimum number of threads maintained in the pool
+ * - sms.executor.max-pool-size – Maximum number of threads during peak load
+ * - sms.executor.queue-capacity – Size of the queue to hold pending SMS tasks
+ * - sms.executor.keep-alive-seconds – Time to keep idle threads alive before termination
+ * - sms.executor.await-termination-seconds – Grace period to wait for ongoing tasks before shutdown
+ * - sms.executor.thread-name-prefix – Custom prefix for naming worker threads for easier debugging
+ *
+ *
+ * Advantages:
+ *
+ * - Dedicated executor prevents SMS and email tasks from competing for the same thread pool.
+ * - Async execution improves throughput and reduces latency for bulk SMS campaigns.
+ * - Dynamic configuration allows tuning per environment without code changes.
+ *
+ *
+ * Example Usage:
+ *
+ * {@code
+ * @Async("smsExecutor")
+ * public void sendSms(...) {
+ * // SMS sending logic
+ * }
+ * }
+ *
+ *
+ * @author Janardhan B S
+ * @since 1.3.0
+ */
+@Configuration
+public class SmsExecutorConfig {
+
+ @Value("${sms.executor.core-pool-size:1}")
+ private int corePoolSize;
+
+ @Value("${sms.executor.max-pool-size:2}")
+ private int maxPoolSize;
+
+ @Value("${sms.executor.queue-capacity:500}")
+ private int queueCapacity;
+
+ @Value("${sms.executor.keep-alive-seconds:20}")
+ private int keepAliveSeconds;
+
+ @Value("${sms.executor.await-termination-seconds:20}")
+ private int awaitTerminationSeconds;
+
+ /**
+ * Prefix to name each thread in this executor.
+ * Helps in log tracing and debugging concurrent tasks.
+ * Default: SmsSender-
+ */
+ @Value("${sms.executor.thread-name-prefix:SmsSender-}")
+ private String threadNamePrefix;
+
+ /**
+ * Creates and configures a {@link ThreadPoolTaskExecutor} instance for
+ * asynchronous SMS task execution.
+ *
+ * @return a configured {@link Executor} bean named "smsExecutor"
+ */
+ @Bean(name = "smsExecutor")
+ public Executor smsExecutor() {
+ ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
+ executor.setCorePoolSize(corePoolSize);
+ executor.setMaxPoolSize(maxPoolSize);
+ executor.setQueueCapacity(queueCapacity);
+ executor.setThreadNamePrefix(threadNamePrefix);
+ executor.setAllowCoreThreadTimeOut(true);
+ executor.setKeepAliveSeconds(keepAliveSeconds);
+ executor.setAwaitTerminationSeconds(awaitTerminationSeconds);
+ executor.initialize();
+ return executor;
+ }
+}
diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/EmailNotificationController.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/EmailNotificationController.java
index b0a96dadfca..f6755fd8253 100644
--- a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/EmailNotificationController.java
+++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/EmailNotificationController.java
@@ -19,8 +19,15 @@
import io.swagger.v3.oas.annotations.tags.Tag;
/**
- * Controller class for sending mail.
- *
+ * Email Notification Controller
+ *
+ * This controller exposes REST endpoints for sending emails with support for:
+ *
+ * - Multiple recipients (TO, CC)
+ * - Attachments
+ * - Async email delivery for high throughput
+ *
+ *
* @author Sagar Mahapatra
* @since 1.0.0
*
@@ -33,16 +40,17 @@ public class EmailNotificationController {
* Autowired reference for MailNotifierService.
*/
@Autowired
- EmailNotification emailNotificationService;
+ private EmailNotification emailNotificationService;
/**
- * @param mailTo array of email id's, to which mail should be sent.
- * @param mailCc array of email id's, to which the email should be sent as
- * carbon copy.
- * @param mailSubject the subject.
- * @param mailContent the content.
- * @param attachments the attachments.
- * @return the dto response.
+ * Sends an email with optional attachments asynchronously.
+ *
+ * @param mailTo Array of recipient email addresses (TO). Mandatory.
+ * @param mailCc Array of CC recipient email addresses. Optional.
+ * @param mailSubject Subject line of the email. Mandatory.
+ * @param mailContent Body content of the email. Mandatory.
+ * @param attachments Files to be attached with the email. Optional.
+ * @return A response wrapper containing the delivery status.
*/
@ResponseFilter
@Operation(summary = "Endpoint for sending a email", description = "Endpoint for sending a email", tags = { "emailnotification" })
diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/SmsNotificationController.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/SmsNotificationController.java
index 506f119e1a6..c2c5ed9f6ca 100644
--- a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/SmsNotificationController.java
+++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/controller/SmsNotificationController.java
@@ -22,9 +22,10 @@
import io.swagger.v3.oas.annotations.tags.Tag;
/**
- * This controller class receives contact number and message in data transfer
- * object and sends SMS on the provided contact number.
- *
+ * SMS Notification Controller
+ *
+ * This REST controller handles SMS sending requests with minimal overhead and optimized request validation.
+ *
* @author Ritesh Sinha
* @since 1.0.0
*/
@@ -37,7 +38,7 @@ public class SmsNotificationController {
* The reference that autowire sms notification service class.
*/
@Autowired
- SmsNotification smsNotifierService;
+ private SmsNotification smsNotifierService;
/**
* This method sends sms to the contact number provided.
diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/EmailNotificationServiceImpl.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/EmailNotificationServiceImpl.java
index d2f0a091c41..69416d00078 100644
--- a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/EmailNotificationServiceImpl.java
+++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/EmailNotificationServiceImpl.java
@@ -22,105 +22,114 @@
/**
* Service implementation class for {@link EmailNotification}.
- *
+ *
* @author Sagar Mahapatra
* @since 1.0.0
*/
@Service
public class EmailNotificationServiceImpl implements EmailNotification {
- Logger LOGGER = LoggerFactory.getLogger(EmailNotificationServiceImpl.class);
- /**
- * Autowired reference for {@link JavaMailSender}
- */
- @Autowired
- private JavaMailSender emailSender;
-
- /**
- * Autowired reference for {@link EmailNotificationUtils}
- */
- @Autowired
- EmailNotificationUtils emailNotificationUtils;
-
- /**
- * Optionally an email address can be configured.
- */
- @Nullable
- @Value("${mosip.kernel.notification.email.from:#{null}}")
- private String fromEmailAddress;
-
- @Value("${mosip.kernel.mail.proxy-mail:false}")
- private boolean isProxytrue;
-
- @Value("${mosip.kernel.mail.content.html.enable:true}")
- private boolean isHtmlEnable;
-
- /**
- * SendEmail
- *
- * @param mailTo
- * email address to which mail will be sent.
- * @param mailCc
- * email addresses to be cc'ed.
- * @param mailSubject
- * the subject.
- * @param mailContent
- * the content.
- * @param attachments
- * the attachments.
- * @return the response dto.
- */
- @Override
- public ResponseDto sendEmail(String[] mailTo, String[] mailCc, String mailSubject, String mailContent,
- MultipartFile[] attachments) {
- ResponseDto dto = new ResponseDto();
- LOGGER.info("To Request : " + String.join(",", mailTo));
- if(!isProxytrue) {
- send(mailTo, mailCc, mailSubject, mailContent, attachments);
- }
- dto.setStatus(MailNotifierConstants.MESSAGE_SUCCESS_STATUS.getValue());
- dto.setMessage(MailNotifierConstants.MESSAGE_REQUEST_SENT.getValue());
- return dto;
- }
-
- @Async
- public void send(String[] mailTo, String[] mailCc, String mailSubject, String mailContent,
- MultipartFile[] attachments) {
- EmailNotificationUtils.validateMailArguments(fromEmailAddress, mailTo, mailSubject, mailContent);
- /**
- * Creates the message.
- */
- MimeMessage message = emailSender.createMimeMessage();
- MimeMessageHelper helper;
- try {
- helper = new MimeMessageHelper(message, true);
- /**
- * Sets to, subject, content.
- */
- helper.setTo(mailTo);
-
- if (null != fromEmailAddress){
- helper.setFrom(fromEmailAddress);
- }
- if (mailCc != null) {
- helper.setCc(mailCc);
- }
- if (mailSubject != null) {
- helper.setSubject(mailSubject);
- }
- helper.setText(mailContent,isHtmlEnable);
- } catch (MessagingException exception) {
- throw new NotificationException(exception);
- }
- if (attachments != null) {
- /**
- * Adds attachments.
- */
- emailNotificationUtils.addAttachments(attachments, helper);
- }
- /**
- * Sends the mail.
- */
- emailNotificationUtils.sendMessage(message, emailSender);
- }
-}
+ private static final Logger LOGGER = LoggerFactory.getLogger(EmailNotificationServiceImpl.class);
+ /**
+ * Autowired reference for {@link JavaMailSender}
+ */
+ @Autowired
+ private JavaMailSender emailSender;
+
+ /**
+ * Autowired reference for {@link EmailNotificationUtils}
+ */
+ @Autowired
+ private EmailNotificationUtils emailNotificationUtils;
+
+ /**
+ * Optionally an email address can be configured.
+ */
+ @Nullable
+ @Value("${mosip.kernel.notification.email.from:#{null}}")
+ private String fromEmailAddress;
+
+ @Value("${mosip.kernel.mail.proxy-mail:false}")
+ private boolean isProxytrue;
+
+ @Value("${mosip.kernel.mail.content.html.enable:true}")
+ private boolean isHtmlEnable;
+
+ /**
+ * Sends an email with the specified parameters. In proxy mode, skips actual sending.
+ *
+ * @param mailTo recipient email addresses
+ * @param mailCc CC email addresses (optional)
+ * @param mailSubject subject line of the email
+ * @param mailContent body content of the email
+ * @param attachments optional attachments
+ * @return a {@link ResponseDto} indicating send status
+ */
+ @Override
+ public ResponseDto sendEmail(String[] mailTo, String[] mailCc, String mailSubject, String mailContent,
+ MultipartFile[] attachments) {
+ ResponseDto dto = new ResponseDto();
+ LOGGER.info("To Request : " + String.join(",", mailTo));
+
+ if (!isProxytrue) {
+ send(mailTo, mailCc, mailSubject, mailContent, attachments);
+ }
+
+ dto.setStatus(MailNotifierConstants.MESSAGE_SUCCESS_STATUS.getValue());
+ dto.setMessage(MailNotifierConstants.MESSAGE_REQUEST_SENT.getValue());
+ return dto;
+ }
+
+ /**
+ * Asynchronously builds and sends the email message.
+ *
+ * @param mailTo recipient addresses
+ * @param mailCc CC addresses
+ * @param mailSubject subject
+ * @param mailContent content
+ * @param attachments files to attach
+ */
+ @Async("mailExecutor")
+ public void send(String[] mailTo, String[] mailCc, String mailSubject, String mailContent,
+ MultipartFile[] attachments) {
+ EmailNotificationUtils.validateMailArguments(fromEmailAddress, mailTo, mailSubject, mailContent);
+ /**
+ * Creates the message.
+ */
+ MimeMessage message = emailSender.createMimeMessage();
+ try {
+ MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ /**
+ * Sets to, subject, content.
+ */
+ helper.setTo(mailTo);
+
+ if (null != fromEmailAddress) {
+ helper.setFrom(fromEmailAddress);
+ }
+
+ if (mailCc != null) {
+ helper.setCc(mailCc);
+ }
+
+ if (mailSubject != null) {
+ helper.setSubject(mailSubject);
+ }
+
+ helper.setText(mailContent, isHtmlEnable);
+
+ if (attachments != null) {
+ /**
+ * Adds attachments.
+ */
+ emailNotificationUtils.addAttachments(attachments, helper);
+ }
+ /**
+ * Sends the mail.
+ */
+ emailNotificationUtils.sendMessage(message, emailSender);
+ } catch (MessagingException exception) {
+ throw new NotificationException(exception);
+ }
+ }
+}
\ No newline at end of file
diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/SmsNotificationServiceImpl.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/SmsNotificationServiceImpl.java
index 2a55b30fc11..cbc496ca797 100644
--- a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/SmsNotificationServiceImpl.java
+++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/service/impl/SmsNotificationServiceImpl.java
@@ -1,8 +1,11 @@
package io.mosip.kernel.emailnotification.service.impl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import io.mosip.kernel.core.notification.model.SMSResponseDto;
@@ -10,8 +13,14 @@
import io.mosip.kernel.emailnotification.service.SmsNotification;
/**
- * This service class send SMS on the contact number provided.
- *
+ * SMS Notification Service Implementation
+ *
+ *
+ * This service class is optimized to handle high-volume SMS delivery requests
+ * with minimal latency and resource usage. It supports both real and proxy
+ * modes to allow testing without an external SMS gateway.
+ *
+ *
* @author Ritesh Sinha
* @since 1.0.0
*
@@ -20,32 +29,69 @@
@Service
public class SmsNotificationServiceImpl implements SmsNotification {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SmsNotificationServiceImpl.class);
+
@Value("${spring.profiles.active}")
- String activeProfile;
+ private String activeProfile;
@Autowired
private SMSServiceProvider smsServiceProvider;
-
+
@Value("${mosip.kernel.sms.proxy-sms:false}")
private boolean isProxytrue;
- @Value("${mosip.kernel.sms.success-message:SMS request sent")
+ @Value("${mosip.kernel.sms.success-message:Sms Request Sent}")
private String sucessMessage;
- /*
- * (non-Javadoc)
- *
- * @see
- * io.mosip.kernel.core.notification.spi.SmsNotification#sendSmsNotification(
- * java.lang.String, java.lang.String)
+
+ /**
+ * Pre-built static success response for proxy/local mode to reduce allocations.
+ */
+ private volatile SMSResponseDto cachedSuccessResponse;
+
+ /**
+ * Initializes the cached success response for local/proxy mode.
+ */
+ private SMSResponseDto getCachedSuccessResponse() {
+ if (cachedSuccessResponse == null) {
+ SMSResponseDto response = new SMSResponseDto();
+ response.setMessage(sucessMessage);
+ response.setStatus("success");
+ cachedSuccessResponse = response;
+ }
+ return cachedSuccessResponse;
+ }
+
+ /**
+ * Sends an SMS notification to the specified contact number.
+ * In local or proxy mode, a simulated success response is returned without
+ * invoking the external SMS service provider.
+ *
+ * @param contactNumber The target phone number (must be non-null, non-empty).
+ * @param contentMessage The SMS content to send (must be non-null, non-empty).
+ * @return A {@link SMSResponseDto} indicating success or failure.
*/
@Override
public SMSResponseDto sendSmsNotification(String contactNumber, String contentMessage) {
- if (activeProfile.equalsIgnoreCase("local") || isProxytrue) {
- SMSResponseDto smsResponseDTO = new SMSResponseDto();
- smsResponseDTO.setMessage(sucessMessage);
- smsResponseDTO.setStatus("success");
- return smsResponseDTO;
+
+ // Check for proxy or local mode
+ boolean isLocalProfile = "local".equalsIgnoreCase(activeProfile);
+ if (!isLocalProfile && !isProxytrue) {
+ send(contactNumber, contentMessage); // async
+ }
+
+ return getCachedSuccessResponse();
+ }
+
+ /**
+ * Asynchronously sends the SMS using the service provider.
+ */
+ @Async("smsExecutor")
+ public void send(String contactNumber, String contentMessage) {
+ try {
+ smsServiceProvider.sendSms(contactNumber, contentMessage);
+ } catch (Exception e) {
+ LOGGER.error("Failed to send SMS to {}: {}", contactNumber, e.getMessage(), e);
+ // Optionally, send fallback or raise alert
}
- return smsServiceProvider.sendSms(contactNumber, contentMessage);
}
}
\ No newline at end of file
diff --git a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/util/EmailNotificationUtils.java b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/util/EmailNotificationUtils.java
index 91162fb925c..e1075bb4c56 100644
--- a/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/util/EmailNotificationUtils.java
+++ b/kernel/kernel-notification-service/src/main/java/io/mosip/kernel/emailnotification/util/EmailNotificationUtils.java
@@ -1,11 +1,9 @@
package io.mosip.kernel.emailnotification.util;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.AddressException;
@@ -13,6 +11,7 @@
import jakarta.mail.internet.MimeMessage;
import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.InputStreamSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
@@ -34,10 +33,13 @@
@Component
public class EmailNotificationUtils {
/**
- * This method sends the message.
- *
- * @param message the message to be sent.
- * @param emailSender the EmailSender object.
+ * Sends an email message asynchronously using the configured JavaMailSender.
+ *
+ * This method leverages a custom ThreadPoolTaskExecutor (defined as "mailExecutor")
+ * to handle high-throughput email sending without blocking the main application thread.
+ *
+ * @param message the MimeMessage object containing the email details
+ * @param emailSender the JavaMailSender instance used to send the email
*/
@Async
public void sendMessage(MimeMessage message, JavaMailSender emailSender) {
@@ -45,10 +47,15 @@ public void sendMessage(MimeMessage message, JavaMailSender emailSender) {
}
/**
- * This method adds the attachments to the mail.
- *
- * @param attachments the attachments.
- * @param helper the helper object.
+ * Adds one or more attachments to a MimeMessageHelper instance in a memory-efficient way.
+ *
+ * Instead of loading the entire file into memory as a byte array,
+ * attachments are streamed using InputStreamSource to optimize memory usage,
+ * especially for large files.
+ *
+ * @param attachments an array of MultipartFile objects representing the attachments
+ * @param helper the MimeMessageHelper to which attachments are added
+ * @throws NotificationException if adding any attachment fails
*/
public void addAttachments(MultipartFile[] attachments, MimeMessageHelper helper) {
Arrays.asList(attachments).forEach(attachment -> {
@@ -61,62 +68,97 @@ public void addAttachments(MultipartFile[] attachments, MimeMessageHelper helper
}
/**
- * This method handles argument validations.
- *
- * @param mailTo the to address to be validated.
- * @param mailSubject the subject to be validated.
- * @param mailContent the content to be validated.
+ * Validates the email notification arguments for correctness.
+ * This method performs:
+ *
+ * - Validation of sender's email address
+ * - Validation of recipient list (non-null, non-empty, valid format)
+ * - Validation of email subject (non-null, non-empty)
+ * - Validation of email content (non-null, non-empty)
+ *
+ *
+ * Validation is regex-based to avoid exception overhead.
+ * If validation errors are found, an {@link InvalidArgumentsException} is thrown with a list of errors.
+ *
+ * @param fromEmail the sender email address
+ * @param mailTo an array of recipient email addresses
+ * @param mailSubject the subject of the email
+ * @param mailContent the body content of the email
+ * @throws InvalidArgumentsException if one or more validation errors occur
*/
public static void validateMailArguments(String fromEmail, String[] mailTo, String mailSubject, String mailContent){
- Set validationErrorsList = new HashSet<>();
-
- if (null != fromEmail ) {
-
- try {
- validateEmailAddress(fromEmail);
- }
- catch(AddressException ex){
- validationErrorsList.add(new ServiceError(MailNotifierArgumentErrorConstants.SENDER_ADDRESS_NOT_FOUND.getErrorCode(),
- MailNotifierArgumentErrorConstants.SENDER_ADDRESS_NOT_FOUND.getErrorMessage()));
- }
-
+ Set validationErrors = Collections.newSetFromMap(new ConcurrentHashMap<>());
+
+ // Validate sender email
+ if (fromEmail == null || !safeValidateEmail(fromEmail)) {
+ validationErrors.add(new ServiceError(
+ MailNotifierArgumentErrorConstants.SENDER_ADDRESS_NOT_FOUND.getErrorCode(),
+ MailNotifierArgumentErrorConstants.SENDER_ADDRESS_NOT_FOUND.getErrorMessage()
+ ));
}
+ // Validate recipient emails
if (mailTo == null || mailTo.length == Integer.parseInt(MailNotifierConstants.DIGIT_ZERO.getValue())) {
- validationErrorsList
- .add(new ServiceError(MailNotifierArgumentErrorConstants.RECEIVER_ADDRESS_NOT_FOUND.getErrorCode(),
- MailNotifierArgumentErrorConstants.RECEIVER_ADDRESS_NOT_FOUND.getErrorMessage()));
+ validationErrors.add(new ServiceError(
+ MailNotifierArgumentErrorConstants.RECEIVER_ADDRESS_NOT_FOUND.getErrorCode(),
+ MailNotifierArgumentErrorConstants.RECEIVER_ADDRESS_NOT_FOUND.getErrorMessage()
+ ));
} else {
- List tos = Arrays.asList(mailTo);
- tos.forEach(to -> {
- try {
- validateEmailAddress(to);
- }
- catch(AddressException ex){
- validationErrorsList.add(new ServiceError(MailNotifierArgumentErrorConstants.SENDER_ADDRESS_NOT_FOUND.getErrorCode(),
- MailNotifierArgumentErrorConstants.SENDER_ADDRESS_NOT_FOUND.getErrorMessage()));
+ Arrays.stream(mailTo).parallel().forEach(to -> {
+ if (!safeValidateEmail(to)) {
+ validationErrors.add(new ServiceError(
+ MailNotifierArgumentErrorConstants.RECEIVER_ADDRESS_NOT_FOUND.getErrorCode(),
+ MailNotifierArgumentErrorConstants.RECEIVER_ADDRESS_NOT_FOUND.getErrorMessage()
+ ));
}
});
}
+
+ // Validate subject
if (mailSubject == null || mailSubject.trim().isEmpty()) {
- validationErrorsList
- .add(new ServiceError(MailNotifierArgumentErrorConstants.SUBJECT_NOT_FOUND.getErrorCode(),
- MailNotifierArgumentErrorConstants.SUBJECT_NOT_FOUND.getErrorMessage()));
+ validationErrors.add(new ServiceError(
+ MailNotifierArgumentErrorConstants.SUBJECT_NOT_FOUND.getErrorCode(),
+ MailNotifierArgumentErrorConstants.SUBJECT_NOT_FOUND.getErrorMessage()
+ ));
}
+
+ // Validate content
if (mailContent == null || mailContent.trim().isEmpty()) {
- validationErrorsList
- .add(new ServiceError(MailNotifierArgumentErrorConstants.CONTENT_NOT_FOUND.getErrorCode(),
- MailNotifierArgumentErrorConstants.CONTENT_NOT_FOUND.getErrorMessage()));
+ validationErrors.add(new ServiceError(
+ MailNotifierArgumentErrorConstants.CONTENT_NOT_FOUND.getErrorCode(),
+ MailNotifierArgumentErrorConstants.CONTENT_NOT_FOUND.getErrorMessage()
+ ));
}
- if (!validationErrorsList.isEmpty()) {
- throw new InvalidArgumentsException(new ArrayList(validationErrorsList));
+
+ if (!validationErrors.isEmpty()) {
+ throw new InvalidArgumentsException(new ArrayList<>(validationErrors));
}
}
+ /**
+ * Strict RFC-compliant email validation using Jakarta Mail's InternetAddress.
+ *
+ * @param emailId email address to validate
+ * @return true if valid, throws AddressException otherwise
+ * @throws AddressException if email format is invalid
+ */
private static boolean validateEmailAddress(String emailId ) throws AddressException{
-
InternetAddress fromEmailAddr = new InternetAddress(emailId);
fromEmailAddr.validate();
return true;
}
+
+ /**
+ * Wrapper around validateEmailAddress() to avoid throwing checked exceptions inside streams.
+ *
+ * @param email email address
+ * @return true if valid, false otherwise
+ */
+ private static boolean safeValidateEmail(String email) {
+ try {
+ return validateEmailAddress(email);
+ } catch (AddressException e) {
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/kernel/kernel-notification-service/src/test/java/io/mosip/kernel/emailnotification/test/service/MailNotifierServiceTest.java b/kernel/kernel-notification-service/src/test/java/io/mosip/kernel/emailnotification/test/service/MailNotifierServiceTest.java
index f06da22fbb2..70989920d32 100644
--- a/kernel/kernel-notification-service/src/test/java/io/mosip/kernel/emailnotification/test/service/MailNotifierServiceTest.java
+++ b/kernel/kernel-notification-service/src/test/java/io/mosip/kernel/emailnotification/test/service/MailNotifierServiceTest.java
@@ -6,6 +6,7 @@
import jakarta.mail.internet.MimeMessage;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
@@ -62,6 +63,7 @@ public void verifyAddAttachmentFunctionality() throws Exception {
@Test
public void verifySendMessageFunctionality() throws Exception {
+ String fromEmail = "from.test@mosip.io";
String[] mailTo = { "test@gmail.com" };
String[] mailCc = { "testTwo@gmail.com" };
String mailSubject = "Test Subject";
@@ -70,10 +72,12 @@ public void verifySendMessageFunctionality() throws Exception {
MultipartFile[] attachments = { attachment };
MimeMessage message = emailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ //helper.setFrom(fromEmail);
helper.setTo(mailTo);
helper.setCc(mailCc);
helper.setSubject(mailSubject);
helper.setText(mailContent);
+ ReflectionTestUtils.setField(service, "fromEmailAddress", fromEmail);
doNothing().when(utils).sendMessage(Mockito.any(), Mockito.any());
service.sendEmail(mailTo, mailCc, mailSubject, mailContent, attachments);
verify(utils, times(1)).sendMessage(Mockito.any(), Mockito.any());
diff --git a/kernel/kernel-pdfgenerator/pom.xml b/kernel/kernel-pdfgenerator/pom.xml
index 142d3bd61bc..2fdf5774189 100644
--- a/kernel/kernel-pdfgenerator/pom.xml
+++ b/kernel/kernel-pdfgenerator/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip.kernel
kernel-pdfgenerator
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-pdfgenerator
generate pdf for given template
@@ -22,7 +22,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-pinvalidator/pom.xml b/kernel/kernel-pinvalidator/pom.xml
index 5838223bf79..cf02670a647 100644
--- a/kernel/kernel-pinvalidator/pom.xml
+++ b/kernel/kernel-pinvalidator/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip.kernel
kernel-pinvalidator
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
http://maven.apache.org
UTF-8
@@ -12,7 +12,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
@@ -20,7 +20,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-pridgenerator-service/pom.xml b/kernel/kernel-pridgenerator-service/pom.xml
index b4554932ca0..4a7d4f7cd44 100644
--- a/kernel/kernel-pridgenerator-service/pom.xml
+++ b/kernel/kernel-pridgenerator-service/pom.xml
@@ -10,21 +10,21 @@
21
21
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
**/constant/**,**/config/**,**/httpfilter/**,**/cache/**,**/entity/**,**/model/**,**/exception/**,**/repository/**,**/verticle/**,**/spi/**,"**/proxy/**","**/entities/**","**/filter/**","**/util/**","**/verifier/**","**/KernelPridgeneratorServiceApplication.java"
kernel-pridgenerator-service
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-qrcodegenerator-zxing/pom.xml b/kernel/kernel-qrcodegenerator-zxing/pom.xml
index d4292f7eedb..c7b0357d80f 100644
--- a/kernel/kernel-qrcodegenerator-zxing/pom.xml
+++ b/kernel/kernel-qrcodegenerator-zxing/pom.xml
@@ -6,7 +6,7 @@
io.mosip.kernel
kernel-qrcodegenerator-zxing
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
jar
kernel-qrcodegenerator-zxing
@@ -16,7 +16,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
3.4.1
diff --git a/kernel/kernel-ridgenerator-service/pom.xml b/kernel/kernel-ridgenerator-service/pom.xml
index 132431f1dc6..b93c059643f 100644
--- a/kernel/kernel-ridgenerator-service/pom.xml
+++ b/kernel/kernel-ridgenerator-service/pom.xml
@@ -6,7 +6,7 @@
io.mosip.kernel
kernel-ridgenerator-service
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel-ridgenerator-service
Mosip commons project
https://github.com/mosip/commons
@@ -17,9 +17,9 @@
21
21
3.8.0
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
1.5.10
@@ -29,7 +29,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-salt-generator/pom.xml b/kernel/kernel-salt-generator/pom.xml
index 1dbb257c564..6521f96f97e 100644
--- a/kernel/kernel-salt-generator/pom.xml
+++ b/kernel/kernel-salt-generator/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip.kernel
kernel-salt-generator
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
Kernel Salt Generator
Batch Job Application to for one-time populating of salt values for MOSIP related application salt tables.
https://github.com/mosip/commons
@@ -24,8 +24,8 @@
3.2.0
2.3
- 1.2.1-SNAPSHOT
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
2.12.0
@@ -34,7 +34,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-templatemanager-velocity/pom.xml b/kernel/kernel-templatemanager-velocity/pom.xml
index 950c9c8859c..c5f6f0019bb 100644
--- a/kernel/kernel-templatemanager-velocity/pom.xml
+++ b/kernel/kernel-templatemanager-velocity/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip.kernel
kernel-templatemanager-velocity
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
Mosip Template Manager Using Velocity Template Engine
UTF-8
@@ -12,7 +12,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
1.7
@@ -23,7 +23,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-transliteration-icu4j/pom.xml b/kernel/kernel-transliteration-icu4j/pom.xml
index c71ad87cf82..55cfed1df4c 100644
--- a/kernel/kernel-transliteration-icu4j/pom.xml
+++ b/kernel/kernel-transliteration-icu4j/pom.xml
@@ -7,7 +7,7 @@
io.mosip.kernel
kernel-transliteration-icu4j
jar
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
kernel transliteration icu4j
https://github.com/mosip/commons
kernel-transliteration-icu4j
@@ -18,7 +18,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
73.2
@@ -27,7 +27,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/kernel-websubclient-api/pom.xml b/kernel/kernel-websubclient-api/pom.xml
index 1bdd64d8c5e..6130e4b0792 100644
--- a/kernel/kernel-websubclient-api/pom.xml
+++ b/kernel/kernel-websubclient-api/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip.kernel
kernel-websubclient-api
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
jar
kernel-websubclient-api
Mosip commons project
@@ -15,7 +15,7 @@
21
21
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
0.8.11
1.2.3
@@ -24,7 +24,7 @@
io.mosip.kernel
kernel-bom
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
import
diff --git a/kernel/pom.xml b/kernel/pom.xml
index 9987e27b2f7..20fccd6647f 100644
--- a/kernel/pom.xml
+++ b/kernel/pom.xml
@@ -2,7 +2,7 @@
4.0.0
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
io.mosip.kernel
kernel-parent
pom
diff --git a/pom.xml b/pom.xml
index 1237f0b2bfb..06893a3ee3e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.mosip
commons
- 1.2.1-SNAPSHOT
+ 1.2.2-SNAPSHOT
pom
MOSIP Commons Parent POM