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 018a12f30 [#4303]compatible to old systems allowing different response
type in exception processing(#4307)
018a12f30 is described below
commit 018a12f30f73556c38a16d1352e0e3bac3932341
Author: liubao68 <[email protected]>
AuthorDate: Mon May 6 10:39:45 2024 +0800
[#4303]compatible to old systems allowing different response type in
exception processing(#4307)
---
.../common/rest/RestProducerInvocationFlow.java | 4 +-
.../rest/filter/inner/RestServerCodecFilter.java | 11 +++-
.../core/exception/DefaultExceptionProcessor.java | 3 +-
.../apache/servicecomb/core/filter/FilterNode.java | 11 +---
.../core/invocation/ProducerInvocationFlow.java | 72 ++++++++--------------
.../demo/springmvc/SpringmvcClient.java | 2 +-
.../demo/springmvc/client/ResponseOKData.java | 48 +++++++++++++++
.../demo/springmvc/client/TestDownloadSchema.java | 47 ++++++++++++++
.../demo/springmvc/server/DownloadSchema.java | 41 +++++++++---
.../demo/springmvc/server/ResponseOKData.java | 44 +++++++++++++
.../demo/springmvc/server/ResponseOKException.java | 20 ++++++
.../server/ResponseOKExceptionConverter.java | 40 ++++++++++++
...e.servicecomb.core.exception.ExceptionConverter | 18 ++++++
.../highway/HighwayServerCodecFilter.java | 12 +++-
.../highway/HighwayServerCodecFilterTest.java | 3 +-
15 files changed, 305 insertions(+), 71 deletions(-)
diff --git
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java
index c8e32471f..b09406bd4 100644
---
a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java
+++
b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java
@@ -29,6 +29,7 @@ import
org.apache.servicecomb.foundation.common.utils.PartUtils;
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.apache.servicecomb.swagger.invocation.exception.InvocationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -60,7 +61,8 @@ public class RestProducerInvocationFlow extends
ProducerInvocationFlow {
@Override
protected void sendResponse(Invocation invocation, Response response) {
invocation.getInvocationStageTrace().startProviderSendResponse();
- if (isDownloadFileResponseType(invocation, response)) {
+ boolean failed = response.getResult() instanceof InvocationException;
+ if (!failed && isDownloadFileResponseType(invocation, response)) {
responseEx.sendPart(PartUtils.getSinglePart(null, response.getResult()))
.whenComplete((r, e) -> flushResponse(invocation));
return;
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 d727a7141..b1b3c37b7 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
@@ -46,6 +46,7 @@ import
org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream;
import org.apache.servicecomb.swagger.invocation.Response;
import org.apache.servicecomb.swagger.invocation.context.TransportContext;
import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -124,13 +125,19 @@ public class RestServerCodecFilter extends AbstractFilter
implements ProviderFil
responseEx.setStatus(response.getStatusCode());
copyHeadersToHttpResponse(response.getHeaders(), responseEx);
- if (download) {
+ boolean failed = response.getResult() instanceof InvocationException;
+
+ if (!failed && download) {
return CompletableFuture.completedFuture(response);
}
responseEx.setContentType(produceProcessor.getName());
try (BufferOutputStream output = new
BufferOutputStream(Unpooled.compositeBuffer())) {
- produceProcessor.encodeResponse(output, response.getResult());
+ if (failed) {
+ produceProcessor.encodeResponse(output, ((InvocationException)
response.getResult()).getErrorData());
+ } else {
+ produceProcessor.encodeResponse(output, response.getResult());
+ }
responseEx.setBodyBuffer(output.getBuffer());
diff --git
a/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java
b/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java
index 2d9a0b14e..f0f101f91 100644
---
a/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java
+++
b/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java
@@ -191,8 +191,7 @@ public class DefaultExceptionProcessor implements
ExceptionProcessor {
if (invocation != null) {
logProducerException(invocation, invocationException);
}
- return Response.status(invocationException.getStatus())
- .entity(invocationException.getErrorData());
+ return Response.createFail(invocationException);
}
@Override
diff --git
a/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java
b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java
index b589730f7..44a5b34d9 100644
--- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java
+++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java
@@ -70,15 +70,6 @@ public class FilterNode {
String stage =
invocation.getInvocationStageTrace().recordStageBegin(this.filter.getNameWithOrder());
return AsyncUtils.tryCatchSupplierFuture(() -> filter.onFilter(invocation,
nextNode)
- .whenComplete((r, e) ->
invocation.getInvocationStageTrace().recordStageEnd(stage)))
- .thenApply(this::rethrowExceptionInResponse);
- }
-
- private Response rethrowExceptionInResponse(Response response) {
- if (response.isFailed() && response.getResult() instanceof Throwable) {
- throw AsyncUtils.rethrow(response.getResult());
- }
-
- return response;
+ .whenComplete((r, e) ->
invocation.getInvocationStageTrace().recordStageEnd(stage)));
}
}
diff --git
a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
index 9ccef3b99..88371433c 100644
---
a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
+++
b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java
@@ -19,14 +19,16 @@ package org.apache.servicecomb.core.invocation;
import java.util.concurrent.CompletableFuture;
import org.apache.servicecomb.core.Invocation;
-import org.apache.servicecomb.core.exception.Exceptions;
-import org.apache.servicecomb.foundation.common.utils.ExceptionUtils;
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.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import jakarta.ws.rs.core.Response.Status;
+
public abstract class ProducerInvocationFlow {
private static final Logger LOGGER =
LoggerFactory.getLogger(ProducerInvocationFlow.class);
@@ -66,56 +68,36 @@ public abstract class ProducerInvocationFlow {
if (invocation.isEdge()) {
invocation.getMicroserviceMeta().getEdgeFilterChain()
.onFilter(invocation)
- .whenComplete((response, Throwable) -> sendResponse(invocation,
response))
- .whenComplete((response, Throwable) -> finishInvocation(invocation,
response, Throwable));
+ .whenComplete((response, throwable) -> {
+ if (throwable != null) {
+ // Server codec operates on Response. So the filter chain result
should be Response and
+ // will never throw exception.
+ LOGGER.error("Maybe a fatal bug that should be addressed.",
throwable);
+ response = Response.createFail(new
InvocationException(Status.INTERNAL_SERVER_ERROR,
+ new CommonExceptionData("Internal error, check logs for
details.")));
+ }
+ sendResponse(invocation, response);
+ finishInvocation(invocation, response);
+ });
return;
}
invocation.getMicroserviceMeta().getProviderFilterChain()
.onFilter(invocation)
- .whenComplete((response, Throwable) -> sendResponse(invocation,
response))
- .whenComplete((response, Throwable) -> finishInvocation(invocation,
response, Throwable));
+ .whenComplete((response, throwable) -> {
+ if (throwable != null) {
+ // Server codec operates on Response. So the filter chain result
should be Response and
+ // will never throw exception.
+ LOGGER.error("Maybe a fatal bug that should be addressed.",
throwable);
+ response = Response.createFail(new
InvocationException(Status.INTERNAL_SERVER_ERROR,
+ new CommonExceptionData("Internal error, check logs for
details.")));
+ }
+ sendResponse(invocation, response);
+ finishInvocation(invocation, response);
+ });
}
- private void finishInvocation(Invocation invocation, Response response,
Throwable throwable) {
+ private void finishInvocation(Invocation invocation, Response response) {
invocation.onFinish(response);
-
- tryLogException(invocation, throwable);
- }
-
- private void tryLogException(Invocation invocation, Throwable throwable) {
- if (throwable == null) {
- return;
- }
-
- throwable = Exceptions.unwrap(throwable);
- if (requestEx == null) {
- logException(invocation, throwable);
- return;
- }
-
- logException(invocation, requestEx, throwable);
- }
-
- private void logException(Invocation invocation, Throwable throwable) {
- if (Exceptions.isPrintInvocationStackTrace()) {
- LOGGER.error("Failed to finish invocation, operation:{}.",
invocation.getMicroserviceQualifiedName(), throwable);
- return;
- }
-
- LOGGER.error("Failed to finish invocation, operation:{}, message={}.",
invocation.getMicroserviceQualifiedName(),
- ExceptionUtils.getExceptionMessageWithoutTrace(throwable));
- }
-
- private void logException(Invocation invocation, HttpServletRequestEx
requestEx, Throwable throwable) {
- if (Exceptions.isPrintInvocationStackTrace()) {
- LOGGER.error("Failed to finish invocation, operation:{}, request
uri:{}.",
- invocation.getMicroserviceQualifiedName(),
requestEx.getRequestURI(), throwable);
- return;
- }
-
- LOGGER.error("Failed to finish invocation, operation:{}, request uri:{},
message={}.",
- invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(),
- ExceptionUtils.getExceptionMessageWithoutTrace(throwable));
}
protected abstract Invocation sendCreateInvocationException(Throwable
throwable);
diff --git
a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java
b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java
index 291900ea4..849fd56a1 100644
---
a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java
+++
b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java
@@ -80,7 +80,7 @@ public class SpringmvcClient {
LOGGER.error("-------------- test failed -------------");
}
TestMgr.summary();
- LOGGER.info("-------------- last time updated checks(maybe more/less):
1313 -------------");
+ LOGGER.info("-------------- last time updated checks(maybe more/less):
1341 -------------");
}
private static void changeTransport(String microserviceName, String
transport) {
diff --git
a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java
b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java
new file mode 100644
index 000000000..df2bf9c5f
--- /dev/null
+++
b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java
@@ -0,0 +1,48 @@
+/*
+ * 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.demo.springmvc.client;
+
+public class ResponseOKData {
+ private String errorCode;
+
+ private String errorMessage;
+
+ public ResponseOKData() {
+
+ }
+
+ public ResponseOKData(String errorCode, String errorMessage) {
+ this.errorCode = errorCode;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getErrorCode() {
+ return errorCode;
+ }
+
+ public void setErrorCode(String errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+}
diff --git
a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java
b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java
index 20a920a63..4e5fc967a 100644
---
a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java
+++
b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java
@@ -17,25 +17,72 @@
package org.apache.servicecomb.demo.springmvc.client;
+import java.net.URI;
import java.util.Collections;
+import java.util.List;
import org.apache.servicecomb.demo.CategorizedTestCase;
import org.apache.servicecomb.demo.TestMgr;
import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart;
import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder;
+import org.apache.servicecomb.registry.DiscoveryManager;
+import org.apache.servicecomb.registry.api.DiscoveryInstance;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestOperations;
+import org.springframework.web.client.RestTemplate;
@Component
public class TestDownloadSchema implements CategorizedTestCase {
+ @Autowired
+ DiscoveryManager discoveryManager;
+
@Override
public void testRestTransport() throws Exception {
testDownloadFileAndDeleted();
testDownloadFileNotDeleted();
testDownloadFileWithNull();
testSetContentTypeByResponseEntity();
+ testResponseOKException();
+ }
+
+ private void testResponseOKException() {
+ List<? extends DiscoveryInstance> instances =
+ discoveryManager.findServiceInstances("springmvctest", "springmvc");
+ String endpoint = instances.get(0).getEndpoints().stream()
+ .filter(item -> item.startsWith("rest")).findFirst().get();
+ URI endpointItem = URI.create(endpoint);
+ RestTemplate template = new RestTemplate();
+
+ // This is for compatible usage. For best practise, any status code
+ // should have only one type of response.
+ ResponseEntity<ResponseOKData> resultFail = template.getForEntity(
+ "http://" + endpointItem.getHost() + ":" + endpointItem.getPort()
+ + "/api/download/testResponseOKExceptionBean?exception=true",
ResponseOKData.class);
+ TestMgr.check(200, resultFail.getStatusCode().value());
+ TestMgr.check("code-005", resultFail.getBody().getErrorCode());
+ TestMgr.check("error-005", resultFail.getBody().getErrorMessage());
+ ResponseEntity<Boolean> resultOK = template.getForEntity(
+ "http://" + endpointItem.getHost() + ":" + endpointItem.getPort()
+ + "/api/download/testResponseOKExceptionBean?exception=false",
boolean.class);
+ TestMgr.check(true, resultOK.getBody());
+
+ resultFail = template.getForEntity(
+ "http://" + endpointItem.getHost() + ":" + endpointItem.getPort()
+ +
"/api/download/testResponseOKExceptionDownload?exception=true&content=ddd&contentType=plain/text",
+ ResponseOKData.class);
+ TestMgr.check(200, resultFail.getStatusCode().value());
+ TestMgr.check("code-005", resultFail.getBody().getErrorCode());
+ TestMgr.check("error-005", resultFail.getBody().getErrorMessage());
+
+ ResponseEntity<String> resultPartOK = template.getForEntity(
+ "http://" + endpointItem.getHost() + ":" + endpointItem.getPort()
+ +
"/api/download/testResponseOKExceptionDownload?exception=false&content=ddd&contentType=plain/text",
+ String.class);
+ TestMgr.check(200, resultPartOK.getStatusCode().value());
+ TestMgr.check("ddd", resultPartOK.getBody());
}
private void testDownloadFileAndDeleted() throws Exception {
diff --git
a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DownloadSchema.java
b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DownloadSchema.java
index cffe5ae85..5db71e59d 100644
---
a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DownloadSchema.java
+++
b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/DownloadSchema.java
@@ -22,8 +22,6 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
-import jakarta.servlet.http.Part;
-
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.servicecomb.foundation.common.part.FilePart;
@@ -35,6 +33,8 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
+import jakarta.servlet.http.Part;
+
@RestSchema(schemaId = "DownloadSchema")
@RequestMapping(path = "/download", produces =
MediaType.APPLICATION_JSON_VALUE)
public class DownloadSchema {
@@ -93,18 +93,45 @@ public class DownloadSchema {
}
@GetMapping(path = "/setContentTypeByResponseEntity")
- public ResponseEntity<Part>
setContentTypeByResponseEntity(@RequestParam("content") String content,
@RequestParam("contentType") String contentType) throws IOException {
+ public ResponseEntity<Part>
setContentTypeByResponseEntity(@RequestParam("content") String content,
+ @RequestParam("contentType") String contentType) throws IOException {
File file = createTempFile(content);
return ResponseEntity
- .ok()
- .header(HttpHeaders.CONTENT_TYPE, contentType)
- .header(HttpHeaders.CONTENT_DISPOSITION,
"attachment;filename=tempFileEntity.txt")
- .body(new FilePart(null, file));
+ .ok()
+ .header(HttpHeaders.CONTENT_TYPE, contentType)
+ .header(HttpHeaders.CONTENT_DISPOSITION,
"attachment;filename=tempFileEntity.txt")
+ .body(new FilePart(null, file));
}
@GetMapping(path = "/assertLastFileDeleted")
public boolean assertLastFileDeleted() {
return lastFile.exists();
}
+
+ @GetMapping(path = "/testResponseOKExceptionDownload")
+ public ResponseEntity<Part> testResponseOKExceptionDownload(
+ @RequestParam("exception") boolean exception,
+ @RequestParam("content") String content,
+ @RequestParam("contentType") String contentType) throws IOException {
+ if (exception) {
+ throw new ResponseOKException();
+ }
+
+ File file = createTempFile(content);
+
+ return ResponseEntity
+ .ok()
+ .header(HttpHeaders.CONTENT_TYPE, contentType)
+ .header(HttpHeaders.CONTENT_DISPOSITION,
"attachment;filename=tempFileEntity.txt")
+ .body(new FilePart(null, file));
+ }
+
+ @GetMapping(path = "/testResponseOKExceptionBean")
+ public boolean testResponseOKExceptionBean(@RequestParam("exception")
boolean exception) {
+ if (exception) {
+ throw new ResponseOKException();
+ }
+ return true;
+ }
}
diff --git
a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java
b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java
new file mode 100644
index 000000000..aeadbbeda
--- /dev/null
+++
b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java
@@ -0,0 +1,44 @@
+/*
+ * 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.demo.springmvc.server;
+
+public class ResponseOKData {
+ private String errorCode;
+
+ private String errorMessage;
+
+ public ResponseOKData(String errorCode, String errorMessage) {
+ this.errorCode = errorCode;
+ this.errorMessage = errorMessage;
+ }
+
+ public String getErrorCode() {
+ return errorCode;
+ }
+
+ public void setErrorCode(String errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+}
diff --git
a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java
b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java
new file mode 100644
index 000000000..6dcafb2db
--- /dev/null
+++
b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java
@@ -0,0 +1,20 @@
+/*
+ * 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.demo.springmvc.server;
+
+public class ResponseOKException extends RuntimeException {
+}
diff --git
a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java
b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java
new file mode 100644
index 000000000..7358b7217
--- /dev/null
+++
b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.demo.springmvc.server;
+
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.exception.ExceptionConverter;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
+
+import jakarta.ws.rs.core.Response.Status;
+import jakarta.ws.rs.core.Response.StatusType;
+
+public class ResponseOKExceptionConverter implements
+ ExceptionConverter<ResponseOKException> {
+
+ @Override
+ public boolean canConvert(Throwable throwable) {
+ return throwable instanceof ResponseOKException;
+ }
+
+ @Override
+ public InvocationException convert(Invocation invocation,
ResponseOKException throwable, StatusType genericStatus) {
+ // This is for compatible usage. For best practise, any status code
+ // should have only one type of response.
+ return new InvocationException(Status.OK, new ResponseOKData("code-005",
"error-005"));
+ }
+}
diff --git
a/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter
b/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter
new file mode 100644
index 000000000..f25ea28e1
--- /dev/null
+++
b/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter
@@ -0,0 +1,18 @@
+#
+# 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.
+#
+
+org.apache.servicecomb.demo.springmvc.server.ResponseOKExceptionConverter
diff --git
a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java
b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java
index 819fc6399..1f165f922 100644
---
a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java
+++
b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java
@@ -30,6 +30,7 @@ import org.apache.servicecomb.core.filter.FilterNode;
import org.apache.servicecomb.core.filter.ProviderFilter;
import org.apache.servicecomb.foundation.common.utils.AsyncUtils;
import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
import org.apache.servicecomb.transport.highway.message.ResponseHeader;
import io.vertx.core.buffer.Buffer;
@@ -92,8 +93,15 @@ public class HighwayServerCodecFilter extends AbstractFilter
implements Provider
ResponseRootSerializer bodySchema =
operationProtobuf.findResponseRootSerializer(response.getStatusCode());
try {
- Buffer respBuffer = HighwayCodec.encodeResponse(
- msgId, header, bodySchema, response.getResult());
+ boolean failed = response.getResult() instanceof InvocationException;
+ Buffer respBuffer;
+ if (failed) {
+ respBuffer = HighwayCodec.encodeResponse(
+ msgId, header, bodySchema, ((InvocationException)
response.getResult()).getErrorData());
+ } else {
+ respBuffer = HighwayCodec.encodeResponse(
+ msgId, header, bodySchema, response.getResult());
+ }
transportContext.setResponseBuffer(respBuffer);
invocation.onEncodeResponseFinish();
diff --git
a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java
b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java
index 60cbf0154..a9356e338 100644
---
a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java
+++
b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java
@@ -38,6 +38,7 @@ import
org.apache.servicecomb.core.invocation.InvocationFactory;
import org.apache.servicecomb.foundation.common.LegacyPropertyFactory;
import
org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace;
import org.apache.servicecomb.swagger.invocation.Response;
+import org.apache.servicecomb.swagger.invocation.exception.InvocationException;
import org.apache.servicecomb.transport.highway.message.RequestHeader;
import org.apache.servicecomb.transport.highway.message.ResponseHeader;
import org.junit.After;
@@ -145,7 +146,7 @@ public class HighwayServerCodecFilterTest {
Response response = codecFilter.onFilter(invocation, nextNode).get();
assertThat(response.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR);
- assertThat(Json.encode(response.getResult()))
+ assertThat(Json.encode(((InvocationException)
response.getResult()).getErrorData()))
.isEqualTo("{\"code\":\"SCB.50000000\",\"message\":\"Unexpected "
+ "exception when processing null. encode request failed\"}");
}