httpResponse, boolean remoteSuccess, HttpClientConfig httpClientConfig) {
- this(originRequest, originRequestBody, httpResponse, remoteSuccess, false, httpClientConfig);
- }
-
- public RpcResult(HttpRequest originRequest, String originRequestBody, HttpResponse> httpResponse, boolean remoteSuccess, boolean isStreaming, HttpClientConfig httpClientConfig) {
+ public RpcResult(HttpRequest originRequest, String originRequestBody, HttpResponse> httpResponse, boolean remoteSuccess, boolean isStreaming, HttpClientConfig httpClientConfig, SessionContext context) {
this.originRequest = originRequest;
this.originRequestBody = originRequestBody;
this.httpResponse = httpResponse;
this.rpcResponseBody = httpResponse == null || isStreaming ? null : String.valueOf(httpResponse.body());
this.httpStatusCode = httpResponse == null ? 0 : httpResponse.statusCode();
+ this.context = context;
this.httpStatus = HttpResponseStatus.valueOf(httpStatusCode);
this.remoteSuccess = remoteSuccess;
this.isStreaming = isStreaming;
@@ -146,23 +145,87 @@ public ContentType contentType() {
return contentType;
}
+ /**
+ *
+ * @param successResponseClass
+ * @return
+ * @deprecated use { @link #deserialize(Class)}
+ * Sample code:
+ *
{@code
+ * if(rpcResult.isRemoteSuccess()){
+ * MySuccessObj obj = rpcResult.deserialize(MySuccessObj.class);
+ * } else {
+ * MyErrorObj err = rpcResult.deserialize(MyErrorObj.class);
+ * }
+ * }
+ */
+ @Deprecated(since = "2.7.4", forRemoval = false)
+ public RpcResult update(Class successResponseClass) {
+ return update(successResponseClass, context);
+ }
+
+ /**
+ *
+ * @param successResponseClass
+ * @param context
+ * @return
+ * @deprecated use { @link #deserialize(Class)}
+ * Sample code:
+ *
{@code
+ * if(rpcResult.isRemoteSuccess()){
+ * MySuccessObj obj = rpcResult.deserialize(MySuccessObj.class);
+ * } else {
+ * MyErrorObj err = rpcResult.deserialize(MyErrorObj.class);
+ * }
+ * }
+ */
+ @Deprecated(since = "2.7.4", forRemoval = false)
+ public RpcResult update(Class successResponseClass, final SessionContext context) {
+ if (remoteSuccess) {
+ successResponse = deserialize(httpClientConfiguredObjectMapper, null, successResponseClass, true, context);
+ }
+ return this;
+ }
+
+ /**
+ *
+ * @param responseClass
+ * @param context
+ * @param
+ * @return
+ * @deprecated use { @link #deserialize(Class)}
+ * Sample code:
+ *
{@code
+ * if(rpcResult.isRemoteSuccess()){
+ * MySuccessObj obj = rpcResult.deserialize(MySuccessObj.class);
+ * } else {
+ * MyErrorObj err = rpcResult.deserialize(MyErrorObj.class);
+ * }
+ * }
+ */
+ @Deprecated(since = "2.7.4", forRemoval = false)
public R deserialize(Class responseClass, final SessionContext context) {
- return deserialize(httpClientConfiguredObjectMapper, responseClass, context);
+ return deserialize(httpClientConfiguredObjectMapper, null, responseClass, true, context);
+ }
+
+ public R deserialize(Class responseClass) {
+ return deserialize(httpClientConfiguredObjectMapper, responseClass);
}
- public R deserialize(JavaType responseType, final SessionContext context) {
- return deserialize(httpClientConfiguredObjectMapper, responseType, context);
+ public R deserialize(JavaType responseType) {
+ return deserialize(httpClientConfiguredObjectMapper, responseType);
}
- public R deserialize(ObjectMapper jacksonMapper, Class responseClass, final SessionContext context) {
+ public R deserialize(ObjectMapper jacksonMapper, Class responseClass) {
return deserialize(jacksonMapper, null, responseClass, true, context);
}
- public R deserialize(ObjectMapper jacksonMapper, JavaType responseType, final SessionContext context) {
+ public R deserialize(ObjectMapper jacksonMapper, JavaType responseType) {
return deserialize(jacksonMapper, responseType, null, true, context);
}
- // Debug 现场还原的最大字符数(通常 4KB~8KB 足够看清出错的 JSON 结构了)
+
+ // 💡 for debugging: Maximum number of characters to retrieve for reconstruction (typically 4KB–8KB is sufficient to clearly identify a malformed JSON structure)
private static final int SNAPSHOT_SIZE = 8 * 1024;
protected R deserialize(ObjectMapper jacksonMapper, JavaType responseType, Class responseClass, boolean doValidation, final SessionContext context) {
@@ -174,10 +237,10 @@ protected R deserialize(ObjectMapper jacksonMapper, JavaType responseType, C
try {
if (isStreaming) {
try (InputStream rawInputStream = (InputStream) httpResponse.body()) {
- // 💡 1. 创建一个极小的内存口袋,只用来抄录前 8KB 的数据快照
+ // 💡 1. Create a tiny memory pocket, used exclusively for recording an 8KB snapshot of the data.
ByteArrayOutputStream debugSnapshot = new ByteArrayOutputStream(SNAPSHOT_SIZE);
- // 2. 包装原始网络流:在 Jackson 边读边解的过程中,顺便往快照里写一份
+ // 💡 2. Wrapping the raw network stream: while Jackson reads and parses the data, simultaneously write a copy to a snapshot.
InputStream wrappedStream = new InputStream() {
private int bytesCopied = 0;
@@ -211,7 +274,9 @@ public int read(byte[] b, int off, int len) throws java.io.IOException {
: jacksonMapper.readValue(wrappedStream, responseClass);
} catch (JacksonException ex) {
String jsonSnapshotStr = debugSnapshot.toString(StandardCharsets.UTF_8);
- context.memo(RpcMemo.MEMO_RPC_RESPONSE_DATA, jsonSnapshotStr);
+ if (context != null) {
+ context.memo(RpcMemo.MEMO_RPC_RESPONSE_DATA, jsonSnapshotStr);
+ }
throw ex;
}
}
diff --git a/src/main/java/org/summerboot/jexpress/boot/BootConstants.java b/src/main/java/org/summerboot/jexpress/boot/BootConstants.java
index 2d75d066..720588da 100644
--- a/src/main/java/org/summerboot/jexpress/boot/BootConstants.java
+++ b/src/main/java/org/summerboot/jexpress/boot/BootConstants.java
@@ -33,7 +33,7 @@ static boolean isDebugMode() {
String APP_ID = String.format("%06d", APP_ID_VALUE);
//version
- String VERSION = "jExpress 2.7.4";
+ String VERSION = "jExpress 2.7.5";
String JEXPRESS_PACKAGE_NAME = "org.summerboot.jexpress";
String JSONFILTER_NAME_SERVICEERROR = "ServiceErrorFilter";
diff --git a/src/main/java/org/summerboot/jexpress/integration/rpc/http/RpcDelegateHttpClientImpl.java b/src/main/java/org/summerboot/jexpress/integration/rpc/http/RpcDelegateHttpClientImpl.java
index 385154df..b2ad98fd 100644
--- a/src/main/java/org/summerboot/jexpress/integration/rpc/http/RpcDelegateHttpClientImpl.java
+++ b/src/main/java/org/summerboot/jexpress/integration/rpc/http/RpcDelegateHttpClientImpl.java
@@ -107,7 +107,7 @@ protected RpcResult rpcEx(SessionContext context, HttpRequest httpRequest
Thread.currentThread().interrupt();
Err e = new Err(BootErrorCode.APP_INTERRUPTED, null, "Http Client Interrupted", ex);
context.status(HttpResponseStatus.INTERNAL_SERVER_ERROR).error(e);
- RpcResult rpcResult = new RpcResult<>(httpRequest, originRequestBody, null, false, getHttpClientConfig());
+ RpcResult rpcResult = new RpcResult<>(httpRequest, originRequestBody, null, false, false, getHttpClientConfig(), context);
return rpcResult;
} finally {
context.poi(BootPoi.RPC_END);
@@ -128,7 +128,7 @@ protected RpcResult rpcEx(SessionContext context, HttpRequest httpRequest
}
//3b. update status
- RpcResult rpcResult = new RpcResult<>(httpRequest, originRequestBody, isStreaming ? httpResponse2 : httpResponse1, isRemoteSuccess, isStreaming, getHttpClientConfig());
+ RpcResult rpcResult = new RpcResult<>(httpRequest, originRequestBody, isStreaming ? httpResponse2 : httpResponse1, isRemoteSuccess, isStreaming, getHttpClientConfig(), context);
String rpcResponseJsonBody = rpcResult.httpResponseBody();
context.memo(RpcMemo.MEMO_RPC_RESPONSE, rpcResult.httpStatusCode() + " " + (isStreaming ? httpResponse2.headers() : httpResponse1.headers()));
context.memo(RpcMemo.MEMO_RPC_RESPONSE_DATA, isStreaming ? "stream data" : rpcResponseJsonBody);
diff --git a/src/main/java/org/summerboot/jexpress/security/token/jwt/Jackson3Deserializer.java b/src/main/java/org/summerboot/jexpress/security/token/jwt/Jackson3Deserializer.java
new file mode 100644
index 00000000..5a0882f5
--- /dev/null
+++ b/src/main/java/org/summerboot/jexpress/security/token/jwt/Jackson3Deserializer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2005-2026 Du Law Office - jExpress, The Summer Boot Framework Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://apache.org
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.summerboot.jexpress.security.token.jwt;
+
+
+import io.jsonwebtoken.io.DeserializationException;
+import io.jsonwebtoken.io.Deserializer;
+import tools.jackson.databind.ObjectMapper;
+
+import java.io.Reader;
+import java.util.Map;
+
+/**
+ * REF275-1: Enforce JJWT to use Jackson 3
+ *
+ * @author Changski Tie Zheng Zhang 张铁铮, 魏泽北, 杜旺财, 杜富贵
+ */
+public class Jackson3Deserializer implements Deserializer