Namespaces
Les espaces de noms dans webforJ fournissent un mécanisme pour stocker et récupérer des données partagées à travers différents scopes dans une application web. Ils permettent la communication de données inter-composants et multiplateforme sans dépendre de techniques de stockage traditionnelles comme les attributs de session ou les champs statiques. Cette abstraction permet aux développeurs d'encapsuler et d'accéder à l'état de manière contrôlée et sécurisée pour les threads. Les espaces de noms sont idéaux pour construire des outils de collaboration multi-utilisateurs ou simplement maintenir des paramètres globaux cohérents, et vous permettent de coordonner les données en toute sécurité et efficacement.
Qu'est-ce qu'un espace de noms?
Un espace de noms est un conteneur nommé qui stocke des paires clé-valeur. Ces valeurs peuvent être accédées et modifiées à travers différentes parties de votre application selon le type d'espace de noms que vous utilisez. Pensez-y comme une carte distribuée et sécurisée pour les threads, dotée de gestion d'événements et de mécanismes de verrouillage intégrés.
Quand utiliser des espaces de noms
Utilisez des espaces de noms lorsque :
- Vous devez partager des valeurs entre des sessions utilisateur ou des composants d'application.
- Vous souhaitez réagir aux changements de valeur via des écouteurs.
- Vous avez besoin d'un verrouillage granulaire pour des sections critiques.
- Vous devez persister et récupérer l'état efficacement dans votre application.
Types d'espaces de noms
webforJ offre trois types d'espaces de noms :
| Type | Portée | Utilisation typique |
|---|---|---|
| Privé | Partagé entre les clients qui utilisent le même préfixe et le même nom. La mémoire est libérée automatiquement lorsque plus de références ne restent. | État partagé entre des sessions utilisateur connexes. |
| Groupe | Partagé par tous les threads générés depuis le même thread parent. | Coordination de l'état au sein d'un groupe de threads. |
| Global | Accessible à travers tous les threads serveurs (au niveau JVM). La mémoire est conservée tant que les clés ne sont pas explicitement supprimées. | État partagé au niveau de l'application. |
PrivateNamespaceEn cas de doute, utilisez un PrivateNamespace. Il offre un partage sûr et localisé entre des sessions connexes sans impacter l'état global ou au niveau serveur. Cela en fait un choix fiable par défaut pour la plupart des applications.
Créer et utiliser un espace de noms
Les espaces de noms sont créés en instanciant l'un des types disponibles. Chaque type définit comment et où les données sont partagées. Les exemples ci-dessous démontrent comment créer un espace de noms et interagir avec ses valeurs.
Espace de noms Privé
Le nom de l'espace de noms privé est composé de deux parties :
- Préfixe : Un identifiant défini par le développeur qui doit être unique à votre application ou module pour éviter les conflits.
- Nom de base : Le nom spécifique pour le contexte ou les données partagées que vous souhaitez gérer.
Ensemble, ils forment le nom complet de l'espace de noms en utilisant le format :
prefix + "." + baseName
Par exemple, "myApp.sharedState".
Les espaces de noms créés avec le même préfixe et nom de base font toujours référence à la même instance sous-jacente. Cela garantit un accès partagé cohérent à travers tous les appels à PrivateNamespace utilisant les mêmes identifiants.
// Créer ou récupérer un espace de noms privé
PrivateNamespace ns = new PrivateNamespace("myApp", "sharedState");
Vous pouvez vérifier l'existence avant la création :
if (PrivateNamespace.isPresent("myApp.sharedState")) {
PrivateNamespace ns = PrivateNamespace.ofExisting("myApp.sharedState");
}
Lors de la nomination d'un PrivateNamespace, suivez ces règles :
- Les deux parties doivent être non vides.
- Chacune doit commencer par une lettre.
- Seuls des caractères imprimables sont autorisés.
- Les espaces ne sont pas permis.
Exemples :
- ✓ mycrm.sessionData
- ✓ acme.analytics
- X shared.data (trop générique, susceptible de conflit)
Espaces de noms Groupe et Global
En plus de PrivateNamespace, webforJ fournit deux autres types pour des contextes de partage plus larges. Ceux-ci sont utiles lorsque l'état doit persister au-delà d'une seule session ou groupe de threads.
- Espace de noms Global : Accessible à travers tous les threads serveurs (au niveau JVM).
- Espace de noms de Groupe : Partagé parmi les threads qui proviennent du même parent.
// État partagé global, accessible à l'échelle de l'application
GlobalNamespace globalNs = new GlobalNamespace();
globalNs.put("globalTheme", "dark");
// État spécifique au groupe, limité aux threads partageant un parent commun
GroupNamespace groupNs = new GroupNamespace();
groupNs.put("localCache", new HashMap<>());
Travailler avec les valeurs
Les espaces de noms fournissent une interface cohérente pour la gestion des données partagées à travers des paires clé-valeur. Cela inclut la définition, la récupération, la suppression de valeurs, la synchronisation d'accès et l'observation des changements en temps réel.
Définir et supprimer des valeurs
Utilisez put() pour stocker une valeur sous une clé spécifique. Si la clé est actuellement verrouillée, la méthode attend jusqu'à ce que le verrou soit libéré ou que le délai d'expiration soit atteint.
// Attend jusqu'à 20ms (par défaut) pour définir la valeur
ns.put("username", "admin");
// Spécifier un délai d'expiration personnalisé en millisecondes
ns.put("config", configObject, 100);
Pour supprimer une clé de l'espace de noms :
ns.remove("username");
Les opérations put() et remove() sont des opérations bloquantes si la clé cible est verrouillée. Si le délai d'expiration est atteint avant que le verrou soit libéré, une NamespaceLockedException est levée.
Pour des mises à jour concurrentes sûres où vous n'avez besoin que de remplacer la valeur, utilisez atomicPut(). Cela verrouille la clé, écrit la valeur et libère le verrou en une seule étape :
ns.atomicPut("counter", 42);
Cela empêche les conditions de course et évite le besoin de verrouillage manuel dans des scénarios de mise à jour simples.
Obtenir des valeurs
Pour récupérer une valeur, utilisez get() :
Object value = ns.get("username");
Si la clé n'existe pas, cela lève une NoSuchElementException. Pour éviter des exceptions, utilisez getOrDefault() :
Object value = ns.getOrDefault("username", "guest");
Pour vérifier si une clé est définie :
if (ns.contains("username")) {
// la clé existe
}
Si vous souhaitez initialiser une valeur paresseusement uniquement lorsqu'elle manque, utilisez computeIfAbsent() :
Object token = ns.computeIfAbsent("authToken", key -> generateToken());
Ceci est utile pour des valeurs partagées qui sont créées une fois et réutilisées, comme des jetons de session, des blocs de configuration ou des données mises en cache.
Verrouillage manuel
Si vous devez effectuer plusieurs opérations sur la même clé ou coordonner plusieurs clés, utilisez le verrouillage manuel.
ns.setLock("flag", 500); // Attendez jusqu'à 500 ms pour le verrou
// La section critique commence
Object existing = ns.get("flag");
ns.put("flag", "in-progress");
// La section critique se termine
ns.removeLock("flag");
Utilisez ce modèle lorsque une séquence d'opérations doit être réalisée atomiquement sur des lectures et écritures. Assurez-vous toujours que le verrou est libéré pour éviter de bloquer d'autres threads.
Écoute des changements
Les espaces de noms supportent les écouteurs d'événements qui vous permettent de réagir à l'accès ou à la modification de valeurs. Ceci est utile pour des scénarios tels que :
- Journaliser ou auditer l'accès aux clés sensibles
- Déclencher des mises à jour lorsque la valeur d'une configuration change
- Surveiller les changements d'état partagé dans des applications multi-utilisateurs
Méthodes d'écouteur disponibles
| Méthode | Déclenchement | Portée |
|---|---|---|
onAccess | Toute clé est lue | Tout l'espace de noms |
onChange | Toute clé est modifiée | Tout l'espace de noms |
onKeyAccess("key") | Une clé spécifique est lue | Par clé |
onKeyChange("key") | Une clé spécifique est modifiée | Par clé |
Chaque écouteur reçoit un objet d'événement contenant :
- Le nom de la clé
- L'ancienne valeur
- La nouvelle valeur
- Une référence à l'espace de noms
Exemple : Réagir à tout changement de clé
ns.onChange(event -> {
System.out.println("Clé changée : " + event.getVariableName());
System.out.println("Ancienne valeur : " + event.getOldValue());
System.out.println("Nouvelle valeur : " + event.getNewValue());
});
Exemple : Suivre l'accès à une clé spécifique
ns.onKeyAccess("sessionToken", event -> {
System.out.println("Le jeton a été accédé : " + event.getNewValue());
});
Les écouteurs retournent un objet ListenerRegistration que vous pouvez utiliser pour désinscrire l'écouteur ultérieurement :
ListenerRegistration<NamespaceKeyChangeEvent> reg = ns.onKeyChange("status", event -> {
// logique
});
reg.remove();
Exemple : Partage de l'état du jeu dans Tic-Tac-Toe
La démonstration Tic-Tac-Toe de webforJ fournit un simple jeu à deux joueurs où les tours sont partagés entre les utilisateurs. Le projet démontre comment Namespace peut être utilisé pour coordonner l'état sans s'appuyer sur des outils externes comme des bases de données ou des API.
Dans cet exemple, un objet de jeu Java partagé est stocké dans un PrivateNamespace, permettant à plusieurs clients d'interagir avec la même logique de jeu. L'espace de noms sert de conteneur central pour l'état du jeu, garantissant que :
- Les deux joueurs voient des mises à jour de plateau cohérentes
- Les tours sont synchronisés
- La logique de jeu est partagée entre les sessions
Aucun service externe (comme REST ou WebSockets) n'est nécessaire. Toute la coordination se fait à travers des espaces de noms, soulignant leur capacité à gérer l'état partagé en temps réel avec une infrastructure minimale.
Explorez le code : webforj/webforj-tictactoe