diff --git a/pom.xml b/pom.xml index c73bd45..2b5af1b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,9 +4,9 @@ 4.0.0 org.summerboot jexpress - 2.5.0 + 2.5.1 jar - Summer Boot jExpress + Summer Boot jExpress ${version} Summer Boot jExpress focuses on solving non-functional and operational maintainability requirements, some of which Spring Boot has (may) not yet provided @@ -182,7 +182,7 @@ 21 - 3.4.0 + 3.4.1 3.13.0 3.8.1 3.4.2 @@ -206,8 +206,8 @@ 0.12.6 - 4.1.117.Final - 2.0.69.Final + 4.1.119.Final + 2.0.70.Final 1.70.0 33.4.0-jre @@ -218,7 +218,7 @@ - 3.0.0 + 3.1.0 4.0.0 3.0.0 @@ -228,14 +228,14 @@ 2.18.2 6.0.1 - 11.0.2 + 11.0.4 8.0.2.Final 7.0.0 - 6.6.5.Final + 6.6.9.Final 6.2.1 @@ -254,11 +254,11 @@ 1.0.10 1.18 - 9.0.0 - 6.0.0 + 9.1.0 + 6.1.0 - 7.10.2 + 7.11.0 9.2.0 diff --git a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java index 0811d26..c8dd259 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BackOffice.java +++ b/src/main/java/org/summerboot/jexpress/boot/BackOffice.java @@ -314,7 +314,7 @@ public Map getBootErrorCodeMapping() { @Config(key = "naming.cli.psv", defaultValue = "psv") private String cliName_psv = "psv"; - @Config(key = "naming.memo.delimiter", defaultValue = ": ") + @Config(key = "naming.memo.delimiter", defaultValue = ": ", trim = false) private String memoDelimiter = ": "; public Set getRootPackageNames() { diff --git a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java index 0f4d404..58a5ea9 100644 --- a/src/main/java/org/summerboot/jexpress/boot/BootConstant.java +++ b/src/main/java/org/summerboot/jexpress/boot/BootConstant.java @@ -17,17 +17,18 @@ import org.apache.logging.log4j.Level; -import java.util.Random; +import java.security.SecureRandom; /** * @author Changski Tie Zheng Zhang 张铁铮, 魏泽北, 杜旺财, 杜富贵 */ public interface BootConstant { - String APP_ID = String.format("%06d", new Random().nextInt(999999)); + int APP_ID_VALUE = new SecureRandom().nextInt(999999); + String APP_ID = String.format("%06d", APP_ID_VALUE); //version - String VERSION = "jExpress 2.5.0"; + String VERSION = "jExpress 2.5.1"; String JEXPRESS_PACKAGE_NAME = "org.summerboot.jexpress"; String DEFAULT_ADMIN_MM = "changeit"; 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 f59eb5e..8691b82 100644 --- a/src/main/java/org/summerboot/jexpress/boot/config/BootConfig.java +++ b/src/main/java/org/summerboot/jexpress/boot/config/BootConfig.java @@ -274,7 +274,7 @@ protected void loadField(Field field, String configFolder, ConfigUtil helper, Pr } boolean isSpecifiedInCfgFile = props.containsKey(annotationKey); if (isSpecifiedInCfgFile) {// 2. empty cfg value as null - Object nullValue = ReflectionUtil.toStandardJavaType(null, field.getType(), false, false, null); + Object nullValue = ReflectionUtil.toStandardJavaType(null, false, field.getType(), false, false, null); field.set(this, nullValue); return; } else { diff --git a/src/main/java/org/summerboot/jexpress/boot/config/annotation/Config.java b/src/main/java/org/summerboot/jexpress/boot/config/annotation/Config.java index 22df4b1..891fae4 100644 --- a/src/main/java/org/summerboot/jexpress/boot/config/annotation/Config.java +++ b/src/main/java/org/summerboot/jexpress/boot/config/annotation/Config.java @@ -62,4 +62,6 @@ public enum Validate { String collectionDelimiter() default ","; + boolean trim() default true; + } diff --git a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestParameter.java b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestParameter.java index d2517c1..48c7361 100644 --- a/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestParameter.java +++ b/src/main/java/org/summerboot/jexpress/nio/server/ws/rs/JaxRsRequestParameter.java @@ -370,21 +370,21 @@ protected Object parse(String value, String defaultValue, ServiceContext context Err e = new Err(BootErrorCode.BAD_REQUEST_MISSING_REQUIRED_FILED, null, null, null, "Missing Required Filed: " + type + "{" + key + "}=" + value); context.status(HttpResponseStatus.BAD_REQUEST).error(e); } - return ReflectionUtil.toStandardJavaType(null, targetClass, false, false, null);//primitive types devault value or null + return ReflectionUtil.toStandardJavaType(null, false, targetClass, false, false, null);//primitive types devault value or null } } String regex = pattern == null ? null : pattern.regexp(); if (regex != null && !value.matches(regex)) { Err e = new Err(BootErrorCode.BAD_REQUEST_DATA, null, null, null, "Failed to parse data type: invalid " + type + "{" + key + "}=" + value + " by regex=" + regex); context.status(HttpResponseStatus.BAD_REQUEST).error(e); - return ReflectionUtil.toStandardJavaType(null, targetClass, false, false, null);//primitive types devault value or null + return ReflectionUtil.toStandardJavaType(null, false, targetClass, false, false, null);//primitive types devault value or null } try { - return ReflectionUtil.toJavaType(targetClass, parameterizedType, value, false, false, enumConvert, collectionDelimiter); + return ReflectionUtil.toJavaType(targetClass, parameterizedType, value, true, false, false, enumConvert, collectionDelimiter); } catch (Throwable ex) { Err e = new Err(BootErrorCode.BAD_REQUEST_DATA, null, null, ex, "Failed to parse data type: invalid " + type + "{" + key + "}=" + value); context.status(HttpResponseStatus.BAD_REQUEST).error(e); - return ReflectionUtil.toStandardJavaType(null, targetClass, false, false, null);//primitive types devault value or null + return ReflectionUtil.toStandardJavaType(null, false, targetClass, false, false, null);//primitive types devault value or null } } } diff --git a/src/main/java/org/summerboot/jexpress/util/FormatterUtil.java b/src/main/java/org/summerboot/jexpress/util/FormatterUtil.java index fcc24a1..6acef62 100644 --- a/src/main/java/org/summerboot/jexpress/util/FormatterUtil.java +++ b/src/main/java/org/summerboot/jexpress/util/FormatterUtil.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.summerboot.jexpress.boot.BootConstant; import org.summerboot.jexpress.security.SecurityUtil; import javax.imageio.ImageIO; @@ -288,10 +289,24 @@ public static byte[] toByteArray(BufferedImage bi, String format) throws IOExcep } public static String toString(ByteBuffer buffer) { - return toString(buffer, true, true, 8, " "); + return toString(buffer, true, true, 10, "\t"); } - public static String toString(ByteBuffer buffer, boolean showStatus, boolean showHeaderfooter, int showNumberOfBytesPerLine, String delimiter) { + public static String toString(ByteBuffer buffer, boolean showStatus, boolean showHeaderfooter, int numberOfBytesPerLine) { + return toString(buffer, showStatus, showHeaderfooter, numberOfBytesPerLine, "\t"); + } + + public static String toString(ByteBuffer buffer, boolean showStatus, boolean showHeaderfooter, int numberOfBytesPerLine, String delimiter) { + return toString(buffer, showStatus, showHeaderfooter, numberOfBytesPerLine, delimiter, BootConstant.BR, "ByteBuffer Contents starts", "ByteBuffer Contents ends"); + } + + public static String toString(ByteBuffer buffer, boolean showStatus, boolean showHeaderfooter, int numberOfBytesPerLine, String delimiter, String br, String header, String footer) { + if (buffer == null) { + return ""; + } + if (br == null) { + br = BootConstant.BR; + } StringBuilder sb = new StringBuilder(); if (showStatus) { sb.append("ByteBuffer status:") @@ -299,28 +314,68 @@ public static String toString(ByteBuffer buffer, boolean showStatus, boolean sho .append(" Position=").append(buffer.position()) .append(" Limit=").append(buffer.limit()) .append(" Capacity=").append(buffer.capacity()) - .append(" Remaining=").append(buffer.remaining()); + .append(" Remaining=").append(buffer.remaining()) + .append(br); } if (showHeaderfooter) { - sb.append("\n************** ByteBuffer Contents starts **************\n"); + buuldHeaderfooter(header, numberOfBytesPerLine, delimiter, sb, br); } boolean eol = false; - if (showNumberOfBytesPerLine > 0) { + if (numberOfBytesPerLine > 0) { byte[] array = buffer.array(); for (int i = 0; i < buffer.limit(); i++) { - eol = (i + 1) % showNumberOfBytesPerLine == 0; - sb.append(String.format("0x%02X", array[i])).append(eol ? "\n" : delimiter); + eol = (i + 1) % numberOfBytesPerLine == 0; + //String hexChars = String.format("0x%02X", array[i]); + // replaced String.format("0x%02X", i) with better performance api, 100 times faster via byte operations: 10k loads performace: 317ms vs 2ms + char[] hexChars = toString(array[i], true); + sb.append(hexChars).append(eol ? br : delimiter); } } if (showHeaderfooter) { if (!eol) { sb.append("\n"); } - sb.append("************** ByteBuffer Contents ends **************\n"); + buuldHeaderfooter(footer, numberOfBytesPerLine, delimiter, sb, br); } return sb.toString(); } + public static final short HEX_STRING_SIZE = 4; + public static final char[] HexArrayIndexTable = "0123456789ABCDEF".toCharArray(); + + private static void buuldHeaderfooter(String title, int numberOfBytesPerLine, String delimiter, StringBuilder sb, String br) { + int delimiterSize = delimiter.equals("\t") ? 4 : delimiter.length(); + int lineSize = HEX_STRING_SIZE * numberOfBytesPerLine + delimiterSize * (numberOfBytesPerLine - 1); + int titleSize = title.length() + 2; + int totalAsteriskSize = lineSize - titleSize; + int asteriskSize = Math.max(2, totalAsteriskSize / 2 + totalAsteriskSize % 2); + String asteriskLine = StringUtils.repeat("*", asteriskSize); + sb.append(asteriskLine).append(" ").append(title).append(" ").append(asteriskLine).append(br); + } + + /** + * replaced String.format("0x%02X", i) with better performance api, 100 times faster via byte operations: 10k loads performace: 317ms vs 2ms + * + * @param v + * @return + */ + public static char[] toString(byte v, boolean append0x) { + int b = v & 0xFF; + char[] hexChars; + if (append0x) { + hexChars = new char[4]; + hexChars[0] = '0'; + hexChars[1] = 'x'; + hexChars[2] = HexArrayIndexTable[b >>> 4]; + hexChars[3] = HexArrayIndexTable[b & 0x0F]; + } else { + hexChars = new char[2]; + hexChars[0] = HexArrayIndexTable[b >>> 4]; + hexChars[1] = HexArrayIndexTable[b & 0x0F]; + } + return hexChars; + } + /** * For old Java before Java 17 HexFormat.of().parseHex(s) * diff --git a/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java b/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java index c08038e..e3dd745 100644 --- a/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java +++ b/src/main/java/org/summerboot/jexpress/util/ReflectionUtil.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSortedSet; import org.apache.commons.lang3.StringUtils; import org.reflections.Reflections; +import org.summerboot.jexpress.boot.config.annotation.Config; import org.summerboot.jexpress.nio.server.ws.rs.EnumConvert; import org.summerboot.jexpress.security.SecurityUtil; @@ -209,19 +210,23 @@ public static void loadField(Object instance, Field field, String value, final b Class targetClass = field.getType(); Type genericType = field.getGenericType(); field.setAccessible(true); - field.set(instance, toJavaType(targetClass, genericType, value, autoDecrypt, isEmailRecipients, null, collectionDelimiter)); + Config cfgSettings = field.getAnnotation(Config.class); + boolean trim = cfgSettings == null ? false : cfgSettings.trim(); + field.set(instance, toJavaType(targetClass, genericType, value, trim, autoDecrypt, isEmailRecipients, null, collectionDelimiter)); } protected static final Type[] DEFAULT_ARG_TYPES = {String.class, String.class}; - public static Object toJavaType(Class targetClass, Type genericType, String value, final boolean autoDecrypt, + public static Object toJavaType(Class targetClass, Type genericType, String value, final boolean trim, final boolean autoDecrypt, final boolean isEmailRecipients, EnumConvert.To enumConvert, String collectionDelimiter) throws IllegalAccessException { if (StringUtils.isBlank(value)) { - Object nullValue = ReflectionUtil.toStandardJavaType(null, targetClass, autoDecrypt, false, enumConvert); + Object nullValue = ReflectionUtil.toStandardJavaType(null, trim, targetClass, autoDecrypt, false, enumConvert); return nullValue; } - value = value.trim(); + if (trim) { + value = value.trim(); + } // Class targetClass = field.getType(); // Type genericType = field.getGenericType(); Type[] argTypes = DEFAULT_ARG_TYPES; @@ -253,7 +258,7 @@ public static Object toJavaType(Class targetClass, Type genericType, String valu Class classT = targetClass.getComponentType(); Object array = Array.newInstance(classT, valuesStr.length); for (int i = 0; i < valuesStr.length; i++) { - Array.set(array, i, toStandardJavaType(valuesStr[i], classT, autoDecrypt, isEmailRecipients, enumConvert)); + Array.set(array, i, toStandardJavaType(valuesStr[i], trim, classT, autoDecrypt, isEmailRecipients, enumConvert)); } return array; } else if (targetClass.equals(Set.class)) { @@ -264,7 +269,7 @@ public static Object toJavaType(Class targetClass, Type genericType, String valu Class classT = upperBoundClasses[0];//(Class) argTypes[0]; Object array = Array.newInstance(classT, valuesStr.length); for (int i = 0; i < valuesStr.length; i++) { - Array.set(array, i, toStandardJavaType(valuesStr[i], classT, autoDecrypt, isEmailRecipients, enumConvert)); + Array.set(array, i, toStandardJavaType(valuesStr[i], trim, classT, autoDecrypt, isEmailRecipients, enumConvert)); } return Set.of((Object[]) array); } else if (targetClass.equals(SortedSet.class)) { @@ -275,7 +280,7 @@ public static Object toJavaType(Class targetClass, Type genericType, String valu Class classT = upperBoundClasses[0];//(Class) argTypes[0]; Object array = Array.newInstance(classT, valuesStr.length); for (int i = 0; i < valuesStr.length; i++) { - Array.set(array, i, toStandardJavaType(valuesStr[i], classT, autoDecrypt, isEmailRecipients, enumConvert)); + Array.set(array, i, toStandardJavaType(valuesStr[i], trim, classT, autoDecrypt, isEmailRecipients, enumConvert)); } return ImmutableSortedSet.copyOf(List.of((Object[]) array)); } else if (targetClass.equals(List.class)) { @@ -286,7 +291,7 @@ public static Object toJavaType(Class targetClass, Type genericType, String valu Class classT = upperBoundClasses[0];//(Class) argTypes[0]; Object array = Array.newInstance(classT, valuesStr.length); for (int i = 0; i < valuesStr.length; i++) { - Array.set(array, i, toStandardJavaType(valuesStr[i], classT, autoDecrypt, isEmailRecipients, enumConvert)); + Array.set(array, i, toStandardJavaType(valuesStr[i], trim, classT, autoDecrypt, isEmailRecipients, enumConvert)); } return List.of((Object[]) array); } else if (targetClass.equals(Map.class)) { @@ -299,8 +304,8 @@ public static Object toJavaType(Class targetClass, Type genericType, String valu Map ret = new HashMap(); for (var k : stringMap.keySet()) { String v = stringMap.get(k); - Object keyT = toStandardJavaType(k, classT1, autoDecrypt, isEmailRecipients, enumConvert); - Object valueT = toStandardJavaType(v, classT2, autoDecrypt, isEmailRecipients, enumConvert); + Object keyT = toStandardJavaType(k, trim, classT1, autoDecrypt, isEmailRecipients, enumConvert); + Object valueT = toStandardJavaType(v, trim, classT2, autoDecrypt, isEmailRecipients, enumConvert); ret.put(keyT, valueT); } return Map.copyOf(ret); @@ -321,7 +326,7 @@ public static Object toJavaType(Class targetClass, Type genericType, String valu throw new IllegalArgumentException("invalid json data: " + value, ex); } } else { - Object v = toStandardJavaType(value, targetClass, autoDecrypt, isEmailRecipients, enumConvert); + Object v = toStandardJavaType(value, trim, targetClass, autoDecrypt, isEmailRecipients, enumConvert); return v; } } @@ -337,7 +342,7 @@ public static Object toJavaType(Class targetClass, Type genericType, String valu * @param isEmailRecipients * @return */ - public static Object toStandardJavaType(String value, final Class targetClass, final boolean autoDecrypt, + public static Object toStandardJavaType(String value, final boolean trim, final Class targetClass, final boolean autoDecrypt, final boolean isEmailRecipients, EnumConvert.To enumConvert) { if (StringUtils.isBlank(value)) { if (targetClass.equals(boolean.class)) { @@ -357,7 +362,9 @@ public static Object toStandardJavaType(String value, final Class targetClass, f return null; } } - value = value.trim(); + if (trim) { + value = value.trim(); + } if (autoDecrypt && value.startsWith(ENCRYPTED_WARPER_PREFIX + "(") && value.endsWith(")")) { try { value = SecurityUtil.decrypt(value, true);