From 23812e1149b7b74a005d3bcc4ca7860d2ffa9124 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Tue, 5 Mar 2024 17:13:53 +0200 Subject: [PATCH 1/7] changed type of ThingsboardErrorResponse timestamp from Date to long --- .../ThingsboardErrorController.java | 56 +++++++++++++++++++ .../exception/ThingsboardErrorResponse.java | 6 +- 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/controller/ThingsboardErrorController.java diff --git a/application/src/main/java/org/thingsboard/server/controller/ThingsboardErrorController.java b/application/src/main/java/org/thingsboard/server/controller/ThingsboardErrorController.java new file mode 100644 index 0000000000..b70edcba45 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/ThingsboardErrorController.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2024 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * 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.thingsboard.server.controller; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.servlet.error.ErrorController; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.RequestMapping; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.exception.ThingsboardErrorResponse; + +import javax.servlet.RequestDispatcher; +import javax.servlet.http.HttpServletRequest; + +@Slf4j +@ControllerAdvice +public class ThingsboardErrorController implements ErrorController { + + public static final String PATH_NOT_FOUND_ERROR_DESCRIPTION = "Path is not found."; + public static final String GENERAL_ERROR_DESCRIPTION = "Something went wrong! Our Engineers are on it"; + + @RequestMapping("/error") + public ResponseEntity handleError(HttpServletRequest request) { + Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_JSON); + + if (status != null) { + int statusCode = Integer.parseInt(status.toString()); + + if(statusCode == HttpStatus.NOT_FOUND.value()) { + return new ResponseEntity<>(ThingsboardErrorResponse.of(PATH_NOT_FOUND_ERROR_DESCRIPTION, ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND), httpHeaders, HttpStatus.NOT_FOUND); + } + } + return new ResponseEntity<>(ThingsboardErrorResponse.of(GENERAL_ERROR_DESCRIPTION, ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR), httpHeaders, HttpStatus.INTERNAL_SERVER_ERROR); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java index 26084ad077..513aff6891 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java @@ -33,13 +33,13 @@ public class ThingsboardErrorResponse { // Error code private final ThingsboardErrorCode errorCode; - private final Date timestamp; + private final long timestamp; protected ThingsboardErrorResponse(final String message, final ThingsboardErrorCode errorCode, HttpStatus status) { this.message = message; this.errorCode = errorCode; this.status = status; - this.timestamp = new java.util.Date(); + this.timestamp = System.currentTimeMillis(); } public static ThingsboardErrorResponse of(final String message, final ThingsboardErrorCode errorCode, HttpStatus status) { @@ -75,7 +75,7 @@ public class ThingsboardErrorResponse { } @ApiModelProperty(position = 4, value = "Timestamp", accessMode = ApiModelProperty.AccessMode.READ_ONLY) - public Date getTimestamp() { + public long getTimestamp() { return timestamp; } } From 686e41bd393c221534bf2f969e541dcc13746554 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 6 Mar 2024 17:04:25 +0200 Subject: [PATCH 2/7] moved path not found error handing from ThingsboardErrorController to ThingsboardErrorResponseHandler --- .../ThingsboardErrorController.java | 56 ------------------- .../ThingsboardErrorResponseHandler.java | 33 ++++++++++- 2 files changed, 32 insertions(+), 57 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/controller/ThingsboardErrorController.java diff --git a/application/src/main/java/org/thingsboard/server/controller/ThingsboardErrorController.java b/application/src/main/java/org/thingsboard/server/controller/ThingsboardErrorController.java deleted file mode 100644 index b70edcba45..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/ThingsboardErrorController.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright © 2016-2024 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * 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.thingsboard.server.controller; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.web.servlet.error.ErrorController; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.RequestMapping; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; -import org.thingsboard.server.exception.ThingsboardErrorResponse; - -import javax.servlet.RequestDispatcher; -import javax.servlet.http.HttpServletRequest; - -@Slf4j -@ControllerAdvice -public class ThingsboardErrorController implements ErrorController { - - public static final String PATH_NOT_FOUND_ERROR_DESCRIPTION = "Path is not found."; - public static final String GENERAL_ERROR_DESCRIPTION = "Something went wrong! Our Engineers are on it"; - - @RequestMapping("/error") - public ResponseEntity handleError(HttpServletRequest request) { - Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); - - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.setContentType(MediaType.APPLICATION_JSON); - - if (status != null) { - int statusCode = Integer.parseInt(status.toString()); - - if(statusCode == HttpStatus.NOT_FOUND.value()) { - return new ResponseEntity<>(ThingsboardErrorResponse.of(PATH_NOT_FOUND_ERROR_DESCRIPTION, ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND), httpHeaders, HttpStatus.NOT_FOUND); - } - } - return new ResponseEntity<>(ThingsboardErrorResponse.of(GENERAL_ERROR_DESCRIPTION, ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR), httpHeaders, HttpStatus.INTERNAL_SERVER_ERROR); - } - -} diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index b2509187d5..ec149fede8 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -16,6 +16,7 @@ package org.thingsboard.server.exception; import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; @@ -28,7 +29,9 @@ import org.springframework.security.authentication.LockedException; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.context.request.WebRequest; @@ -43,6 +46,7 @@ import org.thingsboard.server.service.security.exception.JwtExpiredTokenExceptio import org.thingsboard.server.service.security.exception.UserPasswordExpiredException; import org.thingsboard.server.service.security.exception.UserPasswordNotValidException; +import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -50,9 +54,15 @@ import java.io.IOException; import java.util.HashMap; import java.util.Map; +import static javax.servlet.RequestDispatcher.ERROR_EXCEPTION; + @Slf4j +@Controller @RestControllerAdvice -public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler { +public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler, ErrorController { + + public static final String PATH_NOT_FOUND_ERROR_DESCRIPTION = "Path is not found."; + public static final String GENERAL_ERROR_DESCRIPTION = "Something went wrong!"; private static final Map statusToErrorCodeMap = new HashMap<>(); static { @@ -90,6 +100,27 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand return errorCodeToStatusMap.getOrDefault(errorCode, HttpStatus.INTERNAL_SERVER_ERROR); } + @RequestMapping("/error") + public ResponseEntity handleError(HttpServletRequest request) { + Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); + + if (status != null) { + int statusCode = Integer.parseInt(status.toString()); + if(statusCode == HttpStatus.NOT_FOUND.value()) { + return new ResponseEntity<>(ThingsboardErrorResponse.of(PATH_NOT_FOUND_ERROR_DESCRIPTION, ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND), HttpStatus.NOT_FOUND); + } + } + String errorMessage; + Throwable throwable = (Throwable) + request.getAttribute(ERROR_EXCEPTION); + if (throwable != null) { + errorMessage = throwable.getMessage(); + } else { + errorMessage = GENERAL_ERROR_DESCRIPTION; + } + return new ResponseEntity<>(ThingsboardErrorResponse.of(errorMessage, ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR), HttpStatus.INTERNAL_SERVER_ERROR); + } + @Override @ExceptionHandler(AccessDeniedException.class) public void handle(HttpServletRequest request, HttpServletResponse response, From b67189429dbf45defc2b6929b4c9152550f7ee57 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Wed, 6 Mar 2024 18:32:43 +0200 Subject: [PATCH 3/7] code formatting --- .../server/exception/ThingsboardErrorResponseHandler.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index ec149fede8..f843a8ec6f 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -111,8 +111,7 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand } } String errorMessage; - Throwable throwable = (Throwable) - request.getAttribute(ERROR_EXCEPTION); + Throwable throwable = (Throwable) request.getAttribute(ERROR_EXCEPTION); if (throwable != null) { errorMessage = throwable.getMessage(); } else { From 2e216242f2ac63ef9a6ee2f8c9420f18df9bfe8a Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 7 Mar 2024 14:44:20 +0200 Subject: [PATCH 4/7] clone repo if tmp directory was deleted --- .../sync/vc/DefaultGitRepositoryService.java | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java index 24d23c60a1..8bbc9defa2 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java @@ -157,7 +157,7 @@ public class DefaultGitRepositoryService implements GitRepositoryService { @Override public void fetch(TenantId tenantId) throws GitAPIException { - var repository = repositories.get(tenantId); + var repository = checkRepository(tenantId); if (repository != null) { log.debug("[{}] Fetching tenant repository.", tenantId); repository.fetch(); @@ -195,8 +195,17 @@ public class DefaultGitRepositoryService implements GitRepositoryService { } private GitRepository checkRepository(TenantId tenantId) { - return Optional.ofNullable(repositories.get(tenantId)) + GitRepository gitRepository = Optional.ofNullable(repositories.get(tenantId)) .orElseThrow(() -> new IllegalStateException("Repository is not initialized")); + + if (!Files.exists(Path.of(gitRepository.getDirectory()))) { + try { + return cloneRepository(tenantId, gitRepository.getSettings()); + } catch (Exception e) { + throw new IllegalStateException("Repository is not initialized"); + } + } + return gitRepository; } @Override @@ -229,17 +238,7 @@ public class DefaultGitRepositoryService implements GitRepositoryService { testRepository(tenantId, settings); clearRepository(tenantId); - log.debug("[{}] Init tenant repository started.", tenantId); - Path repositoryDirectory = Path.of(repositoriesFolder, tenantId.getId().toString()); - GitRepository repository; - if (Files.exists(repositoryDirectory)) { - FileUtils.forceDelete(repositoryDirectory.toFile()); - } - - Files.createDirectories(repositoryDirectory); - repository = GitRepository.clone(settings, repositoryDirectory.toFile()); - repositories.put(tenantId, repository); - log.debug("[{}] Init tenant repository completed.", tenantId); + cloneRepository(tenantId, settings); } @Override @@ -276,4 +275,18 @@ public class DefaultGitRepositoryService implements GitRepositoryService { String entityId = StringUtils.substringBetween(path, "/", ".json"); return EntityIdFactory.getByTypeAndUuid(entityType, entityId); } + + private GitRepository cloneRepository(TenantId tenantId, RepositorySettings settings) throws Exception { + log.debug("[{}] Init tenant repository started.", tenantId); + Path repositoryDirectory = Path.of(repositoriesFolder, tenantId.getId().toString()); + + if (Files.exists(repositoryDirectory)) { + FileUtils.forceDelete(repositoryDirectory.toFile()); + } + Files.createDirectories(repositoryDirectory); + GitRepository repository = GitRepository.clone(settings, repositoryDirectory.toFile()); + repositories.put(tenantId, repository); + log.debug("[{}] Init tenant repository completed.", tenantId); + return repository; + } } From 04ff4bd9ae15bcddd9fdf411c7258391f2c9cb10 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 7 Mar 2024 15:38:27 +0200 Subject: [PATCH 5/7] refactored handleError method: status code and message are being retrieved from exception --- .../ThingsboardErrorResponseHandler.java | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index f843a8ec6f..e773e5ffd6 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -16,6 +16,7 @@ package org.thingsboard.server.exception; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; @@ -50,9 +51,11 @@ import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.swing.text.html.Option; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import static javax.servlet.RequestDispatcher.ERROR_EXCEPTION; @@ -102,22 +105,13 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand @RequestMapping("/error") public ResponseEntity handleError(HttpServletRequest request) { - Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); - - if (status != null) { - int statusCode = Integer.parseInt(status.toString()); - if(statusCode == HttpStatus.NOT_FOUND.value()) { - return new ResponseEntity<>(ThingsboardErrorResponse.of(PATH_NOT_FOUND_ERROR_DESCRIPTION, ThingsboardErrorCode.ITEM_NOT_FOUND, HttpStatus.NOT_FOUND), HttpStatus.NOT_FOUND); - } - } - String errorMessage; - Throwable throwable = (Throwable) request.getAttribute(ERROR_EXCEPTION); - if (throwable != null) { - errorMessage = throwable.getMessage(); - } else { - errorMessage = GENERAL_ERROR_DESCRIPTION; - } - return new ResponseEntity<>(ThingsboardErrorResponse.of(errorMessage, ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR), HttpStatus.INTERNAL_SERVER_ERROR); + HttpStatus httpStatus = Optional.ofNullable(request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)) + .map(status -> HttpStatus.resolve(Integer.parseInt(status.toString()))) + .orElse(HttpStatus.INTERNAL_SERVER_ERROR); + String errorMessage = Optional.ofNullable(request.getAttribute(ERROR_EXCEPTION)) + .map(e -> (ExceptionUtils.getMessage((Throwable) e))) + .orElse(httpStatus.getReasonPhrase()); + return new ResponseEntity<>(ThingsboardErrorResponse.of(errorMessage, statusToErrorCode(httpStatus), httpStatus), httpStatus); } @Override From 658f6679bc073304c13a53589495b2082ad16e53 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 7 Mar 2024 15:41:14 +0200 Subject: [PATCH 6/7] improved error logging --- .../service/sync/vc/DefaultGitRepositoryService.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java index 8bbc9defa2..6b6812b8ef 100644 --- a/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java +++ b/common/version-control/src/main/java/org/thingsboard/server/service/sync/vc/DefaultGitRepositoryService.java @@ -158,11 +158,9 @@ public class DefaultGitRepositoryService implements GitRepositoryService { @Override public void fetch(TenantId tenantId) throws GitAPIException { var repository = checkRepository(tenantId); - if (repository != null) { - log.debug("[{}] Fetching tenant repository.", tenantId); - repository.fetch(); - log.debug("[{}] Fetched tenant repository.", tenantId); - } + log.debug("[{}] Fetching tenant repository.", tenantId); + repository.fetch(); + log.debug("[{}] Fetched tenant repository.", tenantId); } @Override @@ -202,7 +200,7 @@ public class DefaultGitRepositoryService implements GitRepositoryService { try { return cloneRepository(tenantId, gitRepository.getSettings()); } catch (Exception e) { - throw new IllegalStateException("Repository is not initialized"); + throw new IllegalStateException("Could not initialize the repository: " + e.getMessage(), e); } } return gitRepository; From 9fca9e235fbaa2a7f992638a286c20fe7d48a8a2 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 7 Mar 2024 16:32:30 +0200 Subject: [PATCH 7/7] deleted redundant const --- .../server/exception/ThingsboardErrorResponseHandler.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java index e773e5ffd6..2e572ce8c4 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -51,7 +51,6 @@ import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.swing.text.html.Option; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -64,9 +63,6 @@ import static javax.servlet.RequestDispatcher.ERROR_EXCEPTION; @RestControllerAdvice public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler, ErrorController { - public static final String PATH_NOT_FOUND_ERROR_DESCRIPTION = "Path is not found."; - public static final String GENERAL_ERROR_DESCRIPTION = "Something went wrong!"; - private static final Map statusToErrorCodeMap = new HashMap<>(); static { statusToErrorCodeMap.put(HttpStatus.BAD_REQUEST, ThingsboardErrorCode.BAD_REQUEST_PARAMS);