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 4e40b2c384..ada5cea2e9 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponse.java @@ -19,8 +19,6 @@ import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.http.HttpStatus; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; -import java.util.Date; - @Schema public class ThingsboardErrorResponse { // HTTP Response Status Code @@ -32,13 +30,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) { @@ -74,7 +72,7 @@ public class ThingsboardErrorResponse { } @Schema(description = "Timestamp", accessMode = Schema.AccessMode.READ_ONLY) - public Date getTimestamp() { + public long getTimestamp() { return timestamp; } } 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 14cfbcea39..d097beb4c2 100644 --- a/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java +++ b/application/src/main/java/org/thingsboard/server/exception/ThingsboardErrorResponseHandler.java @@ -15,7 +15,13 @@ */ package org.thingsboard.server.exception; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; 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; import org.springframework.http.HttpStatusCode; @@ -29,7 +35,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; @@ -44,18 +52,18 @@ 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 jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; @Slf4j +@Controller @RestControllerAdvice -public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler { +public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHandler implements AccessDeniedHandler, ErrorController { private static final Map statusToErrorCodeMap = new HashMap<>(); + static { statusToErrorCodeMap.put(HttpStatus.BAD_REQUEST, ThingsboardErrorCode.BAD_REQUEST_PARAMS); statusToErrorCodeMap.put(HttpStatus.UNAUTHORIZED, ThingsboardErrorCode.AUTHENTICATION); @@ -68,7 +76,9 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand statusToErrorCodeMap.put(HttpStatus.INTERNAL_SERVER_ERROR, ThingsboardErrorCode.GENERAL); statusToErrorCodeMap.put(HttpStatus.SERVICE_UNAVAILABLE, ThingsboardErrorCode.GENERAL); } + private static final Map errorCodeToStatusMap = new HashMap<>(); + static { errorCodeToStatusMap.put(ThingsboardErrorCode.GENERAL, HttpStatus.INTERNAL_SERVER_ERROR); errorCodeToStatusMap.put(ThingsboardErrorCode.AUTHENTICATION, HttpStatus.UNAUTHORIZED); @@ -91,6 +101,17 @@ public class ThingsboardErrorResponseHandler extends ResponseEntityExceptionHand return errorCodeToStatusMap.getOrDefault(errorCode, HttpStatus.INTERNAL_SERVER_ERROR); } + @RequestMapping("/error") + public ResponseEntity handleError(HttpServletRequest request) { + 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(RequestDispatcher.ERROR_EXCEPTION)) + .map(e -> (ExceptionUtils.getMessage((Throwable) e))) + .orElse(httpStatus.getReasonPhrase()); + return new ResponseEntity<>(ThingsboardErrorResponse.of(errorMessage, statusToErrorCode(httpStatus), httpStatus), httpStatus); + } + @Override @ExceptionHandler(AccessDeniedException.class) public void handle(HttpServletRequest request, HttpServletResponse response, 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 61ff339fa8..46f4f83a04 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,12 +157,10 @@ public class DefaultGitRepositoryService implements GitRepositoryService { @Override public void fetch(TenantId tenantId) throws GitAPIException { - var repository = repositories.get(tenantId); - if (repository != null) { - log.debug("[{}] Fetching tenant repository.", tenantId); - repository.fetch(); - log.debug("[{}] Fetched tenant repository.", tenantId); - } + var repository = checkRepository(tenantId); + log.debug("[{}] Fetching tenant repository.", tenantId); + repository.fetch(); + log.debug("[{}] Fetched tenant repository.", tenantId); } @Override @@ -195,8 +193,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("Could not initialize the repository: " + e.getMessage(), e); + } + } + return gitRepository; } @Override @@ -229,17 +236,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 +273,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; + } }