chore(quarkus-327): bump to Quarkus 3.27.3 LTS, rename quarkus-resteasy-reactive → quarkus-rest, fix testGetAuditQuestions map vs list, rename deprecated config keys

This commit is contained in:
2026-04-23 14:47:47 +00:00
parent c336984bec
commit 51265fb0fa
127 changed files with 17488 additions and 9557 deletions

View File

@@ -1,339 +1,339 @@
package dev.lions.config;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import dev.lions.exceptions.ConfigurationException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* Configuration centrale de l'application Lions Dev.
* Cette classe gère l'ensemble des paramètres de configuration de manière thread-safe
* et fournit une interface unifiée pour accéder aux différentes configurations.
*
* @author Lions Dev Team
* @version 2.0
*/
@Slf4j
@ApplicationScoped
@Getter
public class ApplicationConfig {
/**
* Énumération des environnements d'exécution supportés.
*/
public enum Environment {
DEVELOPMENT("development"),
STAGING("staging"),
PRODUCTION("production");
private final String value;
Environment(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static Environment fromString(String value) {
return Arrays.stream(values())
.filter(env -> env.getValue().equalsIgnoreCase(value))
.findFirst()
.orElse(DEVELOPMENT);
}
}
// Constantes de configuration
private static final String DEFAULT_ENVIRONMENT = "development";
private static final long DEFAULT_MAX_FILE_SIZE = 10_485_760L; // 10MB
private static final int DEFAULT_CACHE_SIZE = 1000;
private static final int MIN_PORT = 1;
private static final int MAX_PORT = 65535;
// Configuration de base de l'application
@Inject
@ConfigProperty(name = "app.name", defaultValue = "Lions Dev")
private String applicationName;
@Inject
@ConfigProperty(name = "app.environment", defaultValue = DEFAULT_ENVIRONMENT)
private String environment;
@NotBlank
@Inject
@ConfigProperty(name = "app.base-url")
private String baseUrl;
// Configuration du stockage
@NotBlank
@Inject
@ConfigProperty(name = "app.storage.base-path")
private String storageBasePath;
@NotBlank
@Inject
@ConfigProperty(name = "app.storage.images.path", defaultValue = "images")
private String imageStoragePath;
@Inject
@ConfigProperty(name = "app.storage.allowed-types", defaultValue = "jpg,jpeg,png,gif")
private String allowedFileTypes;
@Min(1_048_576L) // 1MB minimum
@Max(104_857_600L) // 100MB maximum
@Inject
@ConfigProperty(name = "app.storage.max-size", defaultValue = "10485760")
private Long maxFileSize;
// Configuration des emails
@NotBlank
@Inject
@ConfigProperty(name = "app.email.from")
private String emailFrom;
@NotBlank
@Inject
@ConfigProperty(name = "app.email.support")
private String emailSupport;
@Inject
@ConfigProperty(name = "app.email.template-path", defaultValue = "templates/email")
private String emailTemplatePath;
// Configuration SMTP
@NotBlank
@Inject
@ConfigProperty(name = "app.smtp.host")
private String smtpHost;
@Min(MIN_PORT)
@Max(MAX_PORT)
@Inject
@ConfigProperty(name = "app.smtp.port")
private Integer smtpPort;
@Inject
@ConfigProperty(name = "app.smtp.username")
private Optional<String> smtpUsername;
@Inject
@ConfigProperty(name = "app.smtp.password")
private Optional<String> smtpPassword;
@NotBlank
@Inject
@ConfigProperty(name = "app.admin.email")
private String adminEmailAddress;
// Collections thread-safe pour les configurations dynamiques
private final Map<String, String> applicationUrls = new ConcurrentHashMap<>();
private final Map<Environment, String> environmentConfigs = new EnumMap<>(Environment.class);
private List<String> allowedFileTypesList;
/**
* Initialise la configuration après l'injection des propriétés.
* Valide et prépare l'ensemble des paramètres de configuration.
*
* @throws ConfigurationException si la configuration est invalide
*/
@PostConstruct
void initialize() {
try {
log.info("Initialisation de la configuration de l'application: {}", applicationName);
validateConfiguration();
initializeApplicationUrls();
initializeAllowedFileTypes();
initializeEnvironmentConfigs();
log.info("Configuration initialisée avec succès en environnement: {}", environment);
} catch (Exception e) {
String errorMessage = "Erreur lors de l'initialisation de la configuration";
log.error(errorMessage, e);
throw new ConfigurationException(errorMessage, e);
}
}
/**
* Valide l'ensemble de la configuration.
*
* @throws ConfigurationException si la validation échoue
*/
private void validateConfiguration() {
log.debug("Validation de la configuration");
validateEnvironment();
validateStoragePaths();
validateSmtpConfiguration();
validateFileSize();
}
/**
* Valide l'environnement d'exécution.
*/
private void validateEnvironment() {
if (!isValidEnvironment(environment)) {
throw new ConfigurationException("Environnement non reconnu: " + environment);
}
}
/**
* Valide les chemins de stockage.
*/
private void validateStoragePaths() {
Path basePath = Paths.get(storageBasePath);
validatePath(basePath, "stockage principal");
Path imagesPath = basePath.resolve(imageStoragePath);
validatePath(imagesPath, "stockage des images");
}
/**
* Valide un chemin spécifique.
*/
private void validatePath(Path path, String description) {
if (!path.toFile().exists() && !path.toFile().mkdirs()) {
throw new ConfigurationException(
"Impossible de créer le répertoire de " + description + ": " + path);
}
}
/**
* Valide la configuration SMTP.
*/
private void validateSmtpConfiguration() {
if (isSmtpConfigured() && (smtpPort < MIN_PORT || smtpPort > MAX_PORT)) {
throw new ConfigurationException("Port SMTP invalide: " + smtpPort);
}
}
/**
* Valide la taille maximale des fichiers.
*/
private void validateFileSize() {
if (maxFileSize <= 0) {
throw new ConfigurationException(
"Taille maximale de fichier invalide: " + maxFileSize);
}
}
/**
* Récupère l'adresse email système (expéditeur par défaut).
*
* @return Adresse email système
*/
public String getSystemEmailAddress() {
return emailFrom;
}
/**
* Vérifie si le SSL est activé pour le serveur SMTP.
*
* @return true si SSL est activé, sinon false
*/
public boolean isSmtpSslEnabled() {
return smtpPort == 465; // Port 465 est commun pour SMTP avec SSL
}
/**
* Initialise les URLs de l'application.
*/
private void initializeApplicationUrls() {
applicationUrls.clear();
applicationUrls.put("home", "/");
applicationUrls.put("services", "/services");
applicationUrls.put("contact", "/contact");
applicationUrls.put("admin", "/admin");
applicationUrls.put("projects", "/projects");
applicationUrls.put("portfolio", "/portfolio");
}
/**
* Initialise la liste des types de fichiers autorisés.
*/
private void initializeAllowedFileTypes() {
allowedFileTypesList = Collections.unmodifiableList(
Arrays.asList(allowedFileTypes.toLowerCase().split(","))
);
}
/**
* Initialise les configurations spécifiques aux environnements.
*/
private void initializeEnvironmentConfigs() {
environmentConfigs.put(Environment.DEVELOPMENT, "dev");
environmentConfigs.put(Environment.STAGING, "stage");
environmentConfigs.put(Environment.PRODUCTION, "prod");
}
// Méthodes publiques utilitaires
/**
* Récupère le chemin complet pour le stockage des images.
*/
public String getImageStoragePath() {
return Paths.get(storageBasePath, imageStoragePath).toString();
}
/**
* Vérifie si un type de fichier est autorisé.
*/
public boolean isFileTypeAllowed(String fileType) {
return fileType != null && allowedFileTypesList.contains(fileType.toLowerCase().trim());
}
/**
* Récupère l'URL d'une section de l'application.
*/
public String getUrl(String key) {
return applicationUrls.getOrDefault(key, "/");
}
/**
* Vérifie si l'environnement est en développement.
*/
public boolean isDevelopment() {
return Environment.DEVELOPMENT.getValue().equals(environment);
}
/**
* Vérifie si l'environnement est en production.
*/
public boolean isProduction() {
return Environment.PRODUCTION.getValue().equals(environment);
}
/**
* Vérifie si la configuration SMTP est complète.
*/
public boolean isSmtpConfigured() {
return smtpUsername.isPresent() && smtpPassword.isPresent() &&
smtpHost != null && !smtpHost.equals("localhost");
}
/**
* Vérifie si un environnement est valide.
*/
private boolean isValidEnvironment(String env) {
return Arrays.stream(Environment.values())
.anyMatch(e -> e.getValue().equals(env));
}
package dev.lions.config;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import dev.lions.exceptions.ConfigurationException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* Configuration centrale de l'application Lions Dev.
* Cette classe gère l'ensemble des paramètres de configuration de manière thread-safe
* et fournit une interface unifiée pour accéder aux différentes configurations.
*
* @author Lions Dev Team
* @version 2.0
*/
@Slf4j
@ApplicationScoped
@Getter
public class ApplicationConfig {
/**
* Énumération des environnements d'exécution supportés.
*/
public enum Environment {
DEVELOPMENT("development"),
STAGING("staging"),
PRODUCTION("production");
private final String value;
Environment(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static Environment fromString(String value) {
return Arrays.stream(values())
.filter(env -> env.getValue().equalsIgnoreCase(value))
.findFirst()
.orElse(DEVELOPMENT);
}
}
// Constantes de configuration
private static final String DEFAULT_ENVIRONMENT = "development";
private static final long DEFAULT_MAX_FILE_SIZE = 10_485_760L; // 10MB
private static final int DEFAULT_CACHE_SIZE = 1000;
private static final int MIN_PORT = 1;
private static final int MAX_PORT = 65535;
// Configuration de base de l'application
@Inject
@ConfigProperty(name = "app.name", defaultValue = "Lions Dev")
private String applicationName;
@Inject
@ConfigProperty(name = "app.environment", defaultValue = DEFAULT_ENVIRONMENT)
private String environment;
@NotBlank
@Inject
@ConfigProperty(name = "app.base-url")
private String baseUrl;
// Configuration du stockage
@NotBlank
@Inject
@ConfigProperty(name = "app.storage.base-path")
private String storageBasePath;
@NotBlank
@Inject
@ConfigProperty(name = "app.storage.images.path", defaultValue = "images")
private String imageStoragePath;
@Inject
@ConfigProperty(name = "app.storage.allowed-types", defaultValue = "jpg,jpeg,png,gif")
private String allowedFileTypes;
@Min(1_048_576L) // 1MB minimum
@Max(104_857_600L) // 100MB maximum
@Inject
@ConfigProperty(name = "app.storage.max-size", defaultValue = "10485760")
private Long maxFileSize;
// Configuration des emails
@NotBlank
@Inject
@ConfigProperty(name = "app.email.from")
private String emailFrom;
@NotBlank
@Inject
@ConfigProperty(name = "app.email.support")
private String emailSupport;
@Inject
@ConfigProperty(name = "app.email.template-path", defaultValue = "templates/email")
private String emailTemplatePath;
// Configuration SMTP
@NotBlank
@Inject
@ConfigProperty(name = "app.smtp.host")
private String smtpHost;
@Min(MIN_PORT)
@Max(MAX_PORT)
@Inject
@ConfigProperty(name = "app.smtp.port")
private Integer smtpPort;
@Inject
@ConfigProperty(name = "app.smtp.username")
private Optional<String> smtpUsername;
@Inject
@ConfigProperty(name = "app.smtp.password")
private Optional<String> smtpPassword;
@NotBlank
@Inject
@ConfigProperty(name = "app.admin.email")
private String adminEmailAddress;
// Collections thread-safe pour les configurations dynamiques
private final Map<String, String> applicationUrls = new ConcurrentHashMap<>();
private final Map<Environment, String> environmentConfigs = new EnumMap<>(Environment.class);
private List<String> allowedFileTypesList;
/**
* Initialise la configuration après l'injection des propriétés.
* Valide et prépare l'ensemble des paramètres de configuration.
*
* @throws ConfigurationException si la configuration est invalide
*/
@PostConstruct
void initialize() {
try {
log.info("Initialisation de la configuration de l'application: {}", applicationName);
validateConfiguration();
initializeApplicationUrls();
initializeAllowedFileTypes();
initializeEnvironmentConfigs();
log.info("Configuration initialisée avec succès en environnement: {}", environment);
} catch (Exception e) {
String errorMessage = "Erreur lors de l'initialisation de la configuration";
log.error(errorMessage, e);
throw new ConfigurationException(errorMessage, e);
}
}
/**
* Valide l'ensemble de la configuration.
*
* @throws ConfigurationException si la validation échoue
*/
private void validateConfiguration() {
log.debug("Validation de la configuration");
validateEnvironment();
validateStoragePaths();
validateSmtpConfiguration();
validateFileSize();
}
/**
* Valide l'environnement d'exécution.
*/
private void validateEnvironment() {
if (!isValidEnvironment(environment)) {
throw new ConfigurationException("Environnement non reconnu: " + environment);
}
}
/**
* Valide les chemins de stockage.
*/
private void validateStoragePaths() {
Path basePath = Paths.get(storageBasePath);
validatePath(basePath, "stockage principal");
Path imagesPath = basePath.resolve(imageStoragePath);
validatePath(imagesPath, "stockage des images");
}
/**
* Valide un chemin spécifique.
*/
private void validatePath(Path path, String description) {
if (!path.toFile().exists() && !path.toFile().mkdirs()) {
throw new ConfigurationException(
"Impossible de créer le répertoire de " + description + ": " + path);
}
}
/**
* Valide la configuration SMTP.
*/
private void validateSmtpConfiguration() {
if (isSmtpConfigured() && (smtpPort < MIN_PORT || smtpPort > MAX_PORT)) {
throw new ConfigurationException("Port SMTP invalide: " + smtpPort);
}
}
/**
* Valide la taille maximale des fichiers.
*/
private void validateFileSize() {
if (maxFileSize <= 0) {
throw new ConfigurationException(
"Taille maximale de fichier invalide: " + maxFileSize);
}
}
/**
* Récupère l'adresse email système (expéditeur par défaut).
*
* @return Adresse email système
*/
public String getSystemEmailAddress() {
return emailFrom;
}
/**
* Vérifie si le SSL est activé pour le serveur SMTP.
*
* @return true si SSL est activé, sinon false
*/
public boolean isSmtpSslEnabled() {
return smtpPort == 465; // Port 465 est commun pour SMTP avec SSL
}
/**
* Initialise les URLs de l'application.
*/
private void initializeApplicationUrls() {
applicationUrls.clear();
applicationUrls.put("home", "/");
applicationUrls.put("services", "/services");
applicationUrls.put("contact", "/contact");
applicationUrls.put("admin", "/admin");
applicationUrls.put("projects", "/projects");
applicationUrls.put("portfolio", "/portfolio");
}
/**
* Initialise la liste des types de fichiers autorisés.
*/
private void initializeAllowedFileTypes() {
allowedFileTypesList = Collections.unmodifiableList(
Arrays.asList(allowedFileTypes.toLowerCase().split(","))
);
}
/**
* Initialise les configurations spécifiques aux environnements.
*/
private void initializeEnvironmentConfigs() {
environmentConfigs.put(Environment.DEVELOPMENT, "dev");
environmentConfigs.put(Environment.STAGING, "stage");
environmentConfigs.put(Environment.PRODUCTION, "prod");
}
// Méthodes publiques utilitaires
/**
* Récupère le chemin complet pour le stockage des images.
*/
public String getImageStoragePath() {
return Paths.get(storageBasePath, imageStoragePath).toString();
}
/**
* Vérifie si un type de fichier est autorisé.
*/
public boolean isFileTypeAllowed(String fileType) {
return fileType != null && allowedFileTypesList.contains(fileType.toLowerCase().trim());
}
/**
* Récupère l'URL d'une section de l'application.
*/
public String getUrl(String key) {
return applicationUrls.getOrDefault(key, "/");
}
/**
* Vérifie si l'environnement est en développement.
*/
public boolean isDevelopment() {
return Environment.DEVELOPMENT.getValue().equals(environment);
}
/**
* Vérifie si l'environnement est en production.
*/
public boolean isProduction() {
return Environment.PRODUCTION.getValue().equals(environment);
}
/**
* Vérifie si la configuration SMTP est complète.
*/
public boolean isSmtpConfigured() {
return smtpUsername.isPresent() && smtpPassword.isPresent() &&
smtpHost != null && !smtpHost.equals("localhost");
}
/**
* Vérifie si un environnement est valide.
*/
private boolean isValidEnvironment(String env) {
return Arrays.stream(Environment.values())
.anyMatch(e -> e.getValue().equals(env));
}
}

View File

@@ -1,180 +1,180 @@
package dev.lions.config;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import dev.lions.events.ConfigurationEvent;
import dev.lions.exceptions.ConfigurationException;
import dev.lions.utils.EncryptionUtils;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* Service de gestion avancée de la configuration de l'application.
* Fournit une interface enrichie pour accéder et gérer les paramètres de configuration
* de manière thread-safe, sécurisée et optimisée.
*
* @author Lions Dev Team
* @version 2.1
*/
@Slf4j
@ApplicationScoped
public class ApplicationConfigService {
private static final long CACHE_DURATION_MS = 300_000; // 5 minutes
private static final long MIN_DISK_SPACE_BYTES = 100 * 1024 * 1024; // 100 MB
private static final String[] MANDATORY_DIRECTORIES = {"logs", "data", "temp"};
private final ApplicationConfig applicationConfig;
private final Map<String, CachedValue<Object>> configCache;
private final AtomicLong lastHealthCheck;
@Inject
private Event<ConfigurationEvent> configurationEvent;
@Inject
private EncryptionUtils encryptionUtils;
@Inject
public ApplicationConfigService(@NotNull ApplicationConfig applicationConfig) {
this.applicationConfig = applicationConfig;
this.configCache = new ConcurrentHashMap<>();
this.lastHealthCheck = new AtomicLong(0);
initializeService();
}
/**
* Valide la configuration actuelle de l'application.
* Vérifie les chemins de stockage, les répertoires obligatoires et les paramètres critiques.
*/
private void validateConfiguration() {
log.info("Validation de la configuration de l'application...");
try {
validateStorageBasePath();
validateMandatoryDirectories();
validateSecuritySettings();
log.info("Configuration de l'application validée avec succès.");
} catch (ConfigurationException e) {
log.error("Validation échouée : {}", e.getMessage());
throw e; // Relancer l'exception après log
} catch (Exception e) {
log.error("Erreur inattendue lors de la validation de la configuration", e);
throw new ConfigurationException("Erreur inattendue lors de la validation", e);
}
}
/**
* Vérifie l'existence et l'accessibilité du chemin de stockage.
*/
private void validateStorageBasePath() {
Path storagePath = Paths.get(applicationConfig.getStorageBasePath());
if (!Files.exists(storagePath) || !Files.isDirectory(storagePath)) {
throw new ConfigurationException("Le chemin de stockage est invalide : " + storagePath);
}
log.debug("Chemin de stockage validé : {}", storagePath);
}
/**
* Vérifie l'existence des répertoires obligatoires.
*/
private void validateMandatoryDirectories() {
String basePath = applicationConfig.getStorageBasePath();
for (String directory : MANDATORY_DIRECTORIES) {
Path directoryPath = Paths.get(basePath, directory);
if (!Files.exists(directoryPath) || !Files.isWritable(directoryPath)) {
throw new ConfigurationException("Répertoire obligatoire manquant ou inaccessible : " + directoryPath);
}
log.debug("Répertoire valide : {}", directoryPath);
}
}
/**
* Valide les paramètres de sécurité essentiels.
*/
private void validateSecuritySettings() {
if (applicationConfig.isProduction()) {
if (!applicationConfig.isSmtpConfigured()) {
throw new ConfigurationException("La configuration SMTP est obligatoire en production");
}
log.debug("Paramètres SMTP validés pour l'environnement production");
}
log.debug("Paramètres de sécurité validés");
}
private void initializeService() {
try {
log.info("Initialisation du service de configuration");
createMandatoryDirectories();
validateConfiguration();
initializeCache();
notifyServiceInitialized();
log.info("Service de configuration initialisé avec succès");
} catch (Exception e) {
log.error("Erreur lors de l'initialisation du service de configuration", e);
throw new ConfigurationException("Échec de l'initialisation du service", e);
}
}
private void createMandatoryDirectories() {
String basePath = applicationConfig.getStorageBasePath();
for (String directory : MANDATORY_DIRECTORIES) {
Path directoryPath = Paths.get(basePath, directory);
if (!Files.exists(directoryPath)) {
try {
Files.createDirectories(directoryPath);
log.debug("Répertoire créé : {}", directoryPath);
} catch (Exception e) {
throw new ConfigurationException("Impossible de créer le répertoire : " + directoryPath, e);
}
}
}
}
private void initializeCache() {
log.info("Initialisation du cache de configuration...");
configCache.clear();
}
private void notifyServiceInitialized() {
ConfigurationEvent event = new ConfigurationEvent(
"SERVICE_INITIALIZED",
Map.of("timestamp", System.currentTimeMillis(),
"environment", applicationConfig.getEnvironment())
);
configurationEvent.fire(event);
}
/**
* Classe interne représentant une valeur mise en cache.
*/
private static class CachedValue<T> {
private final T value;
private final long timestamp;
CachedValue(T value) {
this.value = value;
this.timestamp = System.currentTimeMillis();
}
T getValue() {
return value;
}
}
}
package dev.lions.config;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import dev.lions.events.ConfigurationEvent;
import dev.lions.exceptions.ConfigurationException;
import dev.lions.utils.EncryptionUtils;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* Service de gestion avancée de la configuration de l'application.
* Fournit une interface enrichie pour accéder et gérer les paramètres de configuration
* de manière thread-safe, sécurisée et optimisée.
*
* @author Lions Dev Team
* @version 2.1
*/
@Slf4j
@ApplicationScoped
public class ApplicationConfigService {
private static final long CACHE_DURATION_MS = 300_000; // 5 minutes
private static final long MIN_DISK_SPACE_BYTES = 100 * 1024 * 1024; // 100 MB
private static final String[] MANDATORY_DIRECTORIES = {"logs", "data", "temp"};
private final ApplicationConfig applicationConfig;
private final Map<String, CachedValue<Object>> configCache;
private final AtomicLong lastHealthCheck;
@Inject
private Event<ConfigurationEvent> configurationEvent;
@Inject
private EncryptionUtils encryptionUtils;
@Inject
public ApplicationConfigService(@NotNull ApplicationConfig applicationConfig) {
this.applicationConfig = applicationConfig;
this.configCache = new ConcurrentHashMap<>();
this.lastHealthCheck = new AtomicLong(0);
initializeService();
}
/**
* Valide la configuration actuelle de l'application.
* Vérifie les chemins de stockage, les répertoires obligatoires et les paramètres critiques.
*/
private void validateConfiguration() {
log.info("Validation de la configuration de l'application...");
try {
validateStorageBasePath();
validateMandatoryDirectories();
validateSecuritySettings();
log.info("Configuration de l'application validée avec succès.");
} catch (ConfigurationException e) {
log.error("Validation échouée : {}", e.getMessage());
throw e; // Relancer l'exception après log
} catch (Exception e) {
log.error("Erreur inattendue lors de la validation de la configuration", e);
throw new ConfigurationException("Erreur inattendue lors de la validation", e);
}
}
/**
* Vérifie l'existence et l'accessibilité du chemin de stockage.
*/
private void validateStorageBasePath() {
Path storagePath = Paths.get(applicationConfig.getStorageBasePath());
if (!Files.exists(storagePath) || !Files.isDirectory(storagePath)) {
throw new ConfigurationException("Le chemin de stockage est invalide : " + storagePath);
}
log.debug("Chemin de stockage validé : {}", storagePath);
}
/**
* Vérifie l'existence des répertoires obligatoires.
*/
private void validateMandatoryDirectories() {
String basePath = applicationConfig.getStorageBasePath();
for (String directory : MANDATORY_DIRECTORIES) {
Path directoryPath = Paths.get(basePath, directory);
if (!Files.exists(directoryPath) || !Files.isWritable(directoryPath)) {
throw new ConfigurationException("Répertoire obligatoire manquant ou inaccessible : " + directoryPath);
}
log.debug("Répertoire valide : {}", directoryPath);
}
}
/**
* Valide les paramètres de sécurité essentiels.
*/
private void validateSecuritySettings() {
if (applicationConfig.isProduction()) {
if (!applicationConfig.isSmtpConfigured()) {
throw new ConfigurationException("La configuration SMTP est obligatoire en production");
}
log.debug("Paramètres SMTP validés pour l'environnement production");
}
log.debug("Paramètres de sécurité validés");
}
private void initializeService() {
try {
log.info("Initialisation du service de configuration");
createMandatoryDirectories();
validateConfiguration();
initializeCache();
notifyServiceInitialized();
log.info("Service de configuration initialisé avec succès");
} catch (Exception e) {
log.error("Erreur lors de l'initialisation du service de configuration", e);
throw new ConfigurationException("Échec de l'initialisation du service", e);
}
}
private void createMandatoryDirectories() {
String basePath = applicationConfig.getStorageBasePath();
for (String directory : MANDATORY_DIRECTORIES) {
Path directoryPath = Paths.get(basePath, directory);
if (!Files.exists(directoryPath)) {
try {
Files.createDirectories(directoryPath);
log.debug("Répertoire créé : {}", directoryPath);
} catch (Exception e) {
throw new ConfigurationException("Impossible de créer le répertoire : " + directoryPath, e);
}
}
}
}
private void initializeCache() {
log.info("Initialisation du cache de configuration...");
configCache.clear();
}
private void notifyServiceInitialized() {
ConfigurationEvent event = new ConfigurationEvent(
"SERVICE_INITIALIZED",
Map.of("timestamp", System.currentTimeMillis(),
"environment", applicationConfig.getEnvironment())
);
configurationEvent.fire(event);
}
/**
* Classe interne représentant une valeur mise en cache.
*/
private static class CachedValue<T> {
private final T value;
private final long timestamp;
CachedValue(T value) {
this.value = value;
this.timestamp = System.currentTimeMillis();
}
T getValue() {
return value;
}
}
}

View File

@@ -1,183 +1,183 @@
package dev.lions.config;
import dev.lions.exceptions.ConfigurationException;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.faces.annotation.FacesConfig;
import jakarta.faces.application.ViewHandler;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.PostConstructApplicationEvent;
import jakarta.faces.event.PreDestroyApplicationEvent;
import jakarta.faces.event.SystemEvent;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.Map;
/**
* Configuration Jakarta Server Faces (JSF) de l'application.
* Cette classe gère l'ensemble des paramètres et comportements spécifiques à JSF,
* assurant une expérience utilisateur cohérente et performante.
*
* @author Lions Dev Team
* @version 2.1
*/
@Slf4j
@ApplicationScoped
@FacesConfig
public class JSFConfiguration {
@Inject
@ConfigProperty(name = "jakarta.faces.PROJECT_STAGE", defaultValue = "Development")
String projectStage;
@Inject
@ConfigProperty(name = "jakarta.faces.FACELETS_REFRESH_PERIOD", defaultValue = "2")
Integer faceletsRefreshPeriod;
@Inject
@ConfigProperty(name = "jakarta.faces.STATE_SAVING_METHOD", defaultValue = "server")
String stateSavingMethod;
@Inject
@ConfigProperty(name = "primefaces.THEME", defaultValue = "saga")
private String primefacesTheme;
@Inject
@ConfigProperty(name = "jakarta.faces.VALIDATE_EMPTY_FIELDS", defaultValue = "true")
private Boolean validateEmptyFields;
@Inject
@ConfigProperty(name = "jakarta.faces.FACELETS_SKIP_COMMENTS", defaultValue = "true")
private Boolean skipComments;
/**
* Initialise la configuration JSF au démarrage de l'application.
*
* @param event Événement de construction de l'application.
*/
public void initialize(@Observes @NotNull PostConstructApplicationEvent event) {
try {
log.info("Initialisation de la configuration JSF");
configureFacesContext();
configureViewHandler();
configurePrimeFaces();
applyPerformanceOptimizations();
log.info("Configuration JSF initialisée avec succès - Mode: {}", projectStage);
} catch (Exception e) {
String message = "Erreur lors de l'initialisation de la configuration JSF";
log.error(message, e);
throw new ConfigurationException(message, e);
}
}
/**
* Nettoie les ressources JSF avant l'arrêt de l'application.
*
* @param event Événement de destruction de l'application.
*/
public void cleanup(@Observes @NotNull PreDestroyApplicationEvent event) {
log.info("Nettoyage des ressources JSF");
}
/**
* Configure le contexte Faces avec les paramètres spécifiques.
*/
private void configureFacesContext() {
log.debug("Configuration du contexte Faces");
setFacesParameter("jakarta.faces.PROJECT_STAGE", projectStage);
setFacesParameter("jakarta.faces.STATE_SAVING_METHOD", stateSavingMethod);
setFacesParameter("jakarta.faces.FACELETS_REFRESH_PERIOD", faceletsRefreshPeriod.toString());
setFacesParameter("jakarta.faces.VALIDATE_EMPTY_FIELDS", validateEmptyFields.toString());
setFacesParameter("jakarta.faces.FACELETS_SKIP_COMMENTS", skipComments.toString());
}
/**
* Configure les paramètres spécifiques au gestionnaire de vue.
*/
private void configureViewHandler() {
log.debug("Configuration du gestionnaire de vues JSF");
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext == null) {
log.warn("Impossible de configurer le gestionnaire de vue : FacesContext est null.");
return;
}
ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
UIViewRoot root = facesContext.getViewRoot();
if (root == null) {
root = viewHandler.createView(facesContext, "/index.xhtml");
facesContext.setViewRoot(root);
}
root.getAttributes().put("encoding", "UTF-8");
root.getAttributes().put("contentType", "text/html");
root.getAttributes().put("characterEncoding", "UTF-8");
log.debug("Gestionnaire de vues configuré avec succès.");
}
/**
* Configure les paramètres spécifiques à PrimeFaces.
*/
private void configurePrimeFaces() {
log.debug("Configuration de PrimeFaces");
setFacesParameter("primefaces.THEME", primefacesTheme);
setFacesParameter("primefaces.FONT_AWESOME", "true");
setFacesParameter("primefaces.CLIENT_SIDE_VALIDATION", "true");
setFacesParameter("primefaces.UPLOADER", "auto");
configurePrimeFacesCache();
}
/**
* Configure le cache PrimeFaces selon l'environnement.
*/
private void configurePrimeFacesCache() {
String cacheProvider = isDevelopmentMode() ? "memory" : "ehcache";
setFacesParameter("primefaces.CACHE_PROVIDER", cacheProvider);
}
/**
* Applique les optimisations de performance.
*/
private void applyPerformanceOptimizations() {
if (!isDevelopmentMode()) {
setFacesParameter("jakarta.faces.FACELETS_REFRESH_PERIOD", "-1");
setFacesParameter("jakarta.faces.COMPRESS_VIEWSTATE", "true");
setFacesParameter("jakarta.faces.PARTIAL_STATE_SAVING", "true");
}
}
/**
* Définit un paramètre dans le contexte Faces.
*/
private void setFacesParameter(String name, String value) {
FacesContext.getCurrentInstance()
.getExternalContext()
.getApplicationMap()
.put(name, value);
log.debug("Paramètre défini : {} = {}", name, value);
}
/**
* Vérifie si l'application est en mode développement.
*
* @return true si en mode développement, false sinon.
*/
public boolean isDevelopmentMode() {
return "Development".equals(projectStage);
}
}
package dev.lions.config;
import dev.lions.exceptions.ConfigurationException;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.faces.annotation.FacesConfig;
import jakarta.faces.application.ViewHandler;
import jakarta.faces.component.UIViewRoot;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.PostConstructApplicationEvent;
import jakarta.faces.event.PreDestroyApplicationEvent;
import jakarta.faces.event.SystemEvent;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import java.util.Map;
/**
* Configuration Jakarta Server Faces (JSF) de l'application.
* Cette classe gère l'ensemble des paramètres et comportements spécifiques à JSF,
* assurant une expérience utilisateur cohérente et performante.
*
* @author Lions Dev Team
* @version 2.1
*/
@Slf4j
@ApplicationScoped
@FacesConfig
public class JSFConfiguration {
@Inject
@ConfigProperty(name = "jakarta.faces.PROJECT_STAGE", defaultValue = "Development")
String projectStage;
@Inject
@ConfigProperty(name = "jakarta.faces.FACELETS_REFRESH_PERIOD", defaultValue = "2")
Integer faceletsRefreshPeriod;
@Inject
@ConfigProperty(name = "jakarta.faces.STATE_SAVING_METHOD", defaultValue = "server")
String stateSavingMethod;
@Inject
@ConfigProperty(name = "primefaces.THEME", defaultValue = "saga")
private String primefacesTheme;
@Inject
@ConfigProperty(name = "jakarta.faces.VALIDATE_EMPTY_FIELDS", defaultValue = "true")
private Boolean validateEmptyFields;
@Inject
@ConfigProperty(name = "jakarta.faces.FACELETS_SKIP_COMMENTS", defaultValue = "true")
private Boolean skipComments;
/**
* Initialise la configuration JSF au démarrage de l'application.
*
* @param event Événement de construction de l'application.
*/
public void initialize(@Observes @NotNull PostConstructApplicationEvent event) {
try {
log.info("Initialisation de la configuration JSF");
configureFacesContext();
configureViewHandler();
configurePrimeFaces();
applyPerformanceOptimizations();
log.info("Configuration JSF initialisée avec succès - Mode: {}", projectStage);
} catch (Exception e) {
String message = "Erreur lors de l'initialisation de la configuration JSF";
log.error(message, e);
throw new ConfigurationException(message, e);
}
}
/**
* Nettoie les ressources JSF avant l'arrêt de l'application.
*
* @param event Événement de destruction de l'application.
*/
public void cleanup(@Observes @NotNull PreDestroyApplicationEvent event) {
log.info("Nettoyage des ressources JSF");
}
/**
* Configure le contexte Faces avec les paramètres spécifiques.
*/
private void configureFacesContext() {
log.debug("Configuration du contexte Faces");
setFacesParameter("jakarta.faces.PROJECT_STAGE", projectStage);
setFacesParameter("jakarta.faces.STATE_SAVING_METHOD", stateSavingMethod);
setFacesParameter("jakarta.faces.FACELETS_REFRESH_PERIOD", faceletsRefreshPeriod.toString());
setFacesParameter("jakarta.faces.VALIDATE_EMPTY_FIELDS", validateEmptyFields.toString());
setFacesParameter("jakarta.faces.FACELETS_SKIP_COMMENTS", skipComments.toString());
}
/**
* Configure les paramètres spécifiques au gestionnaire de vue.
*/
private void configureViewHandler() {
log.debug("Configuration du gestionnaire de vues JSF");
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext == null) {
log.warn("Impossible de configurer le gestionnaire de vue : FacesContext est null.");
return;
}
ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
UIViewRoot root = facesContext.getViewRoot();
if (root == null) {
root = viewHandler.createView(facesContext, "/index.xhtml");
facesContext.setViewRoot(root);
}
root.getAttributes().put("encoding", "UTF-8");
root.getAttributes().put("contentType", "text/html");
root.getAttributes().put("characterEncoding", "UTF-8");
log.debug("Gestionnaire de vues configuré avec succès.");
}
/**
* Configure les paramètres spécifiques à PrimeFaces.
*/
private void configurePrimeFaces() {
log.debug("Configuration de PrimeFaces");
setFacesParameter("primefaces.THEME", primefacesTheme);
setFacesParameter("primefaces.FONT_AWESOME", "true");
setFacesParameter("primefaces.CLIENT_SIDE_VALIDATION", "true");
setFacesParameter("primefaces.UPLOADER", "auto");
configurePrimeFacesCache();
}
/**
* Configure le cache PrimeFaces selon l'environnement.
*/
private void configurePrimeFacesCache() {
String cacheProvider = isDevelopmentMode() ? "memory" : "ehcache";
setFacesParameter("primefaces.CACHE_PROVIDER", cacheProvider);
}
/**
* Applique les optimisations de performance.
*/
private void applyPerformanceOptimizations() {
if (!isDevelopmentMode()) {
setFacesParameter("jakarta.faces.FACELETS_REFRESH_PERIOD", "-1");
setFacesParameter("jakarta.faces.COMPRESS_VIEWSTATE", "true");
setFacesParameter("jakarta.faces.PARTIAL_STATE_SAVING", "true");
}
}
/**
* Définit un paramètre dans le contexte Faces.
*/
private void setFacesParameter(String name, String value) {
FacesContext.getCurrentInstance()
.getExternalContext()
.getApplicationMap()
.put(name, value);
log.debug("Paramètre défini : {} = {}", name, value);
}
/**
* Vérifie si l'application est en mode développement.
*
* @return true si en mode développement, false sinon.
*/
public boolean isDevelopmentMode() {
return "Development".equals(projectStage);
}
}

View File

@@ -1,264 +1,264 @@
package dev.lions.config;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import dev.lions.events.StorageEvent;
import dev.lions.exceptions.StorageConfigurationException;
import dev.lions.utils.SecurityUtils;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Service de gestion avancée des configurations de stockage.
* Assure la gestion sécurisée et optimisée des paramètres de stockage fichier
* de l'application, avec validation complète et monitoring.
*
* @author Lions Dev Team
* @version 2.1
*/
@Slf4j
@ApplicationScoped
public class StorageConfigService {
private static final String DEFAULT_DIRECTORY_PERMISSIONS = "rwxr-x---";
private static final long MIN_FREE_SPACE_BYTES = 100 * 1024 * 1024; // 100 MB
private static final int CLEANUP_BATCH_SIZE = 100;
private static final Duration CLEANUP_INTERVAL = Duration.ofHours(24);
private final Map<String, Path> pathCache;
private final Map<String, Boolean> fileTypeCache;
private final ApplicationConfig appConfig;
private final SecurityUtils securityUtils;
@Inject
Event<StorageEvent> storageEvent;
/**
* Initialise le service avec la configuration de l'application.
*
* @param appConfig Configuration de l'application
* @param securityUtils Utilitaires de sécurité
*/
@Inject
public StorageConfigService(@NotNull ApplicationConfig appConfig,
@NotNull SecurityUtils securityUtils) {
this.appConfig = appConfig;
this.securityUtils = securityUtils;
this.pathCache = new ConcurrentHashMap<>();
this.fileTypeCache = new ConcurrentHashMap<>();
initializeStorage();
}
/**
* Initialise et valide la configuration du stockage.
*/
private void initializeStorage() {
log.info("Initialisation de la configuration du stockage");
try {
createMainDirectories();
configureSecurity();
scheduleMaintenanceTasks();
validateStorageCapacity();
notifyStorageInitialized();
log.info("Configuration du stockage initialisée avec succès");
} catch (Exception e) {
String message = "Erreur lors de l'initialisation du stockage";
log.error(message, e);
throw new StorageConfigurationException(message, e);
}
}
/**
* Crée les répertoires principaux nécessaires au stockage.
*/
private void createMainDirectories() {
log.debug("Création des répertoires principaux de stockage");
String basePath = appConfig.getStorageBasePath();
Set<String> requiredDirs = Set.of("images", "documents", "temp", "backup");
requiredDirs.forEach(dir -> {
Path dirPath = Paths.get(basePath, dir);
createSecureDirectory(dirPath);
});
log.info("Répertoires principaux créés avec succès.");
}
/**
* Configure les paramètres de sécurité pour la production.
*/
private void applyProductionSecurity() {
log.info("Application des paramètres de sécurité spécifiques pour la production");
try {
Path basePath = Paths.get(appConfig.getStorageBasePath());
// Applique des permissions POSIX sécurisées
Set<PosixFilePermission> permissions =
PosixFilePermissions.fromString(DEFAULT_DIRECTORY_PERMISSIONS);
Files.setPosixFilePermissions(basePath, permissions);
log.info("Permissions POSIX sécurisées appliquées : {}", permissions);
// Vérifie l'accès sécurisé
if (!Files.isWritable(basePath) || !Files.isReadable(basePath)) {
throw new StorageConfigurationException(
"Permissions insuffisantes sur le répertoire de stockage : " + basePath);
}
log.debug("Sécurité des répertoires en production validée");
} catch (IOException e) {
log.error("Erreur lors de l'application des permissions de sécurité", e);
throw new StorageConfigurationException(
"Impossible d'appliquer les paramètres de sécurité en production", e);
}
}
/**
* Configure les paramètres de sécurité pour le stockage.
*/
private void configureSecurity() {
log.info("Configuration des paramètres de sécurité du stockage");
try {
if (appConfig.isProduction()) {
applyProductionSecurity();
}
securityUtils.initializeEncryption();
log.info("Sécurité du stockage configurée avec succès");
} catch (Exception e) {
throw new StorageConfigurationException("Erreur lors de la configuration de la sécurité", e);
}
}
/**
* Planifie les tâches de maintenance pour le stockage.
*/
private void scheduleMaintenanceTasks() {
log.info("Planification des tâches de maintenance du stockage");
try {
scheduleStorageCleanup();
scheduleCapacityCheck();
log.info("Tâches de maintenance planifiées avec succès.");
} catch (Exception e) {
throw new StorageConfigurationException("Erreur lors de la planification des tâches de maintenance", e);
}
}
/**
* Crée un répertoire sécurisé avec les permissions appropriées.
*/
private void createSecureDirectory(Path path) {
try {
if (!Files.exists(path)) {
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions
.asFileAttribute(PosixFilePermissions.fromString(DEFAULT_DIRECTORY_PERMISSIONS));
Files.createDirectories(path, attr);
log.debug("Répertoire créé avec succès : {}", path);
}
validateDirectoryAccess(path);
} catch (IOException e) {
throw new StorageConfigurationException(
"Impossible de créer le répertoire sécurisé : " + path, e);
}
}
/**
* Planifie le nettoyage automatique du stockage.
*/
private void scheduleStorageCleanup() {
log.info("Planification de la tâche de nettoyage automatique du stockage");
// Simulation d'une tâche de nettoyage. Remplacer par un vrai scheduler si nécessaire.
log.debug("Nettoyage automatique exécuté toutes les {} heures, batch size: {}",
CLEANUP_INTERVAL.toHours(), CLEANUP_BATCH_SIZE);
}
/**
* Planifie la vérification périodique de la capacité de stockage.
*/
private void scheduleCapacityCheck() {
log.info("Planification de la vérification périodique de la capacité de stockage");
// Simulation d'une vérification périodique. À remplacer par un scheduler réel.
log.debug("Vérification de la capacité planifiée toutes les {} heures", CLEANUP_INTERVAL.toHours());
}
/**
* Valide la capacité de stockage disponible.
*/
private void validateStorageCapacity() {
try {
Path storagePath = Paths.get(appConfig.getStorageBasePath());
long freeSpace = Files.getFileStore(storagePath).getUsableSpace();
if (freeSpace < MIN_FREE_SPACE_BYTES) {
throw new StorageConfigurationException(
"Espace de stockage insuffisant. Minimum requis : " +
formatSize(MIN_FREE_SPACE_BYTES));
}
log.debug("Espace de stockage validé : {} disponible", formatSize(freeSpace));
} catch (IOException e) {
throw new StorageConfigurationException(
"Impossible de vérifier l'espace de stockage disponible", e);
}
}
/**
* Formate une taille en bytes en format lisible.
*/
private String formatSize(long bytes) {
if (bytes < 1024) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(1024));
String pre = "KMGTPE".charAt(exp - 1) + "";
return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);
}
/**
* Notifie les observateurs de l'initialisation du stockage.
*/
private void notifyStorageInitialized() {
StorageEvent event = new StorageEvent(
"STORAGE_INITIALIZED",
Map.of(
"basePath", appConfig.getStorageBasePath(),
"environment", appConfig.getEnvironment()
)
);
storageEvent.fire(event);
}
/**
* Valide l'accès au répertoire.
*/
private void validateDirectoryAccess(Path path) {
if (!Files.isDirectory(path)) {
throw new StorageConfigurationException(
"Le chemin n'est pas un répertoire : " + path);
}
if (!Files.isWritable(path)) {
throw new StorageConfigurationException(
"Le répertoire n'est pas accessible en écriture : " + path);
}
}
}
package dev.lions.config;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import dev.lions.events.StorageEvent;
import dev.lions.exceptions.StorageConfigurationException;
import dev.lions.utils.SecurityUtils;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* Service de gestion avancée des configurations de stockage.
* Assure la gestion sécurisée et optimisée des paramètres de stockage fichier
* de l'application, avec validation complète et monitoring.
*
* @author Lions Dev Team
* @version 2.1
*/
@Slf4j
@ApplicationScoped
public class StorageConfigService {
private static final String DEFAULT_DIRECTORY_PERMISSIONS = "rwxr-x---";
private static final long MIN_FREE_SPACE_BYTES = 100 * 1024 * 1024; // 100 MB
private static final int CLEANUP_BATCH_SIZE = 100;
private static final Duration CLEANUP_INTERVAL = Duration.ofHours(24);
private final Map<String, Path> pathCache;
private final Map<String, Boolean> fileTypeCache;
private final ApplicationConfig appConfig;
private final SecurityUtils securityUtils;
@Inject
Event<StorageEvent> storageEvent;
/**
* Initialise le service avec la configuration de l'application.
*
* @param appConfig Configuration de l'application
* @param securityUtils Utilitaires de sécurité
*/
@Inject
public StorageConfigService(@NotNull ApplicationConfig appConfig,
@NotNull SecurityUtils securityUtils) {
this.appConfig = appConfig;
this.securityUtils = securityUtils;
this.pathCache = new ConcurrentHashMap<>();
this.fileTypeCache = new ConcurrentHashMap<>();
initializeStorage();
}
/**
* Initialise et valide la configuration du stockage.
*/
private void initializeStorage() {
log.info("Initialisation de la configuration du stockage");
try {
createMainDirectories();
configureSecurity();
scheduleMaintenanceTasks();
validateStorageCapacity();
notifyStorageInitialized();
log.info("Configuration du stockage initialisée avec succès");
} catch (Exception e) {
String message = "Erreur lors de l'initialisation du stockage";
log.error(message, e);
throw new StorageConfigurationException(message, e);
}
}
/**
* Crée les répertoires principaux nécessaires au stockage.
*/
private void createMainDirectories() {
log.debug("Création des répertoires principaux de stockage");
String basePath = appConfig.getStorageBasePath();
Set<String> requiredDirs = Set.of("images", "documents", "temp", "backup");
requiredDirs.forEach(dir -> {
Path dirPath = Paths.get(basePath, dir);
createSecureDirectory(dirPath);
});
log.info("Répertoires principaux créés avec succès.");
}
/**
* Configure les paramètres de sécurité pour la production.
*/
private void applyProductionSecurity() {
log.info("Application des paramètres de sécurité spécifiques pour la production");
try {
Path basePath = Paths.get(appConfig.getStorageBasePath());
// Applique des permissions POSIX sécurisées
Set<PosixFilePermission> permissions =
PosixFilePermissions.fromString(DEFAULT_DIRECTORY_PERMISSIONS);
Files.setPosixFilePermissions(basePath, permissions);
log.info("Permissions POSIX sécurisées appliquées : {}", permissions);
// Vérifie l'accès sécurisé
if (!Files.isWritable(basePath) || !Files.isReadable(basePath)) {
throw new StorageConfigurationException(
"Permissions insuffisantes sur le répertoire de stockage : " + basePath);
}
log.debug("Sécurité des répertoires en production validée");
} catch (IOException e) {
log.error("Erreur lors de l'application des permissions de sécurité", e);
throw new StorageConfigurationException(
"Impossible d'appliquer les paramètres de sécurité en production", e);
}
}
/**
* Configure les paramètres de sécurité pour le stockage.
*/
private void configureSecurity() {
log.info("Configuration des paramètres de sécurité du stockage");
try {
if (appConfig.isProduction()) {
applyProductionSecurity();
}
securityUtils.initializeEncryption();
log.info("Sécurité du stockage configurée avec succès");
} catch (Exception e) {
throw new StorageConfigurationException("Erreur lors de la configuration de la sécurité", e);
}
}
/**
* Planifie les tâches de maintenance pour le stockage.
*/
private void scheduleMaintenanceTasks() {
log.info("Planification des tâches de maintenance du stockage");
try {
scheduleStorageCleanup();
scheduleCapacityCheck();
log.info("Tâches de maintenance planifiées avec succès.");
} catch (Exception e) {
throw new StorageConfigurationException("Erreur lors de la planification des tâches de maintenance", e);
}
}
/**
* Crée un répertoire sécurisé avec les permissions appropriées.
*/
private void createSecureDirectory(Path path) {
try {
if (!Files.exists(path)) {
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions
.asFileAttribute(PosixFilePermissions.fromString(DEFAULT_DIRECTORY_PERMISSIONS));
Files.createDirectories(path, attr);
log.debug("Répertoire créé avec succès : {}", path);
}
validateDirectoryAccess(path);
} catch (IOException e) {
throw new StorageConfigurationException(
"Impossible de créer le répertoire sécurisé : " + path, e);
}
}
/**
* Planifie le nettoyage automatique du stockage.
*/
private void scheduleStorageCleanup() {
log.info("Planification de la tâche de nettoyage automatique du stockage");
// Simulation d'une tâche de nettoyage. Remplacer par un vrai scheduler si nécessaire.
log.debug("Nettoyage automatique exécuté toutes les {} heures, batch size: {}",
CLEANUP_INTERVAL.toHours(), CLEANUP_BATCH_SIZE);
}
/**
* Planifie la vérification périodique de la capacité de stockage.
*/
private void scheduleCapacityCheck() {
log.info("Planification de la vérification périodique de la capacité de stockage");
// Simulation d'une vérification périodique. À remplacer par un scheduler réel.
log.debug("Vérification de la capacité planifiée toutes les {} heures", CLEANUP_INTERVAL.toHours());
}
/**
* Valide la capacité de stockage disponible.
*/
private void validateStorageCapacity() {
try {
Path storagePath = Paths.get(appConfig.getStorageBasePath());
long freeSpace = Files.getFileStore(storagePath).getUsableSpace();
if (freeSpace < MIN_FREE_SPACE_BYTES) {
throw new StorageConfigurationException(
"Espace de stockage insuffisant. Minimum requis : " +
formatSize(MIN_FREE_SPACE_BYTES));
}
log.debug("Espace de stockage validé : {} disponible", formatSize(freeSpace));
} catch (IOException e) {
throw new StorageConfigurationException(
"Impossible de vérifier l'espace de stockage disponible", e);
}
}
/**
* Formate une taille en bytes en format lisible.
*/
private String formatSize(long bytes) {
if (bytes < 1024) return bytes + " B";
int exp = (int) (Math.log(bytes) / Math.log(1024));
String pre = "KMGTPE".charAt(exp - 1) + "";
return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);
}
/**
* Notifie les observateurs de l'initialisation du stockage.
*/
private void notifyStorageInitialized() {
StorageEvent event = new StorageEvent(
"STORAGE_INITIALIZED",
Map.of(
"basePath", appConfig.getStorageBasePath(),
"environment", appConfig.getEnvironment()
)
);
storageEvent.fire(event);
}
/**
* Valide l'accès au répertoire.
*/
private void validateDirectoryAccess(Path path) {
if (!Files.isDirectory(path)) {
throw new StorageConfigurationException(
"Le chemin n'est pas un répertoire : " + path);
}
if (!Files.isWritable(path)) {
throw new StorageConfigurationException(
"Le répertoire n'est pas accessible en écriture : " + path);
}
}
}