From e0bbad10d073c3f25f68a8d73d8231c1b6b322a8 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Wed, 3 Jul 2024 16:13:30 -0400 Subject: [PATCH 01/34] enable configurable TimeZone for JSON parser in Nio and Http config --- pom.xml | 22 ++++++++++++++----- .../summerboot/jexpress/boot/BackOffice.java | 9 -------- .../httpclient/HttpClientConfig.java | 9 +++++++- .../integration/httpclient/RPCResult.java | 14 +++++------- .../integration/quartz/BootJobListener.java | 1 - .../nio/server/HttpNioChannelInitializer.java | 2 +- .../jexpress/nio/server/NioConfig.java | 12 ++++++++-- .../nio/server/domain/ServiceContext.java | 9 +++++++- .../summerboot/jexpress/util/BeanUtil.java | 18 +++++++-------- .../jexpress/util/ReflectionUtil.java | 2 +- .../jexpress/template/log4j2.xml.temp | 18 ++++++++++++--- 11 files changed, 73 insertions(+), 43 deletions(-) diff --git a/pom.xml b/pom.xml index 29948de5..e8879de8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.summerboot jexpress - 2.4.8 + 2.4.9-SNAPSHOT jar Summer Boot jExpress Summer Boot jExpress focuses on solving non-functional and operational maintainability requirements, @@ -151,6 +151,16 @@ true + + + + + + + + + + @@ -171,8 +181,8 @@ UTF-8 - 11 - 11 + 17 + 17 3.3.2 3.12.1 3.6.1 @@ -199,9 +209,9 @@ 4.1.111.Final 2.0.65.Final - 1.64.0 + 1.65.0 33.2.1-jre - 4.27.1 + 4.27.2 2.2.22 @@ -248,7 +258,7 @@ 7.10.2 - 8.4.0 + 9.0.0 diff --git a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java index 03065843..0fdce219 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java +++ b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java @@ -29,7 +29,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import java.util.TimeZone; import java.util.concurrent.ThreadPoolExecutor; import java.util.stream.Collectors; @@ -207,10 +206,6 @@ public Map getBootErrorCodeMapping() { @Config(key = "backoffice.executor.allowCoreThreadTimeOut", defaultValue = "false") private boolean allowCoreThreadTimeOut = false; - @Config(key = "backoffice.jsonParser.TimeZone", desc = "The ID for a TimeZone, either an abbreviation such as \"PST\", a full name such as \"America/Los_Angeles\", or a custom ID such as \"GMT-8:00\", or \"default\" as system default timezone.", defaultValue = "UTC") - private TimeZone timeZone = TimeZone.getTimeZone("UTC");//TimeZone.getDefault(); - - @ConfigHeader(title = "4.1 Default Path/File Naming") @Config(key = "naming.folder.domainPrefix", defaultValue = "standalone") private String domainFolderPrefix = "standalone"; @@ -334,10 +329,6 @@ public String getPortInUseAlertMessage() { return portInUseAlertMessage; } - public TimeZone getTimeZone() { - return timeZone; - } - public String getDomainFolderPrefix() { return domainFolderPrefix; } diff --git a/src/main/java/org/summerboot/jexpress/integration/httpclient/HttpClientConfig.java b/src/main/java/org/summerboot/jexpress/integration/httpclient/HttpClientConfig.java index 2dcc1e2f..effcbc83 100644 --- a/src/main/java/org/summerboot/jexpress/integration/httpclient/HttpClientConfig.java +++ b/src/main/java/org/summerboot/jexpress/integration/httpclient/HttpClientConfig.java @@ -44,6 +44,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.TimeZone; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; @@ -149,6 +150,8 @@ protected void generateTemplate_truststore(StringBuilder sb) { protected volatile boolean fromJsonCaseInsensitive = false; @Config(key = "httpclient.fromJson.failOnUnknownProperties") protected volatile boolean fromJsonFailOnUnknownProperties = true; + @Config(key = "httpclient.fromJson.TimeZone", desc = "The ID for a TimeZone, either an abbreviation such as \"UTC\", a full name such as \"America/Toronto\", or a custom ID such as \"GMT-8:00\", or \"system\" as system default timezone.", defaultValue = "system") + protected TimeZone jsonParserTimeZone = TimeZone.getDefault(); //3.2 HTTP Client Performance @ConfigHeader(title = "2. HTTP Client Performance") @@ -237,7 +240,7 @@ protected void loadCustomizedConfigs(File cfgFile, boolean isReal, ConfigUtil he } }); - RPCResult.init(fromJsonFailOnUnknownProperties, fromJsonCaseInsensitive); + RPCResult.init(jsonParserTimeZone, fromJsonFailOnUnknownProperties, fromJsonCaseInsensitive); // 3.1 HTTP Client keystore KeyManager[] keyManagers = kmf == null ? null : kmf.getKeyManagers(); @@ -403,6 +406,10 @@ public boolean isFromJsonFailOnUnknownProperties() { return fromJsonFailOnUnknownProperties; } + public TimeZone getJsonParserTimeZone() { + return jsonParserTimeZone; + } + public long getHttpConnectTimeoutMs() { return httpConnectTimeoutMs; } diff --git a/src/main/java/org/summerboot/jexpress/integration/httpclient/RPCResult.java b/src/main/java/org/summerboot/jexpress/integration/httpclient/RPCResult.java index 1565c65f..2aa0500a 100644 --- a/src/main/java/org/summerboot/jexpress/integration/httpclient/RPCResult.java +++ b/src/main/java/org/summerboot/jexpress/integration/httpclient/RPCResult.java @@ -24,7 +24,6 @@ import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import io.netty.handler.codec.http.HttpResponseStatus; import org.apache.commons.lang3.StringUtils; -import org.summerboot.jexpress.boot.BackOffice; import org.summerboot.jexpress.boot.BootErrorCode; import org.summerboot.jexpress.nio.server.domain.Err; import org.summerboot.jexpress.nio.server.domain.ServiceContext; @@ -33,6 +32,7 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.List; +import java.util.TimeZone; /** * @param Success(JSON) result type @@ -41,29 +41,27 @@ */ public class RPCResult { - protected static boolean isFromJsonFailOnUnknownProperties = true; public static ObjectMapper DefaultJacksonMapper = new ObjectMapper(); - public static void update(ObjectMapper objectMapper) { + public static void update(ObjectMapper objectMapper, TimeZone timeZone, boolean isFromJsonFailOnUnknownProperties) { objectMapper.registerModules(new JavaTimeModule()); - objectMapper.setTimeZone(BackOffice.agent.getTimeZone()); + objectMapper.setTimeZone(timeZone); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, isFromJsonFailOnUnknownProperties); } - public static void init(boolean fromJsonFailOnUnknownProperties, boolean fromJsonCaseInsensitive) { - isFromJsonFailOnUnknownProperties = fromJsonFailOnUnknownProperties; + public static void init(TimeZone timeZone, boolean fromJsonFailOnUnknownProperties, boolean fromJsonCaseInsensitive) { if (fromJsonCaseInsensitive) { DefaultJacksonMapper = JsonMapper.builder().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true).build(); } - update(DefaultJacksonMapper); + update(DefaultJacksonMapper, timeZone, fromJsonFailOnUnknownProperties); } static { - init(true, false); + init(TimeZone.getDefault(), true, false); } protected final HttpRequest originRequest; diff --git a/src/main/java/org/summerboot/jexpress/integration/quartz/BootJobListener.java b/src/main/java/org/summerboot/jexpress/integration/quartz/BootJobListener.java index 717a752a..7779e54e 100644 --- a/src/main/java/org/summerboot/jexpress/integration/quartz/BootJobListener.java +++ b/src/main/java/org/summerboot/jexpress/integration/quartz/BootJobListener.java @@ -105,7 +105,6 @@ protected void scheduleFixedDelayJob(final JobExecutionContext context, final Jo .build(); scheduler.rescheduleJob(currentTriggerKey, nextTrigger); log.info(desc + " scheduled@" + nextTime); - } catch (SchedulerException ex) { log.error("failed to reschedule the job with triger: {}", currentTriggerKey, ex); } diff --git a/src/main/java/org/summerboot/jexpress/nio/server/HttpNioChannelInitializer.java b/src/main/java/org/summerboot/jexpress/nio/server/HttpNioChannelInitializer.java index 29dba0f0..9de23511 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/HttpNioChannelInitializer.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/HttpNioChannelInitializer.java @@ -87,7 +87,7 @@ protected void initChannelPipeline(ChannelPipeline channelPipeline, NioConfig ni //channelPipeline.addLast(new HttpContentCompressor()); // 3*. File upload: after codec, chunked and before aggregator - if (namedFileUpload != null) { + if (namedFileUpload != null && !namedFileUpload.isEmpty()) { for (String named : namedFileUpload) { ch = injector.getInstance(Key.get(ChannelHandler.class, Names.named(named))); channelPipeline.addLast("FileUpload_" + named, ch);// to support file upload, must before HttpObjectAggregator diff --git a/src/main/java/org/summerboot/jexpress/nio/server/NioConfig.java b/src/main/java/org/summerboot/jexpress/nio/server/NioConfig.java index 4c6a6b19..1daf4b0b 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/NioConfig.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/NioConfig.java @@ -37,6 +37,7 @@ import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.TimeZone; import java.util.concurrent.ThreadPoolExecutor; import java.util.stream.Collectors; @@ -185,7 +186,7 @@ protected void generateTemplate_keystore(StringBuilder sb) { //protected volatile int nioEventLoopGroupExecutorSize; @Config(key = "nio.server.BizExecutor.mode", defaultValue = "Mixed", - desc = "valid value = CPU, IO (default), Mixed\nuse CPU core + 1 when application is CPU bound\n" + desc = "valid value = CPU, IO and Mixed (default) \nuse CPU core + 1 when application is CPU bound\n" + "use CPU core x 2 + 1 when application is I/O bound\n" + "need to find the best value based on your performance test result when nio.server.BizExecutor.mode=Mixed") protected volatile ThreadingMode tpeThreadingMode = ThreadingMode.Mixed; @@ -239,6 +240,9 @@ protected void generateTemplate_keystore(StringBuilder sb) { @Config(key = "nio.JAX-RS.toJson.Pretty", defaultValue = "false") protected volatile boolean toJsonPretty = false; + @Config(key = "nio.JAX-RS.jsonParser.TimeZone", desc = "The ID for a TimeZone, either an abbreviation such as \"UTC\", a full name such as \"America/Toronto\", or a custom ID such as \"GMT-8:00\", or \"system\" as system default timezone.", defaultValue = "system") + protected TimeZone jsonParserTimeZone = TimeZone.getDefault(); + @Config(key = "nio.WebSocket.Compress", defaultValue = "true") protected volatile boolean webSocketCompress = true; @@ -414,7 +418,7 @@ protected void loadCustomizedConfigs(File cfgFile, boolean isReal, ConfigUtil he tpe = buildThreadPoolExecutor(tpe, "NIO.Biz", tpeThreadingMode, tpeCore, tpeMax, tpeQueue, tpeKeepAliveSeconds, new AbortPolicyWithReport("NIOBizThreadPoolExecutor"), prestartAllCoreThreads, allowCoreThreadTimeOut, false); - BeanUtil.init(fromJsonFailOnUnknownProperties, fromJsonCaseInsensitive, toJsonPretty, toJsonIgnoreNull); + BeanUtil.init(jsonParserTimeZone, fromJsonFailOnUnknownProperties, fromJsonCaseInsensitive, toJsonPretty, toJsonIgnoreNull); //5.1 caller filter switch (filterUserType) { @@ -619,6 +623,10 @@ public boolean isFromJsonFailOnUnknownProperties() { return fromJsonFailOnUnknownProperties; } + public TimeZone getJsonParserTimeZone() { + return jsonParserTimeZone; + } + public boolean isToJsonIgnoreNull() { return toJsonIgnoreNull; } diff --git a/src/main/java/org/summerboot/jexpress/nio/server/domain/ServiceContext.java b/src/main/java/org/summerboot/jexpress/nio/server/domain/ServiceContext.java index 80f738ad..b72013ca 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/domain/ServiceContext.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/domain/ServiceContext.java @@ -45,6 +45,7 @@ import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Path; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -68,6 +69,7 @@ public class ServiceContext { protected final String txId; protected final long hit; protected final long startTs; + protected final OffsetDateTime startDateTime; protected Caller caller; protected String callerId; @@ -135,6 +137,7 @@ protected ServiceContext(ChannelHandlerContext ctx, String txId, long hit, long this.txId = txId; this.hit = hit; this.startTs = startTs; + this.startDateTime = OffsetDateTime.ofInstant(java.time.Instant.ofEpochMilli(startTs), java.time.ZoneId.systemDefault()); this.requestHeaders = requestHeaders; this.requesMethod = requesMethod; this.requesURI = requesURI; @@ -223,6 +226,10 @@ public long startTimestamp() { return startTs; } + public OffsetDateTime startDateTime() { + return startDateTime; + } + public ServiceContext reset() { status = HttpResponseStatus.OK; autoConvertBlank200To204 = true; @@ -956,7 +963,7 @@ public ServiceContext reportPOI(NioConfig cfg, StringBuilder sb) { return this; } NioConfig.VerboseTargetPOIType filterType = cfg == null ? NioConfig.VerboseTargetPOIType.all : cfg.getFilterPOIType(); - sb.append("\n\tPOI: "); + sb.append("\n\tPOI.t0=").append(startDateTime).append(" "); switch (filterType) { case all: poi.forEach((p) -> { diff --git a/src/main/java/org/summerboot/jexpress/util/BeanUtil.java b/src/main/java/org/summerboot/jexpress/util/BeanUtil.java index 74b5e993..542853d2 100644 --- a/src/main/java/org/summerboot/jexpress/util/BeanUtil.java +++ b/src/main/java/org/summerboot/jexpress/util/BeanUtil.java @@ -30,12 +30,12 @@ import jakarta.validation.Validation; import jakarta.validation.ValidatorFactory; import org.apache.commons.lang3.StringUtils; -import org.summerboot.jexpress.boot.BackOffice; import java.lang.reflect.Array; import java.util.Iterator; import java.util.Objects; import java.util.Set; +import java.util.TimeZone; /** * @author Changski Tie Zheng Zhang 张铁铮, 魏泽北, 杜旺财, 杜富贵 @@ -44,7 +44,6 @@ public class BeanUtil { protected static boolean isToJsonIgnoreNull = true; protected static boolean isToJsonPretty = false; - protected static boolean isFromJsonFailOnUnknownProperties = true; public static ObjectMapper JacksonMapper = new ObjectMapper();//JsonMapper.builder().init(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true) public static ObjectMapper JacksonMapperIgnoreNull = new ObjectMapper() @@ -52,30 +51,29 @@ public class BeanUtil { .setSerializationInclusion(Include.NON_EMPTY); public static XmlMapper XMLMapper = new XmlMapper(); - public static void update(ObjectMapper objectMapper) { + public static void update(ObjectMapper objectMapper, TimeZone timeZone, boolean isFromJsonFailOnUnknownProperties) { objectMapper.registerModules(new JavaTimeModule()); - objectMapper.setTimeZone(BackOffice.agent.getTimeZone()); + objectMapper.setTimeZone(timeZone); objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, isFromJsonFailOnUnknownProperties); } - public static void init(boolean fromJsonFailOnUnknownProperties, boolean fromJsonCaseInsensitive, boolean toJsonPretty, boolean toJsonIgnoreNull) { - isFromJsonFailOnUnknownProperties = fromJsonFailOnUnknownProperties; + public static void init(TimeZone timeZone, boolean fromJsonFailOnUnknownProperties, boolean fromJsonCaseInsensitive, boolean toJsonPretty, boolean toJsonIgnoreNull) { isToJsonPretty = toJsonPretty; isToJsonIgnoreNull = toJsonIgnoreNull; if (fromJsonCaseInsensitive) { JacksonMapper = JsonMapper.builder().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true).build(); XMLMapper = XmlMapper.builder().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true).build(); } - update(JacksonMapper); - update(JacksonMapperIgnoreNull); - update(XMLMapper); + update(JacksonMapper, timeZone, fromJsonFailOnUnknownProperties); + update(JacksonMapperIgnoreNull, timeZone, fromJsonFailOnUnknownProperties); + update(XMLMapper, timeZone, fromJsonFailOnUnknownProperties); } static { - init(true, false, false, true); + init(TimeZone.getDefault(), true, false, false, true); } /** diff --git a/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java b/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java index 831cca21..2a7c74ac 100644 --- a/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java +++ b/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java @@ -432,7 +432,7 @@ public static Object toStandardJavaType(String value, final Class targetClass, f } else if (targetClass.equals(LocalDate.class)) { return LocalDate.parse(value, DateTimeFormatter.ISO_LOCAL_DATE); } else if (targetClass.equals(TimeZone.class)) { - if (value.equals("default")) { + if (value.equals("system") || value.equals("default")) { return TimeZone.getDefault(); } return TimeZone.getTimeZone(value); diff --git a/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp b/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp index 4b9c3f43..32bf49df 100644 --- a/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp +++ b/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp @@ -1,5 +1,5 @@ - + @@ -30,7 +30,7 @@ - + + + + + + + + @@ -73,7 +85,7 @@ - + From 015e252d450172919cd265ae68320e64ebc2387f Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Fri, 5 Jul 2024 19:36:19 -0400 Subject: [PATCH 02/34] updated log4j2.xml --- .../jexpress/template/log4j2.xml.temp | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp b/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp index 32bf49df..f071c82d 100644 --- a/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp +++ b/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp @@ -5,14 +5,13 @@ - - + + name="StatusLogFile" + fileName="${sys:logPath}/${sys:appName}_status_${sys:serverName}.log" + filePattern="${sys:logPath}/$${date:yyyy-MM-dd}/${sys:appName}_status_${sys:serverName}_%d{yyyy-MM-dd HH:mm}.%i.log.gz" + immediateFlush="false" + ignoreExceptions="false"> - + - + + name="RequestLogFile" + fileName="${sys:logPath}/${sys:appName}_requests_${sys:serverName}.log" + filePattern="${sys:logPath}/$${date:yyyy-MM-dd}/${sys:appName}_requests_${sys:serverName}_%d{yyyy-MM-dd HH:mm}.%i.log.gz" + immediateFlush="false" + ignoreExceptions="false"> - + - + + name="ScheduledLogFile" + fileName="${sys:logPath}/${sys:appName}_schedule_${sys:serverName}.log" + filePattern="${sys:logPath}/$${date:yyyy-MM-dd}/${sys:appName}_schedule_${sys:serverName}_%d{yyyy-MM-dd HH:mm}.%i.log.gz" + immediateFlush="false" + ignoreExceptions="false"> - + - + @@ -68,23 +67,25 @@ - + - + - - + + - - + + From 834c11573df48baf1a7073c2490a5e543c79b6f6 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Fri, 5 Jul 2024 19:37:05 -0400 Subject: [PATCH 03/34] updated log4j2.xml --- src/main/java/org/summerboot/jexpress/boot/BootConstant.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java index 0cb22837..6329bd53 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java @@ -25,7 +25,7 @@ public interface BootConstant { String APP_ID = String.format("%06d", new Random().nextInt(999999)); //version - String VERSION = "SummerBoot.jExpress 2.4.8"; + String VERSION = "SummerBoot.jExpress 2.4.9"; String JEXPRESS_PACKAGE_NAME = "org.summerboot.jexpress"; String DEFAULT_ADMIN_MM = "changeit"; From f0a0527151e0755608dfd7af21307a8b11effc86 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Mon, 8 Jul 2024 15:00:48 -0400 Subject: [PATCH 04/34] wip --- pom.xml | 2 +- .../jexpress/integration/cache/BootCache.java | 18 +++++++++--------- .../cache/BootCache_RedisImple.java | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index e8879de8..c9a5cadc 100644 --- a/pom.xml +++ b/pom.xml @@ -225,7 +225,7 @@ 0.10.2 - 2.17.1 + 2.17.2 8.0.1.Final 6.0.0 diff --git a/src/main/java/org/summerboot/jexpress/integration/cache/BootCache.java b/src/main/java/org/summerboot/jexpress/integration/cache/BootCache.java index 949e56bd..627478f8 100644 --- a/src/main/java/org/summerboot/jexpress/integration/cache/BootCache.java +++ b/src/main/java/org/summerboot/jexpress/integration/cache/BootCache.java @@ -30,15 +30,15 @@ public interface BootCache { * this is a Distributed non-blocking version of lock() method; it attempts * to acquire the lock immediately, return true if locking succeeds * - * @param lockName the name of the tryLock - * @param unlockPassword unlockPassword is to be used for unlock. To protect - * a tryLock from being unlocked by anyone, a tryLock cannot be released - * when unlockPassword not match - * @param millisecondsToExpireIncaseUnableToUnlock expire time of tryLock in - * case unable to unlock (e.g. exception/error before executing unlock) + * @param lockName the name of the tryLock + * @param unlockPassword unlockPassword is to be used for unlock. To protect + * a tryLock from being unlocked by anyone, a tryLock cannot be released + * when unlockPassword not match + * @param ttlToExpireIncaseUnableToUnlock expire time of tryLock in + * case unable to unlock (e.g. exception/error before executing unlock) * @return the result of get tryLock */ - boolean tryLock(String lockName, String unlockPassword, long millisecondsToExpireIncaseUnableToUnlock); + boolean tryLock(String lockName, String unlockPassword, long ttlToExpireIncaseUnableToUnlock, TimeUnit timeUnit); /** * unlocks the Distributed Lock instance @@ -61,7 +61,7 @@ default String generateUnlockPassword() { * @return true is debounced (failed to acquire lock), false is not debounced (acquired lock successfully */ default boolean debounced(String key, String unlockPassword, long ttlMinute) { - return !tryLock(key, unlockPassword, ttlMinute * 60000); + return debounced(key, unlockPassword, ttlMinute, TimeUnit.MINUTES); } /** @@ -72,7 +72,7 @@ default boolean debounced(String key, String unlockPassword, long ttlMinute) { * @return true is debounced (failed to acquire lock), false is not debounced (acquired lock successfully */ default boolean debounced(String key, String unlockPassword, long ttl, TimeUnit timeUnit) { - return !tryLock(key, unlockPassword, timeUnit.toMillis(ttl)); + return !tryLock(key, unlockPassword, ttl, timeUnit); } /** diff --git a/src/main/java/org/summerboot/jexpress/integration/cache/BootCache_RedisImple.java b/src/main/java/org/summerboot/jexpress/integration/cache/BootCache_RedisImple.java index f40b9f44..fcfe6e1e 100644 --- a/src/main/java/org/summerboot/jexpress/integration/cache/BootCache_RedisImple.java +++ b/src/main/java/org/summerboot/jexpress/integration/cache/BootCache_RedisImple.java @@ -216,19 +216,19 @@ protected void onNoticeAutoFailover(String info, String newNode) { * this is a Distributed non-blocking version of lock() method; it attempts * to acquire the lock immediately, return true if locking succeeds * - * @param lockName the name of the tryLock - * @param unlockPassword unlockPassword is to be used for unlock. To protect - * a tryLock from being unlocked by anyone, a tryLock cannot be released - * when unlockPassword not match - * @param millisecondsToExpireIncaseUnableToUnlock expire time of tryLock in - * case unable to unlock (e.g. exception/error before executing unlock) + * @param lockName the name of the tryLock + * @param unlockPassword unlockPassword is to be used for unlock. To protect + * a tryLock from being unlocked by anyone, a tryLock cannot be released + * when unlockPassword not match + * @param ttlToExpireIncaseUnableToUnlock expire time of tryLock in + * case unable to unlock (e.g. exception/error before executing unlock) * @return the result of get tryLock */ @Override - public boolean tryLock(String lockName, String unlockPassword, long millisecondsToExpireIncaseUnableToUnlock) { + public boolean tryLock(String lockName, String unlockPassword, long ttlToExpireIncaseUnableToUnlock, TimeUnit timeUnit) { final Holder holder = new Holder<>(false); execute(true, jedis -> { - SetParams p = new SetParams().nx().px(millisecondsToExpireIncaseUnableToUnlock); + SetParams p = new SetParams().nx().px(timeUnit.toMillis(ttlToExpireIncaseUnableToUnlock)); String result = jedis.set(lockName, unlockPassword, p); boolean isLocked = REDIS_SUCCESS.equalsIgnoreCase(result); From 1256a52d872b9b67bb17fa74e6bac89bd9d50790 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Mon, 8 Jul 2024 15:18:19 -0400 Subject: [PATCH 05/34] wip --- .../jexpress/integration/cache/BootCache.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/integration/cache/BootCache.java b/src/main/java/org/summerboot/jexpress/integration/cache/BootCache.java index 627478f8..d8c847ae 100644 --- a/src/main/java/org/summerboot/jexpress/integration/cache/BootCache.java +++ b/src/main/java/org/summerboot/jexpress/integration/cache/BootCache.java @@ -36,6 +36,7 @@ public interface BootCache { * when unlockPassword not match * @param ttlToExpireIncaseUnableToUnlock expire time of tryLock in * case unable to unlock (e.g. exception/error before executing unlock) + * @param timeUnit * @return the result of get tryLock */ boolean tryLock(String lockName, String unlockPassword, long ttlToExpireIncaseUnableToUnlock, TimeUnit timeUnit); @@ -54,16 +55,6 @@ default String generateUnlockPassword() { return UUID.randomUUID().toString() + "_" + BootConstant.PID + "_" + Thread.currentThread().getName(); } - /** - * @param key - * @param unlockPassword - * @param ttlMinute - * @return true is debounced (failed to acquire lock), false is not debounced (acquired lock successfully - */ - default boolean debounced(String key, String unlockPassword, long ttlMinute) { - return debounced(key, unlockPassword, ttlMinute, TimeUnit.MINUTES); - } - /** * @param key * @param unlockPassword From c4b54345969c12a9acc87a9c6b26cb00eeec1b28 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Mon, 8 Jul 2024 16:48:40 -0400 Subject: [PATCH 06/34] afterProcess only get triggered when beforeProcess success --- .../jexpress/nio/server/BootHttpRequestHandler.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpRequestHandler.java b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpRequestHandler.java index ba89dab0..b7fa9e57 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpRequestHandler.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpRequestHandler.java @@ -68,6 +68,7 @@ protected ProcessorSettings service(final ChannelHandlerContext ctx, final HttpH final String httpRequestPath, final Map> queryParams, final String httpPostRequestBody, final ServiceContext context) { ProcessorSettings processorSettings = null; RequestProcessor processor = null; + boolean preProcessSuccess = false; try { // step1. find controller and the action in it processor = getRequestProcessor(httptMethod, httpRequestPath); @@ -105,10 +106,12 @@ protected ProcessorSettings service(final ChannelHandlerContext ctx, final HttpH if (authenticator != null && !authenticator.customizedAuthorizationCheck(processor, httpRequestHeaders, httpRequestPath, context)) { return processorSettings; } - if (!httpLifecycleListener.beforeProcess(processor, httpRequestHeaders, httpRequestPath, context)) { + preProcessSuccess = httpLifecycleListener.beforeProcess(processor, httpRequestHeaders, httpRequestPath, context); + if (preProcessSuccess) { + processor.process(ctx, httpRequestHeaders, httpRequestPath, queryParams, httpPostRequestBody, context); + } else { return processorSettings; } - processor.process(ctx, httpRequestHeaders, httpRequestPath, queryParams, httpPostRequestBody, context); //} catch (ExpiredJwtException | SignatureException | MalformedJwtException ex) { // nak(context, HttpResponseStatus.UNAUTHORIZED, BootErrorCode.AUTH_INVALID_TOKEN, "Invalid JWT"); } catch (NamingException ex) { @@ -140,7 +143,9 @@ protected ProcessorSettings service(final ChannelHandlerContext ctx, final HttpH } catch (Throwable ex) { httpExceptionListener.onUnexpectedException(ex, processor, ctx, httpRequestHeaders, httptMethod, httpRequestPath, queryParams, httpPostRequestBody, context); } finally { - httpLifecycleListener.afterProcess(processor, ctx, httpRequestHeaders, httptMethod, httpRequestPath, queryParams, httpPostRequestBody, context); + if (preProcessSuccess) { + httpLifecycleListener.afterProcess(processor, ctx, httpRequestHeaders, httptMethod, httpRequestPath, queryParams, httpPostRequestBody, context); + } context.poi(BootPOI.PROCESS_END); } return processorSettings; From 0ac314ce24c964b23e4e787d99b59891daabf9a3 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Mon, 8 Jul 2024 22:12:54 -0400 Subject: [PATCH 07/34] release2.4.9 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c9a5cadc..ff59505f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.summerboot jexpress - 2.4.9-SNAPSHOT + 2.4.9 jar Summer Boot jExpress Summer Boot jExpress focuses on solving non-functional and operational maintainability requirements, From b5cd44836401d562e3ba6dad3a7ae95e26e2e1d3 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Thu, 11 Jul 2024 17:29:33 -0400 Subject: [PATCH 08/34] fixed: rename config file will pause/resume service --- pom.xml | 2 +- .../jexpress/boot/BootConstant.java | 2 +- .../boot/config/ConfigurationMonitor.java | 22 ++++++++++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index ff59505f..a26e9dac 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.summerboot jexpress - 2.4.9 + 2.4.10-SNAPSHOT jar Summer Boot jExpress Summer Boot jExpress focuses on solving non-functional and operational maintainability requirements, diff --git a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java index 6329bd53..bf962cc0 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java @@ -25,7 +25,7 @@ public interface BootConstant { String APP_ID = String.format("%06d", new Random().nextInt(999999)); //version - String VERSION = "SummerBoot.jExpress 2.4.9"; + String VERSION = "SummerBoot.jExpress 2.4.10"; String JEXPRESS_PACKAGE_NAME = "org.summerboot.jexpress"; String DEFAULT_ADMIN_MM = "changeit"; diff --git a/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java b/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java index 86bd8938..3792b06c 100644 --- a/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java @@ -110,12 +110,27 @@ public void onDirectoryDelete(File file) { @Override public void onFileCreate(File file) { + if (!isPauseFile(file)) { + return; + } log.info(() -> "new " + file.getAbsoluteFile()); HealthMonitor.setPauseStatus(true, "file created " + file.getAbsolutePath()); } + @Override + public void onFileDelete(File file) { + if (!isPauseFile(file)) { + return; + } + log.info(() -> "del " + file.getAbsoluteFile()); + HealthMonitor.setPauseStatus(false, "file deleted " + file.getAbsolutePath()); + } + @Override public void onFileChange(File file) { + if (isPauseFile(file)) { + return; + } log.info(() -> "mod " + file.getAbsoluteFile()); // decouple business logic from framework logic // bad example: if(file.equals(AppConstant.CFG_PATH_EMAIL)){...} @@ -125,10 +140,7 @@ public void onFileChange(File file) { } } - @Override - public void onFileDelete(File file) { - log.info(() -> "del " + file.getAbsoluteFile()); - HealthMonitor.setPauseStatus(false, "file deleted " + file.getAbsolutePath()); + private boolean isPauseFile(File file) { + return APUSE_FILE_NAME.equals(file.getName()); } - } From 8cdd21e68a5d75e2c143282c7798841d623f379f Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Wed, 17 Jul 2024 09:43:55 -0400 Subject: [PATCH 09/34] added CLI: -psv --- pom.xml | 2 +- .../summerboot/jexpress/boot/BackOffice.java | 7 ++ .../jexpress/boot/BootConstant.java | 1 + .../jexpress/boot/SummerBigBang.java | 52 ++++++++++++++- .../jexpress/util/PropertiesFile.java | 64 +++++++++++++++++++ 5 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/summerboot/jexpress/util/PropertiesFile.java diff --git a/pom.xml b/pom.xml index a26e9dac..52a7f904 100644 --- a/pom.xml +++ b/pom.xml @@ -209,7 +209,7 @@ 4.1.111.Final 2.0.65.Final - 1.65.0 + 1.65.1 33.2.1-jre 4.27.2 diff --git a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java index 0fdce219..46c1227e 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java +++ b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java @@ -290,6 +290,9 @@ public Map getBootErrorCodeMapping() { @Config(key = "naming.cli.decrypt", defaultValue = "decrypt") private String cliName_decrypt = "decrypt"; + @Config(key = "naming.cli.psv", defaultValue = "psv") + private String cliName_psv = "psv"; + @Config(key = "naming.memo.delimiter", defaultValue = ": ") private String memoDelimiter = ": "; @@ -437,6 +440,10 @@ public String getCliName_decrypt() { return cliName_decrypt; } + public String getCliName_psv() { + return cliName_psv; + } + public String getMemoDelimiter() { return memoDelimiter; } diff --git a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java index bf962cc0..b452523b 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java @@ -76,6 +76,7 @@ public interface BootConstant { String CLI_JWT = BackOffice.agent.getCliName_jwt(); String CLI_ENCRYPT = BackOffice.agent.getCliName_encrypt(); String CLI_DECRYPT = BackOffice.agent.getCliName_decrypt(); + String CLI_PSV = BackOffice.agent.getCliName_psv(); String MEMO_DELIMITER = BackOffice.agent.getMemoDelimiter(); /* diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java b/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java index e9bf126b..da85f260 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java @@ -26,6 +26,8 @@ import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.ParseException; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; import org.quartz.Job; import org.quartz.Scheduler; import org.summerboot.jexpress.boot.annotation.Controller; @@ -40,8 +42,10 @@ import org.summerboot.jexpress.security.JwtUtil; import org.summerboot.jexpress.security.SecurityUtil; import org.summerboot.jexpress.util.FormatterUtil; +import org.summerboot.jexpress.util.PropertiesFile; import org.summerboot.jexpress.util.ReflectionUtil; +import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; @@ -219,6 +223,13 @@ protected void bigBang_LetThereBeCLI(String[] args) { .build(); cliOptions.addOption(arg); + arg = Option.builder(BootConstant.CLI_PSV) + .hasArg().argName("envId") + .desc("Generate configuration list in PSV format with the specified environment id" + + BootConstant.BR + BootConstant.BR + "\t -" + BootConstant.CLI_PSV + " -" + BootConstant.CLI_CONFIG_DOMAIN + " ") + .build(); + cliOptions.addOption(arg); + arg = Option.builder(BootConstant.CLI_DECRYPT) .desc("Decrypt config file content with all \"ENC(encrypted text)\" using password:" + BootConstant.BR + BootConstant.BR + BootConstant.BR + "\t -" + BootConstant.CLI_DECRYPT + " -" + BootConstant.CLI_CONFIG_DOMAIN + " -" + BootConstant.CLI_ADMIN_PWD + " ") @@ -284,12 +295,12 @@ protected List scanImplementation_SummerInitializer() { protected boolean runCLI_Utils() { log.trace(""); boolean continueCLI = true; - //usage + // usage if (cli.hasOption(BootConstant.CLI_USAGE)) { continueCLI = false; cliHelpFormatter.printHelp(appVersion, cliOptions); } - //callerVersion + // callerVersion if (cli.hasOption(BootConstant.CLI_VERSION)) { continueCLI = false; System.out.println(appVersion); @@ -302,7 +313,7 @@ protected boolean runCLI_Utils() { String jwt = JwtUtil.buildSigningKey(signatureAlgorithm); System.out.println(jwt); } - //check unique + // check unique if (cli.hasOption(BootConstant.CLI_LIST_UNIQUE)) { continueCLI = false; String tag = cli.getOptionValue(BootConstant.CLI_LIST_UNIQUE); @@ -389,6 +400,41 @@ protected void bigBang_AndThereWasCLI() { System.exit(0); } + /* + * [generate configurations list in PSV format] + */ + if (cli.hasOption(BootConstant.CLI_PSV)) { + String envId = cli.getOptionValue(BootConstant.CLI_PSV); + StringBuilder sb = new StringBuilder(); + //File path = Paths.get(userSpecifiedConfigDir.getAbsolutePath(), BootConstant.DIR_CONFIGURATION).toFile(); + System.out.println("loading from " + userSpecifiedConfigDir.getAbsolutePath()); + for (final File configFile : userSpecifiedConfigDir.listFiles()) { + if (!configFile.isFile()) { + continue; + } + String fileName = configFile.getName(); + if (!fileName.endsWith(".properties")) { + continue; //skip non-properties file + } + + System.out.println("loading " + configFile.getAbsolutePath()); + try { + PropertiesFile propertiesFile = new PropertiesFile(); + List> pairs = propertiesFile.load(configFile); + for (ImmutablePair pair : pairs) { + String key = pair.getKey(); + String value = pair.getValue(); + sb.append(key).append("|").append(value).append("|").append(envId).append("|").append(fileName).append(BootConstant.BR); + } + } catch (IOException ex) { + sb.append("Failed to generate configurations list in PSV format: " + configFile.getAbsolutePath()).append(BootConstant.BR); + sb.append(ExceptionUtils.getRootCauseMessage(ex)).append(BootConstant.BR); + } + } + System.out.println("\n\n" + sb); + System.exit(0); + } + /* * [IoC] - set user selected implementations to override the default * should be invoked before genesis was initialezed to avoid caller invoks LogManager.static{} diff --git a/src/main/java/org/summerboot/jexpress/util/PropertiesFile.java b/src/main/java/org/summerboot/jexpress/util/PropertiesFile.java new file mode 100644 index 00000000..330960ff --- /dev/null +++ b/src/main/java/org/summerboot/jexpress/util/PropertiesFile.java @@ -0,0 +1,64 @@ +/* + * Copyright 2005-2022 Du Law Office - The Summer Boot Framework Project + * + * The Summer Boot Project licenses this file to you under the Apache License, version 2.0 (the + * "License"); you may not use this file except in compliance with the License and you have no + * policy prohibiting employee contributions back to this file (unless the contributor to this + * file is your current or retired employee). You may obtain a copy of the License at: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.summerboot.jexpress.util; + +import org.apache.commons.lang3.tuple.ImmutablePair; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +/** + * @author Changski Tie Zheng Zhang 张铁铮, 魏泽北, 杜旺财, 杜富贵 + */ +public class PropertiesFile extends Properties { + private List orderedKeys = new ArrayList<>(); + + public List keyList() { + return orderedKeys; + } + + public List> load(File propertiesFile) throws IOException { + try (InputStream is = new FileInputStream(propertiesFile); + InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8);) { + super.load(isr); + } + List> pairs = new ArrayList<>(); + for (String key : orderedKeys) { + pairs.add(new ImmutablePair<>(key, super.getProperty(key))); + } + return pairs; + } + + @Override + public synchronized Object put(Object key, Object value) { + String keyStr = (key instanceof String) ? (String) key : key.toString(); + orderedKeys.add(keyStr); + return super.put(key, value); + } + + @Override + public synchronized void clear() { + orderedKeys.clear(); + super.clear(); + } +} From be99f2445053a08a1a11b429f570e704ecb69f3c Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Wed, 17 Jul 2024 22:33:27 -0400 Subject: [PATCH 10/34] wip: config namespace --- .../summerboot/jexpress/boot/config/BootConfig.java | 12 +++++++++++- .../boot/config/annotation/ImportResource.java | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/config/BootConfig.java b/src/main/java/org/summerboot/jexpress/boot/config/BootConfig.java index 0a845be9..9a8f7565 100644 --- a/src/main/java/org/summerboot/jexpress/boot/config/BootConfig.java +++ b/src/main/java/org/summerboot/jexpress/boot/config/BootConfig.java @@ -26,6 +26,7 @@ import org.apache.logging.log4j.Logger; import org.summerboot.jexpress.boot.config.annotation.Config; import org.summerboot.jexpress.boot.config.annotation.ConfigHeader; +import org.summerboot.jexpress.boot.config.annotation.ImportResource; import org.summerboot.jexpress.security.SecurityUtil; import org.summerboot.jexpress.util.ApplicationUtil; import org.summerboot.jexpress.util.BeanUtil; @@ -380,6 +381,15 @@ public static String generateTemplate(Class configClass) { } } + String namespace = ""; + ImportResource ir = (ImportResource) configClass.getAnnotation(ImportResource.class); + if (ir != null) { + namespace = ir.namespace(); + } + if (!namespace.isBlank()) { + namespace += "."; + } + List configItems = ReflectionUtil.getDeclaredAndSuperClassesFields(configClass, true); boolean hasConfig = false; StringBuilder sb = new StringBuilder(); @@ -501,7 +511,7 @@ public static String generateTemplate(Class configClass) { if (!hasPredefinedValue && !isRequired || hasDefaultValue) { sb.append("#"); } - String key = cfg.key(); + String key = namespace + cfg.key(); sb.append(key).append("="); if (isEncrypted) { sb.append("DEC("); diff --git a/src/main/java/org/summerboot/jexpress/boot/config/annotation/ImportResource.java b/src/main/java/org/summerboot/jexpress/boot/config/annotation/ImportResource.java index 0f99de7b..fb056f80 100644 --- a/src/main/java/org/summerboot/jexpress/boot/config/annotation/ImportResource.java +++ b/src/main/java/org/summerboot/jexpress/boot/config/annotation/ImportResource.java @@ -37,4 +37,6 @@ String checkImplTagUsed() default ""; boolean loadWhenImplTagUsed() default false; + + String namespace() default ""; } From 86e24a4008ae813bb7cd60b1244cb79bffdba6a5 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 23 Jul 2024 21:30:06 -0400 Subject: [PATCH 11/34] wip --- pom.xml | 6 +++--- .../org/summerboot/jexpress/boot/SummerApplication.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 52a7f904..7b5db554 100644 --- a/pom.xml +++ b/pom.xml @@ -190,7 +190,7 @@ 3.3.1 3.2.5 - 3.14.0 + 3.15.0 1.8.0 2.16.1 @@ -206,7 +206,7 @@ 0.11.5 - 4.1.111.Final + 4.1.112.Final 2.0.65.Final 1.65.1 @@ -228,7 +228,7 @@ 2.17.2 8.0.1.Final - 6.0.0 + 6.0.1 7.0.0 diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java index 570da48a..e0b3b1ce 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java @@ -363,7 +363,7 @@ public void start() { } // 6. announcement - log.info(() -> I18n.info.launched.format(userSpecifiedResourceBundle, appVersion + " pid#" + BootConstant.PID)); + log.info(() -> BootConstant.BR + BootConstant.BR + I18n.info.launched.format(userSpecifiedResourceBundle, appVersion + " pid#" + BootConstant.PID) + BootConstant.BR + BootConstant.BR); String fullConfigInfo = sb.toString(); if (appLifecycleListener != null) { From 18f8c23cf0b67dfa6eac944d496530dea52b41c8 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Thu, 25 Jul 2024 10:31:02 -0400 Subject: [PATCH 12/34] refactoring --- .../jexpress/boot/BootConstant.java | 3 + .../jexpress/boot/annotation/Controller.java | 5 +- .../jexpress/nio/server/NioHttpUtil.java | 62 ++++--------------- 3 files changed, 19 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java index b452523b..6b9b018f 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java @@ -59,6 +59,9 @@ public interface BootConstant { String FILE_CFG_NIO = BackOffice.agent.getNioConfigFileName(); String FILE_CFG_GRPC = BackOffice.agent.getgRPCConfigFileName(); + String RESPONSE_HEADER_KEY_REF = "X-Reference"; + String RESPONSE_HEADER_KEY_TS = "X-ServerTs"; + /* * 4. jExpress Default CLI Name */ diff --git a/src/main/java/org/summerboot/jexpress/boot/annotation/Controller.java b/src/main/java/org/summerboot/jexpress/boot/annotation/Controller.java index 7d929796..84e6ce39 100644 --- a/src/main/java/org/summerboot/jexpress/boot/annotation/Controller.java +++ b/src/main/java/org/summerboot/jexpress/boot/annotation/Controller.java @@ -16,6 +16,7 @@ package org.summerboot.jexpress.boot.annotation; import com.google.inject.BindingAnnotation; +import org.summerboot.jexpress.boot.BootConstant; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; @@ -36,7 +37,7 @@ String implTag() default NOT_TAGGED; - String responseHeader_ServerTs() default "X-ServerTs"; + String responseHeader_ServerTs() default BootConstant.RESPONSE_HEADER_KEY_TS; - String responseHeader_Reference() default "X-Reference"; + String responseHeader_Reference() default BootConstant.RESPONSE_HEADER_KEY_REF; } diff --git a/src/main/java/org/summerboot/jexpress/nio/server/NioHttpUtil.java b/src/main/java/org/summerboot/jexpress/nio/server/NioHttpUtil.java index 5cfb4140..37952a35 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/NioHttpUtil.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/NioHttpUtil.java @@ -99,23 +99,24 @@ public static void decodeMimeBase64(String contentBase64, File dest) throws IOEx public static final AsciiString KEEP_ALIVE = new AsciiString("keep-alive"); public static final AsciiString CONNECTION = new AsciiString("Connection"); - public static void sendRedirect(ChannelHandlerContext ctx, String newUri, HttpResponseStatus status) { + private static void sendRedirect(ChannelHandlerContext ctx, String newUri, HttpResponseStatus status) { FullHttpResponse resp = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);//HttpResponseStatus.FOUND, HttpResponseStatus.PERMANENT_REDIRECT : HttpResponseStatus.TEMPORARY_REDIRECT resp.headers().set(HttpHeaderNames.LOCATION, newUri); ctx.writeAndFlush(resp).addListener(ChannelFutureListener.CLOSE); } public static long sendResponse(ChannelHandlerContext ctx, boolean isKeepAlive, final ServiceContext serviceContext, final ErrorAuditor errorAuditor, final ProcessorSettings processorSettings) { - if (processorSettings != null) { - String key = processorSettings.getHttpServiceResponseHeaderName_Reference(); - if (key != null) { - serviceContext.responseHeader(key, serviceContext.txId()); - } - key = processorSettings.getHttpServiceResponseHeaderName_ServerTimestamp(); - if (key != null) { - serviceContext.responseHeader(key, OffsetDateTime.now().format(TimeUtil.ISO_ZONED_DATE_TIME3)); - } + String headerKey_reference; + String headerKey_serverTimestamp; + if (processorSettings == null) { + headerKey_reference = BootConstant.RESPONSE_HEADER_KEY_REF; + headerKey_serverTimestamp = BootConstant.RESPONSE_HEADER_KEY_TS; + } else { + headerKey_reference = processorSettings.getHttpServiceResponseHeaderName_Reference(); + headerKey_serverTimestamp = processorSettings.getHttpServiceResponseHeaderName_ServerTimestamp(); } + serviceContext.responseHeader(headerKey_reference, serviceContext.txId()); + serviceContext.responseHeader(headerKey_serverTimestamp, OffsetDateTime.now().format(TimeUtil.ISO_ZONED_DATE_TIME3)); if (serviceContext.file() != null) { return sendFile(ctx, isKeepAlive, serviceContext); @@ -158,7 +159,7 @@ public static long sendResponse(ChannelHandlerContext ctx, boolean isKeepAlive, protected static final String DEFAULT_CHARSET = "UTF-8"; - public static long sendText(ChannelHandlerContext ctx, boolean isKeepAlive, HttpHeaders serviceHeaders, HttpResponseStatus status, String content, String contentType, String charsetName, boolean flush, ResponseEncoder responseEncoder) { + protected static long sendText(ChannelHandlerContext ctx, boolean isKeepAlive, HttpHeaders serviceHeaders, HttpResponseStatus status, String content, String contentType, String charsetName, boolean flush, ResponseEncoder responseEncoder) { if (content == null) { content = ""; } @@ -223,7 +224,7 @@ public static long sendText(ChannelHandlerContext ctx, boolean isKeepAlive, Http return contentLength; } - public static long sendFile(ChannelHandlerContext ctx, boolean isKeepAlive, final ServiceContext serviceContext) { + private static long sendFile(ChannelHandlerContext ctx, boolean isKeepAlive, final ServiceContext serviceContext) { HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, serviceContext.status()); HttpHeaders h = response.headers(); h.set(serviceContext.responseHeaders()); @@ -385,43 +386,6 @@ public static String sanitizeDocRootUri(String uri, String docroot) { } return System.getProperty("user.dir") + uri; } - - @Deprecated - public static void sendListing(ChannelHandlerContext ctx, File dir) { - FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); - response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8"); - StringBuilder sb = new StringBuilder(); - String dirPath = dir.getPath(); - sb.append("\r\n"); - sb.append(""); - sb.append(dirPath); - sb.append(" dir:"); - sb.append("\r\n"); - sb.append("

"); - sb.append(dirPath).append(" dir:"); - sb.append("

\r\n"); - sb.append("
    "); - sb.append("
  • Link:..
  • \r\n"); - for (File f : dir.listFiles()) { - if (f.isHidden() || !f.canRead()) { - continue; - } - String name = f.getName(); - if (!ALLOWED_FILE_NAME.matcher(name).matches()) { - continue; - } - sb.append("
  • Link:"); - sb.append(name); - sb.append("
  • \r\n"); - } - sb.append("
\r\n"); - ByteBuf buffer = Unpooled.copiedBuffer(sb, io.netty.util.CharsetUtil.UTF_8); - response.content().writeBytes(buffer); - buffer.release(); - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); - } } // List failed = rdlList.keySet() From 27316be3e2aae92904080a1efaeea2c0a9d16feb Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Sat, 27 Jul 2024 02:18:28 -0400 Subject: [PATCH 13/34] refactoring file upload handler --- .../nio/server/BootHttpFileUploadHandler.java | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpFileUploadHandler.java b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpFileUploadHandler.java index 51553594..3c3e8363 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpFileUploadHandler.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpFileUploadHandler.java @@ -40,11 +40,9 @@ import io.netty.util.ReferenceCountUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.summerboot.jexpress.boot.BootConstant; import org.summerboot.jexpress.boot.BootErrorCode; import org.summerboot.jexpress.nio.server.domain.Err; import org.summerboot.jexpress.nio.server.domain.ServiceContext; -import org.summerboot.jexpress.nio.server.domain.ServiceError; import org.summerboot.jexpress.nio.server.multipart.MultipartUtil; import org.summerboot.jexpress.security.auth.Caller; @@ -93,12 +91,12 @@ public BootHttpFileUploadHandler() { protected HttpRequest request; protected boolean isMultipart; protected HttpPostRequestDecoder httpDecoder; - protected long hitIndex; + protected final long hitIndex = NioCounter.COUNTER_BIZ_HIT.incrementAndGet(); + protected final ServiceContext context = ServiceContext.build(hitIndex); protected HttpData partialContent; protected long fileSizeQuota; protected Caller caller; protected Map params; - protected String txId; @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable ex) { @@ -128,8 +126,6 @@ protected void channelRead0(final ChannelHandlerContext ctx, final HttpObject ht isMultipart = MultipartUtil.isMultipart(request); if (isMultipart) { NioCounter.COUNTER_HIT.incrementAndGet(); - hitIndex = NioCounter.COUNTER_BIZ_HIT.incrementAndGet(); - txId = BootConstant.APP_ID + "-" + hitIndex; fileSizeQuota = precheck(ctx, request); if (fileSizeQuota < 1) { ReferenceCountUtil.release(httpObject); @@ -156,8 +152,9 @@ protected void channelRead0(final ChannelHandlerContext ctx, final HttpObject ht if (isOverSized) { reset(); Err err = new Err(BootErrorCode.NIO_FILE_UPLOAD_EXCEED_SIZE_LIMIT, null, String.valueOf(fileSizeQuota), null); - ServiceError e = new ServiceError(txId).addError(err); - NioHttpUtil.sendText(ctx, true, null, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, e.toJson(), null, null, true, null); + ServiceContext context = ServiceContext.build(hitIndex); + context.error(err).status(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE); + NioHttpUtil.sendResponse(ctx, true, context, null, null); } else if (chunk instanceof LastHttpContent) { onLastChunk(ctx); } @@ -212,7 +209,7 @@ protected boolean onPartialChunk(ChannelHandlerContext ctx, long maxAllowedSize) FileUpload fileUpload = (FileUpload) data; if (fileUpload.isCompleted()) { log.debug("file completed " + fileUpload.length()); - onFileUploaded(ctx, fileUpload.getFilename(), fileUpload.getFile(), params, caller); + onFileUploaded(ctx, fileUpload.getFilename(), fileUpload.getFile(), params, caller, context); } break; } @@ -269,10 +266,10 @@ protected void onLastChunk(ChannelHandlerContext ctx) throws IOException { * @return quota (in bytes) of uploaded file size */ protected long precheck(ChannelHandlerContext ctx, HttpRequest req) { - if (!isValidRequestPath(req.method(), req.uri())) { + if (!isValidRequestPath(req.method(), req.uri(), context)) { Err err = new Err(BootErrorCode.NIO_FILE_UPLOAD_BAD_REQUEST, null, "invalid request:" + req.method() + " " + req.uri(), null); - ServiceError e = new ServiceError(txId).addError(err); - NioHttpUtil.sendText(ctx, true, null, HttpResponseStatus.BAD_REQUEST, e.toJson(), null, null, true, null); + context.error(err).status(HttpResponseStatus.BAD_REQUEST); + NioHttpUtil.sendResponse(ctx, true, context, null, null); return 0; } @@ -284,12 +281,12 @@ protected long precheck(ChannelHandlerContext ctx, HttpRequest req) { // } else { // cookies = ServerCookieDecoder.STRICT.decode(value); // } - ServiceContext context = ServiceContext.build(hitIndex); + caller = authenticate(httpHeaders, context); if (caller == null) { Err err = new Err(BootErrorCode.AUTH_INVALID_USER, null, "Unauthorized Caller", null); - ServiceError e = new ServiceError(txId).addError(err); - NioHttpUtil.sendText(ctx, true, null, HttpResponseStatus.FORBIDDEN, e.toJson(), null, null, true, null); + context.error(err).status(HttpResponseStatus.FORBIDDEN); + NioHttpUtil.sendResponse(ctx, true, context, null, null); return 0; } @@ -299,26 +296,26 @@ protected long precheck(ChannelHandlerContext ctx, HttpRequest req) { contentLength = Long.parseLong(cl); } catch (RuntimeException ex) { Err err = new Err(BootErrorCode.NIO_FILE_UPLOAD_BAD_LENGTH, null, "Invalid header: " + HttpHeaderNames.CONTENT_LENGTH + "=" + cl, ex); - ServiceError e = new ServiceError(txId).addError(err); - NioHttpUtil.sendText(ctx, true, null, HttpResponseStatus.BAD_REQUEST, e.toJson(), null, null, true, null); + context.error(err).status(HttpResponseStatus.BAD_REQUEST); + NioHttpUtil.sendResponse(ctx, true, context, null, null); return 0; } - long maxAllowedSize = getCallerFileUploadSizeLimit_Bytes(caller); + long maxAllowedSize = getCallerFileUploadSizeLimit_Bytes(caller, context); if (contentLength > maxAllowedSize) { Err err = new Err(BootErrorCode.NIO_FILE_UPLOAD_EXCEED_SIZE_LIMIT, null, String.valueOf(maxAllowedSize), null); - ServiceError e = new ServiceError(txId).addError(err); - NioHttpUtil.sendText(ctx, true, null, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, e.toJson(), null, null, true, null); + context.error(err).status(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE); + NioHttpUtil.sendResponse(ctx, true, context, null, null); return 0; } return maxAllowedSize; } - protected abstract boolean isValidRequestPath(HttpMethod method, String httpRequestPath); + protected abstract boolean isValidRequestPath(HttpMethod method, String httpRequestPath, ServiceContext context); protected abstract Caller authenticate(final HttpHeaders httpHeaders, ServiceContext context); - protected abstract long getCallerFileUploadSizeLimit_Bytes(Caller caller); + protected abstract long getCallerFileUploadSizeLimit_Bytes(Caller caller, ServiceContext context); - protected abstract void onFileUploaded(ChannelHandlerContext ctx, String fileName, File file, Map params, Caller caller); + protected abstract void onFileUploaded(ChannelHandlerContext ctx, String fileName, File file, Map params, Caller caller, ServiceContext context); } From 7203108a11ea8ac2efeeb77d9ef3d88f62ab0836 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Sat, 27 Jul 2024 09:40:02 -0400 Subject: [PATCH 14/34] HttpClient: remotet Http response status will not be set to the response status of ServiceContext --- .../summerboot/jexpress/integration/httpclient/RPCResult.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/integration/httpclient/RPCResult.java b/src/main/java/org/summerboot/jexpress/integration/httpclient/RPCResult.java index 2aa0500a..ad9e5e2d 100644 --- a/src/main/java/org/summerboot/jexpress/integration/httpclient/RPCResult.java +++ b/src/main/java/org/summerboot/jexpress/integration/httpclient/RPCResult.java @@ -134,9 +134,6 @@ public RPCResult update(JavaType successResponseType, Class successResp } public RPCResult update(ObjectMapper jacksonMapper, JavaType successResponseType, Class successResponseClass, Class errorResponseClass, final ServiceContext context) { - if (context != null) { - context.status(httpStatus); - } if (remoteSuccess) { successResponse = fromJson(jacksonMapper, successResponseType, successResponseClass, context); } else { From 907f07e8844ec70f1983f73fd54a5c21dd2a17da Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Wed, 31 Jul 2024 19:17:09 -0400 Subject: [PATCH 15/34] refactoring --- pom.xml | 4 ++-- .../jexpress/boot/instrumentation/HealthMonitor.java | 2 +- .../jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 7b5db554..0f417790 100644 --- a/pom.xml +++ b/pom.xml @@ -253,8 +253,8 @@ 1.0.10 1.17 - 8.0.4 - 5.0.4 + 8.0.5 + 5.0.5 7.10.2 diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 25a9fa2b..1eaa000e 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -147,7 +147,7 @@ public static boolean isServicePaused() { return paused; } - public static boolean isServiceStatusOk() { + public static boolean isHealthCheckOk() { return healthOk; } diff --git a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java index c61e6af5..60cac3f3 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java @@ -407,7 +407,7 @@ public void process(final ChannelHandlerContext channelHandlerCtx, final HttpHea } try { context.poi(BootPOI.BIZ_BEGIN); - if (rejectWhenHealthCheckFailed && !HealthMonitor.isServiceStatusOk()) { + if (rejectWhenHealthCheckFailed && !HealthMonitor.isServiceAvaliable()) { context.status(HttpResponseStatus.SERVICE_UNAVAILABLE) .error(new Err(BootErrorCode.SERVICE_HEALTH_CHECK_FAILED, null, null, null, "Service health check failed: " + HealthMonitor.getServiceStatusReason())); return; From 0b30b18742f4a0382dbc29bd03cfd08be35f0414 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Thu, 1 Aug 2024 22:36:59 -0400 Subject: [PATCH 16/34] support ISO9601 offset with no colon --- pom.xml | 2 +- .../jexpress/util/FormatterUtil.java | 1 + .../jexpress/util/ReflectionUtil.java | 4 +-- .../summerboot/jexpress/util/TimeUtil.java | 31 +++++++++++++++++++ 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 0f417790..25ebf488 100644 --- a/pom.xml +++ b/pom.xml @@ -211,7 +211,7 @@ 1.65.1 33.2.1-jre - 4.27.2 + 4.27.3 2.2.22 diff --git a/src/main/java/org/summerboot/jexpress/util/FormatterUtil.java b/src/main/java/org/summerboot/jexpress/util/FormatterUtil.java index cceff766..fcc24a16 100644 --- a/src/main/java/org/summerboot/jexpress/util/FormatterUtil.java +++ b/src/main/java/org/summerboot/jexpress/util/FormatterUtil.java @@ -64,6 +64,7 @@ public class FormatterUtil { public static final String REGEX_EMAIL = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"; public static final Pattern REGEX_EMAIL_PATTERN = Pattern.compile(REGEX_EMAIL); + public static String[] parseLines(String txt) { return StringUtils.isBlank(txt) ? EMPTY_STR_ARRAY : txt.split("\\r?\\n"); } diff --git a/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java b/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java index 2a7c74ac..c08038e2 100644 --- a/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java +++ b/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java @@ -424,9 +424,9 @@ public static Object toStandardJavaType(String value, final Class targetClass, f } return Enum.valueOf((Class) targetClass, value); } else if (targetClass.equals(OffsetDateTime.class)) { - return OffsetDateTime.parse(value, DateTimeFormatter.ISO_ZONED_DATE_TIME); + return OffsetDateTime.parse(value, TimeUtil.ISO8601_ZONED_DATE_TIME); } else if (targetClass.equals(ZonedDateTime.class)) { - return ZonedDateTime.parse(value, DateTimeFormatter.ISO_ZONED_DATE_TIME); + return ZonedDateTime.parse(value, TimeUtil.ISO8601_ZONED_DATE_TIME); } else if (targetClass.equals(LocalDateTime.class)) { return LocalDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME); } else if (targetClass.equals(LocalDate.class)) { diff --git a/src/main/java/org/summerboot/jexpress/util/TimeUtil.java b/src/main/java/org/summerboot/jexpress/util/TimeUtil.java index 1e72566a..aa899826 100644 --- a/src/main/java/org/summerboot/jexpress/util/TimeUtil.java +++ b/src/main/java/org/summerboot/jexpress/util/TimeUtil.java @@ -45,6 +45,37 @@ public class TimeUtil { .appendOffset("+HH:MM", "Z") .toFormatter(); + + public static final DateTimeFormatter ISO8601_OFFSET_DATE_TIME = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + .parseLenient() + .optionalStart().appendOffset("+H", "Z").optionalEnd() + .optionalStart().appendOffset("+HH", "Z").optionalEnd() + .optionalStart().appendOffset("+HHmm", "Z").optionalEnd() + .optionalStart().appendOffset("+HH:mm", "Z").optionalEnd() + .optionalStart().appendOffset("+HHMM", "Z").optionalEnd()// no need + .optionalStart().appendOffset("+HH:MM", "Z").optionalEnd()// no need + .optionalStart().appendOffset("+HHMMss", "Z").optionalEnd()// no need + .optionalStart().appendOffset("+HH:MM:ss", "Z").optionalEnd()// no need + .optionalStart().appendOffset("+HHMMSS", "Z").optionalEnd()// no need + .optionalStart().appendOffset("+HH:MM:SS", "Z").optionalEnd()// no need + .optionalStart().appendOffset("+HHmmss", "Z").optionalEnd() + .optionalStart().appendOffset("+HH:mm:ss", "Z").optionalEnd() + //.optionalStart().appendOffsetId().optionalEnd() + .parseStrict() + .toFormatter(); + + public static final DateTimeFormatter ISO8601_ZONED_DATE_TIME = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO8601_OFFSET_DATE_TIME) + .optionalStart() + .appendLiteral('[') + .parseCaseSensitive() + .appendZoneRegionId() + .appendLiteral(']') + .toFormatter(); + public static long getSecondsSinceMidnight(Calendar c) { return 3600 * c.get(Calendar.HOUR_OF_DAY) + 60 * c.get(Calendar.MINUTE) + c.get(Calendar.SECOND); } From 16e6b2c147a129c73508bf2333e9c730f0d73c41 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Fri, 2 Aug 2024 20:09:47 -0400 Subject: [PATCH 17/34] PingHandler returns status reason; NioServer only log status on change --- .../summerboot/jexpress/nio/server/BootHttpPingHandler.java | 3 ++- .../java/org/summerboot/jexpress/nio/server/NioServer.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java index a3a02e05..8d1c9743 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java @@ -66,9 +66,10 @@ protected void channelRead0(final ChannelHandlerContext ctx, final HttpObject ht long hit = NioCounter.COUNTER_PING_HIT.incrementAndGet(); try { HttpResponseStatus status = HealthMonitor.isServiceAvaliable() ? HttpResponseStatus.OK : HttpResponseStatus.SERVICE_UNAVAILABLE; + String reason = HealthMonitor.getServiceStatusReason(); boolean isContinue = httpLifecycleListener.beforeProcessPingRequest(ctx, req.uri(), hit, status); if (isContinue) { - NioHttpUtil.sendText(ctx, HttpUtil.isKeepAlive((HttpRequest) req), null, status, null, null, null, true, null); + NioHttpUtil.sendText(ctx, HttpUtil.isKeepAlive((HttpRequest) req), null, status, reason, null, null, true, null); httpLifecycleListener.afterSendPingResponse(ctx, req.uri(), hit, status); } } finally { diff --git a/src/main/java/org/summerboot/jexpress/nio/server/NioServer.java b/src/main/java/org/summerboot/jexpress/nio/server/NioServer.java index c506a1a1..508023bb 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/NioServer.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/NioServer.java @@ -233,9 +233,9 @@ public void bind(NioConfig nioCfg) throws InterruptedException, SSLException { ThreadPoolExecutor tpe = nioCfg.getBizExecutor(); int active = tpe.getActiveCount(); int queue = tpe.getQueue().size(); - if (hps > 0 || tps > 0 || active > 0 || queue > 0 || HealthMonitor.isServicePaused()) { + long activeChannel = NioCounter.COUNTER_ACTIVE_CHANNEL.get(); + if (hps > 0 || tps > 0 || active > 0 || queue > 0 || activeChannel > 0) { long totalChannel = NioCounter.COUNTER_TOTAL_CHANNEL.get(); - long activeChannel = NioCounter.COUNTER_ACTIVE_CHANNEL.get(); long pool = tpe.getPoolSize(); int core = tpe.getCorePoolSize(); //int queueRemainingCapacity = tpe.getQueue().remainingCapacity(); From 162723a931cf668d68185bc5e675de4c687306b2 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Sat, 3 Aug 2024 10:59:30 -0400 Subject: [PATCH 18/34] refactoring --- pom.xml | 2 +- .../boot/instrumentation/HealthMonitor.java | 45 ++++++++++++------- .../nio/server/BootHttpPingHandler.java | 14 +++++- .../server/ws/rs/JaxRsRequestProcessor.java | 8 ++-- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/pom.xml b/pom.xml index 25ebf488..6f9b9e97 100644 --- a/pom.xml +++ b/pom.xml @@ -238,7 +238,7 @@ 5.1.0 - 5.1.3 + 5.1.4 2.5.0-rc1 1.2.5 diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 1eaa000e..9603f99c 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -110,36 +110,42 @@ protected static void startHealthInspectionSingleton(int inspectionIntervalSecon } } - protected static boolean healthOk = true, paused = false; - protected static String statusReason; + protected static volatile boolean isHealthCheckSuccess = true; + protected static volatile boolean paused = false; + protected static volatile String statusReasonHealthCheck; + protected static volatile String statusReasonPaused; + protected static volatile String statusReasonLastKnown; //protected static HttpResponseStatus status = HttpResponseStatus.OK; - protected static boolean serviceAvaliable = true; + protected static volatile boolean serviceAvaliable = true; public static void setHealthStatus(boolean newStatus, String reason, HealthInspector healthInspector) { setHealthStatus(newStatus, reason, healthInspector, nioCfg.getHealthInspectionIntervalSeconds()); } public static void setHealthStatus(boolean newStatus, String reason, HealthInspector healthInspector, int healthInspectionIntervalSeconds) { - boolean serviceStatusChanged = healthOk != newStatus; - healthOk = newStatus; + boolean serviceStatusChanged = isHealthCheckSuccess ^ newStatus; + isHealthCheckSuccess = newStatus; + statusReasonHealthCheck = reason; updateServiceStatus(serviceStatusChanged, reason); - if (!healthOk && healthInspector != null) { + + if (!isHealthCheckSuccess && healthInspector != null) { startHealthInspectionSingleton(healthInspectionIntervalSeconds, healthInspector); } } public static void setPauseStatus(boolean newStatus, String reason) { - boolean serviceStatusChanged = paused != newStatus; + boolean serviceStatusChanged = paused ^ newStatus; paused = newStatus; + statusReasonPaused = reason; updateServiceStatus(serviceStatusChanged, reason); } protected static void updateServiceStatus(boolean serviceStatusChanged, String reason) { - statusReason = reason; - serviceAvaliable = healthOk && !paused; - log.warn("server status changed: paused={}, healthOk={}, serviceStatusChanged={}, reason: {}", paused, healthOk, serviceStatusChanged, reason); + statusReasonLastKnown = reason; + serviceAvaliable = isHealthCheckSuccess && !paused; + log.warn("server status changed: paused={}, healthOk={}, serviceStatusChanged={}, reason: {}", paused, isHealthCheckSuccess, serviceStatusChanged, reason); if (appLifecycleListener != null) { - appLifecycleListener.onApplicationStatusUpdated(healthOk, paused, serviceStatusChanged, reason); + appLifecycleListener.onApplicationStatusUpdated(isHealthCheckSuccess, paused, serviceStatusChanged, reason); } } @@ -147,18 +153,23 @@ public static boolean isServicePaused() { return paused; } - public static boolean isHealthCheckOk() { - return healthOk; + public static String getStatusReasonPaused() { + return statusReasonPaused; + } + + public static boolean isHealthCheckSuccess() { + return isHealthCheckSuccess; + } + + public static String getStatusReasonHealthCheck() { + return statusReasonHealthCheck; } - // public static HttpResponseStatus getServiceStatus() { -// return status; -// } public static boolean isServiceAvaliable() { return serviceAvaliable; } public static String getServiceStatusReason() { - return statusReason; + return statusReasonLastKnown; } } diff --git a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java index 8d1c9743..4c1b0130 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java @@ -65,8 +65,18 @@ protected void channelRead0(final ChannelHandlerContext ctx, final HttpObject ht isPingRequest = true; long hit = NioCounter.COUNTER_PING_HIT.incrementAndGet(); try { - HttpResponseStatus status = HealthMonitor.isServiceAvaliable() ? HttpResponseStatus.OK : HttpResponseStatus.SERVICE_UNAVAILABLE; - String reason = HealthMonitor.getServiceStatusReason(); + HttpResponseStatus status; + String reason; + if (!HealthMonitor.isHealthCheckSuccess()) { + status = HttpResponseStatus.BAD_GATEWAY; + reason = HealthMonitor.getStatusReasonHealthCheck(); + } else if (HealthMonitor.isServicePaused()) { + status = HttpResponseStatus.SERVICE_UNAVAILABLE; + reason = HealthMonitor.getStatusReasonPaused(); + } else { + status = HttpResponseStatus.OK; + reason = null; + } boolean isContinue = httpLifecycleListener.beforeProcessPingRequest(ctx, req.uri(), hit, status); if (isContinue) { NioHttpUtil.sendText(ctx, HttpUtil.isKeepAlive((HttpRequest) req), null, status, reason, null, null, true, null); diff --git a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java index 60cac3f3..a26c6699 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java @@ -407,14 +407,14 @@ public void process(final ChannelHandlerContext channelHandlerCtx, final HttpHea } try { context.poi(BootPOI.BIZ_BEGIN); - if (rejectWhenHealthCheckFailed && !HealthMonitor.isServiceAvaliable()) { - context.status(HttpResponseStatus.SERVICE_UNAVAILABLE) - .error(new Err(BootErrorCode.SERVICE_HEALTH_CHECK_FAILED, null, null, null, "Service health check failed: " + HealthMonitor.getServiceStatusReason())); + if (rejectWhenHealthCheckFailed && !HealthMonitor.isHealthCheckSuccess()) { + context.status(HttpResponseStatus.BAD_GATEWAY) + .error(new Err(BootErrorCode.SERVICE_HEALTH_CHECK_FAILED, null, null, null, "Service health check failed: " + HealthMonitor.getStatusReasonHealthCheck())); return; } if (rejectWhenPaused && HealthMonitor.isServicePaused()) { context.status(HttpResponseStatus.SERVICE_UNAVAILABLE) - .error(new Err(BootErrorCode.SERVICE_PAUSED, null, null, null, "Service is paused: " + HealthMonitor.getServiceStatusReason())); + .error(new Err(BootErrorCode.SERVICE_PAUSED, null, null, null, "Service is paused: " + HealthMonitor.getStatusReasonPaused())); return; } From cdc80ef40f6da8a463fea65eaabd9773ed6c7015 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Sat, 3 Aug 2024 11:06:03 -0400 Subject: [PATCH 19/34] PingHandler returns 502 Bad Gateway when service paused; 503 Service Unavailable when health check failed --- .../jexpress/nio/server/BootHttpPingHandler.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java index 4c1b0130..b1dcd617 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java @@ -66,20 +66,19 @@ protected void channelRead0(final ChannelHandlerContext ctx, final HttpObject ht long hit = NioCounter.COUNTER_PING_HIT.incrementAndGet(); try { HttpResponseStatus status; - String reason; + final String internalReason = null;// Do NOT expose to caller! if (!HealthMonitor.isHealthCheckSuccess()) { status = HttpResponseStatus.BAD_GATEWAY; - reason = HealthMonitor.getStatusReasonHealthCheck(); + //internalReason = HealthMonitor.getStatusReasonHealthCheck(); } else if (HealthMonitor.isServicePaused()) { status = HttpResponseStatus.SERVICE_UNAVAILABLE; - reason = HealthMonitor.getStatusReasonPaused(); + //internalReason = HealthMonitor.getStatusReasonPaused(); } else { status = HttpResponseStatus.OK; - reason = null; } boolean isContinue = httpLifecycleListener.beforeProcessPingRequest(ctx, req.uri(), hit, status); if (isContinue) { - NioHttpUtil.sendText(ctx, HttpUtil.isKeepAlive((HttpRequest) req), null, status, reason, null, null, true, null); + NioHttpUtil.sendText(ctx, HttpUtil.isKeepAlive((HttpRequest) req), null, status, internalReason, null, null, true, null); httpLifecycleListener.afterSendPingResponse(ctx, req.uri(), hit, status); } } finally { From 8500e2807326854657596591744796e8250e42ca Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Sat, 3 Aug 2024 12:01:28 -0400 Subject: [PATCH 20/34] refactoring: HealthInspector can set log level and status of pause/healthcheck --- .../boot/instrumentation/HealthInspector.java | 16 ++++++ .../boot/instrumentation/HealthMonitor.java | 49 ++++++++++++++----- .../nio/server/BootHttpPingHandler.java | 2 +- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java index 1ebf6b2b..57d3b792 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java @@ -15,6 +15,7 @@ */ package org.summerboot.jexpress.boot.instrumentation; +import org.apache.logging.log4j.Level; import org.summerboot.jexpress.nio.server.domain.Err; import java.util.List; @@ -29,4 +30,19 @@ public interface HealthInspector { AtomicLong healthInspectorCounter = new AtomicLong(0); List ping(T... param); + + default Status getStatus() { + return Status.HealthCheckFailed; + } + + /** + * @return null to disable logging + */ + default Level logLevel() { + return Level.WARN; + } + + enum Status { + HealthCheckFailed, ServicePaused + } } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 9603f99c..d57a30f7 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -15,6 +15,7 @@ */ package org.summerboot.jexpress.boot.instrumentation; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.summerboot.jexpress.boot.BackOffice; @@ -70,29 +71,52 @@ protected static void startHealthInspectionSingleton(int inspectionIntervalSecon errors = healthInspector.ping(); } catch (Throwable ex) { } + HealthInspector.Status status = healthInspector.getStatus(); inspectionFailed = errors != null && !errors.isEmpty(); if (inspectionFailed) { - String inspectionReport; - try { - inspectionReport = BeanUtil.toJson(errors, true, true); - } catch (Throwable ex) { - inspectionReport = "total " + ex; + // log error + Level level = healthInspector.logLevel(); + if (level != null && log.isEnabled(level)) { + String inspectionReport; + try { + inspectionReport = BeanUtil.toJson(errors, true, true); + } catch (Throwable ex) { + inspectionReport = "total " + ex; + } + sb.append(inspectionReport); + sb.append(BootConstant.BR).append(", will inspect again in ").append(inspectionIntervalSeconds).append(" seconds"); + log.log(level, sb); } - sb.append(inspectionReport); - sb.append(BootConstant.BR).append(", will inspect again in ").append(inspectionIntervalSeconds).append(" seconds"); - log.warn(sb); - if (appLifecycleListener != null) { - appLifecycleListener.onHealthInspectionFailed(retryIndex, sb.toString(), inspectionIntervalSeconds); + // notify + switch (status) { + case ServicePaused -> { + setPauseStatus(true, sb.toString()); + } + case HealthCheckFailed -> { + if (appLifecycleListener != null) { + appLifecycleListener.onHealthInspectionFailed(retryIndex, sb.toString(), inspectionIntervalSeconds); + } + } } + // wait try { TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } else { + // log success sb.append("passed"); - setHealthStatus(true, sb.toString(), null); + // notify + switch (status) { + case ServicePaused -> { + setPauseStatus(false, sb.toString()); + } + case HealthCheckFailed -> { + setHealthStatus(true, sb.toString(), null); + } + } } } while (inspectionFailed); } finally { @@ -143,6 +167,9 @@ public static void setPauseStatus(boolean newStatus, String reason) { protected static void updateServiceStatus(boolean serviceStatusChanged, String reason) { statusReasonLastKnown = reason; serviceAvaliable = isHealthCheckSuccess && !paused; + if (!serviceStatusChanged) { + return; + } log.warn("server status changed: paused={}, healthOk={}, serviceStatusChanged={}, reason: {}", paused, isHealthCheckSuccess, serviceStatusChanged, reason); if (appLifecycleListener != null) { appLifecycleListener.onApplicationStatusUpdated(isHealthCheckSuccess, paused, serviceStatusChanged, reason); diff --git a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java index b1dcd617..99bdd7b7 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/BootHttpPingHandler.java @@ -66,7 +66,7 @@ protected void channelRead0(final ChannelHandlerContext ctx, final HttpObject ht long hit = NioCounter.COUNTER_PING_HIT.incrementAndGet(); try { HttpResponseStatus status; - final String internalReason = null;// Do NOT expose to caller! + final String internalReason = null;// Do NOT expose to external caller! if (!HealthMonitor.isHealthCheckSuccess()) { status = HttpResponseStatus.BAD_GATEWAY; //internalReason = HealthMonitor.getStatusReasonHealthCheck(); From e901d12bdd4cd3cccc070a8b5cea5321efd8b5c1 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Sat, 3 Aug 2024 12:05:29 -0400 Subject: [PATCH 21/34] java doc plugin to 17 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6f9b9e97..63b28c61 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ maven-javadoc-plugin 3.6.3 - 11 + 17 UTF-8 UTF-8 From d7bbb48241a9a2c7ef23f1349d791ebe1b449c3f Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Sun, 4 Aug 2024 21:45:49 -0400 Subject: [PATCH 22/34] service pause by multiple reasons via password, and can only be resumed by all those reasons via correct password --- .../summerboot/jexpress/boot/BackOffice.java | 26 +- .../jexpress/boot/BootConstant.java | 4 + .../jexpress/boot/BootGuiceModule.java | 27 +- .../jexpress/boot/SummerApplication.java | 33 +-- .../jexpress/boot/SummerBigBang.java | 7 + .../jexpress/boot/SummerRunner.java | 9 +- .../DefaultHealthInspector.java} | 37 +-- .../boot/config/ConfigurationMonitor.java | 9 +- .../boot/event/HttpExceptionHandler.java | 12 +- .../boot/instrumentation/HealthInspector.java | 19 +- .../boot/instrumentation/HealthMonitor.java | 246 +++++++++++------- .../nio/server/ws/rs/BootController.java | 18 +- 12 files changed, 245 insertions(+), 202 deletions(-) rename src/main/java/org/summerboot/jexpress/boot/{instrumentation/BootHealthInspectorImpl.java => annotation/DefaultHealthInspector.java} (52%) diff --git a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java index 46c1227e..8dca86ae 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java +++ b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java @@ -187,15 +187,15 @@ public Map getBootErrorCodeMapping() { private String portInUseAlertMessage = ALERT_MSG_PORT_IN_USE; @Config(key = "backoffice.executor.core", defaultValue = "3", - desc = "MaxSize 0 = current computer/VM's available processors + 1") + desc = "0 = current computer/VM's available processors + 1") private int tpeCore = 3; - @Config(key = "backoffice.executor.max", defaultValue = "" + Integer.MAX_VALUE, - desc = "MaxSize 0 = current computer/VM's available processors + 1") - private int tpeMax = Integer.MAX_VALUE; + @Config(key = "backoffice.executor.max", defaultValue = "3", + desc = "0 = current computer/VM's available processors + 1") + private int tpeMax = 3; - @Config(key = "backoffice.executor.queue", defaultValue = "0") - private int tpeQueue = 0; + @Config(key = "backoffice.executor.queue", defaultValue = "" + Integer.MAX_VALUE) + private int tpeQueue = Integer.MAX_VALUE; @Config(key = "backoffice.executor.keepAliveTimeSec", defaultValue = "60") private int tpeKeepAliveSeconds = 60; @@ -231,6 +231,12 @@ public Map getBootErrorCodeMapping() { @Config(key = "naming.file.gRPCConfig", defaultValue = "cfg_grpc.properties") private String gRPCConfigFileName = "cfg_grpc.properties"; + @Config(key = "HealthMonitor.ReleasePausePassword.viaFile", defaultValue = "releasePausePasswordViafile") + private String healthMonitorReleasePausePasswordViaFile = "releasePausePasswordViafile"; + + @Config(key = "HealthMonitor.ReleasePausePassword.viaWeb", defaultValue = "releasePausePasswordViaWeb") + private String healthMonitorReleasePausePasswordViaWeb = "releasePausePasswordViaWeb"; + @ConfigHeader(title = "4.2 Default Log4j2.xml Variables Naming") @Config(key = "naming.log4j2.xml.var.logId", defaultValue = "logId") private String log4j2LogId = "logId"; @@ -364,6 +370,14 @@ public String getgRPCConfigFileName() { return gRPCConfigFileName; } + public String getHealthMonitorReleasePausePasswordViaFile() { + return healthMonitorReleasePausePasswordViaFile; + } + + public String getHealthMonitorReleasePausePasswordViaWeb() { + return healthMonitorReleasePausePasswordViaWeb; + } + public String getLog4J2LogId() { return log4j2LogId; } diff --git a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java index 6b9b018f..ab87ee8b 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java @@ -62,6 +62,10 @@ public interface BootConstant { String RESPONSE_HEADER_KEY_REF = "X-Reference"; String RESPONSE_HEADER_KEY_TS = "X-ServerTs"; + String HEALTHMONITOR_RELEASEPAUSE_PASSWORD_FILE = BackOffice.agent.getHealthMonitorReleasePausePasswordViaFile(); + String HEALTHMONITOR_RELEASEPAUSE_PASSWORD_WEB = BackOffice.agent.getHealthMonitorReleasePausePasswordViaWeb(); + ; + /* * 4. jExpress Default CLI Name */ diff --git a/src/main/java/org/summerboot/jexpress/boot/BootGuiceModule.java b/src/main/java/org/summerboot/jexpress/boot/BootGuiceModule.java index c036a5a3..800a59e4 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootGuiceModule.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootGuiceModule.java @@ -24,15 +24,14 @@ import org.apache.commons.lang3.StringUtils; import org.quartz.Scheduler; import org.summerboot.jexpress.boot.annotation.Controller; +import org.summerboot.jexpress.boot.annotation.DefaultHealthInspector; import org.summerboot.jexpress.boot.event.AppLifecycleHandler; import org.summerboot.jexpress.boot.event.AppLifecycleListener; import org.summerboot.jexpress.boot.event.HttpExceptionHandler; import org.summerboot.jexpress.boot.event.HttpExceptionListener; import org.summerboot.jexpress.boot.event.HttpLifecycleHandler; import org.summerboot.jexpress.boot.event.HttpLifecycleListener; -import org.summerboot.jexpress.boot.instrumentation.BootHealthInspectorImpl; import org.summerboot.jexpress.boot.instrumentation.HTTPClientStatusListener; -import org.summerboot.jexpress.boot.instrumentation.HealthInspector; import org.summerboot.jexpress.boot.instrumentation.NIOStatusListener; import org.summerboot.jexpress.boot.instrumentation.jmx.InstrumentationMgr; import org.summerboot.jexpress.boot.instrumentation.jmx.InstrumentationMgrImpl; @@ -107,9 +106,6 @@ public void configure() { memo.append(INFO).append(ChannelHandler.class.getName()).append(BIND_TO).append(BootHttpPingHandler.class.getSimpleName()).append(", named=").append(BootHttpPingHandler.class.getSimpleName()); // 4. @Services - bind(HealthInspector.class).to(BootHealthInspectorImpl.class); - memo.append(INFO).append(HealthInspector.class.getName()).append(BIND_TO).append(BootHealthInspectorImpl.class.getName()); - bind(AuthTokenCache.class).to(AuthTokenCacheLocalImpl.class); memo.append(INFO).append(AuthTokenCache.class.getName()).append(BIND_TO).append(AuthTokenCacheLocalImpl.class.getName()); @@ -134,8 +130,9 @@ public void configure() { bind(ChannelHandler.class).annotatedWith(Names.named(BootHttpRequestHandler.class.getSimpleName())).to(BootHttpRequestHandler.class); memo.append(INFO).append(ChannelHandler.class.getName()).append(BIND_TO).append(BootHttpRequestHandler.class.getSimpleName()).append(", named=").append(BootHttpRequestHandler.class.getSimpleName()); - // 5. Controllers + // 5. get instances scanAnnotation_BindInstance(binder(), Controller.class, callerRootPackageName); + scanAnnotation_BindInstance(binder(), DefaultHealthInspector.class, callerRootPackageName); // 6. caller's Main class (App.Main) if (caller != null) { @@ -149,7 +146,8 @@ public void configure() { /** * This method will be called by *
-     * Guice.createInjector(...) from SummerBigBang.genesis(...) to trigger SummerBigBang.onGuiceInjectorCreated_ControllersInjected(@Controller {@code Map} controllers)
+     * Guice.createInjector(...) from SummerBigBang.genesis(...)
+     * it will trigger SummerBigBang.onGuiceInjectorCreated_ControllersInjected(@Controller {@code Map} controllers)
      * 
* * @param binder @@ -167,16 +165,19 @@ protected void scanAnnotation_BindInstance(Binder binder, Class> classes = ReflectionUtil.getAllImplementationsByAnnotation(annotation, false, rootPackageNames); //classesAll.addAll(classes); for (Class c : classes) { - Controller a = (Controller) c.getAnnotation(annotation); - String implTag = a.implTag(); - if (StringUtils.isNotBlank(implTag) && !isCliUseImplTag(implTag)) { - continue; - } - + // + Annotation a = c.getAnnotation(annotation); int mod = c.getModifiers(); if (Modifier.isAbstract(mod) || Modifier.isInterface(mod)) { continue; } + if (a instanceof Controller) { + Controller ca = (Controller) a; + String implTag = ca.implTag(); + if (StringUtils.isNotBlank(implTag) && !isCliUseImplTag(implTag)) { + continue; + } + } classesAll.add(c); } //} diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java index e0b3b1ce..d136abf6 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java @@ -25,7 +25,6 @@ import org.quartz.SchedulerException; import org.summerboot.jexpress.boot.config.ConfigUtil; import org.summerboot.jexpress.boot.event.AppLifecycleListener; -import org.summerboot.jexpress.boot.instrumentation.HealthInspector; import org.summerboot.jexpress.boot.instrumentation.HealthMonitor; import org.summerboot.jexpress.boot.instrumentation.NIOStatusListener; import org.summerboot.jexpress.boot.instrumentation.Timeout; @@ -40,7 +39,6 @@ import org.summerboot.jexpress.nio.server.NioConfig; import org.summerboot.jexpress.nio.server.NioServer; import org.summerboot.jexpress.util.ApplicationUtil; -import org.summerboot.jexpress.util.BeanUtil; import java.net.InetSocketAddress; import java.util.ArrayList; @@ -56,8 +54,6 @@ abstract public class SummerApplication extends SummerBigBang { @Inject protected InstrumentationMgr instrumentationMgr; - @Inject - protected HealthInspector healthInspector; protected NioServer httpServer; protected List gRPCServerList = new ArrayList(); @Inject @@ -231,7 +227,6 @@ protected void traceConfig() { memo.append(BootConstant.BR).append("\t- sys.prop.").append(BootConstant.SYS_PROP_APP_PACKAGE_NAME).append(" = ").append(System.getProperty(BootConstant.SYS_PROP_APP_PACKAGE_NAME)); memo.append(BootConstant.BR).append("\t- start: PostOffice=").append(postOffice.getClass().getName()); - memo.append(BootConstant.BR).append("\t- start: HealthInspector=").append(healthInspector.getClass().getName()); //memo.append(BootConstant.BR).append("\t- start: ConfigChangeListener=").append(configChangeListener.getClass().getName()); memo.append(BootConstant.BR).append("\t- start: InstrumentationMgr=").append(instrumentationMgr.getClass().getName()); memoLogged = true; @@ -274,7 +269,7 @@ public void start() { // 3a. runner.run log.trace("3a. runner.run"); - SummerRunner.RunnerContext context = new SummerRunner.RunnerContext(cli, userSpecifiedConfigDir, guiceInjector, healthInspector, postOffice); + SummerRunner.RunnerContext context = new SummerRunner.RunnerContext(cli, userSpecifiedConfigDir, guiceInjector, postOffice); for (SummerRunner summerRunner : summerRunners) { summerRunner.run(context); } @@ -292,29 +287,7 @@ public void start() { String timeoutDesc = BackOffice.agent.getProcessTimeoutAlertMessage(); // 4. health inspection log.trace("4. health inspection"); - StringBuilder sb = new StringBuilder(); - sb.append(BootConstant.BR).append(HealthMonitor.PROMPT); - if (healthInspector != null) { - try (var a = Timeout.watch(healthInspector.getClass().getName() + ".ping()", timeoutMs).withDesc(timeoutDesc)) { - List errors = healthInspector.ping(log); - if (errors == null || errors.isEmpty()) { - sb.append("passed"); - log.info(sb); - } else { - String inspectionReport; - try { - inspectionReport = BeanUtil.toJson(errors, true, true); - } catch (Throwable ex) { - inspectionReport = "total " + errors.size(); - } - sb.append(inspectionReport); - HealthMonitor.setHealthStatus(false, sb.toString(), healthInspector); - } - } - } else { - sb.append("skipped"); - log.warn(sb); - } + HealthMonitor.start(); // 5a. start server: gRPC if (hasGRPCImpl) { @@ -365,7 +338,7 @@ public void start() { // 6. announcement log.info(() -> BootConstant.BR + BootConstant.BR + I18n.info.launched.format(userSpecifiedResourceBundle, appVersion + " pid#" + BootConstant.PID) + BootConstant.BR + BootConstant.BR); - String fullConfigInfo = sb.toString(); + String fullConfigInfo = "";//sb.toString(); if (appLifecycleListener != null) { appLifecycleListener.onApplicationStart(super.appVersion, fullConfigInfo); } diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java b/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java index da85f260..50a8fbda 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java @@ -31,10 +31,12 @@ import org.quartz.Job; import org.quartz.Scheduler; import org.summerboot.jexpress.boot.annotation.Controller; +import org.summerboot.jexpress.boot.annotation.DefaultHealthInspector; import org.summerboot.jexpress.boot.annotation.Order; import org.summerboot.jexpress.boot.config.BootConfig; import org.summerboot.jexpress.boot.config.ConfigUtil; import org.summerboot.jexpress.boot.config.JExpressConfig; +import org.summerboot.jexpress.boot.instrumentation.HealthMonitor; import org.summerboot.jexpress.i18n.I18n; import org.summerboot.jexpress.integration.quartz.QuartzUtil; import org.summerboot.jexpress.nio.server.ws.rs.JaxRsRequestProcessorManager; @@ -602,7 +604,12 @@ protected void onGuiceInjectorCreated_ControllersInjected(@Controller Map defaultHealthInspectors) { + log.trace(""); + HealthMonitor.registerDefaultHealthInspectors(defaultHealthInspectors, memo); } protected void scanImplementation_SummerRunner(Injector injector) { diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerRunner.java b/src/main/java/org/summerboot/jexpress/boot/SummerRunner.java index 67e1f775..e0a5d7e0 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerRunner.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerRunner.java @@ -17,7 +17,6 @@ import com.google.inject.Injector; import org.apache.commons.cli.CommandLine; -import org.summerboot.jexpress.boot.instrumentation.HealthInspector; import org.summerboot.jexpress.integration.smtp.PostOffice; import java.io.File; @@ -32,14 +31,12 @@ class RunnerContext { protected final CommandLine cli; protected final File configDir; protected final Injector guiceInjector; - protected final HealthInspector healthInspector; protected final PostOffice postOffice; - public RunnerContext(CommandLine cli, File configDir, Injector guiceInjector, HealthInspector healthInspector, PostOffice postOffice) { + public RunnerContext(CommandLine cli, File configDir, Injector guiceInjector, PostOffice postOffice) { this.cli = cli; this.configDir = configDir; this.guiceInjector = guiceInjector; - this.healthInspector = healthInspector; this.postOffice = postOffice; } @@ -55,10 +52,6 @@ public Injector getGuiceInjector() { return guiceInjector; } - public HealthInspector getHealthInspector() { - return healthInspector; - } - public PostOffice getPostOffice() { return postOffice; } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/BootHealthInspectorImpl.java b/src/main/java/org/summerboot/jexpress/boot/annotation/DefaultHealthInspector.java similarity index 52% rename from src/main/java/org/summerboot/jexpress/boot/instrumentation/BootHealthInspectorImpl.java rename to src/main/java/org/summerboot/jexpress/boot/annotation/DefaultHealthInspector.java index 3210b070..889196a2 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/BootHealthInspectorImpl.java +++ b/src/main/java/org/summerboot/jexpress/boot/annotation/DefaultHealthInspector.java @@ -13,35 +13,24 @@ * or implied. See the License for the specific language governing permissions and limitations under * the License. */ -package org.summerboot.jexpress.boot.instrumentation; +package org.summerboot.jexpress.boot.annotation; -import com.google.inject.Singleton; -import jakarta.annotation.Nonnull; -import org.summerboot.jexpress.boot.BootConstant; -import org.summerboot.jexpress.nio.server.domain.Err; -import org.summerboot.jexpress.nio.server.domain.ServiceError; +import com.google.inject.BindingAnnotation; -import java.util.List; +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * @author Changski Tie Zheng Zhang 张铁铮, 魏泽北, 杜旺财, 杜富贵 */ -@Singleton -public class BootHealthInspectorImpl implements HealthInspector { - - /** - * @param args Logger, Boolean - * @return - */ - @Override - public List ping(Object... args) { - ServiceError error = new ServiceError(BootConstant.APP_ID + "- ping"); - healthCheck(error, args); - List errors = error.getErrors(); - return errors; - } - - protected void healthCheck(@Nonnull ServiceError error, Object... args) { - } +@Target(value = {ElementType.TYPE, ElementType.PARAMETER, ElementType.METHOD}) +@Retention(value = RetentionPolicy.RUNTIME) +@Documented +@BindingAnnotation +public @interface DefaultHealthInspector { + String[] poi() default {}; } diff --git a/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java b/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java index 3792b06c..d97f7181 100644 --- a/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java @@ -20,6 +20,7 @@ import org.apache.commons.io.monitor.FileAlterationObserver; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.summerboot.jexpress.boot.BootConstant; import org.summerboot.jexpress.boot.instrumentation.HealthMonitor; import java.io.File; @@ -46,9 +47,11 @@ public class ConfigurationMonitor implements FileAlterationListener { protected ConfigurationMonitor() { } + private static final String RELEASE_PAUSE_PASSWORD = BootConstant.HEALTHMONITOR_RELEASEPAUSE_PASSWORD_FILE; + public void start(File folder, int intervalSec, Map cfgUpdateTasks) throws Exception { File pauseFile = Paths.get(folder.getAbsolutePath(), APUSE_FILE_NAME).toFile(); - HealthMonitor.setPauseStatus(pauseFile.exists(), "by file detection " + pauseFile.getAbsolutePath()); + HealthMonitor.setPauseStatus(pauseFile.exists(), "by file detection " + pauseFile.getAbsolutePath(), RELEASE_PAUSE_PASSWORD); if (running) { return; } @@ -114,7 +117,7 @@ public void onFileCreate(File file) { return; } log.info(() -> "new " + file.getAbsoluteFile()); - HealthMonitor.setPauseStatus(true, "file created " + file.getAbsolutePath()); + HealthMonitor.setPauseStatus(true, "file created " + file.getAbsolutePath(), RELEASE_PAUSE_PASSWORD); } @Override @@ -123,7 +126,7 @@ public void onFileDelete(File file) { return; } log.info(() -> "del " + file.getAbsoluteFile()); - HealthMonitor.setPauseStatus(false, "file deleted " + file.getAbsolutePath()); + HealthMonitor.setPauseStatus(false, "file deleted " + file.getAbsolutePath(), RELEASE_PAUSE_PASSWORD); } @Override diff --git a/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java b/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java index a07fa93d..770e08e9 100644 --- a/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java +++ b/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java @@ -25,7 +25,6 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.logging.log4j.Level; import org.summerboot.jexpress.boot.BootErrorCode; -import org.summerboot.jexpress.boot.instrumentation.HealthInspector; import org.summerboot.jexpress.boot.instrumentation.HealthMonitor; import org.summerboot.jexpress.integration.smtp.PostOffice; import org.summerboot.jexpress.integration.smtp.SMTPClientConfig; @@ -50,9 +49,6 @@ @Singleton public class HttpExceptionHandler implements HttpExceptionListener { - @Inject - protected HealthInspector healthInspector; - @Inject protected PostOffice po; @@ -73,7 +69,7 @@ public void onNamingException(NamingException ex, HttpMethod httptMethod, String cause = ex; } if (cause instanceof IOException) {// java.net.UnknownHostException - HealthMonitor.setHealthStatus(false, ex.toString(), healthInspector); + HealthMonitor.setHealthStatus(false, ex.toString()); nakFatal(context, HttpResponseStatus.BAD_GATEWAY, BootErrorCode.NETWORK_ERROR, "LDAP " + cause.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } else { onNamingException(ex, cause, httptMethod, httpRequestPath, context); @@ -93,7 +89,7 @@ public void onPersistenceException(PersistenceException ex, HttpMethod httptMeth cause = ex; } if (cause instanceof IOException) {// java.net.ConnectException - HealthMonitor.setHealthStatus(false, ex.toString(), healthInspector); + HealthMonitor.setHealthStatus(false, ex.toString()); nakFatal(context, HttpResponseStatus.SERVICE_UNAVAILABLE, BootErrorCode.ACCESS_ERROR_DATABASE, "DB " + cause.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } else { onPersistenceException(ex, cause, httptMethod, httpRequestPath, context); @@ -128,13 +124,13 @@ public void onRejectedExecutionException(Throwable ex, HttpMethod httptMethod, S @Override public void onConnectException(Throwable ex, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { - HealthMonitor.setHealthStatus(false, ex.toString(), healthInspector); + HealthMonitor.setHealthStatus(false, ex.toString()); nakFatal(context, HttpResponseStatus.BAD_GATEWAY, BootErrorCode.IO_BASE, "Failed to connect: " + ex.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } @Override public void onIOException(Throwable ex, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { - HealthMonitor.setHealthStatus(false, ex.toString(), healthInspector); + HealthMonitor.setHealthStatus(false, ex.toString()); nakFatal(context, HttpResponseStatus.SERVICE_UNAVAILABLE, BootErrorCode.IO_BASE, "IO issue: " + ex.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java index 57d3b792..578670eb 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java @@ -25,14 +25,14 @@ * @param parameter * @author Changski Tie Zheng Zhang 张铁铮, 魏泽北, 杜旺财, 杜富贵 */ -public interface HealthInspector { +public interface HealthInspector extends Comparable { AtomicLong healthInspectorCounter = new AtomicLong(0); List ping(T... param); - default Status getStatus() { - return Status.HealthCheckFailed; + default Type type() { + return Type.HealthCheck; } /** @@ -42,7 +42,16 @@ default Level logLevel() { return Level.WARN; } - enum Status { - HealthCheckFailed, ServicePaused + default String releasePausePassword() { + return this.getClass().getName(); + } + + enum Type { + HealthCheck, ServicePaused + } + + //@Override Comparable + default int compareTo(T o) { + return this.getClass().getName().compareTo(o.getClass().getName()); } } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index d57a30f7..719122c9 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -23,10 +23,17 @@ import org.summerboot.jexpress.boot.event.AppLifecycleListener; import org.summerboot.jexpress.nio.server.NioConfig; import org.summerboot.jexpress.nio.server.domain.Err; +import org.summerboot.jexpress.nio.server.domain.ServiceError; import org.summerboot.jexpress.util.BeanUtil; +import java.util.HashSet; import java.util.List; -import java.util.concurrent.RejectedExecutionException; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** @@ -36,137 +43,198 @@ public class HealthMonitor { protected static final Logger log = LogManager.getLogger(HealthMonitor.class.getName()); - protected static NioConfig nioCfg = NioConfig.cfg; - public static final String PROMPT = "\tSelf Inspection Result: "; - protected static AppLifecycleListener appLifecycleListener; + protected static volatile AppLifecycleListener appLifecycleListener; + + protected static volatile ExecutorService tpe = Executors.newSingleThreadExecutor(); + protected static volatile LinkedBlockingQueue healthInspectorQueue = new LinkedBlockingQueue<>(); + + protected static volatile Set registeredHealthInspectors = new HashSet<>(); public static void setAppLifecycleListener(AppLifecycleListener listener) { appLifecycleListener = listener; } - protected static void startHealthInspectionSingleton(int inspectionIntervalSeconds, HealthInspector healthInspector) { - if (healthInspector == null || inspectionIntervalSeconds < 1) { - log.debug(() -> "HealthInspection Skipped: healthInspector=" + healthInspector + ", inspectionIntervalSeconds=" + inspectionIntervalSeconds); + public static void registerDefaultHealthInspectors(Map defaultHealthInspectors, StringBuilder memo) { + registeredHealthInspectors.clear(); + if (defaultHealthInspectors == null || defaultHealthInspectors.isEmpty()) { return; } - long i = HealthInspector.healthInspectorCounter.incrementAndGet(); - if (i > 1) { - log.debug(() -> "Duplicated HealthInspection Rejected: total=" + i); - return; + for (Map.Entry entry : defaultHealthInspectors.entrySet()) { + String name = entry.getKey(); + Object healthInspector = entry.getValue(); + if (healthInspector instanceof HealthInspector) { + registeredHealthInspectors.add((HealthInspector) healthInspector); + memo.append(BootConstant.BR).append("\t- DefaultHealthInspector registered: ").append(name).append("=").append(healthInspector.getClass().getName()); + } } - final long timeoutMs = BackOffice.agent.getProcessTimeoutMilliseconds(); - final String timeoutDesc = BackOffice.agent.getProcessTimeoutAlertMessage(); + } + + private static boolean keepRunning = true; + + public static void start() { + inspect(); Runnable asyncTask = () -> { - HealthInspector.healthInspectorCounter.incrementAndGet(); - boolean inspectionFailed; - try { - int retryIndex = 0; - do { - StringBuilder sb = new StringBuilder(); - sb.append(BootConstant.BR).append(PROMPT); - List errors = null; - try (var a = Timeout.watch(healthInspector.getClass().getName() + ".ping()", timeoutMs).withDesc(timeoutDesc)) { - errors = healthInspector.ping(); - } catch (Throwable ex) { - } - HealthInspector.Status status = healthInspector.getStatus(); - - inspectionFailed = errors != null && !errors.isEmpty(); - if (inspectionFailed) { - // log error - Level level = healthInspector.logLevel(); - if (level != null && log.isEnabled(level)) { - String inspectionReport; - try { - inspectionReport = BeanUtil.toJson(errors, true, true); - } catch (Throwable ex) { - inspectionReport = "total " + ex; - } - sb.append(inspectionReport); - sb.append(BootConstant.BR).append(", will inspect again in ").append(inspectionIntervalSeconds).append(" seconds"); - log.log(level, sb); - } - // notify - switch (status) { - case ServicePaused -> { - setPauseStatus(true, sb.toString()); + int inspectionIntervalSeconds = NioConfig.cfg.getHealthInspectionIntervalSeconds(); + long timeoutMs = BackOffice.agent.getProcessTimeoutMilliseconds(); + String timeoutDesc = BackOffice.agent.getProcessTimeoutAlertMessage(); + final Set batchInspectors = new TreeSet<>(); + do { + ServiceError healthCheckFailedReport = new ServiceError(BootConstant.APP_ID + "-HealthMonitor"); + StringBuilder sb = new StringBuilder(); + sb.append(BootConstant.BR).append(PROMPT); + batchInspectors.clear(); + boolean healthCheckAllPassed = true; + try { + // take all health inspectors from the queue, remove duplicated + do { + HealthInspector healthInspector = healthInspectorQueue.take();// block/wait here for health inspectors + batchInspectors.add(healthInspector); + } while (!healthInspectorQueue.isEmpty()); + // inspect + for (HealthInspector healthInspector : batchInspectors) { + String name = healthInspector.getClass().getName(); + try (var a = Timeout.watch(name + ".ping()", timeoutMs).withDesc(timeoutDesc)) { + HealthInspector.Type type = healthInspector.type(); + List errs = healthInspector.ping(); + boolean currentInspectionPassed = errs == null || errs.isEmpty(); + if (!currentInspectionPassed) { + healthInspectorQueue.offer(healthInspector); } - case HealthCheckFailed -> { - if (appLifecycleListener != null) { - appLifecycleListener.onHealthInspectionFailed(retryIndex, sb.toString(), inspectionIntervalSeconds); + switch (type) { + case ServicePaused -> { + String relasePausePassword = healthInspector.releasePausePassword(); + String reason; + if (currentInspectionPassed) { + reason = name + " success"; + setPauseStatus(false, reason, relasePausePassword); + } else { + try { + reason = BeanUtil.toJson(errs, true, true); + } catch (Throwable ex) { + reason = name + " failed " + ex; + } + setPauseStatus(true, reason, relasePausePassword); + } + } + case HealthCheck -> { + if (currentInspectionPassed) { + //sb.append(name).append(" passed"); + } else { + healthCheckAllPassed = false; + Level level = healthInspector.logLevel(); + if (level != null && log.isEnabled(level)) { + healthCheckFailedReport.addErrors(errs); + } + } } } + } catch (Throwable ex) { + healthInspectorQueue.offer(healthInspector); + log.error("HealthInspector error: " + name, ex); } - // wait - try { - TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } else { + } + if (healthCheckAllPassed) { // log success sb.append("passed"); - // notify - switch (status) { - case ServicePaused -> { - setPauseStatus(false, sb.toString()); - } - case HealthCheckFailed -> { - setHealthStatus(true, sb.toString(), null); - } + log.info(sb); + } else { + String inspectionReport; + try { + inspectionReport = BeanUtil.toJson(healthCheckFailedReport, true, true); + } catch (Throwable ex) { + inspectionReport = " toJson failed " + ex; } + sb.append(inspectionReport); + sb.append(BootConstant.BR).append(", will inspect again in ").append(inspectionIntervalSeconds).append(" seconds"); + log.warn(sb); } - } while (inspectionFailed); - } finally { - HealthInspector.healthInspectorCounter.set(0); - } + setHealthStatus(healthCheckAllPassed, sb.toString()); + // wait + TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + log.error("HealthMonitor interrupted", ex); + } + } while (keepRunning); }; - if (i <= 1) { - try { - BackOffice.execute(asyncTask); - } catch (RejectedExecutionException ex2) { - log.debug(() -> "Duplicated HealthInspection Rejected: " + ex2); + tpe.execute(asyncTask); + } + + public static void inspect() { + registeredHealthInspectors.forEach(healthInspectorQueue::offer); + } + + public static void inspect(HealthInspector... healthInspectors) { + if (healthInspectors == null || healthInspectors.length == 0) { + return; + } + for (HealthInspector healthInspector : healthInspectors) { + if (healthInspector == null) { + continue; } - } else { - log.debug("HealthInspection Skipped"); + healthInspectorQueue.add(healthInspector); } } + public static void shutdown() { + keepRunning = false; + tpe.shutdown(); + } + + static { + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + HealthMonitor.shutdown(); + }, "HealthMonitor.shutdownHook") + ); + } + protected static volatile boolean isHealthCheckSuccess = true; protected static volatile boolean paused = false; protected static volatile String statusReasonHealthCheck; protected static volatile String statusReasonPaused; protected static volatile String statusReasonLastKnown; - //protected static HttpResponseStatus status = HttpResponseStatus.OK; - protected static volatile boolean serviceAvaliable = true; + protected static volatile Set relasePausePasswords = new HashSet<>(); - public static void setHealthStatus(boolean newStatus, String reason, HealthInspector healthInspector) { - setHealthStatus(newStatus, reason, healthInspector, nioCfg.getHealthInspectionIntervalSeconds()); - } - - public static void setHealthStatus(boolean newStatus, String reason, HealthInspector healthInspector, int healthInspectionIntervalSeconds) { + public static void setHealthStatus(boolean newStatus, String reason, HealthInspector... healthInspectors) { boolean serviceStatusChanged = isHealthCheckSuccess ^ newStatus; isHealthCheckSuccess = newStatus; statusReasonHealthCheck = reason; updateServiceStatus(serviceStatusChanged, reason); - if (!isHealthCheckSuccess && healthInspector != null) { - startHealthInspectionSingleton(healthInspectionIntervalSeconds, healthInspector); + if (!isHealthCheckSuccess) { + if (healthInspectors != null && healthInspectors.length > 0) { + inspect(healthInspectors); + } else { + inspect();// use default health inspectors + } } } - public static void setPauseStatus(boolean newStatus, String reason) { - boolean serviceStatusChanged = paused ^ newStatus; - paused = newStatus; + public static void setPauseStatus(boolean pauseService, String reason, String relasePausePassword) { + // check lock + if (relasePausePassword == null) { + relasePausePassword = ""; + } + if (pauseService) { + relasePausePasswords.add(relasePausePassword); + } else { + relasePausePasswords.remove(relasePausePassword); + int size = relasePausePasswords.size(); + if (size > 0) {// keep paused by other reasons with different passwords + pauseService = true; + reason += ", still paused by other " + size + " reason(s) with different password(s)"; + } + } + boolean serviceStatusChanged = paused ^ pauseService; + paused = pauseService; statusReasonPaused = reason; updateServiceStatus(serviceStatusChanged, reason); } protected static void updateServiceStatus(boolean serviceStatusChanged, String reason) { statusReasonLastKnown = reason; - serviceAvaliable = isHealthCheckSuccess && !paused; if (!serviceStatusChanged) { return; } @@ -193,7 +261,7 @@ public static String getStatusReasonHealthCheck() { } public static boolean isServiceAvaliable() { - return serviceAvaliable; + return isHealthCheckSuccess && !paused; } public static String getServiceStatusReason() { diff --git a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java index dee1196e..904959aa 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java @@ -51,7 +51,6 @@ import org.summerboot.jexpress.boot.BootErrorCode; import org.summerboot.jexpress.boot.annotation.Deamon; import org.summerboot.jexpress.boot.annotation.Log; -import org.summerboot.jexpress.boot.instrumentation.HealthInspector; import org.summerboot.jexpress.boot.instrumentation.HealthMonitor; import org.summerboot.jexpress.integration.cache.AuthTokenCache; import org.summerboot.jexpress.nio.server.domain.Err; @@ -65,7 +64,6 @@ import javax.naming.NamingException; import java.io.IOException; -import java.util.List; import java.util.concurrent.TimeUnit; /** @@ -113,9 +111,6 @@ abstract public class BootController extends PingController { protected AuthTokenCache authTokenCache; //abstract protected AuthTokenCache getAuthTokenCache(); - @Inject - protected HealthInspector healthInspector; - @Inject protected Authenticator auth; //abstract protected Authenticator getAuthenticator(); @@ -211,16 +206,7 @@ protected String getVersion() { @Deamon //@CaptureTransaction("admin.inspect") public void inspect(@Parameter(hidden = true) final ServiceContext context) { - if (healthInspector == null) { - context.error(new Err(BootErrorCode.ACCESS_BASE, null, null, null, "HealthInspector not provided")).status(HttpResponseStatus.NOT_IMPLEMENTED); - return; - } - List error = healthInspector.ping(); - if (error == null || error.isEmpty()) { - context.txt("inspection passed").errors(null).status(HttpResponseStatus.OK); - } else { - context.errors(error).status(HttpResponseStatus.INTERNAL_SERVER_ERROR); - } + HealthMonitor.inspect(); } @Operation( @@ -260,7 +246,7 @@ public void inspect(@Parameter(hidden = true) final ServiceContext context) { @Deamon //@CaptureTransaction("admin.changeStatus") public void pause(@QueryParam("pause") boolean pause, @Parameter(hidden = true) final ServiceContext context) throws IOException { - HealthMonitor.setPauseStatus(pause, "request by " + context.caller()); + HealthMonitor.setPauseStatus(pause, "request by " + context.caller(), BootConstant.HEALTHMONITOR_RELEASEPAUSE_PASSWORD_WEB); context.status(HttpResponseStatus.NO_CONTENT); } From 7c31d347edd47efa94f5982765ba544b2ab3cb88 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Sun, 4 Aug 2024 23:07:44 -0400 Subject: [PATCH 23/34] service pause by multiple reasons via password, and can only be resumed by all those reasons via correct password --- .../boot/event/AppLifecycleHandler.java | 5 +-- .../boot/instrumentation/HealthMonitor.java | 43 ++++++++++--------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java b/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java index f25ba15c..bbcc058e 100644 --- a/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java +++ b/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java @@ -17,10 +17,10 @@ import com.google.inject.Inject; import com.google.inject.Singleton; -import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.summerboot.jexpress.boot.config.JExpressConfig; +import org.summerboot.jexpress.boot.instrumentation.HealthMonitor; import org.summerboot.jexpress.integration.smtp.PostOffice; import org.summerboot.jexpress.integration.smtp.SMTPClientConfig; @@ -66,8 +66,7 @@ public void onApplicationStop(String appVersion) { public void onApplicationStatusUpdated(boolean healthOk, boolean paused, boolean serviceStatusChanged, String reason) { if (serviceStatusChanged) { boolean serviceAvaliable = healthOk && !paused; - String content = "\n\t server status changed: paused=" + paused + ", OK=" + healthOk + ", serviceAvaliable=" + serviceAvaliable + "\n\t reason: " + reason; - log.log(healthOk ? Level.WARN : Level.FATAL, content); + String content = HealthMonitor.buildMessage(); if (postOffice != null) { postOffice.sendAlertAsync(SMTPClientConfig.cfg.getEmailToAppSupport(), "Service Status Changed", content, null, false); } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 719122c9..bd6dcfea 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -43,8 +43,6 @@ public class HealthMonitor { protected static final Logger log = LogManager.getLogger(HealthMonitor.class.getName()); - public static final String PROMPT = "\tSelf Inspection Result: "; - protected static volatile AppLifecycleListener appLifecycleListener; protected static volatile ExecutorService tpe = Executors.newSingleThreadExecutor(); @@ -82,8 +80,6 @@ public static void start() { final Set batchInspectors = new TreeSet<>(); do { ServiceError healthCheckFailedReport = new ServiceError(BootConstant.APP_ID + "-HealthMonitor"); - StringBuilder sb = new StringBuilder(); - sb.append(BootConstant.BR).append(PROMPT); batchInspectors.clear(); boolean healthCheckAllPassed = true; try { @@ -135,22 +131,17 @@ public static void start() { log.error("HealthInspector error: " + name, ex); } } + String inspectionReport; if (healthCheckAllPassed) { - // log success - sb.append("passed"); - log.info(sb); + inspectionReport = "All health inspectors passed"; } else { - String inspectionReport; try { inspectionReport = BeanUtil.toJson(healthCheckFailedReport, true, true); } catch (Throwable ex) { inspectionReport = " toJson failed " + ex; } - sb.append(inspectionReport); - sb.append(BootConstant.BR).append(", will inspect again in ").append(inspectionIntervalSeconds).append(" seconds"); - log.warn(sb); } - setHealthStatus(healthCheckAllPassed, sb.toString()); + setHealthStatus(healthCheckAllPassed, inspectionReport); // wait TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); } catch (InterruptedException ex) { @@ -190,15 +181,15 @@ public static void shutdown() { ); } - protected static volatile boolean isHealthCheckSuccess = true; - protected static volatile boolean paused = false; + protected static volatile Boolean isHealthCheckSuccess = null; + protected static volatile Boolean isServicePaused = null; protected static volatile String statusReasonHealthCheck; protected static volatile String statusReasonPaused; protected static volatile String statusReasonLastKnown; protected static volatile Set relasePausePasswords = new HashSet<>(); public static void setHealthStatus(boolean newStatus, String reason, HealthInspector... healthInspectors) { - boolean serviceStatusChanged = isHealthCheckSuccess ^ newStatus; + boolean serviceStatusChanged = isHealthCheckSuccess == null || isHealthCheckSuccess ^ newStatus; isHealthCheckSuccess = newStatus; statusReasonHealthCheck = reason; updateServiceStatus(serviceStatusChanged, reason); @@ -227,25 +218,35 @@ public static void setPauseStatus(boolean pauseService, String reason, String re reason += ", still paused by other " + size + " reason(s) with different password(s)"; } } - boolean serviceStatusChanged = paused ^ pauseService; - paused = pauseService; + boolean serviceStatusChanged = isServicePaused == null || isServicePaused ^ pauseService; + isServicePaused = pauseService; statusReasonPaused = reason; updateServiceStatus(serviceStatusChanged, reason); } protected static void updateServiceStatus(boolean serviceStatusChanged, String reason) { statusReasonLastKnown = reason; + log.warn(buildMessage()); if (!serviceStatusChanged) { return; } - log.warn("server status changed: paused={}, healthOk={}, serviceStatusChanged={}, reason: {}", paused, isHealthCheckSuccess, serviceStatusChanged, reason); if (appLifecycleListener != null) { - appLifecycleListener.onApplicationStatusUpdated(isHealthCheckSuccess, paused, serviceStatusChanged, reason); + appLifecycleListener.onApplicationStatusUpdated(isHealthCheckSuccess, isServicePaused, serviceStatusChanged, reason); } } + public static String buildMessage() { + StringBuilder sb = new StringBuilder(); + sb.append(BootConstant.BR) + .append("\t Self Inspection Result: ").append(isHealthCheckSuccess != null && isHealthCheckSuccess ? "passed" : "failed").append(BootConstant.BR) + .append("\t\t cause: ").append(statusReasonHealthCheck).append(BootConstant.BR) + .append("\t Service Status: ").append(isServicePaused != null && isServicePaused ? "paused" : "running").append(BootConstant.BR) + .append("\t\t cause: ").append(statusReasonPaused).append(BootConstant.BR); + return sb.toString(); + } + public static boolean isServicePaused() { - return paused; + return isServicePaused; } public static String getStatusReasonPaused() { @@ -261,7 +262,7 @@ public static String getStatusReasonHealthCheck() { } public static boolean isServiceAvaliable() { - return isHealthCheckSuccess && !paused; + return isHealthCheckSuccess && !isServicePaused; } public static String getServiceStatusReason() { From cbbda7f3f03fc002705b3e0fe470598dba526d20 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Mon, 5 Aug 2024 19:33:30 -0400 Subject: [PATCH 24/34] refactoring --- .../summerboot/jexpress/boot/BackOffice.java | 16 +-- .../jexpress/boot/BootConstant.java | 6 +- .../jexpress/boot/BootGuiceModule.java | 4 +- .../jexpress/boot/SummerBigBang.java | 4 +- ...ultHealthInspector.java => Inspector.java} | 2 +- .../boot/config/ConfigurationMonitor.java | 15 ++- .../boot/event/AppLifecycleHandler.java | 5 +- .../boot/event/AppLifecycleListener.java | 2 +- .../boot/event/HttpExceptionHandler.java | 10 +- .../boot/instrumentation/HealthInspector.java | 8 +- .../boot/instrumentation/HealthMonitor.java | 107 +++++++++++------- .../instrumentation/jmx/ServerStatus.java | 2 +- .../nio/server/ws/rs/BootController.java | 2 +- .../server/ws/rs/JaxRsRequestProcessor.java | 7 +- 14 files changed, 115 insertions(+), 75 deletions(-) rename src/main/java/org/summerboot/jexpress/boot/annotation/{DefaultHealthInspector.java => Inspector.java} (94%) diff --git a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java index 8dca86ae..7c2b6814 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java +++ b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java @@ -231,11 +231,11 @@ public Map getBootErrorCodeMapping() { @Config(key = "naming.file.gRPCConfig", defaultValue = "cfg_grpc.properties") private String gRPCConfigFileName = "cfg_grpc.properties"; - @Config(key = "HealthMonitor.ReleasePausePassword.viaFile", defaultValue = "releasePausePasswordViafile") - private String healthMonitorReleasePausePasswordViaFile = "releasePausePasswordViafile"; + @Config(key = "HealthMonitor.PauseLockCode.viaFile", defaultValue = "PauseLockCode.file") + private String pauseLockCodeViaFile = "PauseLockCode.file"; - @Config(key = "HealthMonitor.ReleasePausePassword.viaWeb", defaultValue = "releasePausePasswordViaWeb") - private String healthMonitorReleasePausePasswordViaWeb = "releasePausePasswordViaWeb"; + @Config(key = "HealthMonitor.PauseLockCode.viaWeb", defaultValue = "PauseLockCode.web") + private String pauseLockCodeViaWeb = "PauseLockCode.web"; @ConfigHeader(title = "4.2 Default Log4j2.xml Variables Naming") @Config(key = "naming.log4j2.xml.var.logId", defaultValue = "logId") @@ -370,12 +370,12 @@ public String getgRPCConfigFileName() { return gRPCConfigFileName; } - public String getHealthMonitorReleasePausePasswordViaFile() { - return healthMonitorReleasePausePasswordViaFile; + public String getPauseLockCodeViaFile() { + return pauseLockCodeViaFile; } - public String getHealthMonitorReleasePausePasswordViaWeb() { - return healthMonitorReleasePausePasswordViaWeb; + public String getPauseLockCodeViaWeb() { + return pauseLockCodeViaWeb; } public String getLog4J2LogId() { diff --git a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java index ab87ee8b..8f7469e3 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java @@ -62,9 +62,9 @@ public interface BootConstant { String RESPONSE_HEADER_KEY_REF = "X-Reference"; String RESPONSE_HEADER_KEY_TS = "X-ServerTs"; - String HEALTHMONITOR_RELEASEPAUSE_PASSWORD_FILE = BackOffice.agent.getHealthMonitorReleasePausePasswordViaFile(); - String HEALTHMONITOR_RELEASEPAUSE_PASSWORD_WEB = BackOffice.agent.getHealthMonitorReleasePausePasswordViaWeb(); - ; + String PAUSE_LOCK_CODE_VIAFILE = BackOffice.agent.getPauseLockCodeViaFile(); + String PAUSE_LOCK_CODE_VIAWEB = BackOffice.agent.getPauseLockCodeViaWeb(); + /* * 4. jExpress Default CLI Name diff --git a/src/main/java/org/summerboot/jexpress/boot/BootGuiceModule.java b/src/main/java/org/summerboot/jexpress/boot/BootGuiceModule.java index 800a59e4..7fd6f1b8 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootGuiceModule.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootGuiceModule.java @@ -24,7 +24,7 @@ import org.apache.commons.lang3.StringUtils; import org.quartz.Scheduler; import org.summerboot.jexpress.boot.annotation.Controller; -import org.summerboot.jexpress.boot.annotation.DefaultHealthInspector; +import org.summerboot.jexpress.boot.annotation.Inspector; import org.summerboot.jexpress.boot.event.AppLifecycleHandler; import org.summerboot.jexpress.boot.event.AppLifecycleListener; import org.summerboot.jexpress.boot.event.HttpExceptionHandler; @@ -132,7 +132,7 @@ public void configure() { // 5. get instances scanAnnotation_BindInstance(binder(), Controller.class, callerRootPackageName); - scanAnnotation_BindInstance(binder(), DefaultHealthInspector.class, callerRootPackageName); + scanAnnotation_BindInstance(binder(), Inspector.class, callerRootPackageName); // 6. caller's Main class (App.Main) if (caller != null) { diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java b/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java index 50a8fbda..6e4488e5 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerBigBang.java @@ -31,7 +31,7 @@ import org.quartz.Job; import org.quartz.Scheduler; import org.summerboot.jexpress.boot.annotation.Controller; -import org.summerboot.jexpress.boot.annotation.DefaultHealthInspector; +import org.summerboot.jexpress.boot.annotation.Inspector; import org.summerboot.jexpress.boot.annotation.Order; import org.summerboot.jexpress.boot.config.BootConfig; import org.summerboot.jexpress.boot.config.ConfigUtil; @@ -607,7 +607,7 @@ protected void onGuiceInjectorCreated_ControllersInjected(@Controller Map defaultHealthInspectors) { + protected void onGuiceInjectorCreated_DefaultHealthInspectorInjected(@Inspector Map defaultHealthInspectors) { log.trace(""); HealthMonitor.registerDefaultHealthInspectors(defaultHealthInspectors, memo); } diff --git a/src/main/java/org/summerboot/jexpress/boot/annotation/DefaultHealthInspector.java b/src/main/java/org/summerboot/jexpress/boot/annotation/Inspector.java similarity index 94% rename from src/main/java/org/summerboot/jexpress/boot/annotation/DefaultHealthInspector.java rename to src/main/java/org/summerboot/jexpress/boot/annotation/Inspector.java index 889196a2..8ad3a460 100644 --- a/src/main/java/org/summerboot/jexpress/boot/annotation/DefaultHealthInspector.java +++ b/src/main/java/org/summerboot/jexpress/boot/annotation/Inspector.java @@ -30,7 +30,7 @@ @Retention(value = RetentionPolicy.RUNTIME) @Documented @BindingAnnotation -public @interface DefaultHealthInspector { +public @interface Inspector { String[] poi() default {}; } diff --git a/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java b/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java index d97f7181..60e77a3f 100644 --- a/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java @@ -47,11 +47,18 @@ public class ConfigurationMonitor implements FileAlterationListener { protected ConfigurationMonitor() { } - private static final String RELEASE_PAUSE_PASSWORD = BootConstant.HEALTHMONITOR_RELEASEPAUSE_PASSWORD_FILE; + private static final String PAUSE_LOCK_CODE = BootConstant.PAUSE_LOCK_CODE_VIAFILE; public void start(File folder, int intervalSec, Map cfgUpdateTasks) throws Exception { File pauseFile = Paths.get(folder.getAbsolutePath(), APUSE_FILE_NAME).toFile(); - HealthMonitor.setPauseStatus(pauseFile.exists(), "by file detection " + pauseFile.getAbsolutePath(), RELEASE_PAUSE_PASSWORD); + boolean pause = pauseFile.exists(); + String cause; + if (pause) { + cause = "File detected: " + pauseFile.getAbsolutePath(); + } else { + cause = "File not detected: " + pauseFile.getAbsolutePath(); + } + HealthMonitor.setPauseStatus(pause, cause, PAUSE_LOCK_CODE); if (running) { return; } @@ -117,7 +124,7 @@ public void onFileCreate(File file) { return; } log.info(() -> "new " + file.getAbsoluteFile()); - HealthMonitor.setPauseStatus(true, "file created " + file.getAbsolutePath(), RELEASE_PAUSE_PASSWORD); + HealthMonitor.setPauseStatus(true, "file created " + file.getAbsolutePath(), PAUSE_LOCK_CODE); } @Override @@ -126,7 +133,7 @@ public void onFileDelete(File file) { return; } log.info(() -> "del " + file.getAbsoluteFile()); - HealthMonitor.setPauseStatus(false, "file deleted " + file.getAbsolutePath(), RELEASE_PAUSE_PASSWORD); + HealthMonitor.setPauseStatus(false, "file deleted " + file.getAbsolutePath(), PAUSE_LOCK_CODE); } @Override diff --git a/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java b/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java index bbcc058e..6c538958 100644 --- a/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java +++ b/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java @@ -74,9 +74,10 @@ public void onApplicationStatusUpdated(boolean healthOk, boolean paused, boolean } @Override - public void onHealthInspectionFailed(int retryIndex, String reason, int nextInspectionIntervalSeconds) { + public void onHealthInspectionFailed(boolean healthOk, boolean paused, long retryIndex, int nextInspectionIntervalSeconds) { if (postOffice != null) { - postOffice.sendAlertAsync(SMTPClientConfig.cfg.getEmailToAppSupport(), "Health Inspection Failed", reason, null, true); + String content = HealthMonitor.buildMessage(); + postOffice.sendAlertAsync(SMTPClientConfig.cfg.getEmailToAppSupport(), "Health Inspection Failed", content, null, true); } } diff --git a/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleListener.java b/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleListener.java index 39d95988..4d08f82e 100644 --- a/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleListener.java +++ b/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleListener.java @@ -37,7 +37,7 @@ public interface AppLifecycleListener { */ void onApplicationStatusUpdated(boolean healthOk, boolean paused, boolean serviceStatusChanged, String reason); - void onHealthInspectionFailed(int retryIndex, String reason, int nextInspectionIntervalSeconds); + void onHealthInspectionFailed(boolean healthOk, boolean paused, long retryIndex, int nextInspectionIntervalSeconds); void onConfigChangeBefore(File configFile, JExpressConfig cfg); diff --git a/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java b/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java index 770e08e9..35dfef54 100644 --- a/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java +++ b/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java @@ -79,7 +79,7 @@ public void onNamingException(NamingException ex, HttpMethod httptMethod, String protected void onNamingException(NamingException ex, Throwable cause, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { Err e = new Err(BootErrorCode.ACCESS_ERROR_LDAP, null, null, ex, cause.toString()); - context.error(e).status(HttpResponseStatus.BAD_GATEWAY); + context.error(e).status(HttpResponseStatus.INTERNAL_SERVER_ERROR); } @Override @@ -90,19 +90,20 @@ public void onPersistenceException(PersistenceException ex, HttpMethod httptMeth } if (cause instanceof IOException) {// java.net.ConnectException HealthMonitor.setHealthStatus(false, ex.toString()); - nakFatal(context, HttpResponseStatus.SERVICE_UNAVAILABLE, BootErrorCode.ACCESS_ERROR_DATABASE, "DB " + cause.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); + nakFatal(context, HttpResponseStatus.BAD_GATEWAY, BootErrorCode.ACCESS_ERROR_DATABASE, "DB " + cause.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } else { onPersistenceException(ex, cause, httptMethod, httpRequestPath, context); } } - public void onPersistenceException(PersistenceException ex, Throwable cause, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { + protected void onPersistenceException(PersistenceException ex, Throwable cause, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { Err e = new Err(BootErrorCode.ACCESS_ERROR_DATABASE, null, null, ex, cause.toString()); context.error(e).status(HttpResponseStatus.INTERNAL_SERVER_ERROR); } @Override public void onHttpConnectTimeoutException(HttpConnectTimeoutException ex, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { + HealthMonitor.setHealthStatus(false, ex.toString()); context.status(HttpResponseStatus.GATEWAY_TIMEOUT) .level(Level.WARN) .error(new Err(BootErrorCode.HTTP_CONNECTION_TIMEOUT, null, null, ex, "Http Connect Timeout: " + ex.getMessage())); @@ -131,8 +132,7 @@ public void onConnectException(Throwable ex, HttpMethod httptMethod, String http @Override public void onIOException(Throwable ex, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { HealthMonitor.setHealthStatus(false, ex.toString()); - nakFatal(context, HttpResponseStatus.SERVICE_UNAVAILABLE, BootErrorCode.IO_BASE, "IO issue: " + ex.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); - + nakFatal(context, HttpResponseStatus.BAD_GATEWAY, BootErrorCode.IO_BASE, "IO issue: " + ex.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } @Override diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java index 578670eb..3d99d9bc 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java @@ -25,9 +25,9 @@ * @param parameter * @author Changski Tie Zheng Zhang 张铁铮, 魏泽北, 杜旺财, 杜富贵 */ -public interface HealthInspector extends Comparable { +public interface HealthInspector extends Comparable { - AtomicLong healthInspectorCounter = new AtomicLong(0); + AtomicLong retryIndex = new AtomicLong(0); List ping(T... param); @@ -42,7 +42,7 @@ default Level logLevel() { return Level.WARN; } - default String releasePausePassword() { + default String pauseLockCode() { return this.getClass().getName(); } @@ -51,7 +51,7 @@ enum Type { } //@Override Comparable - default int compareTo(T o) { + default int compareTo(Object o) { return this.getClass().getName().compareTo(o.getClass().getName()); } } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index bd6dcfea..775544fb 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -20,6 +20,7 @@ import org.apache.logging.log4j.Logger; import org.summerboot.jexpress.boot.BackOffice; import org.summerboot.jexpress.boot.BootConstant; +import org.summerboot.jexpress.boot.annotation.Inspector; import org.summerboot.jexpress.boot.event.AppLifecycleListener; import org.summerboot.jexpress.nio.server.NioConfig; import org.summerboot.jexpress.nio.server.domain.Err; @@ -59,14 +60,23 @@ public static void registerDefaultHealthInspectors(Map defaultHe if (defaultHealthInspectors == null || defaultHealthInspectors.isEmpty()) { return; } + StringBuilder sb = new StringBuilder(); + boolean error = false; for (Map.Entry entry : defaultHealthInspectors.entrySet()) { String name = entry.getKey(); Object healthInspector = entry.getValue(); if (healthInspector instanceof HealthInspector) { registeredHealthInspectors.add((HealthInspector) healthInspector); - memo.append(BootConstant.BR).append("\t- DefaultHealthInspector registered: ").append(name).append("=").append(healthInspector.getClass().getName()); + memo.append(BootConstant.BR).append("\t- @DefaultHealthInspector registered: ").append(name).append("=").append(healthInspector.getClass().getName()); + } else { + error = true; + sb.append(BootConstant.BR).append("\tCoding Error: class ").append(healthInspector.getClass().getName()).append(" has annotation @").append(Inspector.class.getSimpleName()).append(", should implement ").append(HealthInspector.class.getName()); } } + if (error) { + log.fatal(BootConstant.BR + sb + BootConstant.BR); + System.exit(2); + } } private static boolean keepRunning = true; @@ -81,7 +91,7 @@ public static void start() { do { ServiceError healthCheckFailedReport = new ServiceError(BootConstant.APP_ID + "-HealthMonitor"); batchInspectors.clear(); - boolean healthCheckAllPassed = true; + Boolean healthCheckAllPassed = null; try { // take all health inspectors from the queue, remove duplicated do { @@ -100,23 +110,27 @@ public static void start() { } switch (type) { case ServicePaused -> { - String relasePausePassword = healthInspector.releasePausePassword(); + String lockCode = healthInspector.pauseLockCode(); String reason; if (currentInspectionPassed) { reason = name + " success"; - setPauseStatus(false, reason, relasePausePassword); + setPauseStatus(false, reason, lockCode, null); } else { try { reason = BeanUtil.toJson(errs, true, true); } catch (Throwable ex) { reason = name + " failed " + ex; } - setPauseStatus(true, reason, relasePausePassword); + setPauseStatus(true, reason, lockCode, null); } } case HealthCheck -> { if (currentInspectionPassed) { - //sb.append(name).append(" passed"); + if (healthCheckAllPassed == null) { + healthCheckAllPassed = true; + } else { + healthCheckAllPassed &= true; + } } else { healthCheckAllPassed = false; Level level = healthInspector.logLevel(); @@ -131,18 +145,28 @@ public static void start() { log.error("HealthInspector error: " + name, ex); } } - String inspectionReport; - if (healthCheckAllPassed) { - inspectionReport = "All health inspectors passed"; - } else { - try { - inspectionReport = BeanUtil.toJson(healthCheckFailedReport, true, true); - } catch (Throwable ex) { - inspectionReport = " toJson failed " + ex; + if (healthCheckAllPassed != null) { + String inspectionReport; + if (healthCheckAllPassed) { + inspectionReport = "Current all health inspectors passed"; + } else { + try { + inspectionReport = BeanUtil.toJson(healthCheckFailedReport, true, true); + } catch (Throwable ex) { + inspectionReport = " toJson failed " + ex; + } + long retryIndex = HealthInspector.retryIndex.get();// not being set yet + if (appLifecycleListener != null) { + appLifecycleListener.onHealthInspectionFailed(isHealthCheckSuccess, isServicePaused, retryIndex, inspectionIntervalSeconds); + } } + setHealthStatus(healthCheckAllPassed, inspectionReport, null); } - setHealthStatus(healthCheckAllPassed, inspectionReport); + log.warn(buildMessage()); // wait + if (!isServiceAvaliable()) { + log.warn("will check again in " + inspectionIntervalSeconds + " seconds"); + } TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); @@ -158,10 +182,14 @@ public static void inspect() { } public static void inspect(HealthInspector... healthInspectors) { - if (healthInspectors == null || healthInspectors.length == 0) { + if (healthInspectors == null) {// no inspectors + return; + } + if (healthInspectors.length == 0) {// use default inspectors + inspect(); return; } - for (HealthInspector healthInspector : healthInspectors) { + for (HealthInspector healthInspector : healthInspectors) {// use specified inspectors if (healthInspector == null) { continue; } @@ -181,55 +209,56 @@ public static void shutdown() { ); } - protected static volatile Boolean isHealthCheckSuccess = null; - protected static volatile Boolean isServicePaused = null; + protected static volatile boolean isHealthCheckSuccess = true; + protected static volatile boolean isServicePaused = false; + protected static volatile boolean isServiceStatusChanged = false; protected static volatile String statusReasonHealthCheck; protected static volatile String statusReasonPaused; protected static volatile String statusReasonLastKnown; - protected static volatile Set relasePausePasswords = new HashSet<>(); + protected static volatile Set pauseReleaseCodes = new HashSet<>(); public static void setHealthStatus(boolean newStatus, String reason, HealthInspector... healthInspectors) { - boolean serviceStatusChanged = isHealthCheckSuccess == null || isHealthCheckSuccess ^ newStatus; + boolean serviceStatusChanged = isHealthCheckSuccess ^ newStatus; isHealthCheckSuccess = newStatus; statusReasonHealthCheck = reason; updateServiceStatus(serviceStatusChanged, reason); if (!isHealthCheckSuccess) { - if (healthInspectors != null && healthInspectors.length > 0) { - inspect(healthInspectors); - } else { - inspect();// use default health inspectors - } + inspect(healthInspectors); } } - public static void setPauseStatus(boolean pauseService, String reason, String relasePausePassword) { + public static void setPauseStatus(boolean pauseService, String reason, String lockCode, HealthInspector... healthInspectors) { // check lock - if (relasePausePassword == null) { - relasePausePassword = ""; + if (lockCode == null) { + lockCode = ""; } if (pauseService) { - relasePausePasswords.add(relasePausePassword); + pauseReleaseCodes.add(lockCode); } else { - relasePausePasswords.remove(relasePausePassword); - int size = relasePausePasswords.size(); + pauseReleaseCodes.remove(lockCode); + int size = pauseReleaseCodes.size(); if (size > 0) {// keep paused by other reasons with different passwords pauseService = true; reason += ", still paused by other " + size + " reason(s) with different password(s)"; } } - boolean serviceStatusChanged = isServicePaused == null || isServicePaused ^ pauseService; + boolean serviceStatusChanged = isServicePaused ^ pauseService; isServicePaused = pauseService; statusReasonPaused = reason; updateServiceStatus(serviceStatusChanged, reason); + + if (isServicePaused) { + inspect(healthInspectors); + } } protected static void updateServiceStatus(boolean serviceStatusChanged, String reason) { - statusReasonLastKnown = reason; - log.warn(buildMessage()); if (!serviceStatusChanged) { return; } + statusReasonLastKnown = reason; + log.warn(buildMessage()); if (appLifecycleListener != null) { appLifecycleListener.onApplicationStatusUpdated(isHealthCheckSuccess, isServicePaused, serviceStatusChanged, reason); } @@ -238,9 +267,11 @@ protected static void updateServiceStatus(boolean serviceStatusChanged, String r public static String buildMessage() { StringBuilder sb = new StringBuilder(); sb.append(BootConstant.BR) - .append("\t Self Inspection Result: ").append(isHealthCheckSuccess != null && isHealthCheckSuccess ? "passed" : "failed").append(BootConstant.BR) - .append("\t\t cause: ").append(statusReasonHealthCheck).append(BootConstant.BR) - .append("\t Service Status: ").append(isServicePaused != null && isServicePaused ? "paused" : "running").append(BootConstant.BR) + .append("\t Self Inspection Result: ").append(isHealthCheckSuccess ? "passed" : "failed").append(BootConstant.BR); + if (!isHealthCheckSuccess) { + sb.append("\t\t cause: ").append(statusReasonHealthCheck).append(BootConstant.BR); + } + sb.append("\t Service Status: ").append(isServicePaused ? "paused" : "running").append(BootConstant.BR) .append("\t\t cause: ").append(statusReasonPaused).append(BootConstant.BR); return sb.toString(); } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/jmx/ServerStatus.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/jmx/ServerStatus.java index d9d3500c..53a2ce2a 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/jmx/ServerStatus.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/jmx/ServerStatus.java @@ -131,7 +131,7 @@ public String getLastIOReport() { @Override public long getHealthInspector() { - return HealthInspector.healthInspectorCounter.get(); + return HealthInspector.retryIndex.get(); } @Override diff --git a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java index 904959aa..61b3a17b 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java @@ -246,7 +246,7 @@ public void inspect(@Parameter(hidden = true) final ServiceContext context) { @Deamon //@CaptureTransaction("admin.changeStatus") public void pause(@QueryParam("pause") boolean pause, @Parameter(hidden = true) final ServiceContext context) throws IOException { - HealthMonitor.setPauseStatus(pause, "request by " + context.caller(), BootConstant.HEALTHMONITOR_RELEASEPAUSE_PASSWORD_WEB); + HealthMonitor.setPauseStatus(pause, "request by " + context.caller(), BootConstant.PAUSE_LOCK_CODE_VIAWEB); context.status(HttpResponseStatus.NO_CONTENT); } diff --git a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java index a26c6699..9551ca52 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestProcessor.java @@ -44,6 +44,7 @@ import java.io.File; import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; @@ -419,9 +420,9 @@ public void process(final ChannelHandlerContext channelHandlerCtx, final HttpHea } ret = javaMethod.invoke(javaInstance, paramValues); - } /*catch (InvocationTargetException ex) { - throw ex.getCause(); - }*/ finally { + } catch (InvocationTargetException ex) { + throw ex.getCause(); + } finally { context.poi(BootPOI.BIZ_END); } From e3050e43d56efeacc8d2417edec9180ff801e5cf Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 13:01:58 -0400 Subject: [PATCH 25/34] refactoring --- .../jexpress/boot/SummerApplication.java | 10 +- .../boot/config/ConfigurationMonitor.java | 6 +- .../boot/event/HttpExceptionHandler.java | 10 +- .../boot/instrumentation/HealthMonitor.java | 94 +++++++++---------- .../boot/instrumentation/Timeout.java | 4 +- .../nio/server/ws/rs/BootController.java | 2 +- .../jexpress/template/log4j2.xml.temp | 3 +- 7 files changed, 68 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java index d136abf6..bd47520c 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java @@ -288,6 +288,7 @@ public void start() { // 4. health inspection log.trace("4. health inspection"); HealthMonitor.start(); + HealthMonitor.inspect(); // 5a. start server: gRPC if (hasGRPCImpl) { @@ -337,10 +338,15 @@ public void start() { // 6. announcement log.info(() -> BootConstant.BR + BootConstant.BR + I18n.info.launched.format(userSpecifiedResourceBundle, appVersion + " pid#" + BootConstant.PID) + BootConstant.BR + BootConstant.BR); + String serviceStatus = HealthMonitor.buildMessage(); + if (!HealthMonitor.isServiceAvaliable()) { + log.warn(serviceStatus); + } else { + log.info(serviceStatus); + } - String fullConfigInfo = "";//sb.toString(); if (appLifecycleListener != null) { - appLifecycleListener.onApplicationStart(super.appVersion, fullConfigInfo); + appLifecycleListener.onApplicationStart(super.appVersion, serviceStatus); } } catch (java.net.BindException ex) {// from NioServer log.fatal(ex + BootConstant.BR + BackOffice.agent.getPortInUseAlertMessage()); diff --git a/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java b/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java index 60e77a3f..28864330 100644 --- a/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/config/ConfigurationMonitor.java @@ -58,7 +58,7 @@ public void start(File folder, int intervalSec, Map cfgUpdateTas } else { cause = "File not detected: " + pauseFile.getAbsolutePath(); } - HealthMonitor.setPauseStatus(pause, cause, PAUSE_LOCK_CODE); + HealthMonitor.pauseService(pause, PAUSE_LOCK_CODE, cause); if (running) { return; } @@ -124,7 +124,7 @@ public void onFileCreate(File file) { return; } log.info(() -> "new " + file.getAbsoluteFile()); - HealthMonitor.setPauseStatus(true, "file created " + file.getAbsolutePath(), PAUSE_LOCK_CODE); + HealthMonitor.pauseService(true, PAUSE_LOCK_CODE, "file created " + file.getAbsolutePath()); } @Override @@ -133,7 +133,7 @@ public void onFileDelete(File file) { return; } log.info(() -> "del " + file.getAbsoluteFile()); - HealthMonitor.setPauseStatus(false, "file deleted " + file.getAbsolutePath(), PAUSE_LOCK_CODE); + HealthMonitor.pauseService(false, PAUSE_LOCK_CODE, "file deleted " + file.getAbsolutePath()); } @Override diff --git a/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java b/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java index 35dfef54..111f7cf7 100644 --- a/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java +++ b/src/main/java/org/summerboot/jexpress/boot/event/HttpExceptionHandler.java @@ -69,7 +69,7 @@ public void onNamingException(NamingException ex, HttpMethod httptMethod, String cause = ex; } if (cause instanceof IOException) {// java.net.UnknownHostException - HealthMonitor.setHealthStatus(false, ex.toString()); + HealthMonitor.inspect(); nakFatal(context, HttpResponseStatus.BAD_GATEWAY, BootErrorCode.NETWORK_ERROR, "LDAP " + cause.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } else { onNamingException(ex, cause, httptMethod, httpRequestPath, context); @@ -89,7 +89,7 @@ public void onPersistenceException(PersistenceException ex, HttpMethod httptMeth cause = ex; } if (cause instanceof IOException) {// java.net.ConnectException - HealthMonitor.setHealthStatus(false, ex.toString()); + HealthMonitor.inspect(); nakFatal(context, HttpResponseStatus.BAD_GATEWAY, BootErrorCode.ACCESS_ERROR_DATABASE, "DB " + cause.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } else { onPersistenceException(ex, cause, httptMethod, httpRequestPath, context); @@ -103,7 +103,7 @@ protected void onPersistenceException(PersistenceException ex, Throwable cause, @Override public void onHttpConnectTimeoutException(HttpConnectTimeoutException ex, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { - HealthMonitor.setHealthStatus(false, ex.toString()); + HealthMonitor.inspect(); context.status(HttpResponseStatus.GATEWAY_TIMEOUT) .level(Level.WARN) .error(new Err(BootErrorCode.HTTP_CONNECTION_TIMEOUT, null, null, ex, "Http Connect Timeout: " + ex.getMessage())); @@ -125,13 +125,13 @@ public void onRejectedExecutionException(Throwable ex, HttpMethod httptMethod, S @Override public void onConnectException(Throwable ex, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { - HealthMonitor.setHealthStatus(false, ex.toString()); + HealthMonitor.inspect(); nakFatal(context, HttpResponseStatus.BAD_GATEWAY, BootErrorCode.IO_BASE, "Failed to connect: " + ex.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } @Override public void onIOException(Throwable ex, HttpMethod httptMethod, String httpRequestPath, ServiceContext context) { - HealthMonitor.setHealthStatus(false, ex.toString()); + HealthMonitor.inspect(); nakFatal(context, HttpResponseStatus.BAD_GATEWAY, BootErrorCode.IO_BASE, "IO issue: " + ex.getClass().getSimpleName(), ex, SMTPClientConfig.cfg.getEmailToAppSupport(), httptMethod + " " + httpRequestPath); } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 775544fb..2af3a962 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -15,7 +15,6 @@ */ package org.summerboot.jexpress.boot.instrumentation; -import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.summerboot.jexpress.boot.BackOffice; @@ -46,18 +45,21 @@ public class HealthMonitor { protected static volatile AppLifecycleListener appLifecycleListener; - protected static volatile ExecutorService tpe = Executors.newSingleThreadExecutor(); - protected static volatile LinkedBlockingQueue healthInspectorQueue = new LinkedBlockingQueue<>(); + protected static final ExecutorService tpe = Executors.newSingleThreadExecutor(); + protected static final LinkedBlockingQueue healthInspectorQueue = new LinkedBlockingQueue<>(); - protected static volatile Set registeredHealthInspectors = new HashSet<>(); + protected static final Set registeredHealthInspectors = new HashSet<>(); public static void setAppLifecycleListener(AppLifecycleListener listener) { appLifecycleListener = listener; } + private static final String ANNOTATION = HealthInspector.class.getSimpleName(); + public static void registerDefaultHealthInspectors(Map defaultHealthInspectors, StringBuilder memo) { registeredHealthInspectors.clear(); if (defaultHealthInspectors == null || defaultHealthInspectors.isEmpty()) { + memo.append(BootConstant.BR).append("\t- @" + ANNOTATION + " registered: none"); return; } StringBuilder sb = new StringBuilder(); @@ -79,6 +81,29 @@ public static void registerDefaultHealthInspectors(Map defaultHe } } + /** + * use default inspectors + */ + public static void inspect() { + registeredHealthInspectors.forEach(healthInspectorQueue::offer); + } + + /** + * @param healthInspectors use specified inspectors, if null or empty, use default inspectors + */ + public static void inspect(HealthInspector... healthInspectors) { + if (healthInspectors == null || healthInspectors.length == 0) {// use specified inspectors + inspect(); + return; + } + for (HealthInspector healthInspector : healthInspectors) {// use specified inspectors + if (healthInspector == null) { + continue; + } + healthInspectorQueue.add(healthInspector); + } + } + private static boolean keepRunning = true; public static void start() { @@ -114,14 +139,14 @@ public static void start() { String reason; if (currentInspectionPassed) { reason = name + " success"; - setPauseStatus(false, reason, lockCode, null); + pauseService(false, lockCode, reason); } else { try { reason = BeanUtil.toJson(errs, true, true); } catch (Throwable ex) { reason = name + " failed " + ex; } - setPauseStatus(true, reason, lockCode, null); + pauseService(true, lockCode, reason); } } case HealthCheck -> { @@ -133,10 +158,11 @@ public static void start() { } } else { healthCheckAllPassed = false; - Level level = healthInspector.logLevel(); + healthCheckFailedReport.addErrors(errs); + /*Level level = healthInspector.logLevel(); if (level != null && log.isEnabled(level)) { healthCheckFailedReport.addErrors(errs); - } + }*/ } } } @@ -160,13 +186,15 @@ public static void start() { appLifecycleListener.onHealthInspectionFailed(isHealthCheckSuccess, isServicePaused, retryIndex, inspectionIntervalSeconds); } } - setHealthStatus(healthCheckAllPassed, inspectionReport, null); + setHealthStatus(healthCheckAllPassed, inspectionReport); } - log.warn(buildMessage()); + + /*if (!isServiceAvaliable()) { + log.warn(buildMessage() + "\n\t will check again in " + inspectionIntervalSeconds + " seconds"); + } else { + log.info(buildMessage()); + }*/ // wait - if (!isServiceAvaliable()) { - log.warn("will check again in " + inspectionIntervalSeconds + " seconds"); - } TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); @@ -177,26 +205,6 @@ public static void start() { tpe.execute(asyncTask); } - public static void inspect() { - registeredHealthInspectors.forEach(healthInspectorQueue::offer); - } - - public static void inspect(HealthInspector... healthInspectors) { - if (healthInspectors == null) {// no inspectors - return; - } - if (healthInspectors.length == 0) {// use default inspectors - inspect(); - return; - } - for (HealthInspector healthInspector : healthInspectors) {// use specified inspectors - if (healthInspector == null) { - continue; - } - healthInspectorQueue.add(healthInspector); - } - } - public static void shutdown() { keepRunning = false; tpe.shutdown(); @@ -211,24 +219,20 @@ public static void shutdown() { protected static volatile boolean isHealthCheckSuccess = true; protected static volatile boolean isServicePaused = false; - protected static volatile boolean isServiceStatusChanged = false; protected static volatile String statusReasonHealthCheck; protected static volatile String statusReasonPaused; protected static volatile String statusReasonLastKnown; - protected static volatile Set pauseReleaseCodes = new HashSet<>(); + protected static final Set pauseReleaseCodes = new HashSet<>(); - public static void setHealthStatus(boolean newStatus, String reason, HealthInspector... healthInspectors) { + protected static void setHealthStatus(boolean newStatus, String reason) { boolean serviceStatusChanged = isHealthCheckSuccess ^ newStatus; isHealthCheckSuccess = newStatus; statusReasonHealthCheck = reason; updateServiceStatus(serviceStatusChanged, reason); - - if (!isHealthCheckSuccess) { - inspect(healthInspectors); - } } - public static void setPauseStatus(boolean pauseService, String reason, String lockCode, HealthInspector... healthInspectors) { + public static void pauseService(boolean pauseService, String lockCode, String reason) { + boolean serviceStatusChanged = isServicePaused ^ pauseService; // check lock if (lockCode == null) { lockCode = ""; @@ -240,17 +244,13 @@ public static void setPauseStatus(boolean pauseService, String reason, String lo int size = pauseReleaseCodes.size(); if (size > 0) {// keep paused by other reasons with different passwords pauseService = true; - reason += ", still paused by other " + size + " reason(s) with different password(s)"; + reason += ", still paused by other " + size + " reason(s) with different lock code(s)"; } } - boolean serviceStatusChanged = isServicePaused ^ pauseService; + //serviceStatusChanged = isServicePaused ^ pauseService; isServicePaused = pauseService; statusReasonPaused = reason; updateServiceStatus(serviceStatusChanged, reason); - - if (isServicePaused) { - inspect(healthInspectors); - } } protected static void updateServiceStatus(boolean serviceStatusChanged, String reason) { diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/Timeout.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/Timeout.java index bef5cbe1..499305ba 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/Timeout.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/Timeout.java @@ -63,10 +63,10 @@ protected void startTheTimer() { lock.lock(); Runnable runnableTask = () -> { try { - log.info("Task started: {} - {}", processName, System.currentTimeMillis()); + log.trace("Task started: {} - {}", processName, System.currentTimeMillis()); if (lock.tryLock(timeoutMilliseconds, TimeUnit.MILLISECONDS)) { lock.unlock(); - log.info("Task finished: {} - {}", processName, System.currentTimeMillis()); + log.trace("Task finished: {} - {}", processName, System.currentTimeMillis()); return; } String desc = message == null diff --git a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java index 61b3a17b..3c774794 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/BootController.java @@ -246,7 +246,7 @@ public void inspect(@Parameter(hidden = true) final ServiceContext context) { @Deamon //@CaptureTransaction("admin.changeStatus") public void pause(@QueryParam("pause") boolean pause, @Parameter(hidden = true) final ServiceContext context) throws IOException { - HealthMonitor.setPauseStatus(pause, "request by " + context.caller(), BootConstant.PAUSE_LOCK_CODE_VIAWEB); + HealthMonitor.pauseService(pause, BootConstant.PAUSE_LOCK_CODE_VIAWEB, "request by " + context.caller()); context.status(HttpResponseStatus.NO_CONTENT); } diff --git a/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp b/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp index f071c82d..b0f435af 100644 --- a/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp +++ b/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp @@ -65,7 +65,8 @@ - + + From 87c02c2431c7c95ae1dd36cc7a6b8dad53b2cc32 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 13:05:49 -0400 Subject: [PATCH 26/34] refactoring --- .../resources/org/summerboot/jexpress/template/log4j2.xml.temp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp b/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp index b0f435af..41677982 100644 --- a/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp +++ b/src/main/resources/org/summerboot/jexpress/template/log4j2.xml.temp @@ -1,5 +1,5 @@ - + From 69c3d948652baa43543c13c527302f5f47f2d036 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 13:26:10 -0400 Subject: [PATCH 27/34] refactoring --- .../jexpress/boot/instrumentation/HealthInspector.java | 2 +- .../summerboot/jexpress/boot/instrumentation/HealthMonitor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java index 3d99d9bc..1f301b3c 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java @@ -47,7 +47,7 @@ default String pauseLockCode() { } enum Type { - HealthCheck, ServicePaused + HealthCheck, PauseCheck } //@Override Comparable diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 2af3a962..95c65e90 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -134,7 +134,7 @@ public static void start() { healthInspectorQueue.offer(healthInspector); } switch (type) { - case ServicePaused -> { + case PauseCheck -> { String lockCode = healthInspector.pauseLockCode(); String reason; if (currentInspectionPassed) { From c4144773dc8b92ab1180927dae0ddb99a00201dc Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 14:05:08 -0400 Subject: [PATCH 28/34] refactoring --- .../org/summerboot/jexpress/boot/SummerApplication.java | 7 +------ .../jexpress/boot/instrumentation/HealthInspector.java | 6 +++--- .../jexpress/boot/instrumentation/HealthMonitor.java | 6 +++--- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java index bd47520c..3642b548 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java @@ -337,13 +337,8 @@ public void start() { } // 6. announcement - log.info(() -> BootConstant.BR + BootConstant.BR + I18n.info.launched.format(userSpecifiedResourceBundle, appVersion + " pid#" + BootConstant.PID) + BootConstant.BR + BootConstant.BR); String serviceStatus = HealthMonitor.buildMessage(); - if (!HealthMonitor.isServiceAvaliable()) { - log.warn(serviceStatus); - } else { - log.info(serviceStatus); - } + log.info(() -> BootConstant.BR + BootConstant.BR + I18n.info.launched.format(userSpecifiedResourceBundle, appVersion + " pid#" + BootConstant.PID) + serviceStatus); if (appLifecycleListener != null) { appLifecycleListener.onApplicationStart(super.appVersion, serviceStatus); diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java index 1f301b3c..3cbb77da 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthInspector.java @@ -31,8 +31,8 @@ public interface HealthInspector extends Comparable { List ping(T... param); - default Type type() { - return Type.HealthCheck; + default InspectionType inspectionType() { + return InspectionType.HealthCheck; } /** @@ -46,7 +46,7 @@ default String pauseLockCode() { return this.getClass().getName(); } - enum Type { + enum InspectionType { HealthCheck, PauseCheck } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 95c65e90..425c2636 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -127,13 +127,13 @@ public static void start() { for (HealthInspector healthInspector : batchInspectors) { String name = healthInspector.getClass().getName(); try (var a = Timeout.watch(name + ".ping()", timeoutMs).withDesc(timeoutDesc)) { - HealthInspector.Type type = healthInspector.type(); + HealthInspector.InspectionType inspectionType = healthInspector.inspectionType(); List errs = healthInspector.ping(); boolean currentInspectionPassed = errs == null || errs.isEmpty(); if (!currentInspectionPassed) { healthInspectorQueue.offer(healthInspector); } - switch (type) { + switch (inspectionType) { case PauseCheck -> { String lockCode = healthInspector.pauseLockCode(); String reason; @@ -258,7 +258,7 @@ protected static void updateServiceStatus(boolean serviceStatusChanged, String r return; } statusReasonLastKnown = reason; - log.warn(buildMessage()); + log.warn(buildMessage());// always warn for status changed if (appLifecycleListener != null) { appLifecycleListener.onApplicationStatusUpdated(isHealthCheckSuccess, isServicePaused, serviceStatusChanged, reason); } From 6dbe1d3a67db41cc509080c9785c84153f042f1f Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 15:15:09 -0400 Subject: [PATCH 29/34] refactoring --- .../summerboot/jexpress/boot/SummerApplication.java | 5 ++--- .../jexpress/boot/instrumentation/HealthMonitor.java | 12 +++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java index 3642b548..26a10bf0 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java @@ -51,7 +51,6 @@ */ abstract public class SummerApplication extends SummerBigBang { - @Inject protected InstrumentationMgr instrumentationMgr; protected NioServer httpServer; @@ -288,7 +287,6 @@ public void start() { // 4. health inspection log.trace("4. health inspection"); HealthMonitor.start(); - HealthMonitor.inspect(); // 5a. start server: gRPC if (hasGRPCImpl) { @@ -339,7 +337,6 @@ public void start() { // 6. announcement String serviceStatus = HealthMonitor.buildMessage(); log.info(() -> BootConstant.BR + BootConstant.BR + I18n.info.launched.format(userSpecifiedResourceBundle, appVersion + " pid#" + BootConstant.PID) + serviceStatus); - if (appLifecycleListener != null) { appLifecycleListener.onApplicationStart(super.appVersion, serviceStatus); } @@ -357,6 +354,8 @@ public void start() { } } + protected static boolean a = true; + public void shutdown() { log.trace(""); if (gRPCServerList != null && !gRPCServerList.isEmpty()) { diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 425c2636..b615af50 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -105,6 +105,7 @@ public static void inspect(HealthInspector... healthInspectors) { } private static boolean keepRunning = true; + private static volatile boolean ready = false; public static void start() { inspect(); @@ -189,11 +190,7 @@ public static void start() { setHealthStatus(healthCheckAllPassed, inspectionReport); } - /*if (!isServiceAvaliable()) { - log.warn(buildMessage() + "\n\t will check again in " + inspectionIntervalSeconds + " seconds"); - } else { - log.info(buildMessage()); - }*/ + ready = true; // wait TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); } catch (InterruptedException ex) { @@ -253,11 +250,12 @@ public static void pauseService(boolean pauseService, String lockCode, String re updateServiceStatus(serviceStatusChanged, reason); } + protected static void updateServiceStatus(boolean serviceStatusChanged, String reason) { - if (!serviceStatusChanged) { + statusReasonLastKnown = reason; + if (!serviceStatusChanged || !ready) { return; } - statusReasonLastKnown = reason; log.warn(buildMessage());// always warn for status changed if (appLifecycleListener != null) { appLifecycleListener.onApplicationStatusUpdated(isHealthCheckSuccess, isServicePaused, serviceStatusChanged, reason); From 29ce7354f994416038287ba632c550cc9c9b3eb2 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 16:01:16 -0400 Subject: [PATCH 30/34] refactoring --- .../org/summerboot/jexpress/boot/event/AppLifecycleHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java b/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java index 6c538958..9d337921 100644 --- a/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java +++ b/src/main/java/org/summerboot/jexpress/boot/event/AppLifecycleHandler.java @@ -68,7 +68,7 @@ public void onApplicationStatusUpdated(boolean healthOk, boolean paused, boolean boolean serviceAvaliable = healthOk && !paused; String content = HealthMonitor.buildMessage(); if (postOffice != null) { - postOffice.sendAlertAsync(SMTPClientConfig.cfg.getEmailToAppSupport(), "Service Status Changed", content, null, false); + postOffice.sendAlertAsync(SMTPClientConfig.cfg.getEmailToAppSupport(), "Service Status Changed at " + OffsetDateTime.now(), content, null, false); } } } From ffb12af5c175534c1eb84b87b1988b88703e6a38 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 23:34:27 -0400 Subject: [PATCH 31/34] refactoring --- .../jexpress/boot/SummerApplication.java | 3 +- .../boot/instrumentation/HealthMonitor.java | 198 ++++++++++-------- 2 files changed, 108 insertions(+), 93 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java index 26a10bf0..17920c4e 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java @@ -286,7 +286,7 @@ public void start() { String timeoutDesc = BackOffice.agent.getProcessTimeoutAlertMessage(); // 4. health inspection log.trace("4. health inspection"); - HealthMonitor.start(); + String serviceStatus = HealthMonitor.start(); // 5a. start server: gRPC if (hasGRPCImpl) { @@ -335,7 +335,6 @@ public void start() { } // 6. announcement - String serviceStatus = HealthMonitor.buildMessage(); log.info(() -> BootConstant.BR + BootConstant.BR + I18n.info.launched.format(userSpecifiedResourceBundle, appVersion + " pid#" + BootConstant.PID) + serviceStatus); if (appLifecycleListener != null) { appLifecycleListener.onApplicationStart(super.appVersion, serviceStatus); diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index b615af50..f259fbc9 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -43,12 +43,25 @@ public class HealthMonitor { protected static final Logger log = LogManager.getLogger(HealthMonitor.class.getName()); + /* + * controller variables + */ protected static volatile AppLifecycleListener appLifecycleListener; - protected static final ExecutorService tpe = Executors.newSingleThreadExecutor(); protected static final LinkedBlockingQueue healthInspectorQueue = new LinkedBlockingQueue<>(); - protected static final Set registeredHealthInspectors = new HashSet<>(); + private static boolean keepRunning = false; + private static volatile boolean started = false; + + /* + * status variables + */ + protected static volatile boolean isHealthCheckSuccess = true; + protected static volatile boolean isServicePaused = false; + protected static volatile String statusReasonHealthCheck; + protected static volatile String statusReasonPaused; + protected static volatile String statusReasonLastKnown; + protected static final Set pauseReleaseCodes = new HashSet<>(); public static void setAppLifecycleListener(AppLifecycleListener listener) { appLifecycleListener = listener; @@ -104,102 +117,111 @@ public static void inspect(HealthInspector... healthInspectors) { } } - private static boolean keepRunning = true; - private static volatile boolean ready = false; - - public static void start() { - inspect(); - Runnable asyncTask = () -> { - int inspectionIntervalSeconds = NioConfig.cfg.getHealthInspectionIntervalSeconds(); - long timeoutMs = BackOffice.agent.getProcessTimeoutMilliseconds(); - String timeoutDesc = BackOffice.agent.getProcessTimeoutAlertMessage(); - final Set batchInspectors = new TreeSet<>(); - do { - ServiceError healthCheckFailedReport = new ServiceError(BootConstant.APP_ID + "-HealthMonitor"); - batchInspectors.clear(); - Boolean healthCheckAllPassed = null; - try { - // take all health inspectors from the queue, remove duplicated - do { - HealthInspector healthInspector = healthInspectorQueue.take();// block/wait here for health inspectors - batchInspectors.add(healthInspector); - } while (!healthInspectorQueue.isEmpty()); - // inspect - for (HealthInspector healthInspector : batchInspectors) { - String name = healthInspector.getClass().getName(); - try (var a = Timeout.watch(name + ".ping()", timeoutMs).withDesc(timeoutDesc)) { - HealthInspector.InspectionType inspectionType = healthInspector.inspectionType(); - List errs = healthInspector.ping(); - boolean currentInspectionPassed = errs == null || errs.isEmpty(); - if (!currentInspectionPassed) { - healthInspectorQueue.offer(healthInspector); - } - switch (inspectionType) { - case PauseCheck -> { - String lockCode = healthInspector.pauseLockCode(); - String reason; - if (currentInspectionPassed) { - reason = name + " success"; - pauseService(false, lockCode, reason); - } else { - try { - reason = BeanUtil.toJson(errs, true, true); - } catch (Throwable ex) { - reason = name + " failed " + ex; - } - pauseService(true, lockCode, reason); + private static final Runnable AsyncTask = () -> { + int inspectionIntervalSeconds = NioConfig.cfg.getHealthInspectionIntervalSeconds(); + long timeoutMs = BackOffice.agent.getProcessTimeoutMilliseconds(); + String timeoutDesc = BackOffice.agent.getProcessTimeoutAlertMessage(); + final Set batchInspectors = new TreeSet<>(); + do { + ServiceError healthCheckFailedReport = new ServiceError(BootConstant.APP_ID + "-HealthMonitor"); + batchInspectors.clear(); + Boolean healthCheckAllPassed = null; + try { + // take all health inspectors from the queue, remove duplicated + do { + HealthInspector healthInspector = healthInspectorQueue.take();// block/wait here for health inspectors + batchInspectors.add(healthInspector); + } while (!healthInspectorQueue.isEmpty()); + // inspect + for (HealthInspector healthInspector : batchInspectors) { + String name = healthInspector.getClass().getName(); + try (var a = Timeout.watch(name + ".ping()", timeoutMs).withDesc(timeoutDesc)) { + HealthInspector.InspectionType inspectionType = healthInspector.inspectionType(); + List errs = healthInspector.ping(); + boolean currentInspectionPassed = errs == null || errs.isEmpty(); + if (!currentInspectionPassed) { + healthInspectorQueue.offer(healthInspector); + } + switch (inspectionType) { + case PauseCheck -> { + String lockCode = healthInspector.pauseLockCode(); + String reason; + if (currentInspectionPassed) { + reason = name + " success"; + pauseService(false, lockCode, reason); + } else { + try { + reason = BeanUtil.toJson(errs, true, true); + } catch (Throwable ex) { + reason = name + " failed " + ex; } + pauseService(true, lockCode, reason); } - case HealthCheck -> { - if (currentInspectionPassed) { - if (healthCheckAllPassed == null) { - healthCheckAllPassed = true; - } else { - healthCheckAllPassed &= true; - } + } + case HealthCheck -> { + if (currentInspectionPassed) { + if (healthCheckAllPassed == null) { + healthCheckAllPassed = true; } else { - healthCheckAllPassed = false; - healthCheckFailedReport.addErrors(errs); + healthCheckAllPassed &= true; + } + } else { + healthCheckAllPassed = false; + healthCheckFailedReport.addErrors(errs); /*Level level = healthInspector.logLevel(); if (level != null && log.isEnabled(level)) { healthCheckFailedReport.addErrors(errs); }*/ - } } } - } catch (Throwable ex) { - healthInspectorQueue.offer(healthInspector); - log.error("HealthInspector error: " + name, ex); } + } catch (Throwable ex) { + healthInspectorQueue.offer(healthInspector); + log.error("HealthInspector error: " + name, ex); } - if (healthCheckAllPassed != null) { - String inspectionReport; - if (healthCheckAllPassed) { - inspectionReport = "Current all health inspectors passed"; - } else { - try { - inspectionReport = BeanUtil.toJson(healthCheckFailedReport, true, true); - } catch (Throwable ex) { - inspectionReport = " toJson failed " + ex; - } - long retryIndex = HealthInspector.retryIndex.get();// not being set yet - if (appLifecycleListener != null) { - appLifecycleListener.onHealthInspectionFailed(isHealthCheckSuccess, isServicePaused, retryIndex, inspectionIntervalSeconds); - } + } + if (healthCheckAllPassed != null) { + String inspectionReport; + if (healthCheckAllPassed) { + inspectionReport = "Current all health inspectors passed"; + } else { + try { + inspectionReport = BeanUtil.toJson(healthCheckFailedReport, true, true); + } catch (Throwable ex) { + inspectionReport = " toJson failed " + ex; + } + long retryIndex = HealthInspector.retryIndex.get();// not being set yet + if (appLifecycleListener != null) { + appLifecycleListener.onHealthInspectionFailed(isHealthCheckSuccess, isServicePaused, retryIndex, inspectionIntervalSeconds); } - setHealthStatus(healthCheckAllPassed, inspectionReport); } - - ready = true; - // wait - TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - log.error("HealthMonitor interrupted", ex); + setHealthStatus(healthCheckAllPassed, inspectionReport); } - } while (keepRunning); - }; - tpe.execute(asyncTask); + started = true; + + // wait + TimeUnit.SECONDS.sleep(inspectionIntervalSeconds); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + log.error("HealthMonitor interrupted", ex); + } + } while (keepRunning); + }; + + public static String start() { + // start sync to get result + inspect(); + keepRunning = false; + AsyncTask.run(); + String ret = buildMessage(); + + // start async in background + keepRunning = true; + inspect(); + tpe.execute(AsyncTask); + + // return sync result + return ret; } public static void shutdown() { @@ -214,12 +236,6 @@ public static void shutdown() { ); } - protected static volatile boolean isHealthCheckSuccess = true; - protected static volatile boolean isServicePaused = false; - protected static volatile String statusReasonHealthCheck; - protected static volatile String statusReasonPaused; - protected static volatile String statusReasonLastKnown; - protected static final Set pauseReleaseCodes = new HashSet<>(); protected static void setHealthStatus(boolean newStatus, String reason) { boolean serviceStatusChanged = isHealthCheckSuccess ^ newStatus; @@ -253,7 +269,7 @@ public static void pauseService(boolean pauseService, String lockCode, String re protected static void updateServiceStatus(boolean serviceStatusChanged, String reason) { statusReasonLastKnown = reason; - if (!serviceStatusChanged || !ready) { + if (!serviceStatusChanged || !started) { return; } log.warn(buildMessage());// always warn for status changed From d187e62494fbb4f89e46bb2e2a89ae90dabe92fc Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 23:37:13 -0400 Subject: [PATCH 32/34] refactoring --- .../jexpress/boot/instrumentation/HealthMonitor.java | 6 ++++-- .../jexpress/boot/instrumentation/jmx/ServerStatus.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index f259fbc9..08d681a9 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -217,7 +217,9 @@ public static String start() { // start async in background keepRunning = true; - inspect(); + if (!isServiceAvailable()) { + inspect(); + } tpe.execute(AsyncTask); // return sync result @@ -306,7 +308,7 @@ public static String getStatusReasonHealthCheck() { return statusReasonHealthCheck; } - public static boolean isServiceAvaliable() { + public static boolean isServiceAvailable() { return isHealthCheckSuccess && !isServicePaused; } diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/jmx/ServerStatus.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/jmx/ServerStatus.java index 53a2ce2a..ce33cc91 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/jmx/ServerStatus.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/jmx/ServerStatus.java @@ -136,7 +136,7 @@ public long getHealthInspector() { @Override public String getServiceStatus() { - return HealthMonitor.isServiceAvaliable() ? "OK" : "Service Unavaliable"; + return HealthMonitor.isServiceAvailable() ? "OK" : "Service Unavaliable"; } @Override From 7bc6cee1cc7117d4837cdf0fb60d8208cdcc7eb7 Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Tue, 6 Aug 2024 23:40:06 -0400 Subject: [PATCH 33/34] refactoring --- .../jexpress/boot/SummerApplication.java | 2 +- .../boot/instrumentation/HealthMonitor.java | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java index 17920c4e..2c5bb1d4 100644 --- a/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java +++ b/src/main/java/org/summerboot/jexpress/boot/SummerApplication.java @@ -286,7 +286,7 @@ public void start() { String timeoutDesc = BackOffice.agent.getProcessTimeoutAlertMessage(); // 4. health inspection log.trace("4. health inspection"); - String serviceStatus = HealthMonitor.start(); + String serviceStatus = HealthMonitor.start(true); // 5a. start server: gRPC if (hasGRPCImpl) { diff --git a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java index 08d681a9..551790b5 100644 --- a/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java +++ b/src/main/java/org/summerboot/jexpress/boot/instrumentation/HealthMonitor.java @@ -208,12 +208,15 @@ public static void inspect(HealthInspector... healthInspectors) { } while (keepRunning); }; - public static String start() { - // start sync to get result - inspect(); - keepRunning = false; - AsyncTask.run(); - String ret = buildMessage(); + public static String start(boolean returnRsult) { + String ret = null; + if (returnRsult) { + // start sync to get result + inspect(); + keepRunning = false; + AsyncTask.run(); + ret = buildMessage(); + } // start async in background keepRunning = true; From c10370ea611a0677e5173c3dda35175d7ede9b9b Mon Sep 17 00:00:00 2001 From: Summer Boot Framework Date: Wed, 7 Aug 2024 09:31:01 -0400 Subject: [PATCH 34/34] release2.4.10 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 63b28c61..bbfb8262 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.summerboot jexpress - 2.4.10-SNAPSHOT + 2.4.10 jar Summer Boot jExpress Summer Boot jExpress focuses on solving non-functional and operational maintainability requirements,