diff --git a/README.md b/README.md index c8da945c..33519bb3 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A Java framework with VERT.X eco, for projects for web, job and more. io.github.sinri Keel - 3.1.9 + 3.2.2 ``` diff --git a/pom.xml b/pom.xml index 4075c04f..13a71cc1 100644 --- a/pom.xml +++ b/pom.xml @@ -4,18 +4,10 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - - UTF-8 - 4.5.4 - 0.21.0 - 2.15.3 - 5.2.5 - - io.github.sinri Keel - 3.2.3-SNAPSHOT - + + 3.2.4 Keel @@ -44,6 +36,14 @@ + + UTF-8 + 4.5.7 + 0.21.0 + 2.15.3 + 5.2.5 + + io.vertx @@ -145,6 +145,11 @@ vertx-redis-client ${vertxVersion} + + io.vertx + vertx-json-schema + ${vertxVersion} + org.apache.poi poi diff --git a/src/main/java/io/github/sinri/keel/core/KeelCronExpression.java b/src/main/java/io/github/sinri/keel/core/KeelCronExpression.java index 4d9d86cd..b29cd582 100644 --- a/src/main/java/io/github/sinri/keel/core/KeelCronExpression.java +++ b/src/main/java/io/github/sinri/keel/core/KeelCronExpression.java @@ -1,5 +1,7 @@ package io.github.sinri.keel.core; + +import javax.annotation.Nonnull; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -13,9 +15,9 @@ public class KeelCronExpression { final Set dayOptions = new HashSet<>(); final Set monthOptions = new HashSet<>(); final Set weekdayOptions = new HashSet<>(); - private final String rawCronExpression; + private final @Nonnull String rawCronExpression; - public KeelCronExpression(String rawCronExpression) { + public KeelCronExpression(@Nonnull String rawCronExpression) { this.rawCronExpression = rawCronExpression; String[] parts = rawCronExpression.trim().split("\\s+"); @@ -36,24 +38,20 @@ public KeelCronExpression(String rawCronExpression) { parseField(weekdayExpression, weekdayOptions, 0, 6); } - public boolean match(Calendar currentCalendar) { - // currentCalendar := Calendar.getInstance(); - int minute = currentCalendar.get(Calendar.MINUTE); - int hour = currentCalendar.get(Calendar.HOUR_OF_DAY); - int day = currentCalendar.get(Calendar.DAY_OF_MONTH); - int month = 1 + currentCalendar.get(Calendar.MONTH);// make JAN 1, ... - int weekday = currentCalendar.get(Calendar.DAY_OF_WEEK) - 1; // make sunday 0, ... - - return minuteOptions.contains(minute) - && hourOptions.contains(hour) - && dayOptions.contains(day) - && monthOptions.contains(month) - && weekdayOptions.contains(weekday); + public boolean match(@Nonnull Calendar currentCalendar) { + ParsedCalenderElements parsedCalenderElements = new ParsedCalenderElements(currentCalendar); + return match(parsedCalenderElements); } - private void parseField(String rawComponent, Set optionSet, int min, int max) { -// System.out.println("parseField: " + rawComponent); + public boolean match(@Nonnull ParsedCalenderElements parsedCalenderElements) { + return minuteOptions.contains(parsedCalenderElements.minute) + && hourOptions.contains(parsedCalenderElements.hour) + && dayOptions.contains(parsedCalenderElements.day) + && monthOptions.contains(parsedCalenderElements.month) + && weekdayOptions.contains(parsedCalenderElements.weekday); + } + private void parseField(@Nonnull String rawComponent, @Nonnull Set optionSet, int min, int max) { if (rawComponent.equals("*")) { for (int i = min; i <= max; i++) { optionSet.add(i); @@ -106,10 +104,46 @@ private void parseField(String rawComponent, Set optionSet, int min, in } } + /** + * @since 3.2.4 + */ + public static ParsedCalenderElements parseCalenderToElements(@Nonnull Calendar currentCalendar) { + return new ParsedCalenderElements(currentCalendar); + } + + @Nonnull public String getRawCronExpression() { return rawCronExpression; } + /** + * @since 3.2.4 + */ + public static class ParsedCalenderElements { + public final int minute; + public final int hour; + public final int day; + public final int month; + public final int weekday; + + // debug use + public final int second; + + public ParsedCalenderElements(@Nonnull Calendar currentCalendar) { + minute = currentCalendar.get(Calendar.MINUTE); + hour = currentCalendar.get(Calendar.HOUR_OF_DAY); + day = currentCalendar.get(Calendar.DAY_OF_MONTH); + month = 1 + currentCalendar.get(Calendar.MONTH);// make JAN 1, ... + weekday = currentCalendar.get(Calendar.DAY_OF_WEEK) - 1; // make sunday 0, ... + second = currentCalendar.get(Calendar.SECOND); + } + + @Override + public String toString() { + return "(" + second + ") " + minute + " " + hour + " " + day + " " + month + " " + weekday; + } + } + @Override public String toString() { return getRawCronExpression(); diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonArrayScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonArrayScheme.java index 35834fa4..1e5b5d7d 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonArrayScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonArrayScheme.java @@ -10,6 +10,7 @@ /** * @since 2.7 */ +@Deprecated(since = "3.2.4") public class JsonArrayScheme extends JsonValueScheme { private final Map> indexedElementSchemeMap = new LinkedHashMap<>(); private JsonElementScheme defaultElementScheme; diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonBooleanScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonBooleanScheme.java index fd19fc7a..87c90b2d 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonBooleanScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonBooleanScheme.java @@ -7,6 +7,7 @@ /** * @since 2.7 */ +@Deprecated(since = "3.2.4") public class JsonBooleanScheme extends JsonValueScheme { private Boolean expected; diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonElementScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonElementScheme.java index 51d0f274..206873c8 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonElementScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonElementScheme.java @@ -6,6 +6,7 @@ /** * @since 2.7 */ +@Deprecated(since = "3.2.4") public interface JsonElementScheme extends JsonifiableEntity> { static JsonElementScheme fromJsonObject(JsonObject jsonObject) { JsonElementSchemeType scheme_type = JsonElementSchemeType.valueOf(jsonObject.getString("scheme_type")); diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonNullScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonNullScheme.java index bc5f0ceb..66a7c1a4 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonNullScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonNullScheme.java @@ -7,6 +7,7 @@ /** * @since 2.7 */ +@Deprecated(since = "3.2.4") public class JsonNullScheme extends JsonValueScheme { public JsonNullScheme() { super(); diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonNumberScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonNumberScheme.java index 03fd98df..5ea412f7 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonNumberScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonNumberScheme.java @@ -8,6 +8,7 @@ /** * @since 2.7 */ +@Deprecated(since = "3.2.4") public class JsonNumberScheme extends JsonValueScheme { //private boolean withFractions; diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonObjectScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonObjectScheme.java index 64726140..f15e870b 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonObjectScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonObjectScheme.java @@ -9,6 +9,7 @@ /** * @since 2.7 */ +@Deprecated(since = "3.2.4") public class JsonObjectScheme extends JsonValueScheme { private final Map> elementSchemeMap = new LinkedHashMap<>(); diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonPlainScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonPlainScheme.java index 520b636e..aafe81c1 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonPlainScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonPlainScheme.java @@ -4,6 +4,7 @@ import javax.annotation.Nonnull; +@Deprecated(since = "3.2.4") public class JsonPlainScheme extends JsonValueScheme { @Override diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonSchemeMismatchException.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonSchemeMismatchException.java index c9b9e463..58f6dead 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonSchemeMismatchException.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonSchemeMismatchException.java @@ -6,6 +6,7 @@ import static io.github.sinri.keel.helper.KeelHelpersInterface.KeelHelpers; +@Deprecated(since = "3.2.4") public class JsonSchemeMismatchException extends Exception { public static final String RuleSchemeError = "SchemeError"; public static final String RuleEmptyArrayNotAllowed = "EmptyArrayNotAllowed"; diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonStringScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonStringScheme.java index a426b0ef..70a847db 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonStringScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonStringScheme.java @@ -8,6 +8,7 @@ /** * @since 2.7 */ +@Deprecated(since = "3.2.4") public class JsonStringScheme extends JsonValueScheme { private Pattern pattern; diff --git a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonValueScheme.java b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonValueScheme.java index 1e4c402b..3ac056ac 100644 --- a/src/main/java/io/github/sinri/keel/core/json/scheme/JsonValueScheme.java +++ b/src/main/java/io/github/sinri/keel/core/json/scheme/JsonValueScheme.java @@ -7,6 +7,7 @@ /** * @since 2.7 */ +@Deprecated(since = "3.2.4") abstract public class JsonValueScheme implements JsonElementScheme { protected T digested; diff --git a/src/main/java/io/github/sinri/keel/elasticsearch/ESApiMixin.java b/src/main/java/io/github/sinri/keel/elasticsearch/ESApiMixin.java index addaa632..f15f153b 100644 --- a/src/main/java/io/github/sinri/keel/elasticsearch/ESApiMixin.java +++ b/src/main/java/io/github/sinri/keel/elasticsearch/ESApiMixin.java @@ -3,6 +3,8 @@ import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.DecodeException; +import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.HttpRequest; import io.vertx.ext.web.client.WebClient; @@ -64,9 +66,7 @@ default Future call(@Nonnull HttpMethod httpMethod, @Nonnull String }) .compose(bufferHttpResponse -> { int statusCode = bufferHttpResponse.statusCode(); - JsonObject resp = bufferHttpResponse.bodyAsJsonObject(); - - if ((statusCode >= 300 || statusCode < 200) || resp == null) { + if ((statusCode >= 300 || statusCode < 200)) { // this.getLogger().error(log -> { // logRequestEnricher.handle(log); // log.message("ES API Response Error") @@ -86,6 +86,14 @@ default Future call(@Nonnull HttpMethod httpMethod, @Nonnull String requestBody )); } + JsonObject resp; + try { + resp = bufferHttpResponse.bodyAsJsonObject(); + } catch (DecodeException decodeException) { + // There are situations that use Json Array as the response body! + resp = new JsonObject() + .put("array", new JsonArray(bufferHttpResponse.bodyAsString())); + } // this.getLogger().info(log -> { // logRequestEnricher.handle(log); // log.message("ES API Response Error") @@ -208,6 +216,18 @@ public ESApiException( this.requestBody = requestBody; } + @Override + public String toString() { + return getClass().getName() + + "{status_code: " + statusCode + + ", response: " + response + + ", http_method: " + httpMethod.name() + + ", endpoint: " + endpoint + + ", queries: " + queries + + ", request_body: " + requestBody + + "}"; + } + public int getStatusCode() { return statusCode; } diff --git a/src/main/java/io/github/sinri/keel/elasticsearch/ElasticSearchConfig.java b/src/main/java/io/github/sinri/keel/elasticsearch/ElasticSearchConfig.java index bbab3257..b9b5a9c6 100644 --- a/src/main/java/io/github/sinri/keel/elasticsearch/ElasticSearchConfig.java +++ b/src/main/java/io/github/sinri/keel/elasticsearch/ElasticSearchConfig.java @@ -1,9 +1,10 @@ package io.github.sinri.keel.elasticsearch; -import io.github.sinri.keel.facade.KeelConfiguration; +import io.github.sinri.keel.facade.configuration.KeelConfigElement; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.List; import java.util.Objects; import static io.github.sinri.keel.facade.KeelInstance.Keel; @@ -11,9 +12,9 @@ /** * @since 3.0.7 */ -public class ElasticSearchConfig extends KeelConfiguration { +public class ElasticSearchConfig extends KeelConfigElement { - public ElasticSearchConfig(KeelConfiguration configuration) { + public ElasticSearchConfig(KeelConfigElement configuration) { super(configuration); } @@ -34,25 +35,23 @@ public ElasticSearchConfig(String esKey) { */ public String username() { - return readString("username"); + return readString("username", null); } public String password() { - return readString("password"); + return readString("password", null); } public @Nonnull String clusterHost() { - return Objects.requireNonNull(readString("cluster", "host")); + return Objects.requireNonNull(readString(List.of("cluster", "host"), null)); } public int clusterPort() { - String s = readString("cluster", "port"); - return Integer.parseInt(Objects.requireNonNullElse(s, "9200")); + return readInteger(List.of("cluster", "port"), 9200); } public @Nonnull String clusterScheme() { - String s = readString("cluster", "scheme"); - return Objects.requireNonNullElse(s, "http"); + return Objects.requireNonNull(readString(List.of("cluster", "scheme"), "http")); } public @Nonnull String clusterApiUrl(@Nonnull String endpoint) { @@ -60,6 +59,6 @@ public int clusterPort() { } public @Nullable String opaqueId() { - return readString("opaqueId"); + return readString("opaqueId", null); } } diff --git a/src/main/java/io/github/sinri/keel/email/smtp/KeelSmtpKit.java b/src/main/java/io/github/sinri/keel/email/smtp/KeelSmtpKit.java index 48f5b5f6..0ff98f08 100644 --- a/src/main/java/io/github/sinri/keel/email/smtp/KeelSmtpKit.java +++ b/src/main/java/io/github/sinri/keel/email/smtp/KeelSmtpKit.java @@ -1,6 +1,5 @@ package io.github.sinri.keel.email.smtp; -import io.github.sinri.keel.facade.KeelConfiguration; import io.vertx.core.Future; import io.vertx.ext.mail.MailClient; import io.vertx.ext.mail.MailConfig; @@ -55,14 +54,15 @@ public KeelSmtpKit() { * As of 3.0.6, only five property keys supported. */ private static MailConfig buildMailConfig(@Nonnull String smtpName) { - KeelConfiguration smtpConfiguration = Keel.getConfiguration().extract("email", "smtp", smtpName); + var smtpConfiguration = Keel.getConfiguration().extract("email", "smtp", smtpName); + Objects.requireNonNull(smtpConfiguration); var mailConfig = new MailConfig(); - mailConfig.setHostname(smtpConfiguration.readString("hostname")); - mailConfig.setPort(Objects.requireNonNull(smtpConfiguration.readAsInteger("port"))); - mailConfig.setUsername(smtpConfiguration.readString("username")); - mailConfig.setPassword(smtpConfiguration.readString("password")); - mailConfig.setSsl("ON".equals(smtpConfiguration.readString("ssl"))); + mailConfig.setHostname(smtpConfiguration.readString("hostname", null)); + mailConfig.setPort(smtpConfiguration.readInteger("port", 25)); + mailConfig.setUsername(smtpConfiguration.readString("username", null)); + mailConfig.setPassword(smtpConfiguration.readString("password", null)); + mailConfig.setSsl(smtpConfiguration.readBoolean("ssl", false)); return mailConfig; } diff --git a/src/main/java/io/github/sinri/keel/facade/KeelConfiguration.java b/src/main/java/io/github/sinri/keel/facade/KeelConfiguration.java deleted file mode 100644 index 8e12e15c..00000000 --- a/src/main/java/io/github/sinri/keel/facade/KeelConfiguration.java +++ /dev/null @@ -1,177 +0,0 @@ -package io.github.sinri.keel.facade; - -import io.github.sinri.keel.core.json.JsonifiableEntity; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.json.JsonObject; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.io.FileReader; -import java.io.IOException; -import java.io.StringReader; -import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; -import java.util.*; - -import static io.github.sinri.keel.facade.KeelInstance.Keel; -import static io.github.sinri.keel.helper.KeelHelpersInterface.KeelHelpers; - -/** - * @since 3.0.0 - */ -public class KeelConfiguration implements JsonifiableEntity { - - private @Nonnull JsonObject data = new JsonObject(); - - public KeelConfiguration() { - } - - /** - * @param another - * @since 3.0.7 - */ - public KeelConfiguration(@Nonnull KeelConfiguration another) { - this(another.toJsonObject()); - } - - public KeelConfiguration(@Nonnull JsonObject jsonObject) { - this.data = jsonObject; - } - - public KeelConfiguration(@Nonnull Properties properties) { - this.data = KeelConfiguration.transformPropertiesToJsonObject(properties); - } - - static @Nonnull JsonObject transformPropertiesToJsonObject(@Nonnull Properties properties) { - JsonObject jsonObject = new JsonObject(); - - Set plainKeySet = new HashSet<>(); - properties.forEach((key, value) -> plainKeySet.add(key.toString())); - - for (var plainKey : plainKeySet) { - String[] components = plainKey.split("\\."); - List keychain = Arrays.asList(components); - try { - KeelHelpers.jsonHelper().writeIntoJsonObject(jsonObject, keychain, properties.getProperty(plainKey)); - } catch (Throwable throwable) { - Keel.getLogger().exception(throwable, "io.github.sinri.keel.facade.KeelConfiguration.transformPropertiesToJsonObject Format Failed" + properties); - } - } - return jsonObject; - } - - static @Nonnull KeelConfiguration createFromPropertiesFile(@Nonnull String propertiesFileName) { - KeelConfiguration p = new KeelConfiguration(); - p.loadPropertiesFile(propertiesFileName); - return p; - } - - static @Nonnull KeelConfiguration createFromProperties(@Nonnull Properties properties) { - KeelConfiguration p = new KeelConfiguration(); - p.putAll(properties); - return p; - } - - static @Nonnull KeelConfiguration createFromJsonObject(@Nonnull JsonObject jsonObject) { - KeelConfiguration p = new KeelConfiguration(); - p.putAll(jsonObject); - return p; - } - - public @Nonnull KeelConfiguration putAll(@Nonnull KeelConfiguration keelConfiguration) { - return putAll(keelConfiguration.toJsonObject()); - } - - public @Nonnull KeelConfiguration putAll(@Nonnull Properties properties) { - return putAll(KeelConfiguration.transformPropertiesToJsonObject(properties)); - } - - public @Nonnull KeelConfiguration putAll(@Nonnull JsonObject jsonObject) { - data.mergeIn(jsonObject); - return this; - } - - /** - * @since 3.0.10 - */ - public @Nonnull KeelConfiguration loadJsonObjectFile(@Nonnull String jsonObjectFileName) { - try { - byte[] bytes = KeelHelpers.fileHelper().readFileAsByteArray(jsonObjectFileName, true); - this.data = new JsonObject(Buffer.buffer(bytes)); - return this; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * @since 3.0.1 - */ - public @Nonnull KeelConfiguration loadPropertiesFile(@Nonnull String propertiesFileName) { - return loadPropertiesFile(propertiesFileName, StandardCharsets.UTF_8); - } - - public @Nonnull KeelConfiguration loadPropertiesFile(@Nonnull String propertiesFileName, @Nonnull Charset charset) { - Properties properties = new Properties(); - try { - // here, the file named as `propertiesFileName` should be put along with JAR - properties.load(new FileReader(propertiesFileName, charset)); - } catch (IOException e) { - System.err.println("Cannot find the file config.properties. Use the embedded one."); - try { - properties.load(KeelConfiguration.class.getClassLoader().getResourceAsStream(propertiesFileName)); - } catch (IOException ex) { - throw new RuntimeException("Cannot find the embedded file config.properties.", ex); - } - } - - return putAll(properties); - } - - /** - * @since 3.0.6 - */ - public @Nonnull KeelConfiguration loadPropertiesFileContent(@Nonnull String content) { - Properties properties = new Properties(); - try { - properties.load(new StringReader(content)); - } catch (IOException e) { - throw new RuntimeException("Cannot load given properties content.", e); - } - return putAll(properties); - } - - public @Nonnull KeelConfiguration extract(String... keychain) { - JsonObject jsonObject = Objects.requireNonNullElse(readJsonObject(keychain), new JsonObject()); - return new KeelConfiguration(jsonObject); - } - - public @Nullable Long readAsLong(String... keychain) { - String s = readString(keychain); - return s == null ? null : Long.valueOf(s); - } - - public @Nullable Integer readAsInteger(String... keychain) { - String s = readString(keychain); - return s == null ? null : Integer.valueOf(s); - } - - /** - * Parse TRUE/FALSE to boolean ignoring case. - */ - public @Nullable Boolean readAsBoolean(String... keychain) { - String s = readString(keychain); - return s == null ? null : Boolean.valueOf(s); - } - - @Override - public @Nonnull JsonObject toJsonObject() { - return this.data; - } - - @Override - public @Nonnull KeelConfiguration reloadDataFromJsonObject(@Nonnull JsonObject data) { - this.data = data; - return this; - } -} diff --git a/src/main/java/io/github/sinri/keel/facade/KeelInstance.java b/src/main/java/io/github/sinri/keel/facade/KeelInstance.java index 630638c7..f89314b1 100644 --- a/src/main/java/io/github/sinri/keel/facade/KeelInstance.java +++ b/src/main/java/io/github/sinri/keel/facade/KeelInstance.java @@ -1,12 +1,15 @@ package io.github.sinri.keel.facade; import io.github.sinri.keel.facade.cluster.KeelClusterKit; +import io.github.sinri.keel.facade.configuration.KeelConfigElement; import io.github.sinri.keel.helper.KeelHelpersInterface; import io.github.sinri.keel.logger.KeelLogLevel; import io.github.sinri.keel.logger.event.KeelEventLogger; import io.github.sinri.keel.logger.issue.center.KeelIssueRecordCenter; -import io.vertx.core.*; -import io.vertx.core.json.JsonObject; +import io.vertx.core.Future; +import io.vertx.core.Promise; +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; import io.vertx.core.spi.cluster.ClusterManager; import javax.annotation.Nonnull; @@ -19,7 +22,10 @@ public class KeelInstance implements KeelHelpersInterface, KeelClusterKit { public static KeelInstance Keel = new KeelInstance(); - private final @Nonnull KeelConfiguration configuration; + /** + * @since 3.2.3 + */ + private final @Nonnull KeelConfigElement configuration; private @Nullable Vertx vertx; private @Nullable ClusterManager clusterManager; /** @@ -29,19 +35,23 @@ public class KeelInstance implements KeelHelpersInterface, KeelClusterKit { private KeelEventLogger eventLogger; private KeelInstance() { - this.configuration = KeelConfiguration.createFromJsonObject(new JsonObject()); + this.configuration = new KeelConfigElement(""); this.eventLogger = KeelIssueRecordCenter.outputCenter().generateEventLogger("Keel"); this.eventLogger.setVisibleLevel(KeelLogLevel.WARNING); } @Nonnull - public KeelConfiguration getConfiguration() { + public KeelConfigElement getConfiguration() { return configuration; } public @Nullable String config(@Nonnull String dotJoinedKeyChain) { String[] split = dotJoinedKeyChain.split("\\."); - return getConfiguration().readString(split); + KeelConfigElement keelConfigElement = this.configuration.extract(split); + if (keelConfigElement == null) { + return null; + } + return keelConfigElement.getValueAsString(); } public @Nonnull Vertx getVertx() { @@ -123,7 +133,7 @@ public KeelInstance setLogger(@Nonnull KeelEventLogger eventLogger) { return this; } - public Future gracefullyClose(@Nonnull Handler> promiseHandler) { + public Future gracefullyClose(@Nonnull io.vertx.core.Handler> promiseHandler) { Promise promise = Promise.promise(); promiseHandler.handle(promise); return promise.future().compose(v -> getVertx().close()); diff --git a/src/main/java/io/github/sinri/keel/facade/async/FutureForEach.java b/src/main/java/io/github/sinri/keel/facade/async/FutureForEach.java index 5fd37f5c..1f22b95a 100644 --- a/src/main/java/io/github/sinri/keel/facade/async/FutureForEach.java +++ b/src/main/java/io/github/sinri/keel/facade/async/FutureForEach.java @@ -20,7 +20,7 @@ private FutureForEach(@Nonnull Function> itemProcessor) { } static Future call(@Nonnull Iterable collection, @Nonnull Function> itemProcessor) { - return new FutureForEach(itemProcessor).process(collection); + return new FutureForEach<>(itemProcessor).process(collection); } private Future process(@Nonnull Iterable collection) { diff --git a/src/main/java/io/github/sinri/keel/facade/async/FutureForEachBreakable.java b/src/main/java/io/github/sinri/keel/facade/async/FutureForEachBreakable.java new file mode 100644 index 00000000..96a82647 --- /dev/null +++ b/src/main/java/io/github/sinri/keel/facade/async/FutureForEachBreakable.java @@ -0,0 +1,39 @@ +package io.github.sinri.keel.facade.async; + +import io.vertx.core.Future; + +import javax.annotation.Nonnull; +import java.util.Iterator; +import java.util.function.BiFunction; + +/** + * @since 3.2.3 + */ +public class FutureForEachBreakable { + + @Nonnull + private final Iterable iterable; + @Nonnull + private final BiFunction> itemProcessor; + private FutureForEachBreakable(@Nonnull Iterable iterable, @Nonnull BiFunction> itemProcessor) { + this.iterable = iterable; + this.itemProcessor = itemProcessor; + } + + public static Future call(@Nonnull Iterable iterable, @Nonnull BiFunction> itemProcessor) { + return new FutureForEachBreakable<>(iterable, itemProcessor).start(); + } + + private Future start() { + Iterator iterator = iterable.iterator(); + return KeelAsyncKit.repeatedlyCall(routineResult -> { + if (iterator.hasNext()) { + T next = iterator.next(); + return this.itemProcessor.apply(next, routineResult); + } else { + routineResult.stop(); + return Future.succeededFuture(); + } + }); + } +} diff --git a/src/main/java/io/github/sinri/keel/facade/async/FutureSleep.java b/src/main/java/io/github/sinri/keel/facade/async/FutureSleep.java index 2aba6d32..82157119 100644 --- a/src/main/java/io/github/sinri/keel/facade/async/FutureSleep.java +++ b/src/main/java/io/github/sinri/keel/facade/async/FutureSleep.java @@ -15,10 +15,6 @@ */ public class FutureSleep { static Future call(long time) { -// Promise promise = Promise.promise(); -// if (time < 1) time = 1; -// Keel.getVertx().setTimer(time, x -> promise.complete()); -// return promise.future(); return call(time, null); } diff --git a/src/main/java/io/github/sinri/keel/facade/async/KeelAsyncKit.java b/src/main/java/io/github/sinri/keel/facade/async/KeelAsyncKit.java index c3d4c156..e060f04d 100644 --- a/src/main/java/io/github/sinri/keel/facade/async/KeelAsyncKit.java +++ b/src/main/java/io/github/sinri/keel/facade/async/KeelAsyncKit.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; @@ -81,6 +82,13 @@ static Future iterativelyBatchCall(@Nonnull Iterator iterator, @Non }); } + /** + * @since 3.2.3 + */ + static Future iterativelyCall(@Nonnull Iterable iterable, @Nonnull BiFunction> itemProcessor) { + return FutureForEachBreakable.call(iterable, itemProcessor); + } + /** * @since 2.9 callFutureForRange * @since 3.0.0 stepwiseCall diff --git a/src/main/java/io/github/sinri/keel/facade/configuration/KeelConfigElement.java b/src/main/java/io/github/sinri/keel/facade/configuration/KeelConfigElement.java new file mode 100644 index 00000000..3e456b9e --- /dev/null +++ b/src/main/java/io/github/sinri/keel/facade/configuration/KeelConfigElement.java @@ -0,0 +1,407 @@ +package io.github.sinri.keel.facade.configuration; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.io.FileReader; +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + + +public class KeelConfigElement { + @Nonnull + private final String name; + @Nonnull + private final Map children; + @Nullable + private String value; + + public KeelConfigElement(@Nonnull String name) { + this.name = name; + this.value = null; + this.children = new ConcurrentHashMap<>(); + } + + public KeelConfigElement(@Nonnull KeelConfigElement another) { + this.name = another.getName(); + this.children = another.getChildren(); + this.value = another.getValueAsString(); + } + + public static KeelConfigElement fromJsonObject(@Nonnull JsonObject jsonObject) { + String name = jsonObject.getString("name"); + KeelConfigElement keelConfigElement = new KeelConfigElement(name); + if (jsonObject.containsKey("value")) { + keelConfigElement.value = jsonObject.getString("value"); + } + JsonArray children = jsonObject.getJsonArray("children"); + children.forEach(child -> { + if (child instanceof JsonObject) { + keelConfigElement.addChild(fromJsonObject((JsonObject) child)); + } else { + throw new IllegalArgumentException(); + } + }); + return keelConfigElement; + } + + @Nonnull + public String getName() { + return name; + } + + @Nullable + public String getValueAsString() { + return value; + } + + @Nullable + public String getValueAsStringElse(@Nullable String def) { + return Objects.requireNonNullElse(value, def); + } + + @Nullable + public String readString() { + return readString(List.of()); + } + + @Nullable + public String readString(@Nonnull List keychain) { + var x = extract(keychain); + if (x == null) return null; + return x.getValueAsString(); + } + + @Nullable + public String readString(@Nonnull List keychain, @Nullable String def) { + KeelConfigElement extracted = this.extract(keychain); + if (extracted == null) return def; + return extracted.getValueAsStringElse(def); + } + + @Nullable + public String readString(@Nonnull String keychain, @Nullable String def) { + return readString(List.of(keychain), def); + } + + @Nullable + public Integer getValueAsInteger() { + if (value == null) return null; + return Integer.parseInt(value); + } + + public int getValueAsIntegerElse(int def) { + if (value == null) return def; + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return def; + } + } + + @Nullable + public Integer readInteger() { + return readInteger(List.of()); + } + + @Nullable + public Integer readInteger(@Nonnull List keychain) { + var x = this.extract(keychain); + if (x == null) return null; + return x.getValueAsInteger(); + } + + public int readInteger(@Nonnull List keychain, int def) { + KeelConfigElement extracted = this.extract(keychain); + if (extracted == null) return def; + return extracted.getValueAsIntegerElse(def); + } + + public int readInteger(@Nonnull String keychain, int def) { + return readInteger(List.of(keychain), def); + } + + @Nullable + public Long getValueAsLong() { + if (value == null) return null; + return Long.parseLong(value); + } + + public long getValueAsLongElse(long def) { + if (value == null) return def; + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + return def; + } + } + + @Nullable + public Long readLong() { + return readLong(List.of()); + } + + @Nullable + public Long readLong(@Nonnull List keychain) { + KeelConfigElement extracted = this.extract(keychain); + if (extracted != null) { + return extracted.getValueAsLong(); + } else { + return null; + } + } + + public long readLong(@Nonnull List keychain, long def) { + KeelConfigElement extracted = this.extract(keychain); + if (extracted == null) return def; + return extracted.getValueAsLongElse(def); + } + + public long readLong(@Nonnull String keychain, long def) { + return readLong(List.of(keychain), def); + } + + @Nullable + public Float getValueAsFloat() { + if (value == null) return null; + return Float.parseFloat(value); + } + + public float getValueAsFloatElse(float def) { + if (value == null) return def; + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return def; + } + } + + @Nullable + public Float readFloat() { + return readFloat(List.of()); + } + + @Nullable + public Float readFloat(@Nonnull List keychain) { + var x = extract(keychain); + if (x == null) return null; + return x.getValueAsFloat(); + } + + public float readFloat(@Nonnull List keychain, float def) { + KeelConfigElement extracted = this.extract(keychain); + if (extracted == null) return def; + return extracted.getValueAsFloatElse(def); + } + + public float readFloat(@Nonnull String keychain, float def) { + return readFloat(List.of(keychain), def); + } + + @Nullable + public Double getValueAsDouble() { + if (value == null) return null; + return Double.parseDouble(value); + } + + public double getValueAsDoubleElse(double def) { + if (value == null) return def; + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + return def; + } + } + + @Nullable + public Double readDouble() { + return readDouble(List.of()); + } + + @Nullable + public Double readDouble(@Nonnull List keychain) { + var x = extract(keychain); + if (x == null) return null; + return x.getValueAsDouble(); + } + + public double readDouble(@Nonnull List keychain, double def) { + KeelConfigElement extracted = this.extract(keychain); + if (extracted == null) return def; + return extracted.getValueAsDoubleElse(def); + } + + public double readDouble(@Nonnull String keychain, double def) { + return readDouble(List.of(keychain), def); + } + + @Nullable + public Boolean getValueAsBoolean() { + if (value == null) return null; + return "YES".equalsIgnoreCase(value) || "TRUE".equalsIgnoreCase(value); + } + + public boolean getValueAsBooleanElse(boolean def) { + if (value == null) return def; + return "YES".equalsIgnoreCase(value) + || "TRUE".equalsIgnoreCase(value) + || "ON".equalsIgnoreCase(value) + || "1".equalsIgnoreCase(value) + ; + } + + @Nullable + public Boolean readBoolean() { + return readBoolean(List.of()); + } + + @Nullable + public Boolean readBoolean(@Nonnull List keychain) { + var x = extract(keychain); + if (x == null) return null; + return x.getValueAsBoolean(); + } + + public boolean readBoolean(@Nonnull List keychain, boolean def) { + KeelConfigElement extracted = this.extract(keychain); + if (extracted == null) return def; + return extracted.getValueAsBooleanElse(def); + } + + public boolean readBoolean(@Nonnull String keychain, boolean def) { + return readBoolean(List.of(keychain), def); + } + + public KeelConfigElement ensureChild(@Nonnull String childName) { + return this.children.computeIfAbsent(childName, x -> new KeelConfigElement(childName)); + } + + public KeelConfigElement addChild(@Nonnull KeelConfigElement child) { + this.children.put(child.getName(), child); + return this; + } + + public KeelConfigElement removeChild(@Nonnull KeelConfigElement child) { + this.children.remove(child.getName()); + return this; + } + + public KeelConfigElement removeChild(@Nonnull String childName) { + this.children.remove(childName); + return this; + } + + public KeelConfigElement setValue(@Nonnull String value) { + this.value = value; + return this; + } + + @Nonnull + public Map getChildren() { + return children; + } + + @Nullable + public KeelConfigElement getChild(@Nonnull String childName) { + return children.get(childName); + } + + public JsonObject toJsonObject() { + JsonArray childArray = new JsonArray(); + children.forEach((cName, c) -> childArray.add(c.toJsonObject())); + var x = new JsonObject() + .put("name", name) + .put("children", childArray); + if (value != null) { + x.put("value", value); + } + return x; + } + + /** + * @param split The list of keys. If empty, give this, or dig in. + */ + public @Nullable KeelConfigElement extract(@Nonnull List split) { + if (split.isEmpty()) return this; + if (split.size() == 1) return this.children.get(split.get(0)); + KeelConfigElement keelConfigElement = this.children.get(split.get(0)); + if (keelConfigElement == null) { + return null; + } + for (int i = 1; i < split.size(); i++) { + keelConfigElement = keelConfigElement.getChild(split.get(i)); + if (keelConfigElement == null) { + return null; + } + } + return keelConfigElement; + } + + public @Nullable KeelConfigElement extract(@Nonnull String... split) { + List list = Arrays.asList(split); + return this.extract(list); + } + + public KeelConfigElement loadProperties(@Nonnull Properties properties) { + properties.forEach((k, v) -> { + String fullKey = k.toString(); + String[] keyArray = fullKey.split("\\."); + KeelConfigElement keelConfigElement = null; + for (int i = 0; i < keyArray.length; i++) { + String key = keyArray[i]; + if (i == 0) { + keelConfigElement = children.computeIfAbsent(key, x -> new KeelConfigElement(key)); + } else { + keelConfigElement = keelConfigElement.ensureChild(key); + } + if (i == keyArray.length - 1) { + keelConfigElement.setValue(properties.getProperty(fullKey)); + } + } + }); + return this; + } + + /** + * @since 3.0.1 + */ + public @Nonnull KeelConfigElement loadPropertiesFile(@Nonnull String propertiesFileName) { + return loadPropertiesFile(propertiesFileName, StandardCharsets.UTF_8); + } + + public @Nonnull KeelConfigElement loadPropertiesFile(@Nonnull String propertiesFileName, @Nonnull Charset charset) { + Properties properties = new Properties(); + try { + // here, the file named as `propertiesFileName` should be put along with JAR + properties.load(new FileReader(propertiesFileName, charset)); + } catch (IOException e) { + System.err.println("Cannot find the file config.properties. Use the embedded one."); + try { + properties.load(getClass().getClassLoader().getResourceAsStream(propertiesFileName)); + } catch (IOException ex) { + throw new RuntimeException("Cannot find the embedded file config.properties.", ex); + } + } + + return loadProperties(properties); + } + + /** + * @since 3.0.6 + */ + public @Nonnull KeelConfigElement loadPropertiesFileContent(@Nonnull String content) { + Properties properties = new Properties(); + try { + properties.load(new StringReader(content)); + } catch (IOException e) { + throw new RuntimeException("Cannot load given properties content.", e); + } + return loadProperties(properties); + } +} diff --git a/src/main/java/io/github/sinri/keel/facade/configuration/package-info.java b/src/main/java/io/github/sinri/keel/facade/configuration/package-info.java new file mode 100644 index 00000000..788bae3a --- /dev/null +++ b/src/main/java/io/github/sinri/keel/facade/configuration/package-info.java @@ -0,0 +1 @@ +package io.github.sinri.keel.facade.configuration; \ No newline at end of file diff --git a/src/main/java/io/github/sinri/keel/mysql/KeelMySQLConfiguration.java b/src/main/java/io/github/sinri/keel/mysql/KeelMySQLConfiguration.java index cb304b8e..a6a9d701 100644 --- a/src/main/java/io/github/sinri/keel/mysql/KeelMySQLConfiguration.java +++ b/src/main/java/io/github/sinri/keel/mysql/KeelMySQLConfiguration.java @@ -1,7 +1,7 @@ package io.github.sinri.keel.mysql; import io.github.sinri.keel.core.TechnicalPreview; -import io.github.sinri.keel.facade.KeelConfiguration; +import io.github.sinri.keel.facade.configuration.KeelConfigElement; import io.github.sinri.keel.mysql.matrix.ResultMatrix; import io.vertx.core.Future; import io.vertx.mysqlclient.MySQLBuilder; @@ -9,6 +9,7 @@ import io.vertx.sqlclient.PoolOptions; import javax.annotation.Nonnull; +import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -24,31 +25,18 @@ * poolShared = false; * tcpKeepAlive=false; */ -public class KeelMySQLConfiguration extends KeelConfiguration { - private final @Nonnull String dataSourceName; - - public KeelMySQLConfiguration(@Nonnull String dataSourceName, @Nonnull KeelConfiguration keelConfiguration) { - this.dataSourceName = dataSourceName; - this.reloadDataFromJsonObject(keelConfiguration.toJsonObject()); - } - -// public String buildJDBCConnectionString() { -// MySQLConnectOptions connectOptions = getConnectOptions(); -// var host = connectOptions.getHost(); -// var port = connectOptions.getPort(); -// var schema = connectOptions.getDatabase(); -// var charset = connectOptions.getCharset(); -// boolean ssl = connectOptions.isSsl(); -// return "jdbc:mysql://" + host + ":" + port + "/" + schema -// + "?useSSL=" + (ssl ? "true" : "false") + "&useUnicode=true" + -// "&characterEncoding=" + charset -// + "&allowPublicKeyRetrieval="+(ssl ? "true" : "false"); -// } +public class KeelMySQLConfiguration extends KeelConfigElement { + //private final @Nonnull String dataSourceName; + + public KeelMySQLConfiguration(@Nonnull KeelConfigElement base) { + super(base); + } + @Nonnull - public static KeelMySQLConfiguration loadConfigurationForDataSource(@Nonnull KeelConfiguration keelConfiguration, @Nonnull String dataSourceName) { - KeelConfiguration configuration = keelConfiguration.extract("mysql", dataSourceName); - return new KeelMySQLConfiguration(dataSourceName, configuration); + public static KeelMySQLConfiguration loadConfigurationForDataSource(@Nonnull KeelConfigElement configCenter, @Nonnull String dataSourceName) { + KeelConfigElement keelConfigElement = configCenter.extract("mysql", dataSourceName); + return new KeelMySQLConfiguration(Objects.requireNonNull(keelConfigElement)); } @Nonnull @@ -94,44 +82,46 @@ public PoolOptions getPoolOptions() { } public String getHost() { - return readString("host"); + return readString(List.of("host"), null); } public Integer getPort() { - return Objects.requireNonNullElse(readAsInteger("port"), 3306); + return readInteger(List.of("port"), 3306); } public String getPassword() { - return readString("password"); + return readString(List.of("password"), null); } public String getUsername() { - var u = readString("username"); + var u = readString("username", null); if (u == null) { - u = readString("user"); + u = readString("user", null); } return u; } public String getDatabase() { - String schema = readString("schema"); + String schema = readString("schema", null); if (schema == null) { - schema = readString("database"); + schema = readString("database", null); } return Objects.requireNonNullElse(schema, ""); } public String getCharset() { - return readString("charset"); + return readString("charset", null); } public Integer getPoolMaxSize() { - return readAsInteger("poolMaxSize"); + var x = getChild("poolMaxSize"); + if (x == null) return null; + return x.getValueAsInteger(); } @Nonnull public String getDataSourceName() { - return dataSourceName; + return getName(); } /** @@ -141,7 +131,11 @@ public String getDataSourceName() { * @since 3.0.1 let it be its original setting! */ private Integer getConnectionTimeout() { - return readAsInteger("connectionTimeout"); + var x = getChild("connectionTimeout"); + if (x == null) { + return null; + } + return x.getValueAsInteger(); } /** @@ -152,7 +146,11 @@ private Integer getConnectionTimeout() { * @see ... */ public Integer getPoolConnectionTimeout() { - return readAsInteger("poolConnectionTimeout"); + KeelConfigElement keelConfigElement = extract("poolConnectionTimeout"); + if (keelConfigElement == null) { + return null; + } + return keelConfigElement.getValueAsInteger(); } /** @@ -162,7 +160,7 @@ public Integer getPoolConnectionTimeout() { * that created it is undeployed. */ public boolean getPoolShared() { - return !("NO".equals(readString("poolShared"))); + return readBoolean("poolShared", true); } diff --git a/src/main/java/io/github/sinri/keel/mysql/KeelMySQLDataSourceProvider.java b/src/main/java/io/github/sinri/keel/mysql/KeelMySQLDataSourceProvider.java index 74f38471..c4805aed 100644 --- a/src/main/java/io/github/sinri/keel/mysql/KeelMySQLDataSourceProvider.java +++ b/src/main/java/io/github/sinri/keel/mysql/KeelMySQLDataSourceProvider.java @@ -1,9 +1,9 @@ package io.github.sinri.keel.mysql; -import io.github.sinri.keel.facade.KeelConfiguration; import io.vertx.sqlclient.SqlConnection; import javax.annotation.Nonnull; +import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -13,7 +13,7 @@ public class KeelMySQLDataSourceProvider { @Nonnull public static String defaultMySQLDataSourceName() { - return Objects.requireNonNullElse(Keel.getConfiguration().readString("mysql", "default_data_source_name"), "default"); + return Objects.requireNonNull(Keel.getConfiguration().readString(List.of("mysql", "default_data_source_name"), "default")); } /** @@ -24,9 +24,9 @@ public static NamedMySQLDataSource initializ @Nonnull String dataSourceName, Function sqlConnectionWrapper ) { - KeelConfiguration configuration = Keel.getConfiguration().extract("mysql", dataSourceName); + var configuration = Keel.getConfiguration().extract("mysql", dataSourceName); Objects.requireNonNull(configuration); - KeelMySQLConfiguration mySQLConfigure = new KeelMySQLConfiguration(dataSourceName, configuration); + KeelMySQLConfiguration mySQLConfigure = new KeelMySQLConfiguration(configuration); return new NamedMySQLDataSource<>(mySQLConfigure, sqlConnectionWrapper); } diff --git a/src/main/java/io/github/sinri/keel/mysql/condition/AmongstCondition.java b/src/main/java/io/github/sinri/keel/mysql/condition/AmongstCondition.java index fcaa3b12..ccb2c0e2 100644 --- a/src/main/java/io/github/sinri/keel/mysql/condition/AmongstCondition.java +++ b/src/main/java/io/github/sinri/keel/mysql/condition/AmongstCondition.java @@ -2,6 +2,7 @@ import io.github.sinri.keel.mysql.Quoter; import io.github.sinri.keel.mysql.exception.KeelSQLGenerateError; +import io.github.sinri.keel.mysql.statement.AbstractReadStatement; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -207,6 +208,14 @@ public AmongstCondition amongstExpressionList(@Nonnull List values) { return this; } + /** + * @param readStatement A READ Statement, such as SELECT. + * @since 3.2.4 + */ + public AmongstCondition amongstReadStatement(@Nonnull AbstractReadStatement readStatement) { + return this.amongstExpression(readStatement.toString()); + } + /** * 生成SQL的比较条件表达式文本。如果出错,则抛出 KeelSQLGenerateError 异常。 * diff --git a/src/main/java/io/github/sinri/keel/mysql/statement/AbstractStatement.java b/src/main/java/io/github/sinri/keel/mysql/statement/AbstractStatement.java index 799addc1..cdae2e3e 100644 --- a/src/main/java/io/github/sinri/keel/mysql/statement/AbstractStatement.java +++ b/src/main/java/io/github/sinri/keel/mysql/statement/AbstractStatement.java @@ -48,9 +48,6 @@ protected String getRemarkAsComment() { } public AbstractStatement setRemarkAsComment(@Nonnull String remarkAsComment) { -// if (remarkAsComment == null) { -// remarkAsComment = ""; -// } remarkAsComment = remarkAsComment.replaceAll("[\\r\\n]+", "¦"); this.remarkAsComment = remarkAsComment; return this; diff --git a/src/main/java/io/github/sinri/keel/mysql/statement/AnyStatement.java b/src/main/java/io/github/sinri/keel/mysql/statement/AnyStatement.java index f9f99b2e..ae736a15 100644 --- a/src/main/java/io/github/sinri/keel/mysql/statement/AnyStatement.java +++ b/src/main/java/io/github/sinri/keel/mysql/statement/AnyStatement.java @@ -66,14 +66,14 @@ static WriteIntoStatement replace(@Nonnull Handler statement return writeIntoStatement; } - static TemplatedReadStatement templatedRead(@Nonnull String path,@Nonnull Handler templatedReadStatementHandler) { + static TemplatedReadStatement templatedRead(@Nonnull String path, @Nonnull Handler templatedReadStatementHandler) { TemplatedReadStatement readStatement = TemplatedStatement.loadTemplateToRead(path); TemplateArgumentMapping arguments = readStatement.getArguments(); templatedReadStatementHandler.handle(arguments); return readStatement; } - static TemplatedModifyStatement templatedModify(@Nonnull String path,@Nonnull Handler templatedModifyStatementHandler) { + static TemplatedModifyStatement templatedModify(@Nonnull String path, @Nonnull Handler templatedModifyStatementHandler) { TemplatedModifyStatement templatedModifyStatement = TemplatedStatement.loadTemplateToModify(path); TemplateArgumentMapping arguments = templatedModifyStatement.getArguments(); templatedModifyStatementHandler.handle(arguments); diff --git a/src/main/java/io/github/sinri/keel/mysql/statement/CatholicQueryCriteria.java b/src/main/java/io/github/sinri/keel/mysql/statement/CatholicQueryCriteria.java deleted file mode 100644 index 32561d38..00000000 --- a/src/main/java/io/github/sinri/keel/mysql/statement/CatholicQueryCriteria.java +++ /dev/null @@ -1,98 +0,0 @@ -package io.github.sinri.keel.mysql.statement; - -import io.github.sinri.keel.mysql.condition.CompareCondition; -import io.vertx.core.json.JsonArray; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -/** - * @since 1.13 - */ -@Deprecated(since = "3.1.8", forRemoval = true) -public class CatholicQueryCriteria { - protected final Map inclusiveCriteria = new HashMap<>(); - protected final Map exclusiveCriteria = new HashMap<>(); - - public CatholicQueryCriteria include(@Nonnull String name, @Nullable T value) { - if (value == null) return this; - inclusiveCriteria.computeIfAbsent(name, x -> new JsonArray()); - inclusiveCriteria.get(name).add(value); - return this; - } - - public CatholicQueryCriteria include(@Nonnull String name, @Nullable Collection values) { - if (values == null) return this; - inclusiveCriteria.computeIfAbsent(name, x -> new JsonArray()); - JsonArray jsonArray = new JsonArray(); - values.forEach(jsonArray::add); - inclusiveCriteria.get(name).addAll(jsonArray); - return this; - } - - public CatholicQueryCriteria include(@Nonnull String name, @Nullable JsonArray values) { - if (values == null) return this; - inclusiveCriteria.computeIfAbsent(name, x -> new JsonArray()); - inclusiveCriteria.get(name).addAll(values); - return this; - } - - public CatholicQueryCriteria exclude(@Nonnull String name, @Nullable T value) { - if (value == null) return this; - exclusiveCriteria.computeIfAbsent(name, x -> new JsonArray()); - exclusiveCriteria.get(name).add(value); - return this; - } - - public CatholicQueryCriteria exclude(@Nonnull String name, @Nullable Collection values) { - if (values == null) return this; - exclusiveCriteria.computeIfAbsent(name, x -> new JsonArray()); - JsonArray jsonArray = new JsonArray(); - values.forEach(jsonArray::add); - exclusiveCriteria.get(name).addAll(jsonArray); - return this; - } - - public CatholicQueryCriteria exclude(@Nonnull String name, @Nullable JsonArray values) { - if (values == null) return this; - exclusiveCriteria.computeIfAbsent(name, x -> new JsonArray()); - exclusiveCriteria.get(name).addAll(values); - return this; - } - - public final ConditionsComponent mergeIntoConditionsComponent(@Nonnull ConditionsComponent conditionsComponent) { - inclusiveCriteria.forEach((name, objects) -> { - if (objects.isEmpty()) return; - if (objects.size() == 1) { - conditionsComponent.comparison(compareCondition -> compareCondition - .filedEqualsValue(name, objects.getValue(0)) - ); - } else { - conditionsComponent.among(amongstCondition -> amongstCondition - .elementAsExpression(name) - .amongstValueList(objects.getList()) - ); - } - }); - exclusiveCriteria.forEach((name, objects) -> { - if (objects.isEmpty()) return; - if (objects.size() == 1) { - conditionsComponent.comparison(compareCondition -> compareCondition - .compare(name) - .operator(CompareCondition.OP_NEQ) - .againstValue(objects.getValue(0)) - ); - } else { - conditionsComponent.among(amongstCondition -> amongstCondition - .elementAsExpression(name) - .not() - .amongstValueList(objects.getList()) - ); - } - }); - return conditionsComponent; - } -} diff --git a/src/main/java/io/github/sinri/keel/mysql/statement/ConditionsComponent.java b/src/main/java/io/github/sinri/keel/mysql/statement/ConditionsComponent.java index d1713eeb..7d55e112 100644 --- a/src/main/java/io/github/sinri/keel/mysql/statement/ConditionsComponent.java +++ b/src/main/java/io/github/sinri/keel/mysql/statement/ConditionsComponent.java @@ -1,8 +1,6 @@ package io.github.sinri.keel.mysql.statement; import io.github.sinri.keel.mysql.condition.*; -import io.vertx.core.json.JsonArray; -import io.vertx.core.json.JsonObject; import javax.annotation.Nonnull; import java.util.ArrayList; @@ -22,6 +20,10 @@ public ConditionsComponent() { conditions = new ArrayList<>(); } + public ConditionsComponent(@Nonnull ConditionsComponent another) { + this.conditions = new ArrayList<>(another.conditions); + } + public boolean isEmpty() { return this.conditions.isEmpty(); } @@ -199,56 +201,9 @@ public ConditionsComponent raw(@Nonnull String raw) { return this; } - /** - * @param catholicQueryCriteria CatholicQueryCriteria Instance - * @return this - * @since 1.13 - */ - @Deprecated(since = "3.1.8", forRemoval = true) - public ConditionsComponent withCatholicQueryCriteria(@Nonnull CatholicQueryCriteria catholicQueryCriteria) { - return catholicQueryCriteria.mergeIntoConditionsComponent(this); - } - @Override public String toString() { if (conditions.isEmpty()) return ""; return KeelHelpers.stringHelper().joinStringArray(conditions, " and "); } - - /** - * @since 2.0 - */ - @Deprecated(since = "3.1.0", forRemoval = true) - public final ConditionsComponent quickMapping(JsonObject mapping) { - mapping.forEach(entry -> quickMapping(entry.getKey(), entry.getValue())); - return this; - } - - /** - * @since 2.0 - */ - @Deprecated(since = "3.1.0", forRemoval = true) - public final ConditionsComponent quickMapping(String key, Object value) { - if (key != null && !key.isEmpty()) { - if (value == null) { - this.comparison(compareCondition -> compareCondition - .compare(key) - .operator(CompareCondition.OP_IS) - .againstExpression("NULL") - ); - } else if (value instanceof JsonArray) { - if (((JsonArray) value).size() > 0) { - this.among(amongstCondition -> amongstCondition - .elementAsExpression(key) - .amongstValueList(((JsonArray) value).getList()) - ); - } - } else { - this.comparison(compareCondition -> compareCondition - .filedEqualsValue(key, value.toString()) - ); - } - } - return this; - } } diff --git a/src/main/java/io/github/sinri/keel/mysql/statement/SelectStatement.java b/src/main/java/io/github/sinri/keel/mysql/statement/SelectStatement.java index 8f4a4abb..394cf336 100644 --- a/src/main/java/io/github/sinri/keel/mysql/statement/SelectStatement.java +++ b/src/main/java/io/github/sinri/keel/mysql/statement/SelectStatement.java @@ -1,10 +1,15 @@ package io.github.sinri.keel.mysql.statement; +import io.github.sinri.keel.mysql.NamedMySQLConnection; import io.github.sinri.keel.mysql.condition.CompareCondition; import io.github.sinri.keel.mysql.condition.GroupCondition; import io.github.sinri.keel.mysql.condition.MySQLCondition; import io.github.sinri.keel.mysql.condition.RawCondition; import io.github.sinri.keel.mysql.exception.KeelSQLGenerateError; +import io.github.sinri.keel.mysql.exception.KeelSQLResultRowIndexError; +import io.github.sinri.keel.mysql.matrix.ResultMatrix; +import io.vertx.core.Future; +import io.vertx.sqlclient.SqlConnection; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -16,23 +21,51 @@ import static io.github.sinri.keel.helper.KeelHelpersInterface.KeelHelpers; public class SelectStatement extends AbstractReadStatement { - // private final List whereConditions = new ArrayList<>(); - final ConditionsComponent whereConditionsComponent = new ConditionsComponent(); - // private final List havingConditions = new ArrayList<>(); - final ConditionsComponent havingConditionsComponent = new ConditionsComponent(); - private final List tables = new ArrayList<>(); - private final List columns = new ArrayList<>(); - private final List categories = new ArrayList<>(); - private final List sortRules = new ArrayList<>(); - private long offset = 0; - private long limit = 0; - private @Nonnull String lockMode = ""; + final ConditionsComponent whereConditionsComponent; + final ConditionsComponent havingConditionsComponent; + private final List tables; + private final List columns; + private final List categories; + private final List sortRules; + private long offset; + private long limit; + private @Nonnull String lockMode; /** * For MySQL 5.7 ,8.0 or higher, in Select, to limit the max execution time in millisecond. * * @since 3.1.0 */ - private @Nullable Long maxExecutionTime = null; + private @Nullable Long maxExecutionTime; + + /** + * @param another To swift clone one instance based on without direct reference. + * @since 3.2.3 + */ + public SelectStatement(@Nonnull SelectStatement another) { + this.whereConditionsComponent = new ConditionsComponent(another.whereConditionsComponent); + this.havingConditionsComponent = new ConditionsComponent(another.havingConditionsComponent); + this.tables = new ArrayList<>(another.tables); + this.columns = new ArrayList<>(another.columns); + this.categories = new ArrayList<>(another.categories); + this.sortRules = new ArrayList<>(another.sortRules); + this.offset = another.offset; + this.limit = another.limit; + this.lockMode = another.lockMode; + this.maxExecutionTime = another.maxExecutionTime; + } + + public SelectStatement() { + this.whereConditionsComponent = new ConditionsComponent(); + this.havingConditionsComponent = new ConditionsComponent(); + this.tables = new ArrayList<>(); + this.columns = new ArrayList<>(); + this.categories = new ArrayList<>(); + this.sortRules = new ArrayList<>(); + this.offset = 0; + this.limit = 0; + this.lockMode = ""; + this.maxExecutionTime = null; + } public SelectStatement from(@Nonnull String tableExpression) { return from(tableExpression, null); @@ -57,11 +90,11 @@ public SelectStatement from(@Nonnull String tableExpression, @Nullable String al /** * @since 2.8 */ - public SelectStatement from(@Nonnull AbstractReadStatement subQuery,@Nonnull String alias) { + public SelectStatement from(@Nonnull AbstractReadStatement subQuery, @Nonnull String alias) { if (alias.isBlank()) { throw new KeelSQLGenerateError("Sub Query without alias"); } - return this.from("(" + subQuery.toString() + ")", alias); + return this.from("(" + subQuery + ")", alias); } public SelectStatement leftJoin(@Nonnull Function joinFunction) { @@ -88,13 +121,18 @@ public SelectStatement straightJoin(@Nonnull Function func) { columns.add(func.apply(new ColumnComponent()).toString()); return this; } public SelectStatement columnWithAlias(@Nonnull String columnExpression, @Nonnull String alias) { - if(columnExpression.isBlank() || alias.isBlank()){ + if (columnExpression.isBlank() || alias.isBlank()) { throw new IllegalArgumentException("Column or its alias is empty."); } columns.add(columnExpression + " as `" + alias + "`"); @@ -204,7 +242,7 @@ public String toString() { if (limit > 0) { sql.append(AbstractStatement.SQL_COMPONENT_SEPARATOR).append("LIMIT ").append(limit).append(" OFFSET ").append(offset); } - if (!"".equals(lockMode)) { + if (!lockMode.isEmpty()) { sql.append(AbstractStatement.SQL_COMPONENT_SEPARATOR).append(lockMode); } if (!getRemarkAsComment().isEmpty()) { @@ -213,11 +251,64 @@ public String toString() { return String.valueOf(sql); } + /** + * @since 3.2.3 + */ + Future queryForPagination( + NamedMySQLConnection sqlConnection, + long pageNo, + long pageSize + ) { + return this.queryForPagination(sqlConnection.getSqlConnection(), pageNo, pageSize); + } + + /** + * Call from this instance, as the original query as Select Statement for all rows in certain order. + * + * @param pageNo since 1. + * @param pageSize a number + * @since 3.2.3 + */ + Future queryForPagination( + SqlConnection sqlConnection, + long pageNo, + long pageSize + ) { + if (pageSize <= 0) throw new IllegalArgumentException("page size <= 0"); + if (pageNo < 1) throw new IllegalArgumentException("page no < 1"); + var countStatement = new SelectStatement(this) + .resetColumns() + .columnWithAlias("count(*)", "total") + .limit(0, 0); + this.limit(pageSize, (pageNo - 1) * pageSize); + + return Future.all( + countStatement.execute(sqlConnection) + .compose(resultMatrix -> { + try { + long total = resultMatrix.getOneColumnOfFirstRowAsLong("total"); + return Future.succeededFuture(total); + } catch (KeelSQLResultRowIndexError e) { + throw new RuntimeException(e); + } + }), + this.execute(sqlConnection) + ) + .compose(compositeFuture -> { + Long total = compositeFuture.resultAt(0); + ResultMatrix resultMatrix = compositeFuture.resultAt(1); + return Future.succeededFuture(new PaginationResult(total, resultMatrix)); + }); + } + public static class JoinComponent { - @Nonnull final String joinType; + @Nonnull + final String joinType; final List onConditions = new ArrayList<>(); - @Nonnull String tableExpression="NOT-SET"; - @Nullable String alias; + @Nonnull + String tableExpression = "NOT-SET"; + @Nullable + String alias; public JoinComponent(@Nonnull String joinType) { this.joinType = joinType; @@ -267,17 +358,21 @@ public String toString() { } public static class ColumnComponent { - @Nullable String schema; - @Nullable String field="NOT-SET"; - @Nullable String expression; - @Nullable String alias; + @Nullable + String schema; + @Nullable + String field = "NOT-SET"; + @Nullable + String expression; + @Nullable + String alias; public ColumnComponent field(@Nonnull String field) { this.field = field; return this; } - public ColumnComponent field(@Nullable String schema,@Nonnull String field) { + public ColumnComponent field(@Nullable String schema, @Nonnull String field) { this.schema = schema; this.field = field; return this; @@ -311,4 +406,22 @@ public String toString() { return String.valueOf(column); } } + + public static class PaginationResult { + private final long total; + private final ResultMatrix resultMatrix; + + public PaginationResult(long total, ResultMatrix resultMatrix) { + this.total = total; + this.resultMatrix = resultMatrix; + } + + public long getTotal() { + return total; + } + + public ResultMatrix getResultMatrix() { + return resultMatrix; + } + } } diff --git a/src/main/java/io/github/sinri/keel/mysql/statement/WriteIntoStatement.java b/src/main/java/io/github/sinri/keel/mysql/statement/WriteIntoStatement.java index a6c08d79..12a96736 100644 --- a/src/main/java/io/github/sinri/keel/mysql/statement/WriteIntoStatement.java +++ b/src/main/java/io/github/sinri/keel/mysql/statement/WriteIntoStatement.java @@ -74,15 +74,7 @@ public WriteIntoStatement columns(@Nonnull List columns) { public WriteIntoStatement addDataMatrix(@Nonnull List> batch) { for (List row : batch) { - List t = new ArrayList<>(); - for (Object item : row) { - if (item == null) { - t.add("NULL"); - } else { - t.add(new Quoter(String.valueOf(item)).toString()); - } - } - this.batchValues.add(t); + this.addDataRow(row); } return this; } diff --git a/src/main/java/io/github/sinri/keel/redis/RedisKit.java b/src/main/java/io/github/sinri/keel/redis/RedisKit.java index cd7071f9..d6d95473 100644 --- a/src/main/java/io/github/sinri/keel/redis/RedisKit.java +++ b/src/main/java/io/github/sinri/keel/redis/RedisKit.java @@ -7,6 +7,7 @@ import io.vertx.redis.client.RedisConnection; import io.vertx.redis.client.RedisOptions; +import java.util.List; import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; @@ -23,7 +24,7 @@ public RedisKit(String redisInstanceKey) { /* * URL should be redis://[:password@]host[:port][/db-number] */ - String url = Keel.getConfiguration().readString("redis", redisInstanceKey, "url"); + String url = Keel.getConfiguration().readString(List.of("redis", redisInstanceKey, "url"), null); Objects.requireNonNull(url); this.client = Redis.createClient(Keel.getVertx(), new RedisOptions() .setConnectionString(url) diff --git a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundial.java b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundial.java index d814e931..1a2583a3 100644 --- a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundial.java +++ b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundial.java @@ -1,19 +1,22 @@ package io.github.sinri.keel.servant.sundial; +import io.github.sinri.keel.core.KeelCronExpression; +import io.github.sinri.keel.facade.async.KeelAsyncKit; import io.github.sinri.keel.logger.event.KeelEventLogger; import io.github.sinri.keel.logger.issue.center.KeelIssueRecordCenter; import io.github.sinri.keel.verticles.KeelVerticleImplWithEventLogger; import io.vertx.core.Future; +import io.vertx.core.json.JsonObject; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; import static io.github.sinri.keel.facade.KeelInstance.Keel; /** * @since 3.0.0 + * @since 3.2.4 use verticle to handle the sundial plan executing. */ public abstract class KeelSundial extends KeelVerticleImplWithEventLogger { private final Map planMap = new ConcurrentHashMap<>(); @@ -29,51 +32,58 @@ protected KeelEventLogger buildEventLogger() { @Override public void start() throws Exception { - long delaySeconds = 60 - (System.currentTimeMillis() / 1000) % 60; - this.timerID = Keel.getVertx().setPeriodic(delaySeconds, 60_000L, timerID -> { - Calendar calendar = Calendar.getInstance(); - handleEveryMinute(calendar); + int delaySeconds = 61 - KeelCronExpression.parseCalenderToElements(Calendar.getInstance()).second; + this.timerID = Keel.getVertx().setPeriodic(delaySeconds * 1000L, 60_000L, timerID -> { + handleEveryMinute(Calendar.getInstance()); + refreshPlans(); }); } private void handleEveryMinute(Calendar now) { + KeelCronExpression.ParsedCalenderElements parsedCalenderElements = new KeelCronExpression.ParsedCalenderElements(now); planMap.forEach((key, plan) -> { - if (plan.cronExpression().match(now)) { + if (plan.cronExpression().match(parsedCalenderElements)) { + getLogger().debug("Sundial Plan Matched", new JsonObject() + .put("plan_key", plan.key()) + .put("plan_cron", plan.cronExpression()) + .put("now", parsedCalenderElements.toString()) + ); plan.execute(now); + } else { + getLogger().debug("Sundial Plan Not Match", new JsonObject() + .put("plan_key", plan.key()) + .put("plan_cron", plan.cronExpression()) + .put("now", parsedCalenderElements.toString()) + ); } }); - - // refresh plan, pfs {0: not-fetching, more: fetching} - if (planFetchingSemaphore.get() == 0) { - planFetchingSemaphore.incrementAndGet(); - fetchPlans() - .compose(plans -> { - if (plans == null) { - // treat null as NOT MODIFIED - return Future.succeededFuture(); - } - Set toDelete = new HashSet<>(planMap.keySet()); - plans.forEach(plan -> { - toDelete.remove(plan.key()); - planMap.put(plan.key(), plan); - }); - if (!toDelete.isEmpty()) { - toDelete.forEach(planMap::remove); - } - return Future.succeededFuture(); - }) - .eventually(() -> { - planFetchingSemaphore.decrementAndGet(); - return Future.succeededFuture(); - }); - - } - } - @Deprecated(since = "3.0.1", forRemoval = true) - protected Supplier> plansSupplier() { - return null; + /** + * @since 3.2.4 + */ + private void refreshPlans() { + KeelAsyncKit.exclusivelyCall( + "io.github.sinri.keel.servant.sundial.KeelSundial.refreshPlans", + 1000L, + () -> { + return fetchPlans() + .compose(plans -> { + // treat null as NOT MODIFIED + if (plans != null) { + Set toDelete = new HashSet<>(planMap.keySet()); + plans.forEach(plan -> { + toDelete.remove(plan.key()); + planMap.put(plan.key(), plan); + }); + if (!toDelete.isEmpty()) { + toDelete.forEach(planMap::remove); + } + } + return Future.succeededFuture(); + }); + } + ); } /** diff --git a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialPlan.java b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialPlan.java index 50f52b51..37812b2f 100644 --- a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialPlan.java +++ b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialPlan.java @@ -1,16 +1,18 @@ package io.github.sinri.keel.servant.sundial; import io.github.sinri.keel.core.KeelCronExpression; +import io.vertx.core.Future; import java.util.Calendar; /** * @since 3.0.0 + * @since 3.2.4 change sync method `execute` to be async. */ public interface KeelSundialPlan { String key(); KeelCronExpression cronExpression(); - void execute(Calendar now); + Future execute(Calendar now); } diff --git a/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialVerticle.java b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialVerticle.java new file mode 100644 index 00000000..c5129046 --- /dev/null +++ b/src/main/java/io/github/sinri/keel/servant/sundial/KeelSundialVerticle.java @@ -0,0 +1,31 @@ +package io.github.sinri.keel.servant.sundial; + +import io.github.sinri.keel.verticles.KeelVerticleImplPure; +import io.vertx.core.Future; + +import javax.annotation.Nonnull; +import java.util.Calendar; + +/** + * @since 3.2.4 + */ +public class KeelSundialVerticle extends KeelVerticleImplPure { + private final KeelSundialPlan sundialPlan; + private final Calendar now; + + public KeelSundialVerticle(@Nonnull KeelSundialPlan sundialPlan, Calendar now) { + this.sundialPlan = sundialPlan; + this.now = now; + } + + @Override + public void start() throws Exception { + Future.succeededFuture() + .compose(v -> { + return sundialPlan.execute(now); + }) + .onComplete(ar -> { + undeployMe(); + }); + } +} diff --git a/src/test/java/io/github/sinri/keel/test/lab/blocking/AsyncTest.java b/src/test/java/io/github/sinri/keel/test/lab/blocking/AsyncTest.java new file mode 100644 index 00000000..8a9ebe75 --- /dev/null +++ b/src/test/java/io/github/sinri/keel/test/lab/blocking/AsyncTest.java @@ -0,0 +1,75 @@ +package io.github.sinri.keel.test.lab.blocking; + +import io.github.sinri.keel.facade.async.KeelAsyncKit; +import io.github.sinri.keel.tesuto.KeelTest; +import io.github.sinri.keel.tesuto.TestUnit; +import io.vertx.core.Future; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AsyncTest extends KeelTest { + + @TestUnit + public Future foreachBreakableTest() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + list.add(i); + } + + var x = new ForeachBreakable(list) { + + @Override + protected Future handleItem(Integer item, AtomicBoolean breakAtomic) { + getLogger().info("handle " + item); + if (item % 5 == 4) { + breakAtomic.set(true); + } + return Future.succeededFuture(); + } + }; + return x.start() + .compose(Future::succeededFuture) + .onSuccess(v -> { + getLogger().notice("FIN"); + }) + .onFailure(e -> { + getLogger().exception(e); + }); + } + + public abstract static class ForeachBreakable { + private final Iterable iterable; + private final AtomicBoolean breakRef = new AtomicBoolean(false); + + public ForeachBreakable(Iterable iterable) { + this.iterable = iterable; + } + + public Future start() { + Iterator iterator = iterable.iterator(); + return KeelAsyncKit.repeatedlyCall(routineResult -> { + if (iterator.hasNext()) { + T next = iterator.next(); + return handleItem(next, breakRef) + .compose(x -> { + if (breakRef.get()) { + routineResult.stop(); + } + return Future.succeededFuture(); + }); + } else { + routineResult.stop(); + return Future.succeededFuture(); + } + }); + } + + abstract protected Future handleItem(T item, AtomicBoolean breakAtomic); + + } + + +} diff --git a/src/test/java/io/github/sinri/keel/test/lab/blocking/BlockingVerticlePlanA.java b/src/test/java/io/github/sinri/keel/test/lab/blocking/BlockingVerticlePlanA.java index 2757e528..3210f217 100644 --- a/src/test/java/io/github/sinri/keel/test/lab/blocking/BlockingVerticlePlanA.java +++ b/src/test/java/io/github/sinri/keel/test/lab/blocking/BlockingVerticlePlanA.java @@ -45,7 +45,7 @@ public static void main(String[] args) { // System.out.println("isWorkerContext: " + futureForBlocking.context.isWorkerContext()); // System.out.println("isEventLoopContext: " + futureForBlocking.context.isEventLoopContext()); - return Future.succeededFuture(); + return io.vertx.core.Future.succeededFuture(); }) .compose(ready -> { return Future.all( diff --git a/src/test/java/io/github/sinri/keel/test/lab/elasticsearch/ESCatIndicesTest.java b/src/test/java/io/github/sinri/keel/test/lab/elasticsearch/ESCatIndicesTest.java new file mode 100644 index 00000000..fa35dfb6 --- /dev/null +++ b/src/test/java/io/github/sinri/keel/test/lab/elasticsearch/ESCatIndicesTest.java @@ -0,0 +1,36 @@ +package io.github.sinri.keel.test.lab.elasticsearch; + +import io.github.sinri.keel.elasticsearch.ElasticSearchKit; +import io.github.sinri.keel.tesuto.KeelTest; +import io.github.sinri.keel.tesuto.TestUnit; +import io.vertx.core.Future; +import io.vertx.core.http.HttpMethod; + +import javax.annotation.Nonnull; + +import static io.github.sinri.keel.facade.KeelInstance.Keel; + +public class ESCatIndicesTest extends KeelTest { + @Nonnull + @Override + protected Future starting() { + Keel.getConfiguration().loadPropertiesFile("config.properties"); + return super.starting(); + } + + @TestUnit + public Future test() { + // {"client_code":"ai-test","timestamp":1712023360984,"checksum":"d6abf7d98af34907d97f6a6578a429b5","http_method":"GET","endpoint":"/_cat/indices"} + return new ElasticSearchKit("kumori") + .call( + HttpMethod.GET, + "/_cat/indices", + null, + null + ) + .compose(resp -> { + getLogger().info("resp", resp); + return Future.succeededFuture(); + }); + } +} diff --git a/src/test/java/io/github/sinri/keel/test/lab/idea/ChildWithoutMainButSuperHas.java b/src/test/java/io/github/sinri/keel/test/lab/idea/ChildWithoutMainButSuperHas.java new file mode 100644 index 00000000..254f0e9b --- /dev/null +++ b/src/test/java/io/github/sinri/keel/test/lab/idea/ChildWithoutMainButSuperHas.java @@ -0,0 +1,4 @@ +package io.github.sinri.keel.test.lab.idea; + +public class ChildWithoutMainButSuperHas extends SuperWithMain { +} diff --git a/src/test/java/io/github/sinri/keel/test/lab/idea/SuperWithMain.java b/src/test/java/io/github/sinri/keel/test/lab/idea/SuperWithMain.java new file mode 100644 index 00000000..377b33be --- /dev/null +++ b/src/test/java/io/github/sinri/keel/test/lab/idea/SuperWithMain.java @@ -0,0 +1,7 @@ +package io.github.sinri.keel.test.lab.idea; + +public class SuperWithMain { + public static void main(String[] args) { + System.out.println("Super!"); + } +} diff --git a/src/test/java/io/github/sinri/keel/test/lab/idea/package-info.java b/src/test/java/io/github/sinri/keel/test/lab/idea/package-info.java new file mode 100644 index 00000000..d91c756b --- /dev/null +++ b/src/test/java/io/github/sinri/keel/test/lab/idea/package-info.java @@ -0,0 +1 @@ +package io.github.sinri.keel.test.lab.idea; \ No newline at end of file diff --git a/src/test/java/io/github/sinri/keel/test/lab/unit/KeelConfigTest.java b/src/test/java/io/github/sinri/keel/test/lab/unit/KeelConfigTest.java new file mode 100644 index 00000000..1673d506 --- /dev/null +++ b/src/test/java/io/github/sinri/keel/test/lab/unit/KeelConfigTest.java @@ -0,0 +1,26 @@ +package io.github.sinri.keel.test.lab.unit; + +import io.github.sinri.keel.tesuto.KeelTest; +import io.github.sinri.keel.tesuto.TestUnit; +import io.vertx.core.Future; + +import javax.annotation.Nonnull; + +import static io.github.sinri.keel.facade.KeelInstance.Keel; + +public class KeelConfigTest extends KeelTest { + + @Nonnull + @Override + protected Future starting() { + Keel.getConfiguration().loadPropertiesFile("config.properties"); + return Future.succeededFuture(); + } + + @TestUnit + public Future readTest() { + getLogger().info("all", Keel.getConfiguration().toJsonObject()); + getLogger().info("email.smtp.default_smtp_name: " + Keel.config("email.smtp.default_smtp_name")); + return Future.succeededFuture(); + } +}