# 🔧 Refactoring UserServiceImpl - Gestion des Erreurs de Connexion **Date**: 2025-12-18 --- ## 📋 Problème Initial ### NullPointerException dans `createUser` (ligne 196) **Symptôme** : Lorsque le client REST ne peut pas se connecter au service Keycloak et retourne `null`, une `NullPointerException` se produit à la ligne 196 lors de l'appel à `response.getStatus()`. **Cause** : Aucune vérification de nullité pour l'objet `Response` retourné par `usersResource.create(userRep)`. --- ## ✅ Solution Implémentée ### 1. Création d'une Exception Personnalisée **Fichier créé** : `lions-user-manager-server-impl-quarkus/src/main/java/dev/lions/user/manager/service/exception/KeycloakServiceException.java` **Classes d'exception** : - `KeycloakServiceException` : Exception de base pour les erreurs Keycloak - `KeycloakServiceException.ServiceUnavailableException` : Pour les erreurs de connexion (503, 502) - `KeycloakServiceException.TimeoutException` : Pour les timeouts (504) **Avantages** : - ✅ Messages d'erreur clairs et spécifiques - ✅ Codes HTTP préservés pour le debugging - ✅ Hiérarchie d'exceptions pour gestion fine --- ### 2. Méthode Utilitaire `validateResponse` **Fonction** : Valide une réponse HTTP du service Keycloak. **Vérifications** : - ✅ Réponse non null - ✅ Code de statut HTTP attendu - ✅ Gestion des codes d'erreur (400, 401, 403, 404, 409, 500, 502, 503, 504) **Utilisation** : ```java Response response = usersResource.create(userRep); validateResponse(response, "création de l'utilisateur", Response.Status.CREATED.getStatusCode()); ``` --- ### 3. Méthode Utilitaire `handleConnectionException` **Fonction** : Gère les exceptions de connexion et les convertit en `KeycloakServiceException` appropriée. **Détection automatique** : - ✅ `ConnectException` - ✅ `SocketTimeoutException` - ✅ Messages contenant "Connection", "timeout", "refused", "Unable to connect" **Utilisation** : ```java catch (Exception e) { handleConnectionException(e, "création de l'utilisateur"); } ``` --- ### 4. Refactoring de `createUser` **Avant** : ```java var response = usersResource.create(userRep); if (response.getStatus() != 201) { throw new RuntimeException("Échec de la création..."); } ``` **Après** : ```java Response response = usersResource.create(userRep); // Vérifier si la réponse est null (erreur de connexion) if (response == null) { throw new KeycloakServiceException.ServiceUnavailableException( "Impossible de se connecter au service Keycloak..."); } // Vérifier le code de statut HTTP int status = response.getStatus(); if (status != Response.Status.CREATED.getStatusCode()) { // Gérer les différents codes d'erreur HTTP if (status == 400) { ... } else if (status == 409) { ... } else if (status == 503 || status == 502) { ... } // ... } ``` **Améliorations** : - ✅ Vérification de nullité avant `getStatus()` - ✅ Vérification de `getLocation()` avant utilisation - ✅ Gestion spécifique des codes HTTP d'erreur - ✅ Messages d'erreur clairs et actionnables --- ### 5. Refactoring des Autres Méthodes **Méthodes refactorisées** : - ✅ `createUser` - Vérification Response null + codes HTTP - ✅ `updateUser` - Vérification UserResource et UserRepresentation null - ✅ `deleteUser` - Vérification UserResource et UserRepresentation null - ✅ `activateUser` - Vérification UserResource et UserRepresentation null - ✅ `deactivateUser` - Vérification UserResource et UserRepresentation null - ✅ `sendVerificationEmail` - Vérification UserResource null - ✅ `logoutAllSessions` - Vérification UserResource null + getUserSessions null - ✅ `searchUsers` - Utilisation de `handleConnectionException` - ✅ `getUserByUsername` - Vérification users null - ✅ `getUserByEmail` - Vérification users null - ✅ `getUserById` - Utilisation de `handleConnectionException` - ✅ `setPassword` - Utilisation de `handleConnectionException` --- ## 📊 Gestion des Codes HTTP ### Codes Gérés | Code | Exception | Message | |------|-----------|---------| | **400** | `KeycloakServiceException` | "Données invalides" | | **401** | `KeycloakServiceException` | "Non autorisé" | | **403** | `KeycloakServiceException` | "Accès interdit" | | **404** | `KeycloakServiceException` | "Ressource non trouvée" | | **409** | `KeycloakServiceException` | "Conflit" | | **500** | `KeycloakServiceException` | "Erreur serveur interne Keycloak" | | **502** | `ServiceUnavailableException` | "Service Keycloak indisponible" | | **503** | `ServiceUnavailableException` | "Service Keycloak indisponible" | | **504** | `TimeoutException` | "Timeout lors de l'opération" | | **null** | `ServiceUnavailableException` | "Impossible de se connecter au service Keycloak" | --- ## 🔍 Détection des Erreurs de Connexion ### Types d'Exceptions Détectées 1. **Exceptions Java Standard** : - `ConnectException` - `SocketTimeoutException` 2. **Messages d'Erreur** : - Contient "Connection" - Contient "timeout" - Contient "refused" - Contient "Unable to connect" ### Exemple de Détection ```java catch (Exception e) { String errorMessage = e.getMessage(); if (e instanceof ConnectException || e instanceof SocketTimeoutException || (errorMessage != null && errorMessage.contains("Connection"))) { throw new KeycloakServiceException.ServiceUnavailableException(...); } } ``` --- ## 🎯 Résultat ### Avant - ❌ `NullPointerException` si Response est null - ❌ Messages d'erreur génériques (`RuntimeException`) - ❌ Pas de distinction entre types d'erreurs - ❌ Pas de gestion des codes HTTP spécifiques ### Après - ✅ Vérification de nullité systématique - ✅ Exceptions spécifiques avec messages clairs - ✅ Distinction entre erreurs de connexion, validation, autorisation, etc. - ✅ Gestion complète des codes HTTP - ✅ Logs détaillés pour le debugging --- ## 📝 Exemple d'Utilisation ### Cas 1 : Connexion Échouée (Response null) ```java Response response = usersResource.create(userRep); if (response == null) { throw new KeycloakServiceException.ServiceUnavailableException( "Impossible de se connecter au service Keycloak pour créer l'utilisateur: " + user.getUsername()); } ``` ### Cas 2 : Code HTTP d'Erreur ```java int status = response.getStatus(); if (status == 409) { throw new IllegalArgumentException("L'utilisateur existe déjà (conflit détecté par Keycloak)"); } else if (status == 503) { throw new KeycloakServiceException.ServiceUnavailableException("Service Keycloak indisponible"); } ``` ### Cas 3 : Exception de Connexion ```java catch (Exception e) { handleConnectionException(e, "création de l'utilisateur " + user.getUsername()); } ``` --- ## 🔄 Migration des Autres Méthodes Toutes les méthodes qui utilisent le client REST Keycloak ont été refactorisées pour : 1. ✅ Vérifier les objets null retournés 2. ✅ Utiliser `handleConnectionException` pour les erreurs 3. ✅ Lever des `KeycloakServiceException` au lieu de `RuntimeException` 4. ✅ Gérer spécifiquement les codes HTTP d'erreur --- ## 📚 Références - [Keycloak Admin Client Documentation](https://www.keycloak.org/docs/latest/server_development/#admin-rest-api) - [JAX-RS Response API](https://jakarta.ee/specifications/restful-ws/3.0/jakarta-restful-ws-spec-3.0.html#response) - [Java Exception Handling Best Practices](https://docs.oracle.com/javase/tutorial/essential/exceptions/) --- *Document créé le: 2025-12-18*