Index: topia/doc/Todo.rst diff -u topia/doc/Todo.rst:1.2 topia/doc/Todo.rst:1.3 --- topia/doc/Todo.rst:1.2 Mon Apr 4 12:15:17 2005 +++ topia/doc/Todo.rst Tue Jul 19 12:56:08 2005 @@ -49,3 +49,7 @@ Ajouter un findByPrimary sur le PersistenceService. Ajouter lors du store une exception si un objet avec cette primaryKey existe deja. + +Vérifier que la génération des PersistenceServices est encore d'actualité, +peut être qu'en utilisant les génériques ont peut reduire les besoins de +génération. Index: topia/doc/topiaArchitecture.zuml Index: topia/doc/ChoixTech.rst diff -u /dev/null topia/doc/ChoixTech.rst:1.1 --- /dev/null Tue Jul 19 12:56:14 2005 +++ topia/doc/ChoixTech.rst Tue Jul 19 12:56:08 2005 @@ -0,0 +1,93 @@ +==================== +Motivation des choix +==================== + +La persistence +============== + + +Les besoins +----------- + +- Plusieurs types de persistence +- Simple à utiliser +- Possibilité de faire des transactions distribuées +- Le développeur ne doit utiliser que des objets Métiers +- Pas besoins de faire de Cast pour les retours de méthodes +- Les requêtes doivent être les mêmes quelques soit la persistence choisie +- Le code s'appuyant sur ToPIA ne doit pas être modifié si l'on modifie + le type de persistence voulu (seul des fichiers de propriété doivent + varier) +- Plusieurs types de persistence doit pouvoir être utilisés dans la même + application +- On doit pouvoir copier des objets d'un type de persistence à un autre +- + +Le choix +-------- + +ToPIA a besoin d'une couche de persistence qui permette au final plusieurs +type de persistence. Ceci revient au même concept que JDO, même si celui si +le plus souvent pour l'instant ne s'appuie que sur du JDBC, il est prévu +pour supporter d'autre type de persistence. + +La persistence qui serait la plus aproprié pour ToPIA serait donc JDO. +Mais le cycle de post-compilation qui est défini dans la norme ne permet +pas de faire tout ce que l'on souhaite. Par exemple la création de +librairies avec des entitées qui doivent être persistente n'est pas facile. + +Les autres techniques tel que JDBC ne s'abstrait pas assez de la base de +données et l'on est obligé au niveau de la programmation de faire attention +au mapping. + +Le choix de refaire une couche d'abstraction nous parrait donc pertinent. + + +Le choix transactionnel +----------------------- + +Il existe deux façon différent de gérer les transactions. + +La premiere est d'accrocher la transaction au thread courant, ce qui permet +de faire des appels de méthode sans paramètre supplémentaire pour la +transaction, la méthode demande au thread courant la transaction qui lui est +attaché. + +La deuxième est de créer un objet unique que l'on passe à chaque appelle de +méthode, qui permet de savoir à quelle transaction attaché l'appel. + +La première façon est simple pour le programmeur, mais ne permet pas d'avoir +plusieurs transaction pour le même thread, ou des sous transaction. + +La deuxième est beaucoup plus souple mais, oblige le développeur à passer un +paramètre supplémentaire à chaque appel. + +Nous avont donc fait un troisième choix, qui permet d'avoir la simplicité de +la première et la souplesse de la deuxième. + +Les transactions sont créées à partir de l'objet Context, au lieu d'attaché +au thread la transaction, ou de retourner un objet transaction à utilisé +pour les appels de méthodes, on retourne un nouvel objet Context qui sera +utilisé pour tous les besoins de la transaction, celui-ci conserve en +interne l'objet transaction et le donne aux méthodes qui en ont besoin. + +Lorsque l'on veut terminer la transaction il suffit d'appeler la méthode +commit ou rollback du Context. + +Il est possible d'implanter avec cette technique sous transaction, en +demandant simplement un nouveau beginTransaction sur le Context. + +:: + + TopiaContext tc1 = ...; // creation du Context initial + TopiaContext tc2 = tc1.beginTransaction(); // ouverture d'une transaction + TopiaContext tc3 = tc2.beginTransaction(); // ouverture d'une sous transaction + tc2.commitTransaction(); // les transaction tc3 et tc2 sont commitée + + +Implantation +------------ + +Derrière les interfaces d'abstraction de la persistence ToPIA on peut très +bien faire une implantation sur de la serialisation d'objet Java, utilisé +Hibernate, ou faire du JDBC. Index: topia/doc/Fonctionnement.rst diff -u /dev/null topia/doc/Fonctionnement.rst:1.1 --- /dev/null Tue Jul 19 12:56:14 2005 +++ topia/doc/Fonctionnement.rst Tue Jul 19 12:56:08 2005 @@ -0,0 +1,359 @@ +======================= +Fonctionnement de ToPIA +======================= + +Ce document doit permettre d'avoir une vue général des différentes classes +et de leurs intéractions. + + +Todo +==== +Faire un choix sur le type de commit: + +- le dernier à raison +- le dernier ne peut pas commiter (si l'entity à déjà été modifié par un autre + context, le commit échoue) + +Ecrire le fonctionnement des listeners lors d'un commit. Sans doute faut-il que les listeners enregistré sur le context initial recoivent une notification. en plus des listeners enregistré sur le context transactionnel. Cela implique que les contexts conserve une reference sur leur context pere. + +Les factories +============= + +A part le **TopiaContextFactory** toutes les autres factories sont paramètrable +dans les propriétés passées en paramètre pour la création du context. On peut +ainsi choisir une implantation différente d'une factory pour un context +particulier. + +Le context garde donc une référence sur les factories à utiliser, les autres +objets demandent donc au **TopiaContext** la factory s'il a besoin de créer un +objet. + + +TopiaContext +============ + +Le **TopiaContext** est l'élément central du framework. Il est le point d'entré +unique de tout le framework. Il permet: + +- de récupérer les PersistenceService des différents entités +- de récupérer les Services +- d'ouvrir des transactions + +Pour obtenir un **TopiaContext**, on utilise une factory en lui passant un objet +Properties pour paramètrer le **TopiaContext**. Lorsque l'on demande un context +en passant en paramètre les mêmes propriétés on obtient toujours la même +instance de **TopiaContext**. + +On ne peut pas utiliser les méthodes du **TopiaContext** autre que +**beginTransaction** lorsque l'on vient de récupérer le **TopiaContext** de la +factory. Ceci pour obliger l'ouverture d'une transaction pour la récupération +ou la modification de données. Le **TopiaContext** dans cet état n'a pas de +**TopiaPersistenceHelper** et leve une exception lors de l'utilisation des +autres méthodes. + +La méthode **beginTransaction** retourne un nouveau **TopiaContext** qui lui +contient un **TopiaPersistenceHelper**. Cette instance permet de récupérer et +modifier les entités. Les modifications faites sur les entités ne sont prise +en compte que lorsque l'on appelle la méthode **commitTransaction**. + +Un **TopiaPersistenceHelper** n'appartient qu'à un seul **TopiaContext** et +n'est jamais partagé. + +Il est possible sur n'importe quelle **TopiaContext** d'appeler la méthode +**beginTransaction** pour faire des transactions dans une transaction. + +Le **TopiaContext** est complètement configurable au travers de ses propriétés. + +- context.class +- context.helper.distribution +- context.helper.persistence +- context.helper.security +- context.helper.transaction +- context.helper.hook + +Pour chaque helper, il est possible de définir un fichier de propriété +particulier si on le souhaite en ajoutant .properties au nom de la propriété. +Par exemple pour paramètré la distribution, on peut ajouter la propriété +*context.helper.distribution.properties* + +Si la propriété distribution est spécifiée alors la propriété persistence et +transaction ne sont pas prise en compte et un proxy de distribution sera créé +pour faire les appels sur le serveur. + +TopiaPersistenceService +======================= + +Les **TopiaPersistenceService** retourné par le **TopiaContext** permet de +créer, rechercher, modifier et supprimer des entités. Durant la phase de +génération de code, à partir du Schéma UML, des **TopiaPersistenceService** +spécifique à chaque entité sont générés pour permettre d'avoir un ensemble +de méthode cohérente avec l'entité, par exemple des méthodes find sur les +différents attributs. + +Le **TopiaPersistenceService** utilise le **TopiaPersistenceHelper** que lui +donne le **TopiaContext** pour réellement faire le travaille de recherche +et de stockage. + + +TopiaPersistenceHelper +====================== + +Chaque **TopiaContext**, sauf le **TopiaContext** initial contient un +**TopiaPersistenceHelper** qui permet de gérer la persistence des entités. +Cet objet est utilisé par tous les **TopiaPersistenceService**. + +Le **TopiaPersistenceHelper** contient trois objet qui n'appartiennent qu'à +ce **TopiaPersistenceHelper** et ne sont pas partagé: + +- un **TopiaTransaction** qui lui a été passé en paramètre lors de sa création + par le **TopiaContext**. +- un **TopiaPersistenceCache** qui lui permet de retourner toujours les mêmes + instances d'entité lorsqu'on lui demande plusieurs fois la même entité au + travers de son identifiant. +- un **TopiaPersistenceStorage** qui est responsable de la conservation réelle + des entités. Cet interface à plusieurs implantation mais elle doivent toutes + respecter le même contrat, et avoir les mêmes fonctionnalités. Si le + **TopiaPersistenceStorage** utilisé ne supporte pas la recherche alors + le **TopiaPersistenceHelper** doit se charger lui même de la recherche, dans + ce cas la recherche est souvent moins rapide. + +Toutes les méthodes du **TopiaPersistenceHelper** retourne des **TopiaEntity**. + +Les méthodes **find** et **findById** appellent les méthodes équivalentes sur le +**TopiaPersistenceStorage**. Si un objet portant cette idendifiant existe déjà +dans le cache il est alors utilisé sinon un objet **TopiaPersistenceObject** est +créé à partir de l'identifiant retourné qui lui même sera encapsulé dans un +**TopiaPersistenceProxy**, et celui-ci sera ajouté dans le cache. Cette objet +n'est pas initialisé, et les données ne seront en fait récupérer que lorsque +l'application essaiera d'accèder au champs de l'objet. + +Le **TopiaPersistenceHelper** est complètement configurable au travers de ses +propriétés. + +- persistence.cache.class: classe de cache à utiliser +- persistence.storage.factory: classe de factory pour le storage à utiliser +- persistence.storage.class: classe de storage à utiliser +- persistence.storage.properties: fichier de propriété à utiliser + + +TopiaPersistenceCache +===================== + +Le **TopiaPersistenceCache** sert à conserver les instances d'objet utilisé +dans le programme, de cette façon, si le même objet est demandé plusieurs fois, +la même instance est toujours retourné pour la même transaction. + +Le **TopiaEntity** sont placé dans des **SoftReference**, pour utilisé au mieux +la mémoire. + + +TopiaTransaction +================ + +Un **TopiaTransaction** n'appartient qu'à un **TopiaPersistenceHelper**, sauf +dans le cas d'une sous transaction. Dans ce cas le **TopiaTransaction** père +contient la liste des sous transactions créer à partir de lui et les fils +pointent vers le père. + +Une transaction est défini par une date unique et toujours supérieur aux dates +des transactions créées précédement. + +Si la persistence se fait sur une machine distante qui peut-être utilisée par +plusieurs clients, la date est générée coté serveur pour éviter les incohérences +de date si les machines clientes ne sont pas toutes à la même heures. + +Lors d'un commit d'une transaction dans le **TopiaPersistenceStorage** il faut +aussi commiter les sous transactions, et de même pour le rollback. + +Lors de la création d'une nouvelle transaction dans le +**TopiaPersistenceStorage** il faut que la nouvelle transaction conserve le même +père que la précédente, et il faut que le père remplace l'ancienne transaction +de sa liste d'enfant par la nouvelle. Tout cela est fait simplement par la +méthode **regenerateTransaction** si la transaction passé en argument est une +sous transaction. + +Une transaction doit toujours être créée par le **TopiaTransactionFactory** que +l'on peut récupérer sur le **TopiaContext**, il est d'ailleur le seul à créer +des transactions lorsqu'il doit créer un **TopiaPersistenceHelper**. + +Chaque transaction pointe vers le **TopiaTransactionFactory** qui à permit de la +créer car pour les méthodes **newSubTransaction** et **regenerateTransaction** +il faut utiliser la méthode **getUniqueDate** du **TopiaTransactionFactory** + + + +TopiaPersistenceStorage +======================= + +Si l'on souhaite avoir une persistence distribué, il faut implanter un +**TopiaPersistenceStorage** qui fasse cela. Le **TopiaPersistenceHelper** lui +n'a rien a voir avec la distribution. Cela est possible car le +**TopiaPersistenceStorage** prend toujours en paramère une transaction. Pour +utiliser un **TopiaPersistenceStorage** distribué, il faut en même temps +utiliser un **TopiaTransactionFactory** distribué. + +Le **TopiaPersistenceStorage** est responsable du stockage des entités. Il y a +plusieurs implantations. Les implantations doivent implanter toutes les méthodes +sauf **find** qui est optionnelle. Dans ce cas il faut que +**haveFindImplemented** retourne false, le **TopiaPersistenceHelper** est alors +responsable des recherches mais cela est très souvent pénalisant au niveau +des performances et de l'utilisation mémoire. Cette fonctionnalité est surtout +la pour permettre de mettre en place une nouvelle implatantion rapidement pour +la tester, sachant que le plus souvent la méthode **find** est souvent la plus +lourde à implanter. + +Les implantations du **TopiaPersistenceStorage** doivent supporter une +utilisation multi-thread. + +Pour créer un **TopiaPersistenceStorage** il faut utiliser le +**TopiaPersistenceStorageFactory** qui permet de retourner la même instance du +**TopiaPersistenceStorage** si l'on passe les mêmes propriétés en paramètre. De +cette façon on est sur que la resource sous jacente si besoin n'est utilisé que +par une seul instance de **TopiaPersistenceStorage**. + +Le **TopiaPersistenceStorageFactory** pour savoir s'il doit instancier un nouvel +objet même s'il a les mêmes propriétés utilise la méthode **singletonNeeded** si +cette méthode retourne vrai alors la même instance est retourné, sinon +l'instance existante est retourné. + +Lorsque la factory à instancier un nouvelle objet, il appelle la méthode init en +lui passant les propriétés en paramètre pour que le **TopiaPersistenceStorage** +s'initialise. Cette méthode est appelé une fois et une seul durant toute la vie +de l'objet. + +Les méthodes **find** et **getAllId** retourne des **Collection** d'identifiant. + +La méthode **store** est responsable du stockage du **TopiaPersistenceObject** +dans le context de la transaction passé en paramètre, si besoin on peut devoir +restaurer l'entité avant de le stocker par exemple si l'objet n'a pas encore été +restauré mais que des champs on été modifié, suivant l'implantation, il est +possible qu'il faille faire une restauration pour faire la fusion des champs. + +La méthode **restore** est responsable de la restauration de l'objet, c'est à +dire des valeurs des données d'administration si besoin et surtout des données +des champs. Lors du restore il faut faire attention à ne pas écraser les champs +qui aurait été modifié avant la restauration. + +Si l'implantation du **TopiaPersistenceStorage** supporte l'historisation des +modifications, alors **cleanHistory** permet de ne garder l'historique que +jusqu'à la profondeur indiqué, cela permet d'allèger la base. On peut récupérer +les différentes versions avec la méthode **getHistory**, si l'implantation ne +supporte pas l'historisation alors **cleanHistory** ne fait rien et +**getHistory** retourne une **Collection** vide. + +Lorsqu'un nouveau **TopiaPersistenceHelper** va commencer à utiliser un +**TopiaPersistenceStorage**, il fera un appel à la méthode **beginTransaction**. +Il sera donc possible au **TopiaPersistenceStorage** de connaitre toutes les +transaction utilisé sur lui. + +La méthode **commit** est responsable de l'écriture des données de la +transaction en cours en données persistante dans le temps. La méthode retourne +la nouvelle transaction qui doit prendre la place de la précédente si l'on veut +continuer à utiliser le même **TopiaPersistenceHelper** alors que l'on à +commité. + +De la même façon la méthode **rollback** retourne la nouvelle transaction à +utiliser si l'on veut continer à utiliser le même objet +**TopiaPersistenceHelper** pour d'autre modification. Le **rollback** doit +supprimer tous les objets qui faisait partie de la transaction passé en +paramètre. + +La méthode **exist** permet de savoir si un objet existe pour la transaction +passé en paramètre cela implique que l'objet a déjà été créer et n'est pas +supprimé. + + +TopiaPersistenceObject +====================== + +Les **TopiaPersistenceObject** sont constitués de deux partie: + +- Les données administrative pour la gestion de la persistence et des + transactions de l'objet. +- Les données des champs de l'objet qu'il représente. + + +TopiaPersistenceProxy +===================== + +Le **TopiaPersistenceProxy** encapsule un **TopiaPersistenceObject** pour que +l'utilisateur pense utilisé une vrai entité. Cette objet implante l'interface de +l'entité. + +Lors de l'appel d'une méthode sur cette objet, il regarde comment traiter la +demande. Si la méthode apparait dans l'interface des opérations déclarées de +l'entité alors il demande au **TopiaContext** de lui donner l'objet qui lui +permettra d'appeler la méthode et fait l'appelle de méthode. + +Si ce n'est pas une opération de l'entité, cela signifie que c'est un accès au +champs de l'objet. Pour une lecture de champs, on demande alors la restauration +de l'objet si ce n'est pas déjà fait. Pour une modification on a pas besoin de +lire l'objet et on peut faire la modification tout de suite. + + +Distribution +============ + +Un context utilise la distribution dès que dans ses propriétés la valeur de +*context.helper.distribution* est présente et non vide. + +Lorsque l'on souhaite une application distribuée, les méthodes qui doivent être +appelé sur le serveur sont: + +- la persistence: **TopiaPersistenceHelper** +- la création de transaction: **TopiaTransactionHelper** +- l'appel de méthode de service: **TopiaService** +- l'appel de méthode d'entité: **TopiaEntityOperation** + +Tout le reste doit-être fait en local. On doit donc avoir un proxy qui permet +d'implanter ces interfaces de façon a faire l'appel sur le serveur grâce au +**TopiaDistributionHelper**. Les éléments qui doivent-être passés en paramètre +sont le **TopiaTransaction** courant, le nom ou la classe du service, le nom de +la méthode, les arguments de la méthode. + +Coté serveur le **TopiaTransaction** permet de récupérer le bon **TopiaContext** +et sur celui ci de récupérer le service souhaité et d'appeler la méthode. + +Le **TopiaTransaction** peut-être null lors de la création d'une nouvelle transaction. + +Implantation d'un TopiaPersistenceStorage +========================================= + +Recherche +--------- + +Si la recherche ne fait pas mieux que de charger tous les objets pour pouvoir +exécuter une recherche, alors il vaut mieux indiquer que l'on n'implante pas la +recherche. Le **TopiaPersistenceHelper** se chargera alors lui même de la +recherche, et pourra même utiliser sont cache pour de meilleurs performance. + +Stockage +-------- + +Une de meilleurs performances, il est utile de pouvoir modifier les informations +de gestion de stockage des entités sans devoir lire ou sauvé les informations +sur l'entité elle même. + +Informations à pouvoir accèder rapidement +----------------------------------------- + +Il faut pouvoir avoir la liste des identifiants des objets encore existant pour +une transaction rapidement. + +Il faut pouvoir retrouver la dernière version d'un objet pour une transaction +rapidement. + +Il faut pouvoir retrouver la liste complète des objets modifiés et créés dans +une transaction rapidement. + +L'historisation +--------------- + +Lors d'un **beginTransaction** le **TopiaPersistenceStorage** doit conserver la +transaction dans un **WeakReference**, lors d'un **commit**, d'un **rollback**, +ou que la référence est perdu, le **TopiaPersistenceStorage** peut appeler lui +même la méthode **cleanHistory** avec la profondeur défini dans les propriétés. + +La méthode **cleanHistory** lorsqu'elle s'exécute doit faire attention à ne pas +supprimer des objets qui serait encore utile pour les transactions. + Index: topia/doc/PersistenceImplantationTopia.rst diff -u /dev/null topia/doc/PersistenceImplantationTopia.rst:1.1 --- /dev/null Tue Jul 19 12:56:14 2005 +++ topia/doc/PersistenceImplantationTopia.rst Tue Jul 19 12:56:08 2005 @@ -0,0 +1,26 @@ +============================ +La persistence de type Topia +============================ + +Cette persistence est la persistance par défaut, elle utilise la serialisation +des objets dans des fichiers. + +Elle permet +- l'historisation des modifications des objets +- la conversion automatique lors du changement de schéma d'un objet +- les transactions +- les sous transactions + +Avantage +- pas besoin de base de données + +Les sous-transactions +===================== + +Lors d'un rollback d'une sous-transaction, il suffit de faire la même chose +que pour une transaction. + +Lors d'un commit d'une transaction, il ne faut pas générer un id positif pour +le commit, mais prendre l'id de la transaction parente et l'utiliser pour +les objets. + Index: topia/doc/ToPIA-uml.zuml