Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceHelper.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceHelper.java:1.1 --- /dev/null Tue Jun 28 14:08:39 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceHelper.java Tue Jun 28 14:08:34 2005 @@ -0,0 +1,340 @@ +/* *##% + * Copyright (C) 2005 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaPersistenceHelper.java + * + * Created: 22 juin 2005 10:27:31 CEST + * + * @author Benjamin POUSSIN + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2005/06/28 14:08:34 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.persistence.topia; + +import java.lang.reflect.Proxy; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.WeakHashMap; +import org.codelutin.topia.AsynchronousLoader; +import org.codelutin.topia.persistence.AbstractPersistenceHelper; +import org.codelutin.topia.TopiaContext; +import org.codelutin.topia.TopiaEntity; +import org.codelutin.topia.TopiaException; +import org.codelutin.topia.TopiaId; +import org.codelutin.topia.TopiaQuery; +import org.codelutin.topia.Util; + +public class TopiaPersistenceHelper extends AbstractPersistenceHelper { // TopiaPersistenceHelper + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Logger log = Logger.getLogger("org.codelutin.topia.persistence.topia.TopiaPersistenceHelper"); + + // plutot mettre un SoftHashMap qu'un WeakHashMap + protected Map> cache = new HashMap>(); + protected TopiaStorage storage = null; + + public TopiaPersistenceHelper(TopiaContext context, Properties properties) throws TopiaException { + super(context, properties); + String classname = getProperties().getProperty("TopiaPersistenceHelper.storage"); + Class clazz = Util.getClazz(classname); + this.storage = (TopiaStorage)Util.getInstance(clazz, new Object[]{this}); + } + + TopiaStorage getStorage(){ + return storage; + } + + public TopiaEntity create(Class entityClass) throws TopiaException { + String id = TopiaId.create(entityClass); + TopiaPersistenceObject tpo = new TopiaPersistenceObject(id); + TopiaEntity result = TopiaPersistenceProxy.newProxy(this, tpo); + + // on met a jour de la version du schema, de l'etat et la date du tpo + tpo.setSchemaVersion("1"); // TODO recupere la version dans le meta + tpo.setState(TopiaPersistenceObject.State.NEW); + TopiaTransaction tr = TopiaTransaction.get(); + if(tr.isBegin()){ + tpo.setDate(tr.getId()); + } + + // on le met dans le cache car soit il est nouveau avec une date 0 + // soit il est dans une transaction avec date = idTransaction + cachePut(tpo.getDate(), tpo.getId(), result); + + return result; + } + + protected void cachePut(long date, String id, TopiaEntity e){ + Map map = cache.get(date); + if(map == null){ + cache.put(date, map = new WeakHashMap()); + } + map.put(id, e); + } + + protected TopiaEntity cacheGet(long date, String id){ + TopiaEntity result = null; + Map map = cache.get(date); + if(map != null){ + result = map.get(id); + } + return result; + } + + protected TopiaEntity cacheRemove(long date, String id){ + TopiaEntity result = null; + Map map = cache.get(date); + if(map != null){ + result = map.remove(id); + } + return result; + } + + protected Collection cacheGet(long date){ + Collection result = null; + Map map = cache.get(date); + if(map != null){ + result = map.values(); + } else { + result = new HashSet(); + } + return result; + } + + public TopiaEntity makePersistent(TopiaEntity entity) throws TopiaException { + if(entity == null){ + throw new IllegalArgumentException("Null is not a valid entity"); + } + TopiaEntity result = entity; + TopiaPersistenceObject tpo = null; + if(!Proxy.isProxyClass(entity.getClass())){ + // TODO on a un vrai entity, il faut le convertir en tpo pour le + // stocker. Ceci arrive lorsque l'on copie un entity d'un + // context dans un autre + // !!! PROBLEME, comment fait-on lorsqu'il y a des dependances avec + // d'autre entity ? qui ne sont pas forcement deja dans ce context ? + + // Pour l'instant on leve une exception + throw new IllegalArgumentException("L'entity passé en argument doit être un proxy: " + entity.getClass().getName()); + // result = TopiaPersistenceProxy.newProxy(this, tpo); + // cachePut(tpo.getId(), tpo.getDate(), result); + } else { + tpo = ((TopiaPersistenceProxy)Proxy.getInvocationHandler(entity)).getObject(); + } + + // On ne fait des choses que s'il n'est plus stored + // c-a-d s'il est nouveau ou supprimé + if(tpo.isNew() || tpo.isRemoved()){ + long oldDate = tpo.getDate(); + // on met a jour la date du tpo + TopiaTransaction tr = TopiaTransaction.get(); + if(tr.isBegin()){ + tpo.setDate(tr.getId()); + } else { + tpo.setDate(TopiaTransaction.getNextId()); + } + tpo.setRemoved(false); + + cacheRemove(oldDate, tpo.getId()); + if(tr.isBegin()){ + // on ne le remet dans le cache que si il est dans une + // transaction, sinon il faudra toujours le rechercher + cachePut(tpo.getDate(), tpo.getId(), result); + } + + storage.store(tpo); + } + + return result; + } + + public TopiaEntity update(TopiaEntity entity) throws TopiaException { + // on fait la meme chose que makePersistent + return makePersistent(entity); + } + + public void delete(TopiaEntity entity) throws TopiaException { + if(entity != null && Proxy.isProxyClass(entity.getClass())){ + TopiaPersistenceObject tpo = ((TopiaPersistenceProxy)Proxy.getInvocationHandler(entity)).getObject(); + + // on l'enleve du cache + cacheRemove(tpo.getDate(), tpo.getId()); + // on le marque comme supprimé + tpo.setRemoved(true); + // on met a jour la date du tpo + TopiaTransaction tr = TopiaTransaction.get(); + if(tr.isBegin()){ + tpo.setDate(tr.getId()); + } else { + tpo.setDate(TopiaTransaction.getNextId()); + } + if(!tpo.isNew()){ + // on le stocke que s'il etait deja stocké + getStorage().store(tpo); + } else if(tr.isBegin()){ + // si c'etait un nouvel objet et qu'on etait dans une + // transaction alors on le supprime directement du storage + // pour ne pas polluer avec des objets inutils le storage + storage.remove(tpo); + } + } + } + + public List find(TopiaQuery query) throws TopiaException { + return null; + } + + public AsynchronousLoader findAsynchronously(TopiaQuery query) throws TopiaException { + return null; + } + + public List findInRange(TopiaQuery query, int startIndex, int endIndex) throws TopiaException { + return null; + } + + public AsynchronousLoader findInRangeAsynchronously(TopiaQuery query, int startIndex, int endIndex) throws TopiaException { + return null; + } + + public int size(TopiaQuery query) throws TopiaException { + return 0; + } + + public void importXML(String xml) throws TopiaException { + + } + + public String exportXML() throws TopiaException { + return null; + } + + public TopiaEntity findByTopiaId(String id) throws TopiaException { + TopiaEntity result = null; + + long last = 0; + TopiaTransaction tr = TopiaTransaction.get(); + if(tr.isBegin()){ + last = tr.getId(); + result = cacheGet(last, id); + } + + if(result == null){ + TopiaPersistenceObject tpo = new TopiaPersistenceObject(id); + tpo.setDate(last); + result = TopiaPersistenceProxy.newProxy(this, tpo); + if(tr.isBegin()){ + // pour l'instant on ne met en cache que ce qui est dans une + // transaction, car sinon, il vaut mieux toujours verifier + // qu'il n'y a pas une nouvelle version apparu dans le storage + cachePut(last, id, result); + } + } + return result; + } + + /** + * Inquique au StorageThread d'abandonner les chargements et + * de faire les sauvegarde en priorité et + * attend que le thread finisse de sauvegarder + */ + public void destroy() { } + + public synchronized void beginTransaction() throws TopiaException { + TopiaTransaction tr = TopiaTransaction.get(); + tr.begin(); + } + + public synchronized void commitTransaction() throws TopiaException { + TopiaTransaction tr = TopiaTransaction.get(); + tr.commit(); + long date = getStorage().commit(tr.getId()); + // suppression du cache de tous les objets de la transaction + // et modification des dates des objets en utilisant la date retournée + for(TopiaEntity e: cacheGet(tr.getId())){ + TopiaPersistenceObject o = (TopiaPersistenceObject)Proxy.getInvocationHandler(e); + o.setDate(date); + } + // suppression du cache des objets de cette transaction + cache.remove(tr.getId()); + } + + public synchronized void rollbackTransaction() throws TopiaException { + TopiaTransaction tr = TopiaTransaction.get(); + tr.rollback(); + getStorage().rollback(tr.getId()); + // suppression du cache des objets de cette transaction + cache.remove(tr.getId()); + } + + /** + * Permet de nettoyer l'historique pour ne conservé que les derniers + * objets valides. Cette méthode est appelé lorsque la transaction la + * plus ancienne se termine ou lorsque l'utilisateur demande explicitement + * le nettoyage de la base par exemple au demarrage de son application. + */ + protected void cleanHistory(){ + int depth = -1; + String depthString = getProperties().getProperty("TopiaPersistenceHelper.history.depth"); + if(depthString != null && depthString.matches("-?[0-9]+")){ + depth = Integer.parseInt(depthString); + } + if(depth >= 0){ + getStorage().cleanHistory(depth); + } + } + + /** + * Permet de convertir un objet dans la version de schema la plus recente + * pour cela utilise la classe definie dans le fichier de configuration + * dans la propriete "TopiaPersistenceHelper.converter". + * Cette classe doit contenir des methodes de type + * static public TopiaPersistenceObject convert__(TopiaContext, TopiaPersistenceObject) + */ + protected TopiaPersistenceObject convert(TopiaPersistenceObject o) throws TopiaException { + try{ + TopiaPersistenceObject result = o; + // recuperation de la classe permettant de faire la conversion + String classname = this.getProperties().getProperty("TopiaPersistenceHelper.converter"); + if(classname != null && !"".equals(classname)){ + Class converter = Class.forName(classname); + // TODO la conversion + // recherche de la sequence de methode de clazz permettant de convertir + // l'objet dans la bonne version + } + return result; + }catch(Exception eee){ + throw new TopiaException("Erreur du la conversion de l'objet: " + o.getId() + " schemaVersion: " + o.getSchemaVersion()); + } + } + +} // TopiaPersistenceHelper + Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceObject.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceObject.java:1.1 --- /dev/null Tue Jun 28 14:08:39 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceObject.java Tue Jun 28 14:08:34 2005 @@ -0,0 +1,251 @@ +/* *##% +* Copyright (C) 2005 +* Code Lutin, Cédric Pineau, Benjamin Poussin +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*##%*/ + +/* * +* TopiaPersistenceObject.java +* +* Created: 22 juin 2005 11:43:49 CEST +* +* @author Benjamin POUSSIN +* @version $Revision: 1.1 $ +* +* Last update: $Date: 2005/06/28 14:08:34 $ +* by : $Author: bpoussin $ +*/ + +package org.codelutin.topia.persistence.topia; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.Map; +import java.util.Set; + +/** +* Cet objet permet de representer un objet au travers d'une map, avec en plus +* des champs d'administration pour le stockage. +* L'objet peut avoir plusieurs etat: dans une transation ou non, créer, stocker, +* supprimé. +*

+* Créer indique que l'objet n'a pas encore été ajouté au stokage (date == -1). +* Stocker indique que l'objet est dans le stokage (date > 0) +* supprimé indique que l'objet à été supprimé du stockage (removed == true) +*

+* L'objet est dans une transaction si inTransaction == true, dans ce cas +* date == id de la transaction. +*/ +class TopiaPersistenceObject { // TopiaPersistenceObject + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Logger log = Logger.getLogger("org.codelutin.topia.persistence.topia.TopiaPersistenceObject"); + + static final Object UNLOADED_FIELD = new Object(); + static enum State { + /** l'objet n'a pas d'etat connu */ + UNKNOW, + /** l'objet vient d'etre creer mais n'a jamais ete sauvé */ + NEW, + /** l'objet a deja ete sauvé */ + STORED; + } + + /** l'id de l'objet */ + protected String id = null; + /** id de la transaction ou date de commit de l'objet + negatif si cet objet appartient a une transaction. Ce champs n'est pas + sauver car il est dans le nom du fichier de sauvegarde */ + transient protected long date = 0; + /** vrai si l'objet a ete supprimé */ + transient protected boolean removed = false; + /** Etat de l'objet */ + transient protected State state = State.UNKNOW; + /** version de schema de l'objet (definition des champs */ + protected String schemaVersion = null; + /** version de l'objet */ + protected String version = null; + + /** map contenant les champs */ + protected Map fields = new HashMap(); + + /** Liste des champs qui ont été modifié */ + transient protected Set modifiedFields = new HashSet(); + /** Liste des champs qui ont été demandé mais pas encore chargé */ + transient protected Set askedFields = new HashSet(); + + TopiaPersistenceObject(){} + + TopiaPersistenceObject(String id){ + setId(id); + } + + /** + * Get id property. + * + *@return Id property. + */ + String getId() { + return this.id; + } + + /** + * Set id property. + * + *@param id New id property. + */ + void setId(String id) { + this.id = id; + } + + State getState(){ + return state; + } + + void setState(State state){ + this.state = state; + } + + /** + * Get inTransation property. + * + *@return InTransation property. + */ + boolean isInTransation() { + return getDate() < 0; + } + + /** + * Get removed property. + * + *@return Removed property. + */ + boolean isRemoved() { + return removed; + } + + void setRemoved(boolean v){ + this.removed = v; + } + + boolean isNew(){ + return getState() == State.NEW; + } + + boolean isStored(){ + return getState() == State.STORED; + } + + /** + * Get date property. + * + *@return Date property. + */ + long getDate() { + return this.date; + } + + /** + * Set date property. + * + *@param date New date property. + */ + void setDate(long date) { + this.date = date; + } + + + /** + * Get schemaVersion property. + * + *@return SchemaVersion property. + */ + String getSchemaVersion() { + return this.schemaVersion; + } + + /** + * Set schemaVersion property. + * + *@param schemaVersion New schemaVersion property. + */ + void setSchemaVersion(String schemaVersion) { + this.schemaVersion = schemaVersion; + } + + + /** + * Get version property. + * + *@return Version property. + */ + String getVersion() { + return this.version; + } + + /** + * Set version property. + * + *@param version New version property. + */ + void setVersion(String version) { + this.version = version; + } + + /** + * Modifie la valeur d'un champs et le supprime de la liste des champs + * a charger. + */ + void setField(String fieldName, Object value){ + if(value == UNLOADED_FIELD){ + throw new IllegalArgumentException("La variable UNLOADED_FIELD ne peut pas être affectée à un champs"); + } + synchronized(UNLOADED_FIELD) { + loadField(fieldName, value); + modifiedFields.add(fieldName); + } + } + + /** + * Retourne un champs, si le champs n'est pas encore chargé retourne + * UNLOADED_FIELD et ajoute ce champs comme champs a charger. + */ + Object getField(String fieldName){ + Object result = UNLOADED_FIELD; + if(fields.containsKey(fieldName)){ + result = fields.get(fieldName); + } else { + askedFields.add(fieldName); + } + return result; + } + + void loadField(String fieldName, Object value){ + fields.put(fieldName, value); + askedFields.remove(fieldName); + } + + Map getAllFields(){ + return fields; + } + void setAllFields(Map fields){ + this.fields = fields; + } + +} // TopiaPersistenceObject + Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceProxy.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceProxy.java:1.1 --- /dev/null Tue Jun 28 14:08:39 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceProxy.java Tue Jun 28 14:08:34 2005 @@ -0,0 +1,141 @@ +/* *##% + * Copyright (C) 2005 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaPersistenceProxy.java + * + * Created: 22 juin 2005 12:31:52 CEST + * + * @author Benjamin POUSSIN + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2005/06/28 14:08:34 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.persistence.topia; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.codelutin.topia.TopiaException; +import org.codelutin.topia.TopiaEntity; +import org.codelutin.topia.TopiaId; +import org.codelutin.util.StringUtil; + +/** +* Permet de créer un proxy pour un id donnée. On extrait de l'id l'interface +* a utiliser. +*/ +public class TopiaPersistenceProxy implements InvocationHandler { // TopiaPersistenceProxy + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Logger log = Logger.getLogger("org.codelutin.topia.persistence.topia.TopiaPersistenceProxy"); + + static public TopiaEntity newProxy(TopiaPersistenceHelper tph, TopiaPersistenceObject o) throws TopiaException { + Class clazz = TopiaId.getClassName(o.getId()); + TopiaEntity result = (TopiaEntity) Proxy.newProxyInstance(TopiaPersistenceProxy.class.getClassLoader(), + new Class[] { clazz }, new TopiaPersistenceProxy(tph, o)); + return result; + } + + protected TopiaPersistenceObject o = null; + protected TopiaPersistenceHelper tph = null; + + protected TopiaPersistenceProxy(TopiaPersistenceHelper tph, TopiaPersistenceObject o){ + this.tph = tph; + this.o = o; + } + + public TopiaPersistenceObject getObject(){ + return o; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + if(methodName.startsWith("get") || methodName.startsWith("is")){ + int prefix = methodName.startsWith("get")?3:2; + String fieldName = StringUtil.uncapitalize(methodName.substring(prefix)); + + Object result = o.getField(fieldName); + if(result == o.UNLOADED_FIELD){ + if(o.isNew()){ + // TODO retourner la valeur par defaut defini dans le meta + + // si pas de valeur par defaut dans le meta, on retourne + // la valeur par defaut Java + Class returnType = method.getReturnType(); + if(returnType.equals(Boolean.TYPE)){ + result = Boolean.FALSE; + } else if(returnType.equals(Character.TYPE)){ + result = Character.valueOf(Character.MIN_VALUE); + } else if(returnType.equals(Byte.TYPE)){ + result = Byte.valueOf((byte)0); + } else if(returnType.equals(Short.TYPE)){ + result = Short.valueOf((short)0); + } else if(returnType.equals(Integer.TYPE)){ + result = Integer.valueOf(0); + } else if(returnType.equals(Long.TYPE)){ + result = Long.valueOf(0); + } else if(returnType.equals(Float.TYPE)){ + result = Float.valueOf(0); + } else if(returnType.equals(Double.TYPE)){ + result = Double.valueOf(0); + } else { + // pour tous les autres cas la valeur par defaut est null + result = null; + } + } else { + tph.getStorage().restore(o); + result = o.getField(fieldName); + } + } + if(result instanceof String && TopiaId.isValidId((String)result)){ + // TODO peut-etre verifier que l'objet pointer par l'id + // existe reelement et si ce n'est pas le cas, mettre + // a null le result et afficher un message WARNING + result = tph.findByTopiaId((String)result); + } + return result; + } else if(methodName.startsWith("set")){ + String fieldName = StringUtil.uncapitalize(methodName.substring(3)); + Object value = args[0]; + if(value instanceof TopiaEntity){ + value = ((TopiaEntity)value).get_topiaId_(); + } + o.setField(fieldName, value); + // on met a jour la date du tpo + TopiaTransaction tr = TopiaTransaction.get(); + if(tr.isBegin()){ + o.setDate(tr.getId()); + } else { + o.setDate(TopiaTransaction.getNextId()); + } + tph.getStorage().store(o); + } else{ + // FIXME gérer les appels de méthode spécifique d'attribut (add, remove, ...) + // FIXME gérer les appels de méthode + } + return null; + } + +} // TopiaPersistenceProxy + Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorage.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorage.java:1.1 --- /dev/null Tue Jun 28 14:08:39 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorage.java Tue Jun 28 14:08:34 2005 @@ -0,0 +1,103 @@ +/* *##% + * Copyright (C) 2005 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaStorage.java + * + * Created: 22 juin 2005 12:02:07 CEST + * + * @author Benjamin POUSSIN + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2005/06/28 14:08:34 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.persistence.topia; + +import java.util.Set; +import org.codelutin.topia.TopiaException; +import org.codelutin.generator.models.object.ObjectModel; + +public interface TopiaStorage { // TopiaStorage + + /** + * Cette méthode doit rendre persistent le TopiaEntity passé en paramètre + * @throw TopiaException si la sauvegarde n'a pas eu lieu. Par exemple + * si l'on demande la sauvegarde d'un objet qui faisait partie d'une + * transaction qui n'existe plus. + */ + public void store(TopiaPersistenceObject e) throws TopiaException; + + /** + * Cette méthode doit restaurer le TopiaEntity passé en paramètre + */ + public void restore(TopiaPersistenceObject e) throws TopiaException; + + /** + * commit la transaction aillant l'id passé en paramètre + * @return la date utilisé pour commiter les objets + */ + public long commit(long transactionId) throws TopiaException; + + /** + * rollback la transaction aillant l'id passé en paramètre + */ + public void rollback(long transactionId) throws TopiaException; + + /** + * Retourne la liste de tous les objets existants pour une date donnée. + * @param date la date pour lequel on souhaite les id. Si date est négative + * alors date represente l'id d'une transaction. Les id retourné sont donc + * tous les id existant plus ou moins les id de la transaction. + * Si date vaut 0, alors tous les objets existant sont retourné + * si date est superieur a 0 alors seul les objets existant a cette date + * comprise seront retourné. + * @return les id des objets existant + */ + public Set getAllId(long date) throws TopiaException; + + /** + * Nettoie l'historique des objets pour ne conserver que depth en plus + * de la derniere version. Si depth == 0 aucun historique n'est conservé + * si depth == -1 alors toutes les versions sont conservée. + */ + public void cleanHistory(int depth); + + /** + * Demande explicitement la suppression d'un objet. Ne doit etre normalement + * utilisé que pour supprimer des nouveaux objets d'une transaction qui + * ont ete supprimer durant la transaction. + */ + public void remove(TopiaPersistenceObject e) throws TopiaException; + + /** + * Permet de stocker un objet model dans le storage + */ + public void storeSchema(ObjectModel schema); + + /** + * Permet de recuperer un objet model dans le storage + * @param version la version du schema souhaité, null pour avoir le dernier + * schema + */ + public ObjectModel getSchema(String version); + +} // TopiaStorage + Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorageSerialization.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorageSerialization.java:1.1 --- /dev/null Tue Jun 28 14:08:39 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorageSerialization.java Tue Jun 28 14:08:34 2005 @@ -0,0 +1,409 @@ +/* *##% + * Copyright (C) 2005 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaStorageSerialization.java + * + * Created: 22 juin 2005 12:30:39 CEST + * + * @author Benjamin POUSSIN + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2005/06/28 14:08:34 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.persistence.topia; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.Set; +import org.codelutin.generator.models.object.ObjectModel; +import org.codelutin.topia.TopiaException; + +public class TopiaStorageSerialization implements TopiaStorage { // TopiaStorageSerialization + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Logger log = Logger.getLogger("org.codelutin.topia.persistence.topia.TopiaStorageSerialization"); + + enum State { + ALL { String getPattern(){return "(STORED|REMOVED)";}}, + STORED { String getPattern(){return "STORED";}}, + REMOVED { String getPattern(){return "REMOVED";}}; + + abstract String getPattern(); + } + + enum FilenameComponent { + DATE { String get(String filename){return filename.split("=")[0];}}, + STATE { String get(String filename){return filename.split("=")[1];}}, + ID { String get(String filename){return filename.split("=")[2];}}, + SCHEMA_VERSION { String get(String filename){return filename.split("=")[3];}}; + + abstract String get(String filename); + } + + class PatternFilenameFilter implements FilenameFilter { + protected long lastDate = 0; + protected Pattern pattern = null; + + public PatternFilenameFilter(Pattern pattern){ + this(pattern, 0); + } + /** + * Recherche tous les fichier correspondant au pattern, et dont la + * date est inferieur ou egal a lastDate. + * @param pattern le pattern que doit respecter le fichier + * @param lastDate la date a ne pas depasser, si date == 0 alors + * on ne verifie pas la date, tous les fichiers correspondant + * au pattern conviennent. + */ + public PatternFilenameFilter(Pattern pattern, long lastDate){ + this.pattern = pattern; + this.lastDate = lastDate; + } + + public boolean accept(File dir, String name){ + boolean result = false; + if(pattern.matcher(name).matches()){ + if(lastDate != 0){ + String date = FilenameComponent.DATE.get(name); + result = lastDate <= Long.parseLong(date); + } else { + result = true; + } + } + return result; + } + } + + protected String getFilename(TopiaPersistenceObject e){ + return getFilePatternString(e.getDate(), + e.isRemoved()?State.REMOVED:State.STORED, + e.getId(), + e.getSchemaVersion()); + } + + /** + * Permet de creer un pattern pour recherche plus facilement un fichier + * @param date si -1 alors recherche toutes les transactions, si 1 recherche + * tous les objets commité, si 0 recherche tous les objets + * autrement recherche la valeur exacte de date + * @param state l'etat dans lequel doit etre l'objet recherché + * @param id si null alors recherche tous les id, sinon recherche l'id exacte + * @param version la version de schema que l'objet doit avoir, si null toutes + * les versions, sinon recherche une version specifique + */ + static protected String getFilePatternString(long date, State state, String id, String version){ + String result = ""; + if(date == -1){ + // -1 indique qu'on recherche une transaction + result += "-[0-9]+="; + } else if(date == 1){ + // 1 indique qu'on recherche n'import quel objet pas dans une transaction + result += "[0-9]+="; + } else if(date == 0){ + result += "-?[0-9]+="; + } else { + // sinon c une vrai date a rechercher exactement + result += dateFormat.format(date) + "="; + } + + result += state.getPattern() + "="; + + if(id == null){ + result += ".*?="; + } else { + result += id + "="; + } + + if(version == null){ + result += ".*?"; + } else { + result += version; + } + + return result; + } + + static protected Pattern getFilePattern(long date, State state, String id, String version){ + return Pattern.compile("^" + getFilePatternString(date, state, id, version) + "$"); + } + + protected File directory = null; + static protected DecimalFormat dateFormat = new DecimalFormat("00000000000000000000"); + protected TopiaPersistenceHelper tph = null; + + /** + * @param directory le répertoire on les objets sont conservés + */ + public TopiaStorageSerialization(TopiaPersistenceHelper tph){ + this.tph = tph; + this.directory = new File(tph.getProperties().getProperty("TopiaStorageSerialization.directory")); + this.directory.mkdirs(); + } + + /** + * lors de la sauvegarde tous les champs sont sauvé, il faut donc + * que tous les champs soit chargé lorsque l'on fait un restore + */ + public void store(TopiaPersistenceObject e) throws TopiaException { + // On verifie qu'il faut le sauver + if(e.getDate() < 0 && !TopiaTransaction.isValidTransaction(e.getDate())){ + throw new TopiaException("Tentative de sauvegarde d'un objet de transaction, dont la transaction(" + e.getDate() + ") est terminée: " + e.getId()); + } + + // si l'objet n'est pas encore chargé il faut le faire pour que la + // sauvegarde se passe bien + restore(e); + + File f = new File(directory, getFilename(e)); + while(e.getDate() > 0 && f.exists()){ + e.setDate(e.getDate() + 1); + f = new File(directory, getFilename(e)); + } + try{ + FileOutputStream fout = new FileOutputStream(f); + ObjectOutputStream out = new ObjectOutputStream(fout); + out.writeObject(e); + fout.close(); + // on a reussi a l'ecrire on change sont etat + e.setState(TopiaPersistenceObject.State.STORED); + }catch(IOException eee){ + throw new TopiaException("Erreur durant la sauvegarde de l'objet: " + e.getId(), eee); + } + } + + /** + * Si l'objet n'existe pas pour la date demandée, alors on recherche + * la version ayant la date la plus récente. + */ + public void restore(final TopiaPersistenceObject e) throws TopiaException { + if(e.isNew() || e.isStored()){ + // si l'objet est nouveau alors on ne pourra pas le lire car + // il n'existe pas + // si l'objet est stored alors c que la lecture a deja ete faite + // donc ont ne le refait pas + return ; + } + + File f = new File(directory, getFilename(e)); + if(!f.exists()){ + long last = e.getDate(); + if(last < 0){ + last = - last; + } + + // recherche de l'objet le plus récent par rapport a last + // mais n'appatenant pas a une transaction + String [] files = directory.list(new PatternFilenameFilter( + getFilePattern(1, State.ALL, e.getId(), null), last)); + + if(files != null && files.length > 0){ + Arrays.sort(files); + // le dernier est le plus recent + f = new File(directory, files[files.length-1]); + } + } + // si le fichier n'existe pas ou qu'il est marqué supprimé + if(!f.exists() || "REMOVED".equals(FilenameComponent.STATE.get(f.getName()))){ + throw new TopiaException("Object not found: " + e.getId()); + } + try{ + FileInputStream fin = new FileInputStream(f); + ObjectInputStream in = new ObjectInputStream(fin); + TopiaPersistenceObject o = (TopiaPersistenceObject)in.readObject(); + fin.close(); + + // on met a jour les champs de l'objet si besoin + o = tph.convert(o); + + // on fusionne les 2 objets + // on ecrase les champs de l'objet chargé, par ceux que l'utilisateur + // a modifié + o.getAllFields().putAll(e.getAllFields()); + e.setAllFields(o.getAllFields()); + + // on indique que tout est chargé + e.askedFields.clear(); + + // on met a jour la date si besoin + if(e.getDate() == 0){ + String date = FilenameComponent.DATE.get(f.getName()); + e.setDate(Long.parseLong(date)); + } + + // on met a jour la version de l'objet si besoin + if(e.getVersion() == null){ + e.setVersion(o.getVersion()); + } + + // on met a jour l'etat on vient de le + // lire, sont etat doit etre STORED + e.setState(TopiaPersistenceObject.State.STORED); + + }catch(IOException eee){ + throw new TopiaException("Erreur durant la restoration de l'objet: " + e.getId(), eee); + }catch(ClassNotFoundException eee){ + throw new TopiaException("Erreur durant la restoration de l'objet: " + e.getId(), eee); + } + } + + public long commit(final long transactionId) throws TopiaException{ + // Il faut que tous les objets soit sauvé arrivé ici + // Pour l'instant tout est synchrone donc tout doit etre sauvé + + long date = TopiaTransaction.getNextId(); + // pour commiter une transaction il suffit de renommer les fichiers + // de la transaction. + File [] files = directory.listFiles(new PatternFilenameFilter( + getFilePattern(transactionId, State.ALL, null, null))); + + for(File f: files){ + String name = f.getName().replaceFirst("-[0-9]+", dateFormat.format(date)); + File to = new File(directory, name); + f.renameTo(to); + } + + return date; + } + + public void rollback(long transactionId) throws TopiaException{ + // Pour un rollback il suffit de supprimer tous les fichiers de la + // transaction. + File [] files = directory.listFiles(new PatternFilenameFilter( + getFilePattern(transactionId, State.ALL, null, null))); + + for(File f: files){ + f.delete(); + } + } + + /** + * Retourne tous les objets jusqu'a une certaine date + * @param date si 0 alors tous les objets commité, si negatif + * alors tous les objets visible pour la transaction, si position + * alors tous les objets visible jusqu'a la date comprise + */ + public Set getAllId(long date) throws TopiaException{ + HashSet result = new HashSet(); + + long last = date; + if(date == 0){ + last = TopiaTransaction.getNextId(); + } else if(date < 0){ + last = - date; + } + + // on recherche tous les objets qui exist jusqu'a last + String [] files = directory.list(new PatternFilenameFilter( + getFilePattern(1, State.ALL, null, null), last)); + + Arrays.sort(files); + for(String name: files){ + String id = FilenameComponent.ID.get(name); + if("STORED".equals(FilenameComponent.STATE.get(name))){ + result.add(id); + } else { + result.remove(id); + } + } + + // si date est un id de transaction on ajoute les nouveaux objet + // et on supprime les objets supprimé + if(date < 0){ + // on recherche tous les objets de la transaction + files = directory.list(new PatternFilenameFilter( + getFilePattern(date, State.ALL, null, null))); + + for(String name: files){ + String id = name.substring(name.lastIndexOf('=') + 1); + if("STORED".equals(FilenameComponent.STATE.get(name))){ + result.add(id); + } else { + result.remove(id); + } + } + } + return result; + } + + public void remove(TopiaPersistenceObject e) throws TopiaException{ + File f = new File(directory, getFilename(e)); + if(f.exists()){ + f.delete(); + } + } + + public void cleanHistory(int depth){ + if(depth >= 0){ + Map count = new HashMap(); + // on recherche tous les objets qui exist jusqu'a last + String [] files = directory.list(new PatternFilenameFilter( + getFilePattern(1, State.ALL, null, null))); + + Arrays.sort(files); + for(int i=files.length-1; i>=0; i--){ + String id = FilenameComponent.ID.get(files[i]); + Long c = count.get(id); + if(c == null){ + c = 0L; + } else { + c++; + } + count.put(id, c); + if(c > depth){ + File f = new File(directory, files[i]); + f.delete(); + } + } + } + } + + public void storeSchema(ObjectModel schema){ +/* +File f = new File(directory, "schema-" + schema.getVersion()); + FileWriter out = new FileWriter(f); + out.write(schema.toXML()); + out.close(); +*/ + } + + public ObjectModel getSchema(String version){ + // TODO relecture du schema + + return null; + } + +} // TopiaStorageSerialization + Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaTransaction.java diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/topia/TopiaTransaction.java:1.1 --- /dev/null Tue Jun 28 14:08:39 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaTransaction.java Tue Jun 28 14:08:34 2005 @@ -0,0 +1,190 @@ +/* *##% + * Copyright (C) 2005 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaTransaction.java + * + * Created: 22 juin 2005 10:58:02 CEST + * + * @author Benjamin POUSSIN + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2005/06/28 14:08:34 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.persistence.topia; + +import java.util.HashSet; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.codelutin.topia.TopiaException; + +/** +* Un identifiant de transaction est l'opposé de la date de creation de +* la transaction en milliseconde. +* Si la transaction est créer à la date 1119540175357, alors son id sera +* -1119540175357. Si cette id est dejà utilisé alors on passe au suivant (-1) +*/ +public class TopiaTransaction implements Comparable { // TopiaTransaction + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Logger log = Logger.getLogger("org.codelutin.topia.persistence.topia.TopiaTransaction"); + + public enum State { + /** indique qu'il n'y a pas de transaction */ + TRANSACTION_NONE, + /** indique qu'on est dans une transaction en cours */ + TRANSACTION_BEGIN, + /** indique que l'utilisateur a demandé un commit de la transaction */ + TRANSACTION_COMMIT, + /** indique que l'utilisateur a demandé un rollback de la transaction */ + TRANSACTION_ROLLBACK; + } + + protected long id = 0; + + /** contient la liste des transactions valide */ + static protected HashSet validTransation = new HashSet(); + + /** l'etat de la transaction */ + protected State state = State.TRANSACTION_NONE; + + protected TopiaTransaction(){ + } + + /** + * Retourne l'id de la transaction + */ + public long getId(){ + return id; + } + + /** + * Utilisé par le framework + */ + State getState(){ + return state; + } + + void setState(State state){ + this.state = state; + } + + public boolean isBegin(){ + return getState() == State.TRANSACTION_BEGIN; + } + + /** + * Commence une transaction et met l'id a jour + */ + public void begin() throws TopiaException { + if(isBegin()){ + throw new TopiaException("Allready in transaction"); + } + // l'id d'un transaction est negatif + id = - getNextId(); + validTransation.add(id); + setState(State.TRANSACTION_BEGIN); + } + + public void commit() throws TopiaException { + if(!isBegin()){ + throw new TopiaException("Transaction not open"); + } + validTransation.remove(id); + setState(State.TRANSACTION_COMMIT); + } + + public void rollback() throws TopiaException { + if(!isBegin()){ + throw new TopiaException("Transaction not open"); + } + validTransation.remove(id); + setState(State.TRANSACTION_ROLLBACK); + } + + public int compareTo(TopiaTransaction o){ + if(getId() < o.getId()){ + return -1; + } else if(getId() > o.getId()){ + return 1; + } else { + return 0; + } + } + + public int hashCode(){ + return (int)(getId() ^ (getId() >>> 32)); + } + + public boolean equals(Object o){ + return o instanceof TopiaTransaction + && getId() == ((TopiaTransaction)o).getId(); + } + + /** l'id de la derniere transaction retourné */ + private static long lastId = 0; + + /** + * Permet de modifier la valeur du dernier id retrouné. Cette méthode est + * utilisé par le framework lors de l'initialisation. + */ + static void setLastId(long id){ + lastId = id; + } + + /** + * Permet d'avoir un id unique par rapport au temps systeme. + * Utilisé aussi pour les dates de modif des objets qui doivent + * aussi etre unique pour le meme id + */ + static synchronized long getNextId(){ + long result = System.currentTimeMillis(); + // l'id de la transaction retourné doit toujours etre unique + // et superieur a la precedente en valeur absolue + if(result <= lastId){ + result = lastId + 1; + } + lastId = result; + return result; + } + + private static InheritableThreadLocal transaction = + new InheritableThreadLocal() { + protected TopiaTransaction initialValue() { + return new TopiaTransaction(); + } + }; + + /** + * Retourne la transaction pour le Thread courant. Si le thread n'avait pas + * de transaction alors, une transaction est créée avec + * l'etat TRANSACTION_NONE + */ + public static TopiaTransaction get() { + return transaction.get(); + } + + public static boolean isValidTransaction(long id){ + return validTransation.contains(id); + } + +} // TopiaTransaction + Index: topia/src/java/org/codelutin/topia/persistence/topia/package.html diff -u /dev/null topia/src/java/org/codelutin/topia/persistence/topia/package.html:1.1 --- /dev/null Tue Jun 28 14:08:39 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/package.html Tue Jun 28 14:08:34 2005 @@ -0,0 +1,125 @@ + + +Lorsque l'on sauve un objet on remet sa date à la date de sauvegarde, cette date pour cette objet doit-être unique, si on arrive a essayer de sauver un fichier avec le même nom qu'un autre hors d'une transaction, alors on incremente la date de 1 pour que la date soit vraiment unique. Ca pourrait arriver dans de tres rare cas, ou on arrive a faire deux store tres proche l'un de l'autre. + +Hors d'une transaction (ou lors d'un commit): + Objet demandé par l'utilisateur: synchrone + Objet prefetcher: asynchone + Objet sauver: asynchrone || synchrone (suivant implantation du cache apres fin transaction) + +Dans une transaction (ou lors d'un rollback): + Objet demandé par l'utilisateur: synchrone + Objet prefetcher: asynchone + Objet sauver: asynchone (car dispo dans le cache) + +Sauvegarde des objets +Lors de la sauvegarde la date n'est pas conservé dans l'objet, de cette facon lors du commit, il suffit de modifier le nom des fichiers de la transation. Pour cela il faut qu'a la restoration on modifie la date en fonction du nom du fichier. + +Le nom du fichier est creer a partir de l'id et de la date séparé par le signe =. + +On peut retrouver tres simplement les fichiers de transaction car le signe egal est suivi du signe -. + +Lors d'un commit il faut que tous les objets de la transaction soit sauver au prealable. Il suffit ensuite de renommer tous les fichiers en modifiant la date qui suit le signe egal. + +Probleme potentiel +Lorsque l'on creer un objet dans une transaction, que l'on supprime dans la meme transaction, il serait bon qu'il n'apparaisse pas lors du commit. Il faut donc géré ces objets un peu différement. + +Probleme que ce passe-t-il si on prend un objet hors transaction, que l'on commence une transaction et qu'on utilise l'objet ? A priori, il est automatiquement pris en compte dans la transaction. + +Le Cache +Pour l'instant seul les objets nouveaux et les objets de transaction sont mis +en cache. Une fois l'objet sauver ou commité il est retiré du cache. Car Il +pour l'instant pour ne pas compliqué la gestion du cache, il vaut mieux +relire depuis le storage les objets qui hors transaction. + +Commit +Lors d'un commit on modifie la date des objets encore dans le cache pour la transaction en utilisant la date retourné par la méthode commit du storage puis +on les supprime du cache. + + +Rollback +Lors d'un rollback les objets de la transaction sont supprimé du cache, si l'utilisateur a encore des références sur un de ces objets et qu'il essai de les modifier une exception est levé. S'il veut pouvoir réutiliser un ancien objet +de transaction, il faut qu'il fasse d'abord un updte/makePersistent sur l'objet. + + +REVOIR L'HISTOIRE DU CACHE + + +FAIRE ATTENTION LORS DE LA RECUPERATION D'UN OBJET POUR UNE TRANSACTION, A NE PAS RECUPERER DES OBJETS COMMITER APRES LE DEBUT DE LA TRANSACTION. + + +Il faut sans doute ajouter dans les fichiers XMI un tagvalue SchemaVersion. + + +Il faut pouvoir recuperer la description des objets a partir du PersistenceService de l'objet + + +Reflechir a l'appel de methode sur l'objet, le proxy, va sans doute devoir aller chercher la classe qui implante les methodes et les appeler + + +Lorsque l'on fait une conversion de version il peut aussi y avoir besoin de modifier les Id, si le nom des classes des objets a ete modifié. + + +Un changement de nom de classes des objets est un changement majeur du numero de version de la BD. Dans ce cas, lors du lancement de l'application, on force +la mise a jour de tous les objets stockés, pour qu'il soit dans la bonne version. De cette facon, on a pas d'objet qui change d'id dans le temps pour une meme DB. +Pour cela, dans l'objet de conversion il suffit de mettre une variable avec un nom special qui sera lu au demarrage du TopiaPersistenceHelper +"static final public String forcedConvertion=". +Cela indique que tous les objets doivent être au moins dans la version 'version' si ce n'est pas le cas, on convertie les objetsqui ne sont pas dans cette version en la derniere version de schema. + + +Lors d'un export tous les objets sont mis dans la derniere version avant d'etre convertie en XML. On met dans le fichier XML, le schema de la base. +Lors du stockage d'un champs, si ce n'est pas un type primitif ou un String, alors on essai de le convertir en XML, si ce n'est pas possible, on met la version serialise dans le XML. + + +Mode de fonctionnement +- auto-update schema forced(true|false): tous les objets stockés sont toujours mis a jour pour n'avoir a conserver toujours qu'un schema de base. +- history.depth(n): profondeur de l'historique +- auto-load at startup(true|false): tous les objets sont chargé en tache de fond lors du demarrage +- auto-fetch next (true|false): lors du chargement d'un objet par l'utilisateur, on load automatiquement tous les objets qui sont pointé par cet objet +- transaction read required(true|false): si vrai tous les acces en lecture aux données doit etre fait dans une transaction. +- transaction write required(true|false): si vrai tous les acces en ecriture aux données doit etre fait dans une transaction. + + +Schema de BD +Lors de la generation, on sauve l'objetmodel en XML dans un fichier portant le nom de l'application. (ex: Chorem-database-schema.xml) +Lors de la creation du context, on relit ce fichier pour avoir a disposition le modele de données au travers de PersistenceService.getMeta():ObjectModelClassifier +Lors de la creation du TopiaPersistenceHelper on demande au context le schema complet, que l'on stocke dans le storage pour pouvoir recuperer le schema pour plus tard. + + +TODO +- ObjectModel.getVersion() +- ObjectModel.toXML() +