This is an automated email from the ASF dual-hosted git repository.

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new d484b5a86 [SCB-2847]support configure default encoding for request and 
response (#4167)
d484b5a86 is described below

commit d484b5a86c5f17adabbf3bb3517ec909e27022d1
Author: liubao68 <[email protected]>
AuthorDate: Tue Dec 26 20:28:41 2023 +0800

    [SCB-2847]support configure default encoding for request and response 
(#4167)
---
 .../utils/ScopedProtobufSchemaManager.java         |  14 ++-
 .../rest/codec/param/BodyProcessorCreator.java     | 107 +++++++++++++++------
 .../codec/produce/ProduceProcessorManager.java     |  31 ++++--
 .../common/rest/definition/RestOperationMeta.java  |  38 --------
 .../jaxrs/client/MultiErrorCodeServiceClient.java  |   5 +-
 .../protobuf/internal/schema/SchemaManager.java    |  41 +++-----
 .../deserializer/DeserializerSchemaManager.java    |   2 +-
 .../schema/serializer/MessageWriteSchema.java      |   6 +-
 .../schema/serializer/SerializerSchemaManager.java |   2 +-
 9 files changed, 123 insertions(+), 123 deletions(-)

diff --git 
a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java
 
b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java
index e1f08b0dd..3c7e5adbb 100644
--- 
a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java
+++ 
b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java
@@ -42,12 +42,15 @@ public class ScopedProtobufSchemaManager {
   static class SchemaKey {
     String schemaId;
 
+    String rootMessage;
+
     Schema<?> schema;
 
     int hashCode = -1;
 
-    SchemaKey(String schemaId, Schema<?> schema) {
+    SchemaKey(String schemaId, String rootMessage, Schema<?> schema) {
       this.schemaId = schemaId;
+      this.rootMessage = rootMessage;
       this.schema = schema;
     }
 
@@ -60,7 +63,7 @@ public class ScopedProtobufSchemaManager {
         return false;
       }
       SchemaKey other = (SchemaKey) o;
-      return StringUtils.equals(schemaId, other.schemaId)
+      return StringUtils.equals(schemaId, other.schemaId) && 
StringUtils.equals(rootMessage, other.rootMessage)
           && SwaggerUtils.schemaEquals(schema, other.schema);
     }
 
@@ -69,7 +72,7 @@ public class ScopedProtobufSchemaManager {
       if (hashCode != -1) {
         return hashCode;
       }
-      hashCode = schemaId.hashCode() ^ SwaggerUtils.schemaHashCode(schema);
+      hashCode = schemaId.hashCode() ^ rootMessage.hashCode() ^ 
SwaggerUtils.schemaHashCode(schema);
       return hashCode;
     }
   }
@@ -100,8 +103,9 @@ public class ScopedProtobufSchemaManager {
   /**
    * get the ProtoMapper from Schema
    */
-  public ProtoMapper getOrCreateProtoMapper(OpenAPI openAPI, String schemaId, 
String rootMessageName, Schema<?> schema) {
-    SchemaKey schemaKey = new SchemaKey(schemaId, schema);
+  public ProtoMapper getOrCreateProtoMapper(OpenAPI openAPI, String schemaId, 
String rootMessageName,
+      Schema<?> schema) {
+    SchemaKey schemaKey = new SchemaKey(schemaId, rootMessageName, schema);
     return schemaMapperCache.computeIfAbsent(schemaKey, key -> {
       SchemaToProtoGenerator generator = new 
SchemaToProtoGenerator("scb.schema", openAPI,
           key.schema, rootMessageName);
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java
index 78d171121..44a4d3467 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java
@@ -80,6 +80,12 @@ public class BodyProcessorCreator implements 
ParamValueProcessorCreator<RequestB
   // Do not use it if you are sure how it works. And may be deleted in the 
future.
   public static final String PARAM_DECODE_AS_OBJECT = 
"servicecomb.rest.parameter.decodeAsObject";
 
+  public static final String PARAM_DEFAULT_REQUEST_ENCODING = 
"servicecomb.rest.parameter.default-request-encoding";
+
+  private static Boolean decodeAsObject;
+
+  private static String defaultRequestEncoding;
+
   public static class BodyProcessor implements ParamValueProcessor {
     // Producer target type. For consumer, is null.
     protected JavaType targetType;
@@ -176,9 +182,24 @@ public class BodyProcessorCreator implements 
ParamValueProcessorCreator<RequestB
         return null;
       }
 
-      if (!supportedContentTypes.contains(contentType)) {
-        throw new IllegalArgumentException(String.format("operation %s not 
support content-type %s",
-            operationMeta.getSchemaQualifiedName(), contentType));
+      if (MediaType.APPLICATION_JSON.equals(contentType)) {
+        try {
+          ObjectReader reader = serialViewClass != null
+              ? 
RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass)
+              : RestObjectMapperFactory.getRestObjectMapper().reader();
+          if (decodeAsObject()) {
+            return reader.forType(OBJECT_TYPE).readValue(inputStream);
+          }
+          return reader.forType(targetType == null ? OBJECT_TYPE : targetType)
+              .readValue(inputStream);
+        } catch (MismatchedInputException e) {
+          // there is no way to detect InputStream is empty, so have to catch 
the exception
+          if (!isRequired && e.getMessage().contains("No content to map due to 
end-of-input")) {
+            LOGGER.info("Empty content and required is false, taken as null");
+            return null;
+          }
+          throw e;
+        }
       }
 
       if (SwaggerConst.PROTOBUF_TYPE.equals(contentType)) {
@@ -187,38 +208,37 @@ public class BodyProcessorCreator implements 
ParamValueProcessorCreator<RequestB
                 REQUEST_BODY_NAME,
                 
requestBody.getContent().get(SwaggerConst.PROTOBUF_TYPE).getSchema());
         RootDeserializer<PropertyWrapper<Object>> deserializer = 
protoMapper.getDeserializerSchemaManager()
-            
.createRootDeserializer(protoMapper.getProto().getMessage(REQUEST_BODY_NAME), 
targetType);
+            
.createRootDeserializer(protoMapper.getProto().getMessage(REQUEST_BODY_NAME),
+                targetType == null ? OBJECT_TYPE : targetType);
         PropertyWrapper<Object> result = 
deserializer.deserialize(inputStream.readAllBytes());
         return result.getValue();
       }
 
-      // For application/json and text/plain
-      try {
-        if (MediaType.TEXT_PLAIN.equals(contentType) &&
-            targetType != null && 
String.class.equals(targetType.getRawClass())) {
-          return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
-        }
-        ObjectReader reader = serialViewClass != null
-            ? 
RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass)
-            : RestObjectMapperFactory.getRestObjectMapper().reader();
-        if (decodeAsObject()) {
-          return reader.forType(OBJECT_TYPE).readValue(inputStream);
-        }
-        return reader.forType(targetType == null ? OBJECT_TYPE : targetType)
-            .readValue(inputStream);
-      } catch (MismatchedInputException e) {
-        // there is no way to detect InputStream is empty, so have to catch 
the exception
-        if (!isRequired && e.getMessage().contains("No content to map due to 
end-of-input")) {
-          LOGGER.info("Empty content and required is false, taken as null");
-          return null;
+      if (MediaType.TEXT_PLAIN.equals(contentType)) {
+        try {
+          if (targetType != null && 
String.class.equals(targetType.getRawClass())) {
+            return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
+          }
+          ObjectReader reader = serialViewClass != null
+              ? 
RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass)
+              : RestObjectMapperFactory.getRestObjectMapper().reader();
+          if (decodeAsObject()) {
+            return reader.forType(OBJECT_TYPE).readValue(inputStream);
+          }
+          return reader.forType(targetType == null ? OBJECT_TYPE : targetType)
+              .readValue(inputStream);
+        } catch (MismatchedInputException e) {
+          // there is no way to detect InputStream is empty, so have to catch 
the exception
+          if (!isRequired && e.getMessage().contains("No content to map due to 
end-of-input")) {
+            LOGGER.info("Empty content and required is false, taken as null");
+            return null;
+          }
+          throw e;
         }
-        throw e;
       }
-    }
 
-    private boolean decodeAsObject() {
-      return LegacyPropertyFactory
-          .getBooleanProperty(PARAM_DECODE_AS_OBJECT, false);
+      throw new IllegalArgumentException(String.format("operation %s not 
support content-type %s",
+          operationMeta.getSchemaQualifiedName(), contentType));
     }
 
     private String validContentType(String type) {
@@ -226,8 +246,8 @@ public class BodyProcessorCreator implements 
ParamValueProcessorCreator<RequestB
         if (supportedContentTypes.size() == 0) {
           throw new IllegalArgumentException("operation do not have any 
content type support.");
         }
-        if (supportedContentTypes.contains(MediaType.APPLICATION_JSON)) {
-          return MediaType.APPLICATION_JSON;
+        if (supportedContentTypes.contains(clientEncodingDefault())) {
+          return clientEncodingDefault();
         }
         return supportedContentTypes.get(0);
       }
@@ -252,6 +272,13 @@ public class BodyProcessorCreator implements 
ParamValueProcessorCreator<RequestB
      * Serialize body object into body buffer, according to the Content-Type.
      */
     private Buffer createBodyBuffer(String contentType, Object arg) throws 
IOException {
+      if (MediaType.APPLICATION_JSON.equals(contentType)) {
+        try (BufferOutputStream output = new BufferOutputStream()) {
+          RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, 
arg);
+          return output.getBuffer();
+        }
+      }
+
       if (SwaggerConst.PROTOBUF_TYPE.equals(contentType)) {
         ProtoMapper protoMapper = scopedProtobufSchemaManager
             .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(),
@@ -265,9 +292,9 @@ public class BodyProcessorCreator implements 
ParamValueProcessorCreator<RequestB
         return new BufferImpl().appendBytes(serializer.serialize(bodyArg));
       }
 
-      // For application/json and text/plain
+      // For text/plain
       try (BufferOutputStream output = new BufferOutputStream()) {
-        if (MediaType.TEXT_PLAIN.equals(contentType) && (arg instanceof 
String)) {
+        if (arg instanceof String) {
           output.write(((String) arg).getBytes(StandardCharsets.UTF_8));
         } else {
           RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, 
arg);
@@ -366,4 +393,20 @@ public class BodyProcessorCreator implements 
ParamValueProcessorCreator<RequestB
 
     return new BodyProcessor(operationMeta, targetType, parameter);
   }
+
+  private static boolean decodeAsObject() {
+    if (decodeAsObject == null) {
+      decodeAsObject = LegacyPropertyFactory
+          .getBooleanProperty(PARAM_DECODE_AS_OBJECT, false);
+    }
+    return decodeAsObject;
+  }
+
+  private static String clientEncodingDefault() {
+    if (defaultRequestEncoding == null) {
+      defaultRequestEncoding = LegacyPropertyFactory
+          .getStringProperty(PARAM_DEFAULT_REQUEST_ENCODING, 
MediaType.APPLICATION_JSON);
+    }
+    return defaultRequestEncoding;
+  }
 }
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java
index 36b9a8c3e..7182aff20 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import org.apache.http.entity.ContentType;
 import org.apache.servicecomb.core.definition.OperationMeta;
+import org.apache.servicecomb.foundation.common.LegacyPropertyFactory;
 import org.apache.servicecomb.foundation.common.RegisterManager;
 import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
 import org.apache.servicecomb.swagger.generator.SwaggerConst;
@@ -45,6 +46,10 @@ public final class ProduceProcessorManager extends 
RegisterManager<String, Map<S
 
   public static final ProduceProcessorManager INSTANCE = new 
ProduceProcessorManager();
 
+  public static final String PARAM_DEFAULT_RESPONSE_ENCODING = 
"servicecomb.rest.parameter.default-response-encoding";
+
+  private static String defaultResponseEncoding;
+
   private final Map<String, ProduceProcessor> jsonProcessorMap;
 
   private final Map<String, ProduceProcessor> plainProcessorMap;
@@ -77,6 +82,14 @@ public final class ProduceProcessorManager extends 
RegisterManager<String, Map<S
     return produceViewMap.get(DEFAULT_SERIAL_CLASS);
   }
 
+  private static String defaultResponseEncoding() {
+    if (defaultResponseEncoding == null) {
+      defaultResponseEncoding = LegacyPropertyFactory
+          .getStringProperty(PARAM_DEFAULT_RESPONSE_ENCODING, 
MediaType.APPLICATION_JSON);
+    }
+    return defaultResponseEncoding;
+  }
+
   public ProduceProcessor findJsonProcessorByViewClass(Class<?> 
serialViewClass) {
     if (serialViewClass == null) {
       return jsonProcessorMap.get(DEFAULT_SERIAL_CLASS);
@@ -115,8 +128,8 @@ public final class ProduceProcessorManager extends 
RegisterManager<String, Map<S
     }
     String actualAccept = accept;
     if (actualAccept == null) {
-      if (response.getContent().get(MediaType.APPLICATION_JSON) != null) {
-        actualAccept = MediaType.APPLICATION_JSON;
+      if (response.getContent().get(defaultResponseEncoding()) != null) {
+        actualAccept = defaultResponseEncoding();
       } else {
         actualAccept = response.getContent().keySet().iterator().next();
       }
@@ -125,8 +138,8 @@ public final class ProduceProcessorManager extends 
RegisterManager<String, Map<S
     actualAccept = contentType.getMimeType();
     if (MediaType.WILDCARD.equals(contentType.getMimeType()) ||
         MediaType.MEDIA_TYPE_WILDCARD.equals(contentType.getMimeType())) {
-      if (response.getContent().get(MediaType.APPLICATION_JSON) != null) {
-        actualAccept = MediaType.APPLICATION_JSON;
+      if (response.getContent().get(defaultResponseEncoding()) != null) {
+        actualAccept = defaultResponseEncoding();
       } else {
         actualAccept = response.getContent().keySet().iterator().next();
       }
@@ -135,14 +148,14 @@ public final class ProduceProcessorManager extends 
RegisterManager<String, Map<S
       LOGGER.warn("Operation do not support accept type {}/{}", accept, 
actualAccept);
       return findDefaultProcessor();
     }
+    if (MediaType.APPLICATION_JSON.equals(actualAccept)) {
+      return findJsonProcessorByViewClass(serialViewClass);
+    }
     if (SwaggerConst.PROTOBUF_TYPE.equals(actualAccept)) {
       return new ProduceProtoBufferProcessor(operationMeta,
           operationMeta.getSchemaMeta().getSwagger(), 
response.getContent().get(actualAccept).getSchema());
     }
-    if (MediaType.TEXT_PLAIN.equals(actualAccept)) {
-      return findPlainProcessorByViewClass(serialViewClass);
-    }
-    // json
-    return findJsonProcessorByViewClass(serialViewClass);
+    // text plain
+    return findPlainProcessorByViewClass(serialViewClass);
   }
 }
diff --git 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
index 42e2db348..259095e6a 100644
--- 
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
+++ 
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java
@@ -23,8 +23,6 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.commons.lang3.StringUtils;
-import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory;
 import 
org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.PartProcessor;
 import org.apache.servicecomb.common.rest.definition.path.PathRegExp;
 import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder;
@@ -35,18 +33,12 @@ import 
org.apache.servicecomb.swagger.generator.SwaggerConst;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.fasterxml.jackson.databind.JavaType;
-import com.fasterxml.jackson.databind.type.TypeFactory;
-
 import io.swagger.v3.oas.models.OpenAPI;
 import io.swagger.v3.oas.models.Operation;
-import io.swagger.v3.oas.models.media.MapSchema;
 import io.swagger.v3.oas.models.media.Schema;
 import io.swagger.v3.oas.models.parameters.Parameter;
-import io.swagger.v3.oas.models.parameters.RequestBody;
 import io.swagger.v3.oas.models.responses.ApiResponse;
 import io.swagger.v3.oas.models.responses.ApiResponses;
-import jakarta.ws.rs.core.MediaType;
 
 @SuppressWarnings("rawtypes")
 public class RestOperationMeta {
@@ -112,7 +104,6 @@ public class RestOperationMeta {
   private void addRestParamByName(OperationMeta operationMeta, String name, 
Operation operation) {
     Type type = operationMeta.getSwaggerProducerOperation() != null ? 
operationMeta.getSwaggerProducerOperation()
         .getSwaggerParameterTypes().get(name) : null;
-    type = correctFormBodyType(operation.getRequestBody(), type);
     RestParam param = new RestParam(operationMeta, name, 
operation.getRequestBody(), formData, type);
     addParam(param);
   }
@@ -129,35 +120,6 @@ public class RestOperationMeta {
     return 
operation.getRequestBody().getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema();
   }
 
-  /**
-   * EdgeService cannot recognize the map type form body whose value type is 
String,
-   * so there should be this additional setting.
-   * @param parameter the swagger information of the parameter
-   * @param type the resolved param type
-   * @return the corrected param type
-   */
-  private Type correctFormBodyType(RequestBody parameter, Type type) {
-    if (null != type || parameter == null) {
-      return type;
-    }
-    if (parameter.getContent().get(MediaType.APPLICATION_JSON) == null
-        || 
!(parameter.getContent().get(MediaType.APPLICATION_JSON).getSchema() instanceof 
MapSchema)) {
-      return null;
-    }
-    String className = SwaggerUtils.getClassName(parameter.getExtensions());
-    if (!StringUtils.isEmpty(className)) {
-      try {
-        JavaType javaType = 
TypeFactory.defaultInstance().constructFromCanonical(className);
-        return RestObjectMapperFactory.getRestObjectMapper().getTypeFactory()
-            .constructMapType(Map.class, String.class, javaType.getRawClass());
-      } catch (Throwable e) {
-        // ignore
-      }
-    }
-    return RestObjectMapperFactory.getRestObjectMapper().getTypeFactory()
-        .constructMapType(Map.class, String.class, Object.class);
-  }
-
   public boolean isDownloadFile() {
     return downloadFile;
   }
diff --git 
a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java
 
b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java
index 104e5b8e4..5eba5b94c 100644
--- 
a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java
+++ 
b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java
@@ -70,7 +70,6 @@ public class MultiErrorCodeServiceClient implements 
CategorizedTestCase {
     headers.setContentType(MediaType.APPLICATION_JSON);
     String body = "{\"message\":\"hello\",\"code\":\"wrongType\"";
     HttpEntity<String> entity = new HttpEntity<>(body, headers);
-    ResponseEntity<MultiResponse200> result;
     try {
       template
           .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, 
MultiResponse200.class);
@@ -83,7 +82,7 @@ public class MultiErrorCodeServiceClient implements 
CategorizedTestCase {
     try {
       template
           .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, 
MultiResponse200.class);
-      TestMgr.check(590, 200);
+      TestMgr.fail("expect failed.");
     } catch (InvocationException e) {
       TestMgr.check(e.getStatusCode(), 400);
     }
@@ -94,7 +93,7 @@ public class MultiErrorCodeServiceClient implements 
CategorizedTestCase {
     try {
       template
           .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, 
MultiResponse200.class);
-      TestMgr.check(590, 200);
+      TestMgr.fail("expect failed.");
     } catch (InvocationException e) {
       TestMgr.check(e.getStatusCode(), 400);
     }
diff --git 
a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java
 
b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java
index 3beaa0074..331c6b632 100644
--- 
a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java
+++ 
b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java
@@ -55,15 +55,14 @@ public abstract class SchemaManager {
   // key is canonical message name + ":" + canonical type name
   protected final Map<String, SchemaEx<?>> canonicalSchemas = new 
ConcurrentHashMapEx<>();
 
+  // key is canonical message name + ":" + canonical type name
+  protected final Map<String, SchemaEx<?>> canonicalSchemasCreated = new 
ConcurrentHashMapEx<>();
+
   public SchemaManager(ProtoMapper protoMapper) {
     this.protoMapper = protoMapper;
     this.proto = protoMapper.getProto();
   }
 
-  public Map<String, SchemaEx<?>> getCanonicalSchemas() {
-    return canonicalSchemas;
-  }
-
   protected String generateCacheKey(Message message, JavaType javaType) {
     return message.getCanonicalName() + ":" + javaType.toCanonical();
   }
@@ -72,44 +71,25 @@ public abstract class SchemaManager {
 
   protected abstract <T> SchemaEx<T> newMessageSchema(Message message, 
Map<String, Type> types);
 
-  /**
-   *
-   * @param protoField
-   * @param propertyDescriptor provide getter/setter/javaType
-   * @return
-   */
   protected abstract <T> FieldSchema<T> createScalarField(Field protoField, 
PropertyDescriptor propertyDescriptor);
 
   @SuppressWarnings("unchecked")
   protected <T> SchemaEx<T> getOrCreateMessageSchema(Message message, 
Map<String, Type> types) {
     String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE);
-    SchemaEx<T> messageSchema = (SchemaEx<T>) canonicalSchemas.get(cacheKey);
-    if (messageSchema == null) {
-      // messageSchema already put into canonicalSchemas inside 
createMessageSchema
-      messageSchema = createMessageSchema(message, types);
-    }
-    return messageSchema;
+    return (SchemaEx<T>) canonicalSchemasCreated.computeIfAbsent(cacheKey, key 
-> createMessageSchema(message, types));
   }
 
   @SuppressWarnings("unchecked")
   protected <T> SchemaEx<T> getOrCreateMessageSchema(Message message, JavaType 
javaType) {
     String cacheKey = generateCacheKey(message, javaType);
-    SchemaEx<T> messageSchema = (SchemaEx<T>) canonicalSchemas.get(cacheKey);
-    if (messageSchema == null) {
-      // messageSchema already put into canonicalSchemas inside 
createMessageSchema
-      messageSchema = createMessageSchema(message, javaType);
-    }
-    return messageSchema;
+    return (SchemaEx<T>) canonicalSchemasCreated.computeIfAbsent(cacheKey,
+        key -> createMessageSchema(message, javaType));
   }
 
   @SuppressWarnings("unchecked")
-  protected <T> SchemaEx<T> findSchema(String key) {
-    return (SchemaEx<T>) canonicalSchemas.get(key);
-  }
-
   protected <T> SchemaEx<T> createMessageSchema(Message message, Map<String, 
Type> types) {
     String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE);
-    SchemaEx<T> schema = findSchema(cacheKey);
+    SchemaEx<T> schema = (SchemaEx<T>) canonicalSchemas.get(cacheKey);
     if (schema != null) {
       return schema;
     }
@@ -121,9 +101,10 @@ public abstract class SchemaManager {
     return schema;
   }
 
+  @SuppressWarnings("unchecked")
   protected <T> SchemaEx<T> createMessageSchema(Message message, JavaType 
javaType) {
     String cacheKey = generateCacheKey(message, javaType);
-    SchemaEx<T> schema = findSchema(cacheKey);
+    SchemaEx<T> schema = (SchemaEx<T>) canonicalSchemas.get(cacheKey);
     if (schema != null) {
       return schema;
     }
@@ -144,7 +125,7 @@ public abstract class SchemaManager {
     JavaType entryType = 
TypeFactory.defaultInstance().constructParametricType(MapEntry.class,
         javaType.getKeyType(),
         javaType.getContentType());
-    SchemaEx<Entry<Object, Object>> entrySchema = 
getOrCreateMessageSchema((Message) protoField.getType(),
+    SchemaEx<Entry<Object, Object>> entrySchema = 
createMessageSchema((Message) protoField.getType(),
         entryType);
     return new MapSchema<>(protoField, propertyDescriptor, entrySchema);
   }
@@ -215,7 +196,7 @@ public abstract class SchemaManager {
 
     // message
     if (protoField.getType().isMessage()) {
-      SchemaEx<Object> messageSchema = getOrCreateMessageSchema((Message) 
protoField.getType(),
+      SchemaEx<Object> messageSchema = createMessageSchema((Message) 
protoField.getType(),
           propertyDescriptor.getJavaType());
       if (isWrapProperty((Message) protoField.getType())) {
         return new PropertyWrapperAsFieldSchema<>(protoField, 
propertyDescriptor, messageSchema);
diff --git 
a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java
 
b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java
index a86d2aa4f..23b9d85a0 100644
--- 
a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java
+++ 
b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java
@@ -249,7 +249,7 @@ public class DeserializerSchemaManager extends 
SchemaManager {
       if (contentType == null) {
         contentType = ProtoConst.OBJECT_TYPE;
       }
-      SchemaEx<Object> contentSchema = getOrCreateMessageSchema((Message) 
protoField.getType(), contentType);
+      SchemaEx<Object> contentSchema = createMessageSchema((Message) 
protoField.getType(), contentType);
       if (isWrapProperty((Message) protoField.getType())) {
         return PropertyWrapperRepeatedReadSchemas.create(protoField, 
propertyDescriptor, contentSchema);
       }
diff --git 
a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java
 
b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java
index 49b2154cb..ccab4d81f 100644
--- 
a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java
+++ 
b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java
@@ -101,6 +101,8 @@ public class MessageWriteSchema<T> implements SchemaEx<T> {
 
   @Override
   public void init() {
+    this.mapFieldMaps = 
protoMapper.getSerializerSchemaManager().createMapFields(message);
+
     if (ProtoUtils.isWrapProperty(message)) {
       this.mainPojoFieldMaps = createPropertyWrapperFields(javaType);
       return;
@@ -178,10 +180,6 @@ public class MessageWriteSchema<T> implements SchemaEx<T> {
   }
 
   protected final void writeFromMap(OutputEx output, Map<String, Object> map) 
throws IOException {
-    if (mapFieldMaps == null) {
-      mapFieldMaps = 
protoMapper.getSerializerSchemaManager().createMapFields(message);
-    }
-
     for (Entry<String, Object> entry : map.entrySet()) {
       if (entry.getValue() == null) {
         continue;
diff --git 
a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java
 
b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java
index 97ea2af2b..d63944e34 100644
--- 
a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java
+++ 
b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java
@@ -226,7 +226,7 @@ public class SerializerSchemaManager extends SchemaManager {
       if (contentType == null) {
         contentType = ProtoConst.OBJECT_TYPE;
       }
-      SchemaEx<Object> contentSchema = getOrCreateMessageSchema((Message) 
protoField.getType(), contentType);
+      SchemaEx<Object> contentSchema = createMessageSchema((Message) 
protoField.getType(), contentType);
       if (isWrapProperty((Message) protoField.getType())) {
         return PropertyWrapperRepeatedWriteSchemas.create(protoField, 
propertyDescriptor, contentSchema);
       }

Reply via email to