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
commit 2607f03314d7fb6e18e0d2f4188783bd56142220 Author: liubao <[email protected]> AuthorDate: Tue Aug 22 17:33:45 2023 +0800 [SCB-2803]add response proto-buffer codec --- .../common/rest/HttpTransportContext.java | 11 +- .../common/rest/RestProducerInvocationCreator.java | 17 -- .../rest/RestVertxProducerInvocationCreator.java | 3 +- .../common/rest/VertxHttpTransportContext.java | 5 +- .../rest/codec/param/BodyProcessorCreator.java | 10 +- .../codec/produce/ProduceProcessorManager.java | 90 +++++----- .../codec/produce/ProduceProtoBufferProcessor.java | 107 +++++++++++ .../codec/produce/ProduceTextPlainProcessor.java | 23 +-- .../common/rest/definition/RestOperationMeta.java | 124 ------------- .../rest/filter/inner/RestServerCodecFilter.java | 8 +- .../rest/RestProducerInvocationCreatorTest.java | 39 +---- .../common/rest/codec/TestRestCodec.java | 3 +- .../produce/TestProduceTextPlainProcessor.java | 4 +- .../rest/definition/TestRestOperationMeta.java | 195 --------------------- .../filter/inner/RestServerCodecFilterTest.java | 33 ++-- .../transport/rest/client/RestClientDecoder.java | 7 +- .../RestServletProducerInvocationCreator.java | 2 +- 17 files changed, 226 insertions(+), 455 deletions(-) diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java index b9c8ea20a..d9b0df27c 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/HttpTransportContext.java @@ -16,7 +16,6 @@ */ package org.apache.servicecomb.common.rest; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.context.TransportContext; @@ -26,13 +25,9 @@ public class HttpTransportContext implements TransportContext { private final HttpServletResponseEx responseEx; - private final ProduceProcessor produceProcessor; - - public HttpTransportContext(HttpServletRequestEx requestEx, HttpServletResponseEx responseEx, - ProduceProcessor produceProcessor) { + public HttpTransportContext(HttpServletRequestEx requestEx, HttpServletResponseEx responseEx) { this.requestEx = requestEx; this.responseEx = responseEx; - this.produceProcessor = produceProcessor; } public HttpServletRequestEx getRequestEx() { @@ -42,8 +37,4 @@ public class HttpTransportContext implements TransportContext { public HttpServletResponseEx getResponseEx() { return responseEx; } - - public ProduceProcessor getProduceProcessor() { - return produceProcessor; - } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java index e38f228c7..778f3de4c 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java @@ -16,9 +16,7 @@ */ package org.apache.servicecomb.common.rest; -import static jakarta.ws.rs.core.Response.Status.NOT_ACCEPTABLE; import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; -import static org.apache.servicecomb.core.exception.ExceptionCodes.GENERIC_CLIENT; import static org.apache.servicecomb.core.exception.ExceptionCodes.NOT_DEFINED_ANY_SCHEMA; import java.util.HashMap; @@ -26,7 +24,6 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; import javax.annotation.Nonnull; -import jakarta.ws.rs.core.HttpHeaders; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; @@ -46,7 +43,6 @@ import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; import com.netflix.config.DynamicPropertyFactory; import io.vertx.core.json.Json; @@ -81,7 +77,6 @@ public abstract class RestProducerInvocationCreator implements InvocationCreator Invocation invocation = createInstance(); initInvocationContext(invocation); addParameterContext(invocation); - initProduceProcessor(); initTransportContext(invocation); invocation.addLocalContext(RestConst.REST_REQUEST, requestEx); @@ -158,16 +153,4 @@ public abstract class RestProducerInvocationCreator implements InvocationCreator protected OperationLocator locateOperation(ServicePathManager servicePathManager) { return servicePathManager.producerLocateOperation(requestEx.getRequestURI(), requestEx.getMethod()); } - - @VisibleForTesting - void initProduceProcessor() { - produceProcessor = restOperationMeta.ensureFindProduceProcessor(requestEx); - if (produceProcessor == null) { - LOGGER.error("Accept {} is not supported, operation={}.", requestEx.getHeader(HttpHeaders.ACCEPT), - restOperationMeta.getOperationMeta().getMicroserviceQualifiedName()); - - String msg = String.format("Accept %s is not supported", requestEx.getHeader(HttpHeaders.ACCEPT)); - throw Exceptions.create(NOT_ACCEPTABLE, GENERIC_CLIENT, msg); - } - } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java index fcae4c7ab..a809f0968 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestVertxProducerInvocationCreator.java @@ -38,8 +38,7 @@ public class RestVertxProducerInvocationCreator extends RestProducerInvocationCr @Override protected void initTransportContext(Invocation invocation) { - VertxHttpTransportContext transportContext = new VertxHttpTransportContext(routingContext, requestEx, responseEx, - produceProcessor); + VertxHttpTransportContext transportContext = new VertxHttpTransportContext(routingContext, requestEx, responseEx); invocation.setTransportContext(transportContext); routingContext.put(RestConst.REST_INVOCATION_CONTEXT, invocation); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java index b88c1a12b..55576022e 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/VertxHttpTransportContext.java @@ -16,7 +16,6 @@ */ package org.apache.servicecomb.common.rest; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.context.VertxTransportContext; @@ -31,8 +30,8 @@ public class VertxHttpTransportContext extends HttpTransportContext implements V private final Context vertxContext; public VertxHttpTransportContext(RoutingContext routingContext, HttpServletRequestEx requestEx, - HttpServletResponseEx responseEx, ProduceProcessor produceProcessor) { - super(requestEx, responseEx, produceProcessor); + HttpServletResponseEx responseEx) { + super(requestEx, responseEx); this.routingContext = routingContext; this.vertxContext = Vertx.currentContext(); 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 736b8a060..b231f854c 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 @@ -194,6 +194,10 @@ public class BodyProcessorCreator implements ParamValueProcessorCreator<RequestB // 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(); @@ -257,7 +261,11 @@ public class BodyProcessorCreator implements ParamValueProcessorCreator<RequestB // For application/json and text/plain try (BufferOutputStream output = new BufferOutputStream()) { - RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); + if (MediaType.TEXT_PLAIN.equals(contentType) && (arg instanceof String)) { + output.write(((String) arg).getBytes(StandardCharsets.UTF_8)); + } else { + RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); + } return output.getBuffer(); } } 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 c883fa4c1..81302d576 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 @@ -21,13 +21,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import jakarta.ws.rs.core.MediaType; - +import org.apache.http.entity.ContentType; +import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.RegisterManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.util.CollectionUtils; + +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import jakarta.ws.rs.core.MediaType; public final class ProduceProcessorManager extends RegisterManager<String, Map<String, ProduceProcessor>> { private static final Logger LOGGER = LoggerFactory.getLogger(ProduceProcessorManager.class); @@ -37,14 +41,10 @@ public final class ProduceProcessorManager extends RegisterManager<String, Map<S private static final String NAME = "produce processor mgr"; - public static final String DEFAULT_TYPE = MediaType.APPLICATION_JSON; - public static final String DEFAULT_SERIAL_CLASS = "servicecomb_default_class"; public static final ProduceProcessorManager INSTANCE = new ProduceProcessorManager(); - private final Map<String, ProduceProcessor> nonSerialViewMap = new HashMap<>(); - private final Map<String, ProduceProcessor> jsonProcessorMap; private final Map<String, ProduceProcessor> plainProcessorMap; @@ -54,7 +54,6 @@ public final class ProduceProcessorManager extends RegisterManager<String, Map<S private ProduceProcessorManager() { super(NAME); produceProcessor.forEach(processor -> { - nonSerialViewMap.put(processor.getName(), processor); Map<String, ProduceProcessor> prodProcessorMap = getObjMap() .computeIfAbsent(processor.getName(), key -> new HashMap<>()); prodProcessorMap.putIfAbsent(processor.getSerializationView(), processor); @@ -78,32 +77,6 @@ public final class ProduceProcessorManager extends RegisterManager<String, Map<S return produceViewMap.get(DEFAULT_SERIAL_CLASS); } - // key -> accept type - public Map<String, ProduceProcessor> getOrCreateAcceptMap(Class<?> serialViewClass) { - if (serialViewClass == null) { - return nonSerialViewMap; - } - Map<String, ProduceProcessor> result = new HashMap<>(); - getObjMap().forEach((acceptKey, viewMap) -> { - ProduceProcessor produceProcessor = viewMap.computeIfAbsent(serialViewClass.getName(), - viewKey -> cloneNewProduceProcessor(serialViewClass, viewMap)); - result.put(acceptKey, produceProcessor); - }); - return result; - } - - public ProduceProcessor findProcessor(String acceptType, Class<?> serialViewClass) { - Map<String, ProduceProcessor> viewMap = findValue(acceptType); - if (CollectionUtils.isEmpty(viewMap)) { - return null; - } - if (serialViewClass == null) { - return viewMap.get(DEFAULT_SERIAL_CLASS); - } - return viewMap.computeIfAbsent(serialViewClass.getName(), - viewKey -> cloneNewProduceProcessor(serialViewClass, viewMap)); - } - public ProduceProcessor findJsonProcessorByViewClass(Class<?> serialViewClass) { if (serialViewClass == null) { return jsonProcessorMap.get(DEFAULT_SERIAL_CLASS); @@ -112,14 +85,6 @@ public final class ProduceProcessorManager extends RegisterManager<String, Map<S viewKey -> cloneNewProduceProcessor(serialViewClass, jsonProcessorMap)); } - public ProduceProcessor findDefaultProcessorByViewClass(Class<?> serialViewClass) { - if (serialViewClass == null) { - return defaultProcessorMap.get(DEFAULT_SERIAL_CLASS); - } - return defaultProcessorMap.computeIfAbsent(serialViewClass.getName(), - viewKey -> cloneNewProduceProcessor(serialViewClass, defaultProcessorMap)); - } - public ProduceProcessor findPlainProcessorByViewClass(Class<?> serialViewClass) { if (serialViewClass == null) { return plainProcessorMap.get(DEFAULT_SERIAL_CLASS); @@ -139,4 +104,45 @@ public final class ProduceProcessorManager extends RegisterManager<String, Map<S public ProduceProcessor findDefaultPlainProcessor() { return plainProcessorMap.get(DEFAULT_SERIAL_CLASS); } + + public ProduceProcessor createProduceProcessor(OperationMeta operationMeta, + int statusCode, String accept, Class<?> serialViewClass) { + ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); + ApiResponse response = responses.get(String.valueOf(statusCode)); + if (response == null || response.getContent() == null || + response.getContent().size() == 0) { + return findDefaultProcessor(); + } + String actualAccept = accept; + if (actualAccept == null) { + if (response.getContent().get(MediaType.APPLICATION_JSON) != null) { + actualAccept = MediaType.APPLICATION_JSON; + } else { + actualAccept = response.getContent().keySet().iterator().next(); + } + } + ContentType contentType = ContentType.parse(actualAccept); + 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; + } else { + actualAccept = response.getContent().keySet().iterator().next(); + } + } + if (response.getContent().get(actualAccept) == null) { + LOGGER.warn("Operation do not support accept type {}/{}", accept, actualAccept); + return findDefaultProcessor(); + } + if (SwaggerConst.PROTOBUF_TYPE.equals(actualAccept)) { + return new ProduceProtoBufferProcessor(operationMeta, + operationMeta.getSchemaMeta().getSwagger(), response.getContent().get(accept).getSchema()); + } + if (MediaType.TEXT_PLAIN.equals(actualAccept)) { + return findPlainProcessorByViewClass(serialViewClass); + } + // json + return findJsonProcessorByViewClass(serialViewClass); + } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProtoBufferProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProtoBufferProcessor.java new file mode 100644 index 000000000..f1c8bc105 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProtoBufferProcessor.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.common.rest.codec.produce; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.codec.protobuf.utils.ScopedProtobufSchemaManager; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.protobuf.ProtoMapper; +import org.apache.servicecomb.foundation.protobuf.RootDeserializer; +import org.apache.servicecomb.foundation.protobuf.RootSerializer; +import org.apache.servicecomb.foundation.protobuf.internal.bean.PropertyWrapper; +import org.apache.servicecomb.swagger.generator.SwaggerConst; + +import com.fasterxml.jackson.databind.JavaType; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; + +public class ProduceProtoBufferProcessor implements ProduceProcessor { + public static final String RESPONSE_MESSAGE_NAME = "response"; + + public static final String EXT_ID = "protobuf"; + + private static final Object LOCK = new Object(); + + private final OperationMeta operationMeta; + + private final OpenAPI openAPI; + + private final Schema<?> schema; + + private final ScopedProtobufSchemaManager scopedProtobufSchemaManager; + + public ProduceProtoBufferProcessor(OperationMeta operationMeta, OpenAPI openAPI, Schema<?> schema) { + this.operationMeta = operationMeta; + this.openAPI = openAPI; + this.schema = schema; + this.scopedProtobufSchemaManager = getOrCreateScopedProtobufSchemaManager(operationMeta.getMicroserviceMeta()); + } + + private ScopedProtobufSchemaManager getOrCreateScopedProtobufSchemaManager(MicroserviceMeta microserviceMeta) { + ScopedProtobufSchemaManager scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); + if (scopedProtobufSchemaManager == null) { + synchronized (LOCK) { + scopedProtobufSchemaManager = microserviceMeta.getExtData(EXT_ID); + if (scopedProtobufSchemaManager == null) { + scopedProtobufSchemaManager = new ScopedProtobufSchemaManager(); + microserviceMeta.putExtData(EXT_ID, scopedProtobufSchemaManager); + } + } + } + return scopedProtobufSchemaManager; + } + + @Override + public String getName() { + return SwaggerConst.PROTOBUF_TYPE; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void doEncodeResponse(OutputStream output, Object result) throws Exception { + ProtoMapper protoMapper = scopedProtobufSchemaManager + .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), + RESPONSE_MESSAGE_NAME, schema); + RootSerializer serializer = protoMapper.getSerializerSchemaManager() + .createRootSerializer(protoMapper.getProto().getMessage(RESPONSE_MESSAGE_NAME), + Object.class); + Map<String, Object> bodyArg = new HashMap<>(1); + bodyArg.put("value", result); + output.write(serializer.serialize(bodyArg)); + } + + @Override + public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { + ProtoMapper protoMapper = scopedProtobufSchemaManager + .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), + RESPONSE_MESSAGE_NAME, schema); + RootDeserializer<PropertyWrapper<Object>> deserializer = protoMapper.getDeserializerSchemaManager() + .createRootDeserializer(protoMapper.getProto().getMessage(RESPONSE_MESSAGE_NAME), type); + PropertyWrapper<Object> result = deserializer.deserialize(input.readAllBytes()); + return result.getValue(); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java index d5739be48..21d73688d 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceTextPlainProcessor.java @@ -21,13 +21,13 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import jakarta.ws.rs.core.MediaType; - import org.apache.commons.io.IOUtils; import com.fasterxml.jackson.databind.JavaType; -public class ProduceTextPlainProcessor implements ProduceProcessor { +import jakarta.ws.rs.core.MediaType; + +public class ProduceTextPlainProcessor extends ProduceJsonProcessor { @Override public String getName() { return MediaType.TEXT_PLAIN; @@ -35,17 +35,18 @@ public class ProduceTextPlainProcessor implements ProduceProcessor { @Override public void doEncodeResponse(OutputStream output, Object result) throws Exception { - output.write(String.valueOf(result).getBytes(StandardCharsets.UTF_8)); + if (result instanceof String) { + output.write(((String) result).getBytes(StandardCharsets.UTF_8)); + return; + } + super.doEncodeResponse(output, result); } @Override public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { - // plainText类型,肯定是返回string的,想不出有其他类型的场景 - return IOUtils.toString(input, StandardCharsets.UTF_8); - } - - @Override - public int getOrder() { - return 0; + if (String.class.equals(type.getRawClass())) { + return IOUtils.toString(input, StandardCharsets.UTF_8); + } + return super.doDecodeResponse(input, type); } } 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 764160100..48bb32a8b 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 @@ -17,32 +17,23 @@ package org.apache.servicecomb.common.rest.definition; -import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; 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.codec.produce.ProduceProcessor; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.common.rest.definition.path.PathRegExp; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder; -import org.apache.servicecomb.core.Const; import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.foundation.common.utils.MimeTypesUtils; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.SwaggerUtils; -import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -54,7 +45,6 @@ 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.HttpHeaders; import jakarta.ws.rs.core.MediaType; @SuppressWarnings("rawtypes") @@ -63,8 +53,6 @@ public class RestOperationMeta { protected OperationMeta operationMeta; - protected List<String> produces; - protected boolean formData; // make sure if response is file @@ -77,12 +65,6 @@ public class RestOperationMeta { protected List<String> fileKeys = new ArrayList<>(); - // key为数据类型,比如json之类 - private final Map<String, ProduceProcessor> produceProcessorMap = new LinkedHashMap<>(); - - // 不一定等于mgr中的default,因为本operation可能不支持mgr中的default - private ProduceProcessor defaultProcessor; - protected String absolutePath; protected PathRegExp absolutePathRegExp; @@ -95,13 +77,8 @@ public class RestOperationMeta { OpenAPI swagger = operationMeta.getSchemaMeta().getSwagger(); Operation operation = operationMeta.getSwaggerOperation(); - this.produces = - (operation.getResponses().get(SwaggerConst.SUCCESS_KEY) == null || - operation.getResponses().get(SwaggerConst.SUCCESS_KEY).getContent() == null) ? - null : operation.getResponses().get(SwaggerConst.SUCCESS_KEY).getContent().keySet().stream().toList(); this.downloadFile = checkDownloadFileFlag(); - this.createProduceProcessors(); if (operation.getParameters() != null) { for (int swaggerParameterIdx = 0; swaggerParameterIdx < operation.getParameters().size(); swaggerParameterIdx++) { @@ -259,76 +236,10 @@ public class RestOperationMeta { return paramMap.get(name); } - public RestParam getParamByIndex(int index) { - return paramList.get(index); - } - public OperationMeta getOperationMeta() { return operationMeta; } - protected void createProduceProcessors() { - SwaggerProducerOperation producerOperation = operationMeta.getExtData(Const.PRODUCER_OPERATION); - if (producerOperation != null && producerOperation.getProducerMethod() != null) { - createProduceProcessors(producerOperation.getProducerMethod().getDeclaredAnnotations()); - return; - } - createProduceProcessors(null); - } - - // serialViewClass is deterministic for each operation - protected void createProduceProcessors(Annotation[] annotations) { - if (annotations == null || annotations.length < 1) { - doCreateProduceProcessors(null); - return; - } - for (Annotation annotation : annotations) { - if (annotation.annotationType() == JsonView.class) { - Class<?>[] value = ((JsonView) annotation).value(); - if (value.length != 1) { - throw new IllegalArgumentException( - "@JsonView only supported for exactly 1 class argument "); - } - doCreateProduceProcessors(value[0]); - return; - } - } - doCreateProduceProcessors(null); - } - - // 为operation创建支持的多种produce processor - protected void doCreateProduceProcessors(Class<?> serialViewClass) { - if (null == produces || produces.isEmpty()) { - produceProcessorMap.putAll( - ProduceProcessorManager.INSTANCE.getOrCreateAcceptMap(serialViewClass)); - } else { - for (String produce : produces) { - if (produce.contains(";")) { - produce = produce.substring(0, produce.indexOf(";")); - } - ProduceProcessor processor = ProduceProcessorManager.INSTANCE.findProcessor(produce, serialViewClass); - if (processor == null) { - LOGGER.error("produce {} is not supported, operation={}.", produce, - operationMeta.getMicroserviceQualifiedName()); - continue; - } - this.produceProcessorMap.put(produce, processor); - } - - if (produceProcessorMap.isEmpty()) { - produceProcessorMap.put(ProduceProcessorManager.DEFAULT_TYPE, - ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(serialViewClass)); - } - } - - if (produceProcessorMap.get(ProduceProcessorManager.DEFAULT_TYPE) != null) { - defaultProcessor = produceProcessorMap.get(ProduceProcessorManager.DEFAULT_TYPE); - } else { - defaultProcessor = produceProcessorMap.values().stream().findFirst().get(); - } - produceProcessorMap.putIfAbsent(MediaType.WILDCARD, defaultProcessor); - } - public URLPathBuilder getPathBuilder() { return this.pathBuilder; } @@ -345,37 +256,6 @@ public class RestOperationMeta { paramMap.put(param.getParamName(), param); } - public ProduceProcessor findProduceProcessor(String type) { - return this.produceProcessorMap.get(type); - } - - // 选择与accept匹配的produce processor或者缺省的 - public ProduceProcessor ensureFindProduceProcessor(HttpServletRequestEx requestEx) { - String acceptType = requestEx.getHeader(HttpHeaders.ACCEPT); - return ensureFindProduceProcessor(acceptType); - } - - public ProduceProcessor ensureFindProduceProcessor(String acceptType) { - if (downloadFile) { - //do not check accept type, when the produces of provider is text/plain there will return text/plain processor - //when the produces of provider is application/json there will return the application/json processor - //so do not care what accept type the consumer will set. - return this.produceProcessorMap.get(MediaType.WILDCARD); - } - if (StringUtils.isEmpty(acceptType)) { - return defaultProcessor; - } - List<String> mimeTypes = MimeTypesUtils.getSortedAcceptableMimeTypes(acceptType.toLowerCase(Locale.US)); - for (String mime : mimeTypes) { - ProduceProcessor processor = this.produceProcessorMap.get(mime); - if (null != processor) { - return processor; - } - } - - return null; - } - public String getHttpMethod() { return operationMeta.getHttpMethod(); } @@ -383,8 +263,4 @@ public class RestOperationMeta { public List<String> getFileKeys() { return fileKeys; } - - public List<String> getProduces() { - return produces; - } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java index 392112ec6..a40c7cb55 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java @@ -31,6 +31,7 @@ import org.apache.servicecomb.common.rest.HttpTransportContext; import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestCodec; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; +import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.core.Const; import org.apache.servicecomb.core.Invocation; @@ -51,6 +52,7 @@ import org.slf4j.LoggerFactory; import io.netty.buffer.Unpooled; import io.vertx.core.MultiMap; import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; public class RestServerCodecFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(RestServerCodecFilter.class); @@ -100,7 +102,11 @@ public class RestServerCodecFilter implements ProviderFilter { invocation.onEncodeResponseStart(response); HttpTransportContext transportContext = invocation.getTransportContext(); - ProduceProcessor produceProcessor = transportContext.getProduceProcessor(); + + // TODO: response support JsonView + ProduceProcessor produceProcessor = ProduceProcessorManager.INSTANCE + .createProduceProcessor(invocation.getOperationMeta(), response.getStatusCode(), + invocation.getRequestEx().getHeader(HttpHeaders.ACCEPT), null); HttpServletResponseEx responseEx = transportContext.getResponseEx(); boolean download = isDownloadFileResponseType(invocation, response); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java index 730aa22bf..108fb8722 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java @@ -22,8 +22,6 @@ import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import jakarta.ws.rs.core.HttpHeaders; - import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; @@ -52,6 +50,7 @@ import org.mockito.Mockito; import io.vertx.core.json.Json; import io.vertx.ext.web.RoutingContext; +import jakarta.ws.rs.core.HttpHeaders; public class RestProducerInvocationCreatorTest { @@ -113,43 +112,21 @@ public class RestProducerInvocationCreatorTest { assertThat(throwable.getStatusCode()).isEqualTo(NOT_FOUND.getStatusCode()); assertThat(Json.encode(data)).isIn("{\"code\":\"SCB.00000002\",\"message\":\"Not Found\"}", - "{\"message\":\"Not Found\",\"code\":\"SCB.00000002\"}"); - } - } - - @Test - public void should_failed_when_accept_is_not_support() { - try (MockedStatic<ServicePathManager> mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { - mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)).thenReturn(servicePathManager); - Mockito.when(requestEx.getHeader(HttpHeaders.ACCEPT)).thenReturn("test-type"); - Mockito.when(restOperationMeta.ensureFindProduceProcessor(requestEx)).thenReturn(null); - Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); - Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); - Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); - Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); - Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - - InvocationException throwable = (InvocationException) catchThrowable(() -> creator.createAsync().join()); - CommonExceptionData data = (CommonExceptionData) throwable.getErrorData(); - - assertThat(throwable.getStatusCode()).isEqualTo(NOT_ACCEPTABLE.getStatusCode()); - assertThat(Json.encode(data)).isIn("{\"code\":\"SCB.00000000\",\"message\":\"Accept test-type is not supported\"}", - "{\"message\":\"Accept test-type is not supported\",\"code\":\"SCB.00000000\"}"); + "{\"message\":\"Not Found\",\"code\":\"SCB.00000002\"}"); } } @Test public void should_save_requestEx_in_invocation_context() { try (MockedStatic<ServicePathManager> mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { - mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)).thenReturn(servicePathManager); + mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) + .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.doNothing().when(creator).initProduceProcessor(); Invocation invocation = creator.createAsync().join(); @@ -161,14 +138,14 @@ public class RestProducerInvocationCreatorTest { @Test public void should_save_path_var_map_in_requestEx() { try (MockedStatic<ServicePathManager> mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { - mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)).thenReturn(servicePathManager); + mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) + .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.doNothing().when(creator).initProduceProcessor(); creator.createAsync().join(); @@ -179,14 +156,14 @@ public class RestProducerInvocationCreatorTest { @Test public void should_merge_invocation_context_from_request() { try (MockedStatic<ServicePathManager> mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { - mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)).thenReturn(servicePathManager); + mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) + .thenReturn(servicePathManager); Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.doNothing().when(creator).initProduceProcessor(); Mockito.when(requestEx.getHeader(Const.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); Invocation invocation = creator.createAsync().join(); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java index ebbe2975a..5e7270a6e 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java @@ -27,7 +27,6 @@ import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -160,7 +159,7 @@ public class TestRestCodec { success = true; } catch (InvocationException e) { Assertions.assertEquals(e.getStatusCode(), Status.BAD_REQUEST.getStatusCode()); - Assertions.assertTrue(((CommonExceptionData) e.getErrorData()).getMessage() + Assertions.assertTrue(((String) e.getErrorData()) .contains("Parameter is not valid for operation")); } Assertions.assertFalse(success); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java index d7413d938..d717072a2 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/produce/TestProduceTextPlainProcessor.java @@ -52,7 +52,7 @@ public class TestProduceTextPlainProcessor { Object result = pp.decodeResponse(Buffer.buffer(), resultType); Assertions.assertNull(result); - ByteArrayInputStream is = new ByteArrayInputStream(new byte[] {}); + ByteArrayInputStream is = new ByteArrayInputStream("\"\"".getBytes(StandardCharsets.UTF_8)); result = pp.decodeResponse(is, resultType); Assertions.assertEquals(result, ""); } @@ -91,6 +91,6 @@ public class TestProduceTextPlainProcessor { Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); pp.setSerializationView(Object.class); - Assertions.assertEquals(DEFAULT_SERIAL_CLASS, pp.getSerializationView()); + Assertions.assertEquals("java.lang.Object", pp.getSerializationView()); } } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java index 27d70d4e7..911bf2c6f 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/definition/TestRestOperationMeta.java @@ -25,8 +25,6 @@ import java.util.Arrays; import java.util.List; import org.apache.servicecomb.common.rest.RestEngineSchemaListener; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; -import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.SCBEngine; @@ -36,7 +34,6 @@ import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -191,198 +188,6 @@ public class TestRestOperationMeta { Mockito.when(schemaMeta.getSwagger()).thenReturn(swagger); } - @Test - public void testCreateProduceProcessorsNull() { - findOperation("emptyProduces"); - operationMeta.produces = null; - operationMeta.createProduceProcessors(); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - for (String produce : ProduceProcessorManager.INSTANCE.keys()) { - ProduceProcessor expected = ProduceProcessorManager.INSTANCE.findProcessor(produce, null); - Assertions.assertSame(expected, operationMeta.findProduceProcessor(produce)); - } - } - - @Test - public void testCreateProduceProcessorsNullWithView() { - findOperation("emptyProducesWithView"); - operationMeta.produces = null; - operationMeta.createProduceProcessors(); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - for (String produce : ProduceProcessorManager.INSTANCE.keys()) { - ProduceProcessor expected = ProduceProcessorManager.INSTANCE.findProcessor(produce, Object.class); - Assertions.assertSame(expected, operationMeta.findProduceProcessor(produce)); - } - } - - @Test - public void testCreateProduceProcessorsEmpty() { - findOperation("emptyProduces"); - operationMeta.produces = Arrays.asList(); - operationMeta.createProduceProcessors(); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - for (String produce : ProduceProcessorManager.INSTANCE.keys()) { - ProduceProcessor expected = ProduceProcessorManager.INSTANCE.findProcessor(produce, null); - Assertions.assertSame(expected, operationMeta.findProduceProcessor(produce)); - } - } - - @Test - public void testCreateProduceProcessorsEmptyWithView() { - findOperation("emptyProducesWithView"); - operationMeta.produces = Arrays.asList(); - operationMeta.createProduceProcessors(); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - for (String produce : ProduceProcessorManager.INSTANCE.keys()) { - ProduceProcessor expected = ProduceProcessorManager.INSTANCE.findProcessor(produce, Object.class); - Assertions.assertSame(expected, operationMeta.findProduceProcessor(produce)); - } - } - - @Test - public void testCreateProduceProcessorsNormal() { - findOperation("json"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(), - operationMeta.findProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertNull(operationMeta.findProduceProcessor(MediaType.TEXT_PLAIN)); - } - - @Test - public void testCreateProduceProcessorsNormalWithView() { - findOperation("jsonWithView"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.findProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertNull(operationMeta.findProduceProcessor(MediaType.TEXT_PLAIN)); - } - - @Test - public void testCreateProduceProcessorsNotSupported() { - findOperation("notSupport"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessor(), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(), - operationMeta.findProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertNotNull(operationMeta.findProduceProcessor(MediaType.TEXT_PLAIN)); - } - - @Test - public void testCreateProduceProcessorsNotSupportedWithView() { - findOperation("notSupportWithView"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor((String) null)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor("*/*")); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(ProduceProcessorManager.DEFAULT_TYPE)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultProcessorByViewClass(Object.class), - operationMeta.findProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertNotNull(operationMeta.findProduceProcessor(MediaType.TEXT_PLAIN)); - } - - @Test - public void testCreateProduceProcessorsTextAndWildcard() { - findOperation("textPlain"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.WILDCARD)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.TEXT_PLAIN)); - Assertions.assertNull(operationMeta.ensureFindProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(), - operationMeta.ensureFindProduceProcessor( - MediaType.APPLICATION_JSON + "," + MediaType.APPLICATION_XML + "," + MediaType.WILDCARD)); - } - - @Test - public void testCreateProduceProcessorsTextAndWildcardWithView() { - findOperation("textPlainWithView"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findPlainProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(MediaType.WILDCARD)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findPlainProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(MediaType.TEXT_PLAIN)); - Assertions.assertNull(operationMeta.ensureFindProduceProcessor(MediaType.APPLICATION_JSON)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findPlainProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor( - MediaType.APPLICATION_JSON + "," + MediaType.APPLICATION_XML + "," + MediaType.WILDCARD)); - } - - @Test - public void testCreateProduceProcessorsWithSemicolon() { - findOperation("textCharJsonChar"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultPlainProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.TEXT_PLAIN)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findDefaultJsonProcessor(), - operationMeta.ensureFindProduceProcessor(MediaType.APPLICATION_JSON)); - } - - @Test - public void testCreateProduceProcessorsWithSemicolonWithView() { - findOperation("textCharJsonCharWithView"); - - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findPlainProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(MediaType.TEXT_PLAIN)); - Assertions.assertSame(ProduceProcessorManager.INSTANCE.findJsonProcessorByViewClass(Object.class), - operationMeta.ensureFindProduceProcessor(MediaType.APPLICATION_JSON)); - } - - @Test - public void testEnsureFindProduceProcessorAcceptNotFound() { - findOperation("textCharJsonChar"); - Assertions.assertNull(operationMeta.ensureFindProduceProcessor("notSupport")); - } - - @Test - public void testEnsureFindProduceProcessorAcceptNotFoundWithView() { - findOperation("textCharJsonCharWithView"); - Assertions.assertNull(operationMeta.ensureFindProduceProcessor("notSupport")); - } - @Test public void generatesAbsolutePathWithRootBasePath() { findOperation("textCharJsonChar"); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java index 0a88c3c2b..ab982a409 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java @@ -20,14 +20,12 @@ package org.apache.servicecomb.common.rest.filter.inner; import static com.google.common.net.HttpHeaders.CONTENT_LENGTH; import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; -import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.apache.servicecomb.common.rest.HttpTransportContext; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.Endpoint; @@ -43,9 +41,11 @@ import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -54,9 +54,14 @@ import org.mockito.Mockito; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; import io.vertx.core.MultiMap; -import io.vertx.core.json.Json; import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.MediaType; public class RestServerCodecFilterTest { final RestServerCodecFilter codecFilter = new RestServerCodecFilter(); @@ -109,7 +114,6 @@ public class RestServerCodecFilterTest { Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); - Mockito.when(transportContext.getProduceProcessor()).thenReturn(Mockito.mock(ProduceJsonProcessor.class)); invocation = Mockito.spy(InvocationFactory.forProvider(endpoint, operationMeta, null)); } @@ -137,16 +141,25 @@ public class RestServerCodecFilterTest { Mockito.when(invocation.findResponseType(INTERNAL_SERVER_ERROR.getStatusCode())) .thenReturn(TypeFactory.defaultInstance().constructType(String.class)); - Response response = codecFilter.onFilter(invocation, nextNode).get(); - - assertThat(response.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); - assertThat(Json.encode(response.getResult())). - isIn("{\"code\":\"SCB.50000000\",\"message\":\"Unexpected " - + "exception when processing null. mock encode request failed\"}"); + Assertions.assertThrows(ExecutionException.class, + () -> codecFilter.onFilter(invocation, nextNode).get()); } private void success_invocation() throws InterruptedException, ExecutionException { Mockito.when(invocation.getTransportContext()).thenReturn(transportContext); + HttpServletRequestEx requestExt = Mockito.mock(HttpServletRequestEx.class); + Mockito.when(invocation.getRequestEx()).thenReturn(requestExt); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Operation swaggerOperation = Mockito.mock(Operation.class); + Mockito.when(operationMeta.getSwaggerOperation()).thenReturn(swaggerOperation); + ApiResponses apiResponses = Mockito.mock(ApiResponses.class); + Mockito.when(swaggerOperation.getResponses()).thenReturn(apiResponses); + ApiResponse apiResponse = Mockito.mock(ApiResponse.class); + Mockito.when(apiResponses.get("200")).thenReturn(apiResponse); + Content content = new Content(); + content.addMediaType(MediaType.APPLICATION_JSON, new io.swagger.v3.oas.models.media.MediaType()); + content.get(MediaType.APPLICATION_JSON).setSchema(new StringSchema()); + Mockito.when(apiResponse.getContent()).thenReturn(content); Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); Mockito.when(invocation.findResponseType(INTERNAL_SERVER_ERROR.getStatusCode())) .thenReturn(TypeFactory.defaultInstance().constructType(String.class)); diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java index db277b68e..cd3c4e89e 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java @@ -62,14 +62,15 @@ public class RestClientDecoder { } private ProduceProcessor safeFindProduceProcessor(Invocation invocation, Response response) { - RestClientTransportContext transportContext = invocation.getTransportContext(); - String contentType = extractContentType(response); - ProduceProcessor produceProcessor = transportContext.getRestOperationMeta().findProduceProcessor(contentType); + ProduceProcessor produceProcessor = + ProduceProcessorManager.INSTANCE.createProduceProcessor(invocation.getOperationMeta(), response.getStatusCode(), + contentType, null); if (produceProcessor != null) { return produceProcessor; } + RestClientTransportContext transportContext = invocation.getTransportContext(); HttpClientRequest httpClientRequest = transportContext.getHttpClientRequest(); LOGGER.warn( "operation={}, method={}, endpoint={}, uri={}, statusCode={}, reasonPhrase={}, response content-type={} is not supported in operation.", diff --git a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletProducerInvocationCreator.java b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletProducerInvocationCreator.java index 97cf131c5..a12ee8cc0 100644 --- a/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletProducerInvocationCreator.java +++ b/transports/transport-rest/transport-rest-servlet/src/main/java/org/apache/servicecomb/transport/rest/servlet/RestServletProducerInvocationCreator.java @@ -34,7 +34,7 @@ public class RestServletProducerInvocationCreator extends RestProducerInvocation @Override protected void initTransportContext(Invocation invocation) { - HttpTransportContext transportContext = new HttpTransportContext(requestEx, responseEx, produceProcessor); + HttpTransportContext transportContext = new HttpTransportContext(requestEx, responseEx); invocation.setTransportContext(transportContext); } }
