SUPPORT-8529: Добавлены исходники config-data-executor

This commit is contained in:
malkov34 2024-09-05 13:33:02 +03:00
parent f5854c7592
commit 0aae3f43bb
22 changed files with 995 additions and 0 deletions

38
сonfig-data-executor/.gitignore vendored Normal file
View file

@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View file

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.micord</groupId>
<artifactId>config-data-executor</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-bom</artifactId>
<version>1.12.770</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>3.19.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<version>1.18.34</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.arangodb</groupId>
<artifactId>arangodb-java-driver</artifactId>
<version>7.7.1</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
</dependency>
<dependency>
<groupId>com.atomikos</groupId>
<artifactId>transactions-jta</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>maven_central</id>
<name>Maven Central</name>
<url>https://repo.maven.apache.org/maven2/</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>org.micord.Main</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,15 @@
package org.micord;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
/**
* @author Maksim Tereshin
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}

View file

@ -0,0 +1,34 @@
package org.micord.config;
import com.arangodb.ArangoDB;
import com.arangodb.ArangoDBException;
import com.arangodb.ArangoDatabase;
import org.micord.models.AqlConnectionParams;
/**
* @author Maksim Tereshin
*/
public class ArangoDBConnection {
public static ArangoDatabase getConnection(AqlConnectionParams params) {
try {
ArangoDB arangoDB = new ArangoDB.Builder()
.host(params.getHost(), params.getPort())
.user(params.getUsername())
.password(params.getPassword())
.build();
ArangoDatabase db = arangoDB.db(params.getDatabase());
if (!db.exists()) {
throw new ArangoDBException("Database does not exist: " + params.getDatabase());
}
return db;
} catch (ArangoDBException e) {
throw new RuntimeException("Failed to connect to ArangoDB", e);
}
}
}

View file

@ -0,0 +1,27 @@
package org.micord.config;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @author Maksim Tereshin
*/
@Configuration
@EnableTransactionManagement
public class AtomikosConfig {
@Bean(initMethod = "init", destroyMethod = "close")
public UserTransactionManager atomikosTransactionManager() {
return new UserTransactionManager();
}
@Bean
public UserTransactionImp atomikosUserTransaction() {
return new UserTransactionImp();
}
}

View file

@ -0,0 +1,24 @@
package org.micord.config;
import org.micord.models.SqlConnectionParams;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @author Maksim Tereshin
*/
public class DatabaseConnection {
public static Connection getConnection(SqlConnectionParams params) throws SQLException {
try {
Class.forName(params.getJdbcDriverClassName());
} catch (ClassNotFoundException e) {
throw new SQLException("Unable to load the JDBC driver class", e);
}
return DriverManager.getConnection(params.getJdbcUrl(), params.getJdbcUsername(), params.getJdbcPassword());
}
}

View file

@ -0,0 +1,19 @@
package org.micord.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.http.HttpClient;
/**
* @author Maksim Tereshin
*/
@Configuration
public class HttpClientConfig {
@Bean
public HttpClient httpClient() {
return HttpClient.newHttpClient();
}
}

View file

@ -0,0 +1,62 @@
package org.micord.config;
import org.micord.models.S3ConnectionParams;
import org.micord.models.S3Request;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.HttpRequest;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
/**
* @author Maksim Tereshin
*/
public class S3HttpConnection {
public static HttpRequest buildHttpRequest(S3Request request, String file) throws Exception {
S3ConnectionParams connectionParams = request.getS3ConnectionParams();
String host = connectionParams.getHost() + ":" + connectionParams.getPort();
String s3Key = connectionParams.getS3Key();
String s3Secret = connectionParams.getS3Secret();
// The resource to be deleted, typically a file in the S3 bucket
String resource = "/" + file;
String contentType = "application/octet-stream";
String date = ZonedDateTime.now().format(DateTimeFormatter.RFC_1123_DATE_TIME);
// Generate a signature for the DELETE request
String signature = generateSignature("DELETE", contentType, date, resource, s3Secret);
// Build and return the HTTP DELETE request
return HttpRequest.newBuilder()
.uri(URI.create("https://" + host + resource))
.header("Host", host)
.header("Date", date)
.header("Content-Type", contentType)
.header("Authorization", "AWS " + s3Key + ":" + signature)
.DELETE()
.build();
}
// Utility method to generate a signature for the S3-compatible request
private static String generateSignature(String method, String contentType, String date, String resource, String s3Secret) throws Exception {
String stringToSign = method + "\n" +
"\n" + // MD5 - not used for DELETE requests
contentType + "\n" +
date + "\n" +
resource;
// HMAC-SHA1 signature generation
Mac mac = Mac.getInstance("HmacSHA1");
SecretKeySpec secretKey = new SecretKeySpec(s3Secret.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
mac.init(secretKey);
byte[] hash = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
}
}

View file

@ -0,0 +1,47 @@
package org.micord.controller;
import org.micord.exceptions.FileNotModifiedException;
import org.micord.service.ApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.FileNotFoundException;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* REST Controller for API operations.
*/
@RestController
@RequestMapping("/api")
public class ApiController {
@Autowired
private ApiService apiService;
@PostMapping("/block")
public CompletableFuture<ResponseEntity<?>> block(@RequestBody List<String> ids) throws FileNotFoundException, FileNotModifiedException {
return apiService.process("block", ids)
.thenApply(ResponseEntity::ok);
}
@PostMapping("/unblock")
public CompletableFuture<ResponseEntity<?>> unblock(@RequestBody List<String> ids) throws FileNotFoundException, FileNotModifiedException {
return apiService.process("unblock", ids)
.thenApply(ResponseEntity::ok);
}
@PostMapping("/removeFromSystem")
public CompletableFuture<ResponseEntity<?>> removeFromSystem(@RequestBody List<String> ids) throws FileNotFoundException, FileNotModifiedException {
return apiService.process("removeFromSystem", ids)
.thenApply(ResponseEntity::ok);
}
@PostMapping("/removeFromCallList")
public CompletableFuture<ResponseEntity<?>> removeFromCallList(@RequestBody List<String> ids) throws FileNotFoundException, FileNotModifiedException {
return apiService.process("removeFromCallList", ids)
.thenApply(ResponseEntity::ok);
}
}

View file

@ -0,0 +1,10 @@
package org.micord.exceptions;
/**
* @author Maksim Tereshin
*/
public class FileNotModifiedException extends Exception {
public FileNotModifiedException(String message) {
super(message);
}
}

View file

@ -0,0 +1,50 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
/**
* @author Maksim Tereshin
*/
@Setter
public class AqlConnectionParams {
private String host;
private int port;
private String username;
private String password;
private String database;
private String collection;
@XmlElement(name = "Host")
public String getHost() {
return host;
}
@XmlElement(name = "Port")
public int getPort() {
return port;
}
@XmlElement(name = "Username")
public String getUsername() {
return username;
}
@XmlElement(name = "Password")
public String getPassword() {
return password;
}
@XmlElement(name = "Database")
public String getDatabase() {
return database;
}
@XmlElement(name = "Collection")
public String getCollection() {
return collection;
}
}

View file

@ -0,0 +1,20 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
/**
* @author Maksim Tereshin
*/
@Setter
public class AqlRequest extends Request {
private AqlConnectionParams aqlConnectionParams;
@XmlElement(name = "AqlConnectionParams")
public AqlConnectionParams getAqlConnectionParams() {
return aqlConnectionParams;
}
}

View file

@ -0,0 +1,29 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import java.util.List;
/**
* @author Maksim Tereshin
*/
@Setter
@XmlSeeAlso({SqlRequest.class, S3Request.class})
public abstract class Request {
private List<RequestArgument> requestArguments;
private String requestURL;
@XmlElement(name = "RequestArgument")
public List<RequestArgument> getRequestArguments() {
return requestArguments;
}
@XmlElement(name = "RequestURL")
public String getRequestURL() {
return requestURL;
}
}

View file

@ -0,0 +1,34 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author Maksim Tereshin
*/
@Setter
@XmlRootElement(name = "RequestArgument")
public class RequestArgument {
private String id;
private String requestURL;
private SqlConnectionParams sqlConnectionParams;
@XmlElement(name = "SqlConnectionParams")
public SqlConnectionParams getSqlConnectionParams() {
return sqlConnectionParams;
}
@XmlElement(name = "Id")
public String getId() {
return id;
}
@XmlElement(name = "RequestURL")
public String getRequestURL() {
return requestURL;
}
}

View file

@ -0,0 +1,35 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;
/**
* @author Maksim Tereshin
*/
@Setter
@XmlRootElement(name = "Requests")
public class Requests {
private List<SqlRequest> sqlRequests;
private List<AqlRequest> aqlRequests;
private List<S3Request> s3Requests;
@XmlElement(name = "SqlRequest")
public List<SqlRequest> getSqlRequests() {
return sqlRequests;
}
@XmlElement(name = "AqlRequest")
public List<AqlRequest> getAqlRequests() {
return aqlRequests;
}
@XmlElement(name = "S3Request")
public List<S3Request> getS3Requests() {
return s3Requests;
}
}

View file

@ -0,0 +1,38 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
/**
* @author Maksim Tereshin
*/
@Setter
public class S3ConnectionParams {
private String s3Key;
private String s3Secret;
private String host;
private String port;
@XmlElement(name = "S3Key")
public String getS3Key() {
return s3Key;
}
@XmlElement(name = "S3Secret")
public String getS3Secret() {
return s3Secret;
}
@XmlElement(name = "Host")
public String getHost() {
return host;
}
@XmlElement(name = "Port")
public String getPort() {
return port;
}
}

View file

@ -0,0 +1,20 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
/**
* @author Maksim Tereshin
*/
@Setter
public class S3Request extends Request {
private S3ConnectionParams s3ConnectionParams;
@XmlElement(name = "S3ConnectionParams")
public S3ConnectionParams getS3ConnectionParams() {
return s3ConnectionParams;
}
}

View file

@ -0,0 +1,41 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
@Setter
public class SqlConnectionParams {
private String jdbcUrl;
private String jdbcUsername;
private String jdbcPassword;
private String jdbcDriverClassName;
private String jdbcDatabase;
@XmlElement(name = "JdbcUrl")
public String getJdbcUrl() {
return jdbcUrl;
}
@XmlElement(name = "JdbcUsername")
public String getJdbcUsername() {
return jdbcUsername;
}
@XmlElement(name = "JdbcPassword")
public String getJdbcPassword() {
return jdbcPassword;
}
@XmlElement(name = "JdbcDriverClassName")
public String getJdbcDriverClassName() {
return jdbcDriverClassName;
}
@XmlElement(name = "JdbcDatabase")
public String getJdbcDatabase() {
return jdbcDatabase;
}
}

View file

@ -0,0 +1,20 @@
package org.micord.models;
import lombok.Setter;
import javax.xml.bind.annotation.XmlElement;
/**
* @author Maksim Tereshin
*/
@Setter
public class SqlRequest extends Request {
private SqlConnectionParams sqlConnectionParams;
@XmlElement(name = "SqlConnectionParams")
public SqlConnectionParams getSqlConnectionParams() {
return sqlConnectionParams;
}
}

View file

@ -0,0 +1,229 @@
package org.micord.service;
import com.arangodb.ArangoCursor;
import com.arangodb.ArangoDatabase;
import com.atomikos.icatch.jta.UserTransactionImp;
import org.micord.config.ArangoDBConnection;
import org.micord.config.DatabaseConnection;
import org.micord.config.S3HttpConnection;
import org.micord.exceptions.FileNotModifiedException;
import org.micord.models.*;
import org.micord.utils.ConfigLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.FileNotFoundException;
import java.net.HttpURLConnection;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@Service
public class ApiService {
private static final Logger logger = LoggerFactory.getLogger(ApiService.class);
@Autowired
private ConfigLoader configLoader;
@Autowired
private UserTransactionImp userTransaction;
@Autowired
private HttpClient httpClient;
public CompletableFuture<String> process(String methodName, List<String> ids) throws FileNotFoundException, FileNotModifiedException {
Optional<Requests> optionalConfig = configLoader.loadConfigIfModified(methodName);
if (optionalConfig.isEmpty()) {
throw new FileNotFoundException("Configuration for method " + methodName + " could not be loaded.");
}
Requests config = optionalConfig.get();
processRequests(config, ids);
return CompletableFuture.completedFuture("Processing complete");
}
public void processRequests(Requests config, List<String> ids) {
try {
userTransaction.begin();
// Process SQL and AQL requests
processSqlAndAqlRequests(config, ids);
userTransaction.commit();
logger.info("Successfully processed requests for all IDs.");
} catch (Exception e) {
try {
userTransaction.rollback();
logger.error("Transaction rolled back due to failure.", e);
} catch (Exception rollbackException) {
logger.error("Failed to rollback the transaction", rollbackException);
}
logger.error("Failed to process requests", e);
}
// Process S3 requests
processS3Requests(config.getS3Requests(), ids);
}
private void processSqlAndAqlRequests(Requests config, List<String> ids) {
for (SqlRequest request : config.getSqlRequests()) {
processSqlRequests(request, ids);
}
for (AqlRequest request : config.getAqlRequests()) {
processAqlRequests(request, ids);
}
}
private void processSqlRequests(SqlRequest request, List<String> ids) {
try (Connection connection = DatabaseConnection.getConnection(request.getSqlConnectionParams())) {
String query = buildSqlQuery(request, String.join(",", ids));
executeSqlQuery(connection, query);
} catch (SQLException e) {
logger.error("SQL query execution failed", e);
}
}
private String buildSqlQuery(SqlRequest request, String ids) {
StringBuilder clauseBuilder = new StringBuilder("id IN (" + ids + ")");
if (request.getRequestArguments() != null && !request.getRequestArguments().isEmpty()) {
for (RequestArgument argument : request.getRequestArguments()) {
try (Connection connection = DatabaseConnection.getConnection(argument.getSqlConnectionParams())) {
String query = argument.getRequestURL();
List<String> result = fetchFileListFromDatabaseSQL(connection, query);
if (result != null && !result.isEmpty()) {
String resultSet = String.join(", ", result.stream()
.map(s -> "'" + s + "'")
.toArray(String[]::new));
clauseBuilder.append(" OR ").append(argument.getId()).append(" IN (").append(resultSet).append(")");
}
} catch (SQLException e) {
logger.error("Failed to execute query for RequestArgument", e);
}
}
}
String clause = clauseBuilder.toString();
return request.getRequestURL().replace("${clause}", clause);
}
private void executeSqlQuery(Connection connection, String query) throws SQLException {
try (PreparedStatement stmt = connection.prepareStatement(query)) {
stmt.execute();
}
}
private List<String> fetchFileListFromDatabaseSQL(Connection connection, String query) throws SQLException {
List<String> results = new ArrayList<>();
try (PreparedStatement stmt = connection.prepareStatement(query);
ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
results.add(rs.getString(1)); // Fetch the first column
}
}
return results;
}
// Process S3 Requests concurrently
public void processS3Requests(List<S3Request> s3Requests, List<String> ids) {
s3Requests.forEach(request -> {
List<CompletableFuture<Void>> futures = ids.stream()
.map(id -> CompletableFuture.runAsync(() -> processS3Request(request, id)))
.toList();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> logger.info("Successfully processed all S3 requests."))
.exceptionally(ex -> {
logger.error("Failed to process S3 requests", ex);
return null;
});
});
}
private void processS3Request(S3Request request, String id) {
try {
List<String> files = new ArrayList<>();
if (request.getRequestArguments() != null && !request.getRequestArguments().isEmpty()) {
for (RequestArgument argument : request.getRequestArguments()) {
try (Connection connection = DatabaseConnection.getConnection(argument.getSqlConnectionParams())) {
String query = argument.getRequestURL();
List<String> result = fetchFileListFromDatabaseSQL(connection, query);
if (result != null && !result.isEmpty()) {
files.addAll(result);
}
} catch (SQLException e) {
logger.error("Failed to execute query for RequestArgument", e);
}
}
}
files.forEach(file -> {
HttpRequest httpRequest;
try {
httpRequest = S3HttpConnection.buildHttpRequest(request, file);
} catch (Exception e) {
throw new RuntimeException(e);
}
httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString())
.thenAccept(response -> {
if (response.statusCode() == HttpURLConnection.HTTP_NO_CONTENT || response.statusCode() == HttpURLConnection.HTTP_OK) {
logger.info("Successfully deleted object for ID {}", id);
} else {
logger.error("Failed to delete object for ID {}. Response code: {}", id, response.statusCode());
}
})
.exceptionally(ex -> {
logger.error("Failed to delete object for ID {}", id, ex);
return null;
});
});
} catch (Exception e) {
logger.error("Failed to process S3 request for id: {}", id, e);
}
}
// Process AQL requests
private void processAqlRequests(AqlRequest request, List<String> ids) {
ArangoDatabase arangoDb = ArangoDBConnection.getConnection(request.getAqlConnectionParams());
String aqlQuery = buildAqlQuery(request, ids);
List<String> result = executeAqlQueryForStrings(arangoDb, aqlQuery);
logger.info("Successfully executed AQL request and retrieved results: {}", result);
}
private String buildAqlQuery(AqlRequest request, List<String> ids) {
String collection = request.getAqlConnectionParams().getCollection();
String inClause = "FOR doc IN " + collection + " FILTER doc.id IN [" +
String.join(", ", ids.stream().map(id -> "'" + id + "'").toArray(String[]::new)) +
"] RETURN doc";
return request.getRequestURL().replace("${collection}", collection).replace("${clause}", inClause);
}
private List<String> executeAqlQueryForStrings(ArangoDatabase arangoDb, String query) {
List<String> results = new ArrayList<>();
try (ArangoCursor<String> cursor = arangoDb.query(query, null, null, null)) {
while (cursor.hasNext()) {
results.add(cursor.next());
}
} catch (Exception e) {
logger.error("Failed to execute AQL query", e);
}
return results;
}
}

View file

@ -0,0 +1,70 @@
package org.micord.utils;
import org.micord.exceptions.FileNotModifiedException;
import org.micord.models.Requests;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Maksim Tereshin
*/
@Component
public class ConfigLoader {
private static final Logger LOGGER = Logger.getLogger(ConfigLoader.class.getName());
private final Map<String, FileTime> lastModifiedTimes = new HashMap<>();
@Value("${configDirectory}")
private String configDirectory;
public Optional<Requests> loadConfigIfModified(String methodName) throws FileNotModifiedException {
String fileName = methodName + ".xml";
if (configDirectory == null) {
LOGGER.log(Level.SEVERE, "No configuration directory found for method: " + methodName);
return Optional.empty();
}
try {
File configFile = new File(configDirectory + File.separator + fileName);
Path configFilePath = configFile.toPath();
FileTime currentModifiedTime = Files.getLastModifiedTime(configFilePath);
FileTime lastModifiedTime = lastModifiedTimes.getOrDefault(methodName, null);
if (lastModifiedTime == null || currentModifiedTime.compareTo(lastModifiedTime) > 0) {
lastModifiedTimes.put(methodName, currentModifiedTime);
// Load the updated configuration
JAXBContext jaxbContext = JAXBContext.newInstance(Requests.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Requests loadedConfig = (Requests) unmarshaller.unmarshal(configFile);
return Optional.of(loadedConfig);
} else {
throw new FileNotModifiedException("Configuration file for method " + methodName + " has not been modified.");
}
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Failed to load configuration file: " + fileName, e);
return Optional.empty(); // Return empty if there is an IO error
} catch (JAXBException e) {
LOGGER.log(Level.SEVERE, "Failed to unmarshal configuration file: " + fileName, e);
return Optional.empty(); // Return empty if unmarshalling fails
}
}
}

View file

@ -0,0 +1,18 @@
configDirectory: /Users/maksim/Projects/Micord/configs
#spring:
# datasource:
# url: jdbc:mysql://localhost:3306/micord?useSSL=false
# hikari:
# maximum-pool-size: 10
# minimum-idle: 5
# idle-timeout: 30000
# connection-timeout: 20000
# max-lifetime: 1800000
# jpa:
# properties:
# hibernate:
# dialect: org.hibernate.dialect.MySQLDialect
server:
port: 8090