diff --git a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/rest/response/ApiResponse.java b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/rest/response/ApiResponse.java new file mode 100644 index 0000000000..39384c89d5 --- /dev/null +++ b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/rest/response/ApiResponse.java @@ -0,0 +1,69 @@ +/* + * 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.hugegraph.rest.response; + +public class ApiResponse { + private int code; + private String message; + private T data; + private String status; + + public ApiResponse(){ + + } + + public ApiResponse(int code, String message, T data, String status) { + this.code = code; + this.message = message; + this.data = data; + this.status = status; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + +} diff --git a/hugegraph-pd/hg-pd-service/pom.xml b/hugegraph-pd/hg-pd-service/pom.xml index f69bc9dec0..9073d8abcd 100644 --- a/hugegraph-pd/hg-pd-service/pom.xml +++ b/hugegraph-pd/hg-pd-service/pom.xml @@ -175,6 +175,7 @@ org.apache.hugegraph.pd.boot.HugePDServer + exec diff --git a/hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/GenericExceptionMapper.java b/hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/GenericExceptionMapper.java new file mode 100644 index 0000000000..99ef7b158c --- /dev/null +++ b/hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/GenericExceptionMapper.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.hugegraph.pd.rest.exceptionshandler; + +import org.apache.hugegraph.rest.response.ApiResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GenericExceptionMapper { + + private static final Logger logger = LogManager.getLogger(GenericExceptionMapper.class); + + @ExceptionHandler(Exception.class) + public ResponseEntity>handleGenericException(Exception exception) { + + HttpStatus springStatus = HttpStatus.INTERNAL_SERVER_ERROR; + + logger.error("Unexpected error ", exception); + + ApiResponse apiResponse = new ApiResponse<>( + 500, + "An unexpected error occurred", + null, + springStatus.getReasonPhrase()); + + return ResponseEntity.status(springStatus).body(apiResponse); + } +} diff --git a/hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/PDExceptionMapper.java b/hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/PDExceptionMapper.java new file mode 100644 index 0000000000..ef717bb990 --- /dev/null +++ b/hugegraph-pd/hg-pd-service/src/main/java/org/apache/hugegraph/pd/rest/exceptionshandler/PDExceptionMapper.java @@ -0,0 +1,70 @@ +/* + * 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.hugegraph.pd.rest.exceptionshandler; + +import org.apache.hugegraph.pd.common.PDException; +import org.apache.hugegraph.rest.response.ApiResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class PDExceptionMapper { + + private static final Logger logger = LogManager.getLogger(PDExceptionMapper.class); + + @ExceptionHandler(PDException.class) + public ResponseEntity> toResponse(PDException exception) { + + logger.error(exception.getMessage(), exception); + + HttpStatus status = resolveStatus(exception.getErrorCode()); + String reasonPhrase = status.getReasonPhrase(); + + ApiResponse apiResponse = new ApiResponse<>( + exception.getErrorCode(), + exception.getMessage(), + null, + reasonPhrase); + + return ResponseEntity + .status(status) + .body(apiResponse); + + } + + private HttpStatus resolveStatus(int code) { + try { + // Tenta mapear códigos HTTP exatos (ex: 400, 404, 500) + return HttpStatus.valueOf(code); + } catch (IllegalArgumentException e) { + // Se falhar (ex: 4001), extraímos o primeiro dígito para descobrir a família do erro + String codeStr = String.valueOf(code); + + if (codeStr.startsWith("4")) { + return HttpStatus.BAD_REQUEST; // Erros de cliente -> 400 + } + + // Tudo que for da família 5000 ou não reconhecido vira Erro de Servidor -> 500 + return HttpStatus.INTERNAL_SERVER_ERROR; + } + } +} diff --git a/hugegraph-pd/hg-pd-test/src/main/java/org/apache/hugegraph/pd/rest/exceptions/PDExceptionMapperTest.java b/hugegraph-pd/hg-pd-test/src/main/java/org/apache/hugegraph/pd/rest/exceptions/PDExceptionMapperTest.java new file mode 100644 index 0000000000..1f10cf0958 --- /dev/null +++ b/hugegraph-pd/hg-pd-test/src/main/java/org/apache/hugegraph/pd/rest/exceptions/PDExceptionMapperTest.java @@ -0,0 +1,75 @@ +/* + * 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.hugegraph.pd.rest.exceptions; + +import org.junit.Test; +import org.junit.Assert; + +import org.springframework.http.ResponseEntity; + +import org.apache.hugegraph.pd.common.PDException; +import org.apache.hugegraph.pd.rest.exceptionshandler.GenericExceptionMapper; +import org.apache.hugegraph.pd.rest.exceptionshandler.PDExceptionMapper; +import org.apache.hugegraph.rest.response.ApiResponse; + +public class PDExceptionMapperTest { + + @Test + public void testPDExceptionMapping() { + PDExceptionMapper mapper = new PDExceptionMapper(); + + PDException exception = new PDException(4001, "test error"); + + ResponseEntity> response = + mapper.toResponse(exception); + + Assert.assertEquals(400, response.getStatusCodeValue()); + Assert.assertEquals(4001, response.getBody().getCode()); + Assert.assertEquals("test error", response.getBody().getMessage()); + Assert.assertEquals("Bad Request", response.getBody().getStatus()); + } + + @Test + public void testGenericExceptionFallback() { + PDExceptionMapper mapper = new PDExceptionMapper(); + + PDException exception = new PDException(9999, "Erro desconhecido"); + + ResponseEntity> response = + mapper.toResponse(exception); + + Assert.assertEquals(500, response.getStatusCodeValue()); + Assert.assertEquals(9999, response.getBody().getCode()); + Assert.assertEquals("Internal Server Error", response.getBody().getStatus()); + } + + @Test + public void testGenericExceptionMapping() { + GenericExceptionMapper mapper = new GenericExceptionMapper(); + + Exception exception = new RuntimeException("Erro genérico"); + + ResponseEntity> response = + mapper.handleGenericException(exception); + + Assert.assertEquals(500, response.getStatusCodeValue()); + Assert.assertEquals(500, response.getBody().getCode()); + Assert.assertEquals("An unexpected error occurred", response.getBody().getMessage()); + Assert.assertEquals("Internal Server Error", response.getBody().getStatus()); + } +} diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/GenericExceptionMapper.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/GenericExceptionMapper.java new file mode 100644 index 0000000000..a2923a8861 --- /dev/null +++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/GenericExceptionMapper.java @@ -0,0 +1,49 @@ +/* + * 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.hugegraph.api.exceptionshandler; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; +import org.apache.hugegraph.rest.response.ApiResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +@Provider +public class GenericExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LogManager.getLogger(GenericExceptionMapper.class); + + @Override + public Response toResponse(Exception exception) { + LOG.error("Unexpected error occurred", exception); + + int code = 500; + ApiResponse apiResponse = new ApiResponse<>( + code, + "An unexpected error occurred", + null, + Response.Status.INTERNAL_SERVER_ERROR.getReasonPhrase()); + + return Response.status(code) + .type(MediaType.APPLICATION_JSON) + .entity(apiResponse) + .build(); + } +} diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/HugeExceptionMapper.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/HugeExceptionMapper.java new file mode 100644 index 0000000000..8435e16336 --- /dev/null +++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/api/exceptionshandler/HugeExceptionMapper.java @@ -0,0 +1,74 @@ +/* + * 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.hugegraph.api.exceptionshandler; + +import com.alipay.sofa.rpc.log.Logger; +import com.alipay.sofa.rpc.log.LoggerFactory; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; +import org.apache.hugegraph.HugeException; +import org.apache.hugegraph.rest.response.ApiResponse; + +@Provider +public class HugeExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = + LoggerFactory.getLogger(HugeExceptionMapper.class); + + @Override + public Response toResponse(HugeException exception) { + + Throwable rootCause = HugeException.rootCause(exception); + int code; + + if (rootCause instanceof InterruptedException) { + code = 500; + } else if (rootCause instanceof IllegalArgumentException) { + code = 400; + } else if (rootCause instanceof java.util.NoSuchElementException) { + code = 404; + } else { + code = 400; + } + + if (code >= 500) { + LOG.error("Unexpected server error", exception); + } else { + LOG.warn("Request error: {}", exception.getMessage()); + } + + Response.Status statusCode = Response.Status.fromStatusCode(code); + String statusText = (statusCode != null ) ? statusCode.getReasonPhrase() : "BAD REQUEST"; + + ApiResponse apiResponse = new ApiResponse<>( + code, + exception.getMessage(), + null, + statusText + ); + + return Response.status(code) + .type(MediaType.APPLICATION_JSON) + .entity(apiResponse) + .build(); + } +} + diff --git a/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/GenericExceptionMapper.java b/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/GenericExceptionMapper.java new file mode 100644 index 0000000000..913e0ae519 --- /dev/null +++ b/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/GenericExceptionMapper.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.hugegraph.store.node.controller.exceptionhandlers; + +import org.apache.hugegraph.rest.response.ApiResponse; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GenericExceptionMapper { + + private static final Logger logger = LogManager.getLogger(GenericExceptionMapper.class); + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleGenericException(Exception exception) { + + HttpStatus springStatus = HttpStatus.INTERNAL_SERVER_ERROR; + + logger.error("Unexpected error ", exception); + + ApiResponse apiResponse = new ApiResponse<>( + 500, + "An unexpected error occurred", + null, + springStatus.getReasonPhrase()); + + return ResponseEntity.status(springStatus).body(apiResponse); + } +} diff --git a/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/StoreExceptionHandler.java b/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/StoreExceptionHandler.java new file mode 100644 index 0000000000..ed6bc0411d --- /dev/null +++ b/hugegraph-store/hg-store-node/src/main/java/org/apache/hugegraph/store/node/controller/exceptionhandlers/StoreExceptionHandler.java @@ -0,0 +1,65 @@ +/* + * 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.hugegraph.store.node.controller.exceptionhandlers; + +import org.apache.hugegraph.rest.response.ApiResponse; +import org.apache.hugegraph.store.util.HgStoreException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.logging.Level; +import java.util.logging.Logger; + +@RestControllerAdvice +public class StoreExceptionHandler { + + private static final Logger logger = Logger.getLogger(StoreExceptionHandler.class.getName()); + + @ExceptionHandler(HgStoreException.class) + public ResponseEntity> handleHgStoreException(HgStoreException exception) { + int errorCode = exception.getCode(); + HttpStatus status; + + if (errorCode >= 1200 && errorCode < 1300) { + status = HttpStatus.INTERNAL_SERVER_ERROR; + logger.log(Level.SEVERE, + "Critical error at database (Code " + errorCode + "): " + exception.getMessage(), exception); + } else if (errorCode >= 1000 && errorCode < 1200) { + status = HttpStatus.BAD_REQUEST; + + logger.log(Level.WARNING, + "Validation failed at store (Code " + errorCode + "): " + exception.getMessage(), exception); + } else { + status = HttpStatus.INTERNAL_SERVER_ERROR; + + logger.log(Level.SEVERE, + "Unexpected error at store (Code " + errorCode + "): " + exception.getMessage(), exception); + } + + ApiResponse apiResponse = new ApiResponse<>( + status.value(), + exception.getMessage(), + null, + status.getReasonPhrase() + ); + + return ResponseEntity.status(status).body(apiResponse); + } +}