Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceHelper.java diff -u topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceHelper.java:1.1 topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceHelper.java:1.2 --- topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceHelper.java:1.1 Tue Jun 28 14:08:34 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceHelper.java Wed Jul 6 22:35:20 2005 @@ -23,15 +23,19 @@ * Created: 22 juin 2005 10:27:31 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ * - * Last update: $Date: 2005/06/28 14:08:34 $ + * Last update: $Date: 2005/07/06 22:35:20 $ * by : $Author: bpoussin $ */ package org.codelutin.topia.persistence.topia; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; @@ -44,12 +48,19 @@ import java.util.Set; import java.util.TreeSet; import java.util.WeakHashMap; +import org.apache.commons.beanutils.BasicDynaClass; +import org.apache.commons.beanutils.DynaClass; +import org.apache.commons.beanutils.DynaProperty; +import org.codelutin.querycollection.CollectionQueryHelper; +import org.codelutin.queryparser.QueryHelperException; import org.codelutin.topia.AsynchronousLoader; +import org.codelutin.topia.DefaultEntitiesHelper; 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.TopiaNotFoundException; import org.codelutin.topia.TopiaQuery; import org.codelutin.topia.Util; @@ -61,25 +72,93 @@ // plutot mettre un SoftHashMap qu'un WeakHashMap protected Map> cache = new HashMap>(); protected TopiaStorage storage = null; + /** map contenant les descriptions des objets créer, il s'agit de la + description courante de l'application */ + protected Map classCache = new HashMap(); + protected Object entitiesHelper = 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}); + + classname = getProperties().getProperty("TopiaPersistenceHelper.entitiesHelper"); + if(classname == null || "".equals(classname)){ + clazz = DefaultEntitiesHelper.class; + } else { + try{ + clazz = Util.getClazz(classname); + }catch(TopiaNotFoundException eee){ + log.log(Level.WARNING, "Impossible de trouvé la classe demandée pour l'EntitiesHelper(" + classname + ") utilisation de la classe par defaut.", eee); + clazz = DefaultEntitiesHelper.class; + } + } + this.entitiesHelper = Util.getInstance(clazz); + log.info("Class used for EntitiesHelper: " + clazz.getName()); } TopiaStorage getStorage(){ return storage; } + Object getEntitiesHelper(){ + return entitiesHelper; + } + + protected DynaClass getMeta(Class entityClass){ + DynaClass result = classCache.get(entityClass); + if(result == null){ + List properties = new ArrayList(); + Method[] methods = entityClass.getMethods(); + for(Method m: methods){ + String propName = m.getName(); + Class propType = m.getReturnType(); + // FIXME verifier que la construction de la MeraClass est correct + // par rapport au methode generé pour les champs d'un objet + // en fonction du type de champs (collection, map, primitif, entity, ...) + if(propName.startsWith("get") || propName.startsWith("is")){ + DynaProperty prop = new DynaProperty(propName, propType); + properties.add(prop); + } + } + result = new BasicDynaClass(entityClass.getName(), + TopiaPersistenceObject.class, + properties.toArray(new DynaProperty[properties.size()])); + classCache.put(entityClass, result); + } + return result; + } + + /** + * Retourne une nouvel instance + */ + protected TopiaPersistenceObject newTopiaPersistenceObject(String id) throws TopiaException { + try{ + Class entityClass = TopiaId.getClassName(id); + DynaClass dynaClass = getMeta(entityClass); + TopiaPersistenceObject result = (TopiaPersistenceObject)dynaClass.newInstance(); + result.setId(id); + result.setSchemaVersion(getSchemaVersion(entityClass)); + return result; + }catch(IllegalAccessException eee){ + throw new TopiaException("Impossible d'instancier le TopiaPersistenceObject ayant l'id: " + id, eee); + }catch(InstantiationException eee){ + throw new TopiaException("Impossible d'instancier le TopiaPersistenceObject ayant l'id: " + id, eee); + } + } + + public TopiaEntity create(Class entityClass) throws TopiaException { String id = TopiaId.create(entityClass); - TopiaPersistenceObject tpo = new TopiaPersistenceObject(id); + TopiaPersistenceObject tpo = newTopiaPersistenceObject(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 + // String id = TopiaId.create(entityClass); + // TopiaPersistenceObject tpo = new TopiaPersistenceObject(id); + // TopiaEntity result = TopiaPersistenceProxy.newProxy(this, tpo); + + // on met a jour de l'etat et la date du tpo tpo.setState(TopiaPersistenceObject.State.NEW); TopiaTransaction tr = TopiaTransaction.get(); if(tr.isBegin()){ @@ -210,7 +289,36 @@ } public List find(TopiaQuery query) throws TopiaException { - return null; + long date = 0; + TopiaTransaction tr = TopiaTransaction.get(); + if(tr.isBegin()){ + date = tr.getId(); + } else { + date = TopiaTransaction.getNextId(); + } + + // on optimise un peu en ne prenant que les objets necessaire pour la + // requete + ArrayList entities = new ArrayList(); + for(String id: storage.getAllId(date)){ + if(TopiaId.getClassNameAsString(id).equals(query.getFrom())){ + entities.add(findByTopiaId(id)); + } + } + + CollectionQueryHelper queryHelper = new CollectionQueryHelper(entities); + List result = null; + try { + queryHelper.setQuery(query.getQueryString()); + queryHelper.addArgs(query.getArgs()); + result = new ArrayList(); + result.addAll(queryHelper.execute()); + } catch (IOException e) { + e.printStackTrace(); + } catch (QueryHelperException e) { + e.printStackTrace(); + } + return result; } public AsynchronousLoader findAsynchronously(TopiaQuery query) throws TopiaException { @@ -218,7 +326,7 @@ } public List findInRange(TopiaQuery query, int startIndex, int endIndex) throws TopiaException { - return null; + return new ArrayList(); } public AsynchronousLoader findInRangeAsynchronously(TopiaQuery query, int startIndex, int endIndex) throws TopiaException { @@ -226,7 +334,7 @@ } public int size(TopiaQuery query) throws TopiaException { - return 0; + return find(query).size(); } public void importXML(String xml) throws TopiaException { @@ -248,8 +356,13 @@ } if(result == null){ - TopiaPersistenceObject tpo = new TopiaPersistenceObject(id); + TopiaPersistenceObject tpo = newTopiaPersistenceObject(id); tpo.setDate(last); + // on recharge l'objet, ce qui permet en plus de vérifier qu'il + // existe reellement. + // TODO le restore est trop couteux, il faut trouver un autre moyen + // de savoir qu'un objet existe encore. + // storage.restore(tpo); result = TopiaPersistenceProxy.newProxy(this, tpo); if(tr.isBegin()){ // pour l'instant on ne met en cache que ce qui est dans une @@ -334,6 +447,21 @@ }catch(Exception eee){ throw new TopiaException("Erreur du la conversion de l'objet: " + o.getId() + " schemaVersion: " + o.getSchemaVersion()); } + } + + /** + * Permet de recuperer le schemaVersion de l'interface de + * l'entity qui sert de version de schema + */ + protected String getSchemaVersion(Class clazz){ + String result = "0"; + try{ + Field version = clazz.getDeclaredField("schemaVersion"); + result = ""+version.getLong(null); + }catch(Exception eee){ + log.log(Level.WARNING, "Pas de version de schema defini dans l'interface", eee); + } + return result; } } // TopiaPersistenceHelper Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceObject.java diff -u topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceObject.java:1.1 topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceObject.java:1.2 --- topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceObject.java:1.1 Tue Jun 28 14:08:34 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceObject.java Wed Jul 6 22:35:20 2005 @@ -23,9 +23,9 @@ * Created: 22 juin 2005 11:43:49 CEST * * @author Benjamin POUSSIN -* @version $Revision: 1.1 $ +* @version $Revision: 1.2 $ * -* Last update: $Date: 2005/06/28 14:08:34 $ +* Last update: $Date: 2005/07/06 22:35:20 $ * by : $Author: bpoussin $ */ @@ -37,6 +37,8 @@ import java.util.logging.Logger; import java.util.Map; import java.util.Set; +import org.apache.commons.beanutils.BasicDynaBean; +import org.apache.commons.beanutils.DynaClass; /** * Cet objet permet de representer un objet au travers d'une map, avec en plus @@ -51,7 +53,7 @@ * L'objet est dans une transaction si inTransaction == true, dans ce cas * date == id de la transaction. */ -class TopiaPersistenceObject { // TopiaPersistenceObject +public class TopiaPersistenceObject extends BasicDynaBean { // TopiaPersistenceObject /** to use log facility, just put in your code: log.info(\"...\"); */ static private Logger log = Logger.getLogger("org.codelutin.topia.persistence.topia.TopiaPersistenceObject"); @@ -81,20 +83,24 @@ /** version de l'objet */ protected String version = null; - /** map contenant les champs */ - protected Map fields = new HashMap(); + // /** 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(){} - TopiaPersistenceObject(String id){ - setId(id); + public TopiaPersistenceObject(DynaClass meta){ + super(meta); } + // TopiaPersistenceObject(String id){ + // setId(id); + // } + /** * Get id property. * @@ -227,8 +233,8 @@ */ Object getField(String fieldName){ Object result = UNLOADED_FIELD; - if(fields.containsKey(fieldName)){ - result = fields.get(fieldName); + if(getAllFields().containsKey(fieldName)){ + result = getAllFields().get(fieldName); } else { askedFields.add(fieldName); } @@ -236,15 +242,16 @@ } void loadField(String fieldName, Object value){ - fields.put(fieldName, value); + getAllFields().put(fieldName, value); askedFields.remove(fieldName); } Map getAllFields(){ - return fields; + return values; } - void setAllFields(Map fields){ - this.fields = fields; + void setAllFields(Map values){ + getAllFields().clear(); + getAllFields().putAll(values); } } // TopiaPersistenceObject Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceProxy.java diff -u topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceProxy.java:1.1 topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceProxy.java:1.2 --- topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceProxy.java:1.1 Tue Jun 28 14:08:34 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaPersistenceProxy.java Wed Jul 6 22:35:20 2005 @@ -23,9 +23,9 @@ * Created: 22 juin 2005 12:31:52 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ * - * Last update: $Date: 2005/06/28 14:08:34 $ + * Last update: $Date: 2005/07/06 22:35:20 $ * by : $Author: bpoussin $ */ @@ -34,10 +34,12 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; -import org.codelutin.topia.TopiaException; +import org.apache.commons.beanutils.MethodUtils; import org.codelutin.topia.TopiaEntity; +import org.codelutin.topia.TopiaException; import org.codelutin.topia.TopiaId; import org.codelutin.util.StringUtil; @@ -130,9 +132,25 @@ o.setDate(TopiaTransaction.getNextId()); } tph.getStorage().store(o); + } else if(methodName.equals("toString") && method.getParameterTypes().length == 0){ + return MethodUtils.invokeMethod(tph.getEntitiesHelper(), "toString", this); + } else if(methodName.equals("equals") && method.getParameterTypes().length == 1){ + if(args[0] instanceof TopiaEntity){ + TopiaEntity entity = (TopiaEntity)args[0]; + if(Proxy.isProxyClass(args[0].getClass())){ + TopiaPersistenceProxy tpp = (TopiaPersistenceProxy)Proxy.getInvocationHandler(entity); + TopiaPersistenceObject obis = tpp.getObject(); + return o.getId().equals(obis.getId()) && o.getDate() == obis.getDate(); + } else { + return o.getId().equals(entity.get_topiaId_()); + } + } else { + return Boolean.FALSE; + } } else{ - // FIXME gérer les appels de méthode spécifique d'attribut (add, remove, ...) - // FIXME gérer les appels de méthode + log.severe("Can't find called method: " + method + " methodName: " + method.getName() + " methodArgType: " + Arrays.toString(method.getParameterTypes())); + // FIXME gérer les appels de méthode spécifique d'attribut (add, remove, ...) + // FIXME gérer les appels de méthode } return null; } Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorage.java diff -u topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorage.java:1.1 topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorage.java:1.2 --- topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorage.java:1.1 Tue Jun 28 14:08:34 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorage.java Wed Jul 6 22:35:20 2005 @@ -23,9 +23,9 @@ * Created: 22 juin 2005 12:02:07 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ * - * Last update: $Date: 2005/06/28 14:08:34 $ + * Last update: $Date: 2005/07/06 22:35:20 $ * by : $Author: bpoussin $ */ @@ -71,7 +71,7 @@ * comprise seront retourné. * @return les id des objets existant */ - public Set getAllId(long date) throws TopiaException; + public Set getAllId(long date) throws TopiaException; /** * Nettoie l'historique des objets pour ne conserver que depth en plus Index: topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorageSerialization.java diff -u topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorageSerialization.java:1.1 topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorageSerialization.java:1.2 --- topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorageSerialization.java:1.1 Tue Jun 28 14:08:34 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/TopiaStorageSerialization.java Wed Jul 6 22:35:20 2005 @@ -23,9 +23,9 @@ * Created: 22 juin 2005 12:30:39 CEST * * @author Benjamin POUSSIN - * @version $Revision: 1.1 $ + * @version $Revision: 1.2 $ * - * Last update: $Date: 2005/06/28 14:08:34 $ + * Last update: $Date: 2005/07/06 22:35:20 $ * by : $Author: bpoussin $ */ @@ -94,11 +94,12 @@ } public boolean accept(File dir, String name){ + // System.out.println("name = " + name + " pattern: " + pattern + " lastDate: " + lastDate); boolean result = false; if(pattern.matcher(name).matches()){ if(lastDate != 0){ String date = FilenameComponent.DATE.get(name); - result = lastDate <= Long.parseLong(date); + result = Long.parseLong(date) <= lastDate; } else { result = true; } @@ -187,11 +188,14 @@ // sauvegarde se passe bien restore(e); + // normalement le block suivant ne sert plus a rien car + // la date est maintenant toujours unique 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); @@ -315,8 +319,8 @@ * 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(); + public Set getAllId(long date) throws TopiaException{ + HashSet result = new HashSet(); long last = date; if(date == 0){ @@ -329,6 +333,8 @@ String [] files = directory.list(new PatternFilenameFilter( getFilePattern(1, State.ALL, null, null), last)); + // System.out.println("files: " + Arrays.toString(files)); + Arrays.sort(files); for(String name: files){ String id = FilenameComponent.ID.get(name); @@ -355,6 +361,7 @@ } } } + // System.out.println("List des id disponibles: " + result); return result; } Index: topia/src/java/org/codelutin/topia/persistence/topia/package.html diff -u topia/src/java/org/codelutin/topia/persistence/topia/package.html:1.1 topia/src/java/org/codelutin/topia/persistence/topia/package.html:1.2 --- topia/src/java/org/codelutin/topia/persistence/topia/package.html:1.1 Tue Jun 28 14:08:34 2005 +++ topia/src/java/org/codelutin/topia/persistence/topia/package.html Wed Jul 6 22:35:20 2005 @@ -25,15 +25,15 @@ ; Created: 23 juin 2005 22:04:24 CEST ; ; author Benjamin POUSSIN -; version $Revision: 1.1 $ +; version $Revision: 1.2 $ ; -; Last update: $Date: 2005/06/28 14:08:34 $ +; Last update: $Date: 2005/07/06 22:35:20 $ ; by : $Author: bpoussin $ ; ;---------------------------------------------------------------------------- --> -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. +Lorsque l'on sauve un objet on remet sa date à la date de sauvegarde, cette date pour cet objet doit-être unique. Hors d'une transaction (ou lors d'un commit): Objet demandé par l'utilisateur: synchrone @@ -82,6 +82,8 @@ Il faut sans doute ajouter dans les fichiers XMI un tagvalue SchemaVersion. +En fait le mieux serait de calculer un nombre pour chaque entity en fonction +des champs (nom et type). Il faut pouvoir recuperer la description des objets a partir du PersistenceService de l'objet @@ -97,7 +99,7 @@ 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. +Cela indique que tous les objets doivent être au moins dans la version 'version' si ce n'est pas le cas, on convertie les objets qui 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. @@ -122,4 +124,10 @@ TODO - ObjectModel.getVersion() - ObjectModel.toXML() + + + + + +durant la sauvegarde conserve dans 2eme fichier la liste de toute les classes storees