diff --git a/config.md b/config.md
new file mode 100644
index 0000000..00daa29
--- /dev/null
+++ b/config.md
@@ -0,0 +1,41 @@
+# Описание конфигурации параметров проекта
+
+В micord.env заданы следующие переменные окружения:
+
+#### Конфигурация и топики Kafka ЛК РП
+- `AV_KAFKA_BOOTSTRAP_SERVERS` - список пар хост:порт, использующихся для установки первоначального соединения с кластером Kafka
+- `AV_KAFKA_SECURITY_PROTOCOL` - протокол, используемый для взаимодействия с брокерами
+- `AV_KAFKA_SASL_MECHANISM` - механизм SASL, используемый для клиентских подключений
+- `AV_KAFKA_USERNAME` - пользователь для подключения к Kafka
+- `AV_KAFKA_PASSWORD` - пароль для подключения к Kafka
+- `AV_KAFKA_GROUP_ID` - идентификатор группы потребителей, который отвечает за создание группы для объединения нескольких потребителей
+- `AV_KAFKA_TOPIC_NAME` - топик для чтения данных по загруженному в личном кабинете файлу
+- `AV_KAFKA_STATUS_TOPIC_NAME` - топик для отправки статусов проверки файла
+
+#### Конфигурация и топики Kafka ЕРВУ
+- `ERVU_KAFKA_BOOTSTRAP_SERVERS` - список пар хост:порт, использующихся для установки первоначального соединения с кластером Kafka
+- `ERVU_KAFKA_SECURITY_PROTOCOL` - протокол, используемый для взаимодействия с брокерами
+- `ERVU_KAFKA_SASL_MECHANISM` - механизм SASL, используемый для клиентских подключений
+- `ERVU_KAFKA_USERNAME` - пользователь для подключения к Kafka
+- `ERVU_KAFKA_PASSWORD` - пароль для подключения к Kafka
+- `ERVU_KAFKA_GROUP_ID` - идентификатор группы потребителей, который отвечает за создание группы для объединения нескольких потребителей
+- `ERVU_KAFKA_ERROR_TOPIC_NAME` - топик для отправки данных об ошибках проверки файла или наличии вирусов
+- `ERVU_KAFKA_SUCCESS_TOPIC_NAME` - топик для отправки данных об успешной проверке файла
+- `ERVU_KAFKA_RESPONSE_TOPIC_NAME` - топик для чтения статусов файла, полученных от ЕРВУ
+
+#### Настройки взаимодействия с файлами и антивирусом
+- `FILE_SAVING_PATH` - путь для сохранения файла на диске
+- `AV_CHECK_ENABLED` - параметр включения/отключения проверки на вирусы
+- `AV_REST_ADDRESS` - url для отправки файлов на проверку антивирусом
+- `AV_FIRST_TIMEOUT_MILLISECONDS` - таймаут ожидания окончания проверки анивирусом
+- `AV_RETRY_MAX_ATTEMPTS_COUNT` - количество попыток проверки файла
+- `AV_RETRY_DELAY_MILLISECONDS` - задержка между попытками проверки файла
+
+#### Конфигурация S3
+- `S3_ENDPOINT` - url для подключения к S3
+- `S3_ACCESS_KEY` - публичная часть пары ключей AWS
+- `S3_SECRET_KEY` - закрытая часть пары ключей AWS
+- `S3_BUCKET_NAME` - наименование бакета для сохранения проверенного файла
+- `S3_PATH_STYLE_ACCESS_ENABLED` - параметр включения/отключения стиля, при котором название бакета указывается в части пути до объекта в URI
+
+
diff --git a/pom.xml b/pom.xml
index ef0efbb..c58ab24 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,13 +7,13 @@
org.springframework.boot
spring-boot-starter-parent
- 3.3.0
+ 3.3.5
ru.micord.ervu.lkrp
file-upload
- 1.0.0-SNAPSHOT
+ 1.10.0-SNAPSHOT
@@ -42,12 +42,6 @@
4.5.14
-
- org.postgresql
- postgresql
- 42.7.3
-
-
org.projectlombok
lombok
@@ -77,11 +71,6 @@
httpmime
-
- org.postgresql
- postgresql
-
-
org.projectlombok
lombok
@@ -104,10 +93,6 @@
org.springframework.boot
spring-boot-starter-web
-
- org.springframework.boot
- spring-boot-starter-jooq
-
org.springframework.boot
spring-boot-starter-actuator
diff --git a/src/main/java/ru/micord/ervu/av/FileUploadApplication.java b/src/main/java/ru/micord/ervu/av/FileUploadApplication.java
index 0fbd59a..ae6ac96 100644
--- a/src/main/java/ru/micord/ervu/av/FileUploadApplication.java
+++ b/src/main/java/ru/micord/ervu/av/FileUploadApplication.java
@@ -4,13 +4,11 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.retry.annotation.EnableRetry;
-import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @author r.latypov
*/
@EnableRetry
-@EnableTransactionManagement
@SpringBootApplication(exclude = KafkaAutoConfiguration.class)
public class FileUploadApplication {
diff --git a/src/main/java/ru/micord/ervu/av/exception/RetryableException.java b/src/main/java/ru/micord/ervu/av/exception/RetryableException.java
index 3cc7f82..fb162da 100644
--- a/src/main/java/ru/micord/ervu/av/exception/RetryableException.java
+++ b/src/main/java/ru/micord/ervu/av/exception/RetryableException.java
@@ -4,4 +4,8 @@ package ru.micord.ervu.av.exception;
* @author r.latypov
*/
public class RetryableException extends RuntimeException {
+
+ public RetryableException(String message) {
+ super(message);
+ }
}
diff --git a/src/main/java/ru/micord/ervu/av/kafka/dto/FileStatus.java b/src/main/java/ru/micord/ervu/av/kafka/dto/FileStatus.java
index 95729f0..020c879 100644
--- a/src/main/java/ru/micord/ervu/av/kafka/dto/FileStatus.java
+++ b/src/main/java/ru/micord/ervu/av/kafka/dto/FileStatus.java
@@ -16,4 +16,7 @@ public record FileStatus(String code, String status, String description) {
public static final FileStatus FILE_STATUS_04 = new FileStatus("04", "Получен ЕРВУ",
"Файл был принят в обработку"
);
+ public static final FileStatus FILE_STATUS_11 = new FileStatus("11", "Невозможно проверить файл ЛК РП",
+ "Невозможно проверить файл по причине недоступности или ошибки в работе антивируса"
+ );
}
diff --git a/src/main/java/ru/micord/ervu/av/service/FileManager.java b/src/main/java/ru/micord/ervu/av/service/FileManager.java
new file mode 100644
index 0000000..4b52afe
--- /dev/null
+++ b/src/main/java/ru/micord/ervu/av/service/FileManager.java
@@ -0,0 +1,86 @@
+package ru.micord.ervu.av.service;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.conn.HttpHostConnectException;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.springframework.http.HttpStatus;
+import org.springframework.retry.annotation.Backoff;
+import org.springframework.retry.annotation.Retryable;
+import org.springframework.stereotype.Component;
+import ru.micord.ervu.av.exception.FileUploadException;
+import ru.micord.ervu.av.exception.InvalidHttpFileUrlException;
+import ru.micord.ervu.av.exception.RetryableException;
+
+/**
+ * @author gulnaz
+ */
+@Component
+public class FileManager {
+
+ @Retryable(retryFor = {InvalidHttpFileUrlException.class},
+ maxAttemptsExpression = "${av.retry.max.attempts.count}",
+ backoff = @Backoff(delayExpression = "${av.retry.delay.milliseconds}"))
+ public void downloadFile(String fileUrl, Path filePath)
+ throws InvalidHttpFileUrlException, FileUploadException {
+ File file = filePath.toFile();
+ HttpGet request = new HttpGet(fileUrl);
+
+ try (CloseableHttpClient client = HttpClients.createDefault();
+ CloseableHttpResponse response = client.execute(request)) {
+
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode == HttpStatus.OK.value()) {
+ HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ try (FileOutputStream outputStream = new FileOutputStream(file)) {
+ entity.writeTo(outputStream);
+ }
+ }
+ }
+ else {
+ // в хранилище не обнаружено файла; сообщение некорректно
+ String message = "http status code " + statusCode + " : " + fileUrl;
+ throw new InvalidHttpFileUrlException(message);
+ }
+ }
+ catch (HttpHostConnectException e) {
+ throw new FileUploadException(e);
+ }
+ catch (IOException e) {
+ // хранилище недоступно; сообщение некорректно
+ String message =
+ (e.getMessage() == null ? e.getCause().getMessage() : e.getMessage()) + " : " + fileUrl;
+ throw new InvalidHttpFileUrlException(message, e);
+ }
+ }
+
+ @Retryable(retryFor = {RetryableException.class},
+ maxAttemptsExpression = "${av.retry.max.attempts.count}",
+ backoff = @Backoff(delayExpression = "${av.retry.delay.milliseconds}"))
+ public void deleteFile(String fileUrl) throws FileUploadException {
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpDelete delete = new HttpDelete(fileUrl);
+
+ try (CloseableHttpResponse response = client.execute(delete)) {
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (statusCode != HttpStatus.NO_CONTENT.value()) {
+ String message = "http status code " + statusCode + " : " + fileUrl;
+ throw new RetryableException(message);
+ }
+ }
+ }
+ catch (IOException e) {
+ // непредусмотренная ошибка доступа через http-клиент
+ throw new FileUploadException(e);
+ }
+ }
+}
diff --git a/src/main/java/ru/micord/ervu/av/service/FileUploadService.java b/src/main/java/ru/micord/ervu/av/service/FileUploadService.java
index 87457b3..b4f44a7 100644
--- a/src/main/java/ru/micord/ervu/av/service/FileUploadService.java
+++ b/src/main/java/ru/micord/ervu/av/service/FileUploadService.java
@@ -1,7 +1,5 @@
package ru.micord.ervu.av.service;
-import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -9,22 +7,9 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import com.google.gson.JsonSyntaxException;
-import org.apache.http.HttpEntity;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpDelete;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.conn.HttpHostConnectException;
-import org.apache.http.entity.mime.MultipartEntityBuilder;
-import org.apache.http.entity.mime.content.FileBody;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClients;
-import org.apache.http.util.EntityUtils;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.slf4j.Logger;
@@ -32,7 +17,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpStatus;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.Acknowledgment;
@@ -42,10 +26,10 @@ import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;
import ru.micord.ervu.av.exception.FileUploadException;
import ru.micord.ervu.av.exception.InvalidHttpFileUrlException;
+import ru.micord.ervu.av.exception.RetryableException;
import ru.micord.ervu.av.kafka.dto.DownloadRequest;
import ru.micord.ervu.av.kafka.dto.DownloadResponse;
import ru.micord.ervu.av.kafka.dto.FileStatus;
-import ru.micord.ervu.av.response.AvFileSendResponse;
import ru.micord.ervu.av.response.AvResponse;
import ru.micord.ervu.av.s3.S3Service;
@@ -57,10 +41,6 @@ public class FileUploadService {
private static final Logger logger = LoggerFactory.getLogger(FileUploadService.class);
@Value("${av.check.enabled}")
private boolean avCheckEnabled;
- @Value("${av.rest.address}")
- private String avRestAddress;
- @Value("${av.first.timeout.milliseconds}")
- private Long avFirstTimeoutMilliseconds;
@Value("${file.saving.path}")
private String fileSavingPath;
private final KafkaTemplate kafkaTemplate;
@@ -68,14 +48,18 @@ public class FileUploadService {
private final NewTopic outErrorTopic;
private final NewTopic outSuccessTopic;
private final NewTopic inStatusTopic;
+ private final FileManager fIleManager;
private final ReceiveScanReportRetryable receiveScanReportRetryable;
private final S3Service s3Service;
@Autowired
- public FileUploadService(@Qualifier("outputKafkaTemplate") KafkaTemplate kafkaTemplate,
+ public FileUploadService(
+ @Qualifier("outputKafkaTemplate") KafkaTemplate kafkaTemplate,
@Qualifier("inputKafkaTemplate") KafkaTemplate inKafkaTemplate,
- NewTopic outErrorTopic, NewTopic outSuccessTopic, ReceiveScanReportRetryable receiveScanReportRetryable,
- S3Service s3Service, NewTopic inStatusTopic) {
+ NewTopic outErrorTopic, NewTopic outSuccessTopic,
+ ReceiveScanReportRetryable receiveScanReportRetryable,
+ S3Service s3Service, NewTopic inStatusTopic,
+ FileManager fIleManager) {
this.kafkaTemplate = kafkaTemplate;
this.outErrorTopic = outErrorTopic;
this.outSuccessTopic = outSuccessTopic;
@@ -83,6 +67,7 @@ public class FileUploadService {
this.s3Service = s3Service;
this.inKafkaTemplate = inKafkaTemplate;
this.inStatusTopic = inStatusTopic;
+ this.fIleManager = fIleManager;
}
@KafkaListener(id = "${spring.kafka.consumer.group.id}", topics = "${kafka.in.topic.name}",
@@ -103,23 +88,33 @@ public class FileUploadService {
logger.info("working in {}", System.getProperty("user.home"));
Path filePath = Paths.get(fileSavingPath, fileUrl.fileName());
String downloadUrl = fileUrl.fileUrl();
- downloadFile(downloadUrl, filePath);
+ fIleManager.downloadFile(downloadUrl, filePath);
+ boolean isAvError = false;
boolean clean = true;
boolean infected = false;
if (avCheckEnabled) {
- AvResponse avResponse = checkFile(filePath);
-
- clean = Arrays.stream(avResponse.verdicts())
- .anyMatch(verdict -> verdict.equalsIgnoreCase(AvResponse.Scan.VERDICT_CLEAN));
- infected = Arrays.stream(avResponse.verdicts())
- .anyMatch(verdict -> verdict.equalsIgnoreCase(AvResponse.Scan.VERDICT_INFECTED));
+ try {
+ AvResponse avResponse = receiveScanReportRetryable.checkFile(filePath);
+ clean = Arrays.stream(avResponse.verdicts())
+ .anyMatch(verdict -> verdict.equalsIgnoreCase(AvResponse.Scan.VERDICT_CLEAN));
+ infected = Arrays.stream(avResponse.verdicts())
+ .anyMatch(verdict -> verdict.equalsIgnoreCase(AvResponse.Scan.VERDICT_INFECTED));
+ }
+ catch (FileUploadException | RetryableException e) {
+ logger.error(e.getMessage(), e);
+ isAvError = true;
+ }
}
- if (infected || !clean) {
+ if (isAvError || infected || !clean) {
downloadRequest.fileInfo().setFileUrl(null);
- downloadRequest.fileInfo().setFileStatus(FileStatus.FILE_STATUS_02);
- sendMessage(outErrorTopic.name(), downloadRequest, messageId, kafkaTemplate);
+ FileStatus fileStatus = isAvError ? FileStatus.FILE_STATUS_11 : FileStatus.FILE_STATUS_02;
+ downloadRequest.fileInfo().setFileStatus(fileStatus);
+
+ if (!isAvError) {
+ sendMessage(outErrorTopic.name(), downloadRequest, messageId, kafkaTemplate);
+ }
}
else {
String fileRef = s3Service.putFile(filePath, fileUrl.fileName());
@@ -130,7 +125,7 @@ public class FileUploadService {
}
sendMessage(inStatusTopic.name(), downloadRequest, messageId, inKafkaTemplate);
- deleteFile(downloadUrl);
+ fIleManager.deleteFile(downloadUrl);
try {
Files.delete(filePath);
@@ -144,10 +139,12 @@ public class FileUploadService {
}
catch (InvalidHttpFileUrlException e) {
// считаем, что повторная обработка сообщения не нужна
- // ошибку логируем, сообщаем об ошибке, помечаем прочтение сообщения
+ // ошибку логируем, отправляем сообщение с новым статусом, помечаем прочтение сообщения
logger.error(e.getMessage() + ": " + kafkaInMessage);
+ downloadRequest.fileInfo().setFileUrl(null);
+ downloadRequest.fileInfo().setFileStatus(FileStatus.FILE_STATUS_11);
+ sendMessage(inStatusTopic.name(), downloadRequest, messageId, inKafkaTemplate);
acknowledgment.acknowledge();
- throw new RuntimeException(kafkaInMessage, e);
}
catch (FileUploadException e) {
// считаем, что нужно повторное считывание сообщения
@@ -187,120 +184,6 @@ public class FileUploadService {
}
}
- private void downloadFile(String fileUrl, Path filePath)
- throws InvalidHttpFileUrlException, FileUploadException {
- File file = filePath.toFile();
- HttpGet request = new HttpGet(fileUrl);
-
- try (CloseableHttpClient client = HttpClients.createDefault();
- CloseableHttpResponse response = client.execute(request)) {
-
- int statusCode = response.getStatusLine().getStatusCode();
- if (statusCode == HttpStatus.OK.value()) {
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- try (FileOutputStream outputStream = new FileOutputStream(file)) {
- entity.writeTo(outputStream);
- }
- }
- }
- else {
- // в хранилище не обнаружено файла; сообщение некорректно
- String message = "http status code " + statusCode + " : " + fileUrl;
- throw new InvalidHttpFileUrlException(message);
- }
- }
- catch (HttpHostConnectException e) {
- throw new FileUploadException(e);
- }
- catch (IOException e) {
- // хранилище недоступно; сообщение некорректно
- String message =
- (e.getMessage() == null ? e.getCause().getMessage() : e.getMessage()) + " : " + fileUrl;
- throw new InvalidHttpFileUrlException(message, e);
- }
- }
-
- private AvResponse checkFile(Path filePath) throws FileUploadException {
- File file = filePath.toFile();
-
- try (CloseableHttpClient client = HttpClients.createDefault()) {
- HttpPost post = new HttpPost(avRestAddress);
- HttpEntity entity = MultipartEntityBuilder.create()
- .addPart("file", new FileBody(file))
- .build();
- post.setEntity(entity);
-
- try (CloseableHttpResponse postResponse = client.execute(post)) {
- int postStatusCode = postResponse.getStatusLine().getStatusCode();
- String postResponseJson = EntityUtils.toString(postResponse.getEntity());
-
- AvFileSendResponse avFileSendResponse;
- try {
- avFileSendResponse = new Gson().fromJson(postResponseJson, AvFileSendResponse.class);
- }
- catch (JsonSyntaxException e) {
- throw new FileUploadException("error json: " + postResponseJson, e);
- }
-
- if (postStatusCode != HttpStatus.CREATED.value()) {
- StringBuilder stringBuilder = new StringBuilder(
- "http status code " + postStatusCode + " for " + avRestAddress + " post request.");
-
- String status = avFileSendResponse.status();
- if (status != null) {
- stringBuilder.append(" Status: ").append(status).append(".");
- }
- if (avFileSendResponse.error() != null) {
- stringBuilder.append(" Error code: ")
- .append(avFileSendResponse.error().code())
- .append(". Error message: ")
- .append(avFileSendResponse.error().message())
- .append(". ");
- }
- throw new FileUploadException(stringBuilder.toString());
- }
-
- String id = avFileSendResponse.id();
- String reportRequestUri = avRestAddress + "/" + id;
- HttpGet get = new HttpGet(reportRequestUri);
-
- // waiting for timeout time before first request
- try {
- TimeUnit.MILLISECONDS.sleep(
- avFirstTimeoutMilliseconds == null ? 1000L : avFirstTimeoutMilliseconds);
- }
- catch (InterruptedException e) {
- throw new FileUploadException(e);
- }
-
- return receiveScanReportRetryable.receiveScanReport(client, get);
- }
- }
- catch (IOException e) {
- // непредусмотренная ошибка доступа через http-клиент
- throw new FileUploadException(e);
- }
- }
-
- private void deleteFile(String fileUrl) throws FileUploadException {
- try (CloseableHttpClient client = HttpClients.createDefault()) {
- HttpDelete delete = new HttpDelete(fileUrl);
-
- try (CloseableHttpResponse response = client.execute(delete)) {
- int statusCode = response.getStatusLine().getStatusCode();
- if (statusCode != HttpStatus.NO_CONTENT.value()) {
- String message = "http status code " + statusCode + " : " + fileUrl;
- throw new RuntimeException(message);
- }
- }
- }
- catch (IOException e) {
- // непредусмотренная ошибка доступа через http-клиент
- throw new FileUploadException(e);
- }
- }
-
private void sendMessage(@NonNull String topicName, Object object, String messageId,
KafkaTemplate template) {
ProducerRecord record = new ProducerRecord<>(topicName,
diff --git a/src/main/java/ru/micord/ervu/av/service/ReceiveScanReportRetryable.java b/src/main/java/ru/micord/ervu/av/service/ReceiveScanReportRetryable.java
index 77d3838..1b4fbc4 100644
--- a/src/main/java/ru/micord/ervu/av/service/ReceiveScanReportRetryable.java
+++ b/src/main/java/ru/micord/ervu/av/service/ReceiveScanReportRetryable.java
@@ -1,19 +1,30 @@
package ru.micord.ervu.av.service;
+import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
+import java.util.concurrent.TimeUnit;
import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import ru.micord.ervu.av.exception.FileUploadException;
import ru.micord.ervu.av.exception.RetryableException;
+import ru.micord.ervu.av.response.AvFileSendResponse;
import ru.micord.ervu.av.response.AvResponse;
/**
@@ -21,19 +32,92 @@ import ru.micord.ervu.av.response.AvResponse;
*/
@Service
public class ReceiveScanReportRetryable {
+
+ @Value("${av.rest.address}")
+ private String avRestAddress;
+ @Value("${av.first.timeout.milliseconds:1000}")
+ private Long avFirstTimeoutMilliseconds;
+
@Retryable(retryFor = {RetryableException.class},
maxAttemptsExpression = "${av.retry.max.attempts.count}",
backoff = @Backoff(delayExpression = "${av.retry.delay.milliseconds}"))
- public AvResponse receiveScanReport(CloseableHttpClient client, HttpGet get)
- throws FileUploadException {
+ public AvResponse checkFile(Path filePath) throws RetryableException, FileUploadException {
+ File file = filePath.toFile();
+
+ try (CloseableHttpClient client = HttpClients.createDefault()) {
+ HttpPost post = new HttpPost(avRestAddress);
+ HttpEntity entity = MultipartEntityBuilder.create()
+ .addPart("file", new FileBody(file))
+ .build();
+ post.setEntity(entity);
+
+ try (CloseableHttpResponse postResponse = client.execute(post)) {
+ int postStatusCode = postResponse.getStatusLine().getStatusCode();
+ String postResponseJson = EntityUtils.toString(postResponse.getEntity());
+ AvFileSendResponse avFileSendResponse;
+
+ try {
+ avFileSendResponse = new Gson().fromJson(postResponseJson, AvFileSendResponse.class);
+ }
+ catch (JsonSyntaxException e) {
+ throw new FileUploadException("error json: " + postResponseJson, e);
+ }
+
+ if (postStatusCode != HttpStatus.CREATED.value()) {
+ StringBuilder stringBuilder = new StringBuilder(
+ "http status code " + postStatusCode + " for " + avRestAddress + " post request.");
+ String status = avFileSendResponse.status();
+
+ if (status != null) {
+ stringBuilder.append(" Status: ").append(status).append(".");
+ }
+
+ if (avFileSendResponse.error() != null) {
+ stringBuilder.append(" Error code: ")
+ .append(avFileSendResponse.error().code())
+ .append(". Error message: ")
+ .append(avFileSendResponse.error().message())
+ .append(". ");
+ }
+ throw new FileUploadException(stringBuilder.toString());
+ }
+
+ String id = avFileSendResponse.id();
+ String reportRequestUri = avRestAddress + "/" + id;
+ HttpGet get = new HttpGet(reportRequestUri);
+
+ // waiting for timeout time before first request
+ try {
+ TimeUnit.MILLISECONDS.sleep(avFirstTimeoutMilliseconds);
+ }
+ catch (InterruptedException e) {
+ throw new FileUploadException(e);
+ }
+
+ return receiveScanReport(client, get);
+ }
+ }
+ catch (ClientProtocolException e) {
+ throw new RetryableException("Failed to check file");
+ }
+ catch (IOException e) {
+ // непредусмотренная ошибка доступа через http-клиент
+ throw new FileUploadException(e);
+ }
+ }
+
+ private AvResponse receiveScanReport(CloseableHttpClient client, HttpGet get)
+ throws RetryableException, FileUploadException {
try (CloseableHttpResponse getResponse = client.execute(get)) {
int getStatusCode = getResponse.getStatusLine().getStatusCode();
+
if (getStatusCode == HttpStatus.OK.value()) {
String getResponseJson = EntityUtils.toString(getResponse.getEntity());
AvResponse avResponse = new Gson().fromJson(getResponseJson, AvResponse.class);
+
if (avResponse.completed() == null) {
- throw new RetryableException();
+ throw new RetryableException("Failed to complete file scan");
}
return avResponse;
}
@@ -44,7 +128,7 @@ public class ReceiveScanReportRetryable {
}
catch (ClientProtocolException e) {
// непредусмотренная ошибка доступа через http-клиент
- throw new RuntimeException(e);
+ throw new RetryableException("Failed to receive scan report");
}
catch (IOException e) {
// непредусмотренная ошибка доступа через http-клиент
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index d2d72f8..fa9859a 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -63,10 +63,6 @@ s3.out.secret_key=${S3_SECRET_KEY}
s3.out.bucket_name=${S3_OUT_BUCKET_NAME}
s3.out.path.style.access.enabled=${S3_OUT_PATH_STYLE_ACCESS_ENABLED}
#
-# spring jooq dsl bean properties begin ->
-spring.jooq.sql-dialect=Postgres
-spring.datasource.driver-class-name=org.postgresql.Driver
-# spring jooq dsl bean properties <- end
# endpoints management
management.endpoints.web.exposure.include = health, info, metrics