Index: topia-service/src/java/org/codelutin/topia/migration/kernel/ConfigurationAdapter.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/kernel/ConfigurationAdapter.java:1.1 --- /dev/null Mon Apr 2 14:24:42 2007 +++ topia-service/src/java/org/codelutin/topia/migration/kernel/ConfigurationAdapter.java Mon Apr 2 14:24:37 2007 @@ -0,0 +1,588 @@ +/* *##% + * Copyright (C) 2007 Code Lutin + * + * 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. + *##%*/ + +package org.codelutin.topia.migration.kernel; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.topia.migration.common.MapAdapterAdmin; +import org.codelutin.topia.migration.common.MapAdapterImpl; +import org.codelutin.topia.migration.common.ProxyClass; +import org.codelutin.topia.migration.common.ProxyClassMapped; +import org.codelutin.topia.migration.common.SimpleProxyClass; +import org.codelutin.topia.migration.common.SimpleProxyClassMapped; +import org.codelutin.topia.migration.common.Version; +import org.hibernate.EntityMode; +import org.hibernate.LazyInitializationException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.collection.PersistentBag; +import org.hibernate.collection.PersistentList; +import org.hibernate.collection.PersistentSet; +import org.hibernate.mapping.RootClass; +import org.hibernate.proxy.map.MapProxy; + +/** + * ConfigurationAdapter.java + * + * Permet de s'abstraire d'hibernate lors de la migration des donnees. + * Communique avec la base, pour recuperer et suaver des donnees. + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007/04/02 14:24:37 $ + */ +public class ConfigurationAdapter { + + /** + * La session factory + */ + private SessionFactory sessionFactory; + + /** + * La configuration hibernate + */ + private Configuration configuration; + + /** + * La version + */ + private Version version; + + /** + * Logger (common-logging) + */ + private static Log logger = LogFactory.getLog(ConfigurationAdapter.class); + + /** + * Helper pour le calcul des dependances + */ + private DependenciesHelper dependenciesHelper; + + /** + * Constructeur. + * + * @param configuration + * @param version + */ + public ConfigurationAdapter(Configuration configuration, Version version) { + super(); + this.configuration = configuration; + this.sessionFactory = configuration.buildSessionFactory(); + this.version = version; + + // sessionFactory necessaire pour le calcul, c'est moche, mais on a pas + // le choix + dependenciesHelper = new DependenciesHelper(sessionFactory, + configuration); + } + + /** + * Accesseur au dependencies helper + * + * @return le dependencie helper + */ + public DependenciesHelper getDependenciesHelper() { + return dependenciesHelper; + } + + /** + * Retourne la version + * + * @return la version + */ + public Version getVersion() { + return version; + } + + /** + * Retourne le nom de toutes les classes definies dans cette configuration + * + * @return une collection de noms de table + */ + public Collection getClasses() { + // recupere la liste de classes dans la configuration + Iterator iClass = configuration.getClassMappings(); + + // Instancie la collection + Collection cClass = new LinkedList(); + // boucle pour chaque classe de l'iterateur + while (iClass.hasNext()) { + // cast + RootClass rc = (RootClass) (iClass.next()); + + String name = rc.getClassName(); + if (name == null) { + name = rc.getEntityName(); + } + + // on ajoute cette classe a la collection + cClass.add(new SimpleProxyClass(name)); + } + // retourne la collection des noms de classe + return cClass; + } + + /** + * Retourne l'ensemble des tuples d'une map + * + * @param className + * le nom de la classe + * @return une collection de MapAdapterAdmin, un pour chaque tuple + */ + @SuppressWarnings("unchecked") + public Collection getData(ProxyClass className) { + // retour + LinkedList result = new LinkedList(); + + // session + Session session = this.sessionFactory.getCurrentSession(); + + // transaction + session.beginTransaction(); + + logger.debug("Fetching data for class : " + + className.getCanonicalName()); + String nomIdAttribute = configuration.getClassMapping( + className.getCanonicalName()).getIdentifierProperty().getName(); + + // data + Session dynamicSession = session.getSession(EntityMode.MAP); + List lstReponse = dynamicSession.createCriteria( + className.getCanonicalName()).list(); + + for (Map m : lstReponse) { + + m = removeProxyMaps(m); + + MapAdapterAdmin map = new MapAdapterImpl(m, className, + nomIdAttribute, (Serializable) m.get(nomIdAttribute)); + map.setInnerVersion(this.getVersion()); + result.add(map); + } + + // On ferme la transaction en ne faisant pas de save + /* + * Lorsque l'on faisait un session.close(), il y avait toujours un + * verrou sur la table concernée. + */ + session.getTransaction().rollback(); + + return result; + } + + /** + * comment me + */ + @SuppressWarnings("unchecked") + public MapAdapterAdmin getData(ProxyClass className, Serializable idVal) { + + // session + Session session = this.sessionFactory.getCurrentSession(); + + // transaction + session.beginTransaction(); + + //logger.debug("Fetching one row from class : " + className.getCanonicalName()); + + String nomIdAttribute = configuration.getClassMapping( + className.getCanonicalName()).getIdentifierProperty().getName(); + + // data + Session dynamicSession = session.getSession(EntityMode.MAP); + Map m = (Map) dynamicSession.get( + className.getCanonicalName(), idVal); + + MapAdapterAdmin maa = null; + + if (m != null) { + + // suppression des proxy + m = removeProxyMaps(m); + + maa = new MapAdapterImpl(m, className, nomIdAttribute, + (Serializable) m.get(nomIdAttribute)); + maa.setInnerVersion(this.getVersion()); + } + + session.getTransaction().commit(); + + return maa; + } + + /** + * Sauve une collection de map + * + * @param myMaps + * la collection de MapAdapter + * @param linkedClass + * @see MapAdapterAdmin + */ + public void saveMaps(Collection myMaps, + Map linkedClass) { + logger.debug("Saving collection of maps"); + for (MapAdapterAdmin maMap : myMaps) { + this.saveMap(maMap, linkedClass); + } + } + + /** + * Sauve une map + * + * @param maMap + * la map + * @param linkedClass + * @see MapAdapterAdmin + */ + public void saveMap(MapAdapterAdmin maMap, + Map linkedClass) { + // pas de log ici, chaque tuple de la base passe ici + + // extraction des variables + ProxyClass clazz = maMap.getInnerClass(); + + Map mapData = removeImbricatedMaps(null, maMap.getInnerMap(), + linkedClass); + deletePersistentCollection(mapData); + modifyId(mapData, linkedClass); + + // session + Session session = this.sessionFactory.getCurrentSession(); + session.beginTransaction(); + + // mode map + Session dynamicSession = session.getSession(EntityMode.MAP); + + //logger.debug("Saving : " + mapData); + dynamicSession.saveOrUpdate(clazz.getCanonicalName(), mapData); + + session.getTransaction().commit(); + } + + /** + * Modifie le nom de l'attribut identifiant + * @param mapData + * @param linkedClass + */ + @SuppressWarnings("unchecked") + private void modifyId(Map mapData, + Map linkedClass) { + // nom de la classe de mapping des item imbriques + + String clazz = mapData.get("$type$").toString(); + ProxyClassMapped pc = null; + + if ((linkedClass != null) + && (linkedClass.containsKey(new SimpleProxyClassMapped( + new SimpleProxyClass(clazz))))) { + + pc = linkedClass.get(new SimpleProxyClassMapped( + new SimpleProxyClass(clazz))); + clazz = pc.getProxyClass().getCanonicalName(); + } + + // pour cet item, on recuperer le nom de la cle, (par exempe ID) + String nomIdAttribute; + if (pc != null) { + nomIdAttribute = (String) pc.getIdAttribute(); + mapData.put(nomIdAttribute, pc.getMigrationClass().modifyId( + (Serializable) mapData.get(nomIdAttribute))); + mapData.put("$type$", clazz); + } + } + + /** + * + * @param mapData + */ + @SuppressWarnings("unchecked") + private void deletePersistentCollection(Map mapData) { + Map m = new HashMap(); + for (Object key : mapData.keySet()) { + if (mapData.get(key) instanceof PersistentSet) { + Collection c = (Collection) mapData + .get(key); + for (Iterator iter = c.iterator(); iter.hasNext();) { + Object element = (Object) iter.next(); + if (element instanceof Map) { + deletePersistentCollection((Map) element); + } + } + m.put((String) key, new HashSet(c)); + } else if (mapData.get(key) instanceof PersistentBag) { + Collection c = (Collection) mapData + .get(key); + for (Iterator iter = c.iterator(); iter.hasNext();) { + Object element = (Object) iter.next(); + if (element instanceof Map) { + deletePersistentCollection((Map) element); + } + } + // HashBag ajoute une dependance au projet + // hibernate utilise des List + // m.put((String) key, new HashBag(c)); + m.put((String) key, new ArrayList(c)); + } else if (mapData.get(key) instanceof PersistentList) { + Collection c = (Collection) mapData + .get(key); + for (Iterator iter = c.iterator(); iter.hasNext();) { + Object element = (Object) iter.next(); + if (element instanceof Map) { + deletePersistentCollection((Map) element); + } + } + m.put((String) key, new LinkedList(c)); + } + ; + } + for (Object key : m.keySet()) { + mapData.put(key.toString(), m.get(key)); + } + } + + /** + * Suprimme les proxy + * @param map la map + * @return la map sans proxy + */ + private Map removeProxy(Map map) { + Map result = new HashMap(map); + for (Object key : map.keySet()) { + if (map.get(key) instanceof MapProxy) { + Map m = deleteMapProxy((MapProxy) map.get(key)); + map.put((String) key, m); + } + } + return result; + } + + /** + * Supprime les map internes etant des proxy hibernate. + * Cas "lazy". + * + * @param map une map qui peut contenir des proxy + * @return Une map sans map imbrique de type proxy + */ + @SuppressWarnings("unchecked") + private Map removeProxyMaps(Map map) { + + // copy the map + Map m = new HashMap(map); + + // donc, pour tous les cle de cette map ... + for (String key : m.keySet()) { + + // ... certaines sont des maps imbriquees + // et il faut les transformer + + // ici , une liste de maps (relation x>*) + if (m.get(key) instanceof Iterable) { + // pour l'ensemble des items contenu dans cette map + // (on travaille ici au premier niveau, hibernate remplacera les + // sous + // niveau tout seul) + Iterable pl = (Iterable) m.get(key); + for (Iterator iter = pl.iterator(); iter.hasNext();) { + // item courant de la map + Object element = iter.next(); + if (element instanceof Map) { + element = removeProxy((Map) element); + } + } + } + // sinon, une seule map + else if (m.get(key) instanceof Map) { + map.put(key, removeProxy((Map) m.get(key))); + } + /* + * else { logger.debug(" type attribut " + + * m.get(key).getClass().getName()); } + */ + } + + return map; + } + + /** + * Supprime les map etant des proxy hibernate + * + * @param element + * @return une map sans proxy + */ + @SuppressWarnings("unchecked") + private Map deleteMapProxy(MapProxy element) { + + Map result = new HashMap(); + if (element instanceof MapProxy) { + // logger.debug("Avant : " + element); + MapProxy mp = (MapProxy) element; + result.putAll(mp); + // logger.debug("Apres : " + result); + } + return result; + } + + @SuppressWarnings("unchecked") + private Map convertToNewMap(Map element, Session dynamicSession, + Map linkedClass) { + + // nom de la classe de mapping des item imbriques + String clazz = element.get("$type$").toString(); + ProxyClassMapped pc = null; + + if ((linkedClass != null) + && (linkedClass.containsKey(new SimpleProxyClassMapped( + new SimpleProxyClass(clazz))))) { + pc = linkedClass.get(new SimpleProxyClassMapped( + new SimpleProxyClass(clazz))); + clazz = pc.getProxyClass().getCanonicalName(); + } + // pour cet item, on recuperer le nom de la cle, (par exempe ID) + String nomIdAttribute; + if (pc != null) { + nomIdAttribute = (String) pc.getIdAttribute(); + } else { + nomIdAttribute = configuration.getClassMapping(clazz) + .getIdentifierProperty().getName(); + } + + // Valeur de la cle + Object idElement = element.get(nomIdAttribute); + if (pc != null) { + idElement = pc.getMigrationClass().modifyId( + (Serializable) idElement); + } + + /* Récupération de la map */ + Map newMap = null; + + //logger.debug("try to get map for '" + clazz + "' id="+idElement.toString()); + + try { + newMap = (Map) dynamicSession.get(clazz, (Serializable) idElement); + } + catch(LazyInitializationException e) { + // cette exception est bizarement levee + // dans le cas des relations *-* a double visibilite + // si une classe est migrer en premier, ses + // map internes ne peuvent pas etre initialise + // depuis la nouvelle base (elles n'ont pas encore ete traitees) + newMap = null; + } + + if (newMap != null) { + // ca va supprimer l'item courant et le remplacer par l'ancien + element.clear(); + removeProxy(newMap); + element.putAll(newMap); + } else { + logger.debug("No map found for specified identifier in new database, skip"); + } + + return newMap; + } + + /** + * Retire les map interne anciennes, et les remplace par les map de la + * nouvelle base. + * + * @param map + * la map + * @param linkedClass + * @return la nouvelle map + */ + private Map removeImbricatedMaps(Session dSession, + Map map, + Map linkedClass) { + + // session + Session session = this.sessionFactory.getCurrentSession(); + session.beginTransaction(); + + // mode map + Session dynamicSession = session.getSession(EntityMode.MAP); + + // copy the map + Map m = new HashMap(map); + + // donc, pour tous les cle de cette map ... + for (String key : m.keySet()) { + + // ... certaines sont des maps imbriquees + // et il faut les transformer + + // ici , une liste de maps (relation x>*) + if (m.get(key) instanceof Iterable) { + // logger.debug(" type attribut Iterable " + + // m.get(key).getClass().getName()); + // pour l'ensemble des items contenu dans cette map + // (on travaille ici au premier niveau, hibernate remplacera les + // sous + // niveau tout seul) + + Iterable pl = (Iterable) m.get(key); + for (Iterator iter = pl.iterator(); iter.hasNext();) { + + // item courant de la map + Map element = (Map) iter.next(); + element = convertToNewMap(element, dynamicSession, + linkedClass); + } + } + // sinon, une seule map + else if (m.get(key) instanceof Map) { + // logger.debug(" type attribut map"); + Map element = (Map) m.get(key); + element = convertToNewMap(element, dynamicSession, linkedClass); + // remplace la map + map.put(key, element); + } + } + + // On fait un rollback car on a aucun save + session.getTransaction().rollback(); + + return map; + } + + /** + * Retourne le nom de l'attribut identifiant d'une classe en lisant la confuguration. + * + * @param pc le nom de la classe + * @return le nom de l'identifiant + */ + public String getNameIdAttribute(ProxyClass pc) { + return configuration.getClassMapping(pc.getCanonicalName()) + .getIdentifierProperty().getName(); + } +} Index: topia-service/src/java/org/codelutin/topia/migration/kernel/Transformer.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/kernel/Transformer.java:1.1 --- /dev/null Mon Apr 2 14:24:43 2007 +++ topia-service/src/java/org/codelutin/topia/migration/kernel/Transformer.java Mon Apr 2 14:24:37 2007 @@ -0,0 +1,293 @@ +/* *##% + * Copyright (C) 2007 Code Lutin + * + * 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. + *##%*/ + +package org.codelutin.topia.migration.kernel; + +import java.io.Serializable; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.topia.migration.common.MapAdapter; +import org.codelutin.topia.migration.common.MapAdapterAdmin; +import org.codelutin.topia.migration.common.ProxyClass; +import org.codelutin.topia.migration.common.ProxyClassMapped; +import org.codelutin.topia.migration.common.SimpleProxyClass; +import org.codelutin.topia.migration.common.SimpleProxyClassMapped; +import org.codelutin.topia.migration.common.Version; +import org.codelutin.topia.migration.transformation.FinderMigration; +import org.codelutin.topia.migration.transformation.MapHelper; +import org.codelutin.topia.migration.transformation.Migration; +import org.codelutin.topia.migration.transformation.MigrationClass; +import org.codelutin.topia.migration.transformation.MigrationNull; + +/** + * Transformer.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007/04/02 14:24:37 $ + */ +public class Transformer implements MapHelper { + + private FinderMigration classFinder; + + private ConfigurationAdapter baseConf; + + private ConfigurationAdapter nextConf; + + private transient Log log; + + private SortedMap confAdapters; + + private Map linkedClass; + + private Map> loadedClass; + + private Collection alreadyMigrated; + + /** + * Transformer + * + * @param smConfigurationAdapterForVersion + * Map "ordonnees suivant les versions + */ + public Transformer( + SortedMap smConfigurationAdapterForVersion) { + + this.confAdapters = smConfigurationAdapterForVersion; + this.loadedClass = new HashMap>(); + this.baseConf = smConfigurationAdapterForVersion + .get(smConfigurationAdapterForVersion.firstKey()); + + alreadyMigrated = new HashSet(); + this.linkedClass = new HashMap(); + this.classFinder = new FinderMigration(); + log = LogFactory.getLog(this.getClass()); + } + + /** + * transforme la base + */ + public void execute() { + // on l'enleve de la liste des version a obtenir + this.confAdapters.remove(this.baseConf.getVersion()); + // tant qu'on en est pas la derniere + while (!this.confAdapters.isEmpty()) { + // la suivante : + this.nextConf = confAdapters.get(confAdapters.firstKey()); + this.migrateCurrentToNext(); + } + + } + + private void migrateCurrentToNext() { + log.info("Processing step: version " + this.baseConf.getVersion().getVersion() + " to version " + + this.nextConf.getVersion().getVersion()); + // les classes de la version de destination : + Collection classes = this.nextConf.getClasses(); + Map migrations = findCurrentMigrations(classes); + log.debug("Migrations list : " + migrations); + this.performMigrations(migrations); + this.baseConf = nextConf; + alreadyMigrated.clear(); + this.nextConf = null; + this.linkedClass.clear(); + this.loadedClass.clear(); + confAdapters.remove(confAdapters.firstKey()); + + } + + private void performMigrations(Map migrations) { + for (ProxyClass pc : migrations.keySet()) { + log.debug("Migration of '" + pc.getCanonicalName() + "' to " + + this.nextConf.getVersion()); + + migrateAClassRec(pc, migrations,null); + } + } + + private void migrateAClassRec(ProxyClass pc, + Map migrations,ProxyClass grandparent) { + if (alreadyMigrated.contains(pc)) + return; + //log.debug("migration of " + pc); + Set deps = nextConf.getDependenciesHelper() + .getDependencies(pc); + for (ProxyClass dep : deps) { + if (dep.equals(grandparent))continue; + log.debug("Migration of '" + dep.getCanonicalName() + "' dependencies"); + migrateAClassRec(dep, migrations,pc); + } + + Collection data = null; + MigrationClass mc = migrations.get(pc); + + data = baseConf.getData(mc.getParentClassName()); + + try { + Migration migration = (Migration) ClassLoader + .getSystemClassLoader().loadClass( + mc.getNameMigrationClass()).newInstance(); + ProxyClass parent = mc.getParentClassName(); + + log.debug(pc.getCanonicalName() + " (data provided by class '" + parent.getCanonicalName() + "')"); + + ProxyClassMapped pcmBase = new SimpleProxyClassMapped(parent, this.baseConf + .getNameIdAttribute(parent)); + ProxyClassMapped pcmNext = new SimpleProxyClassMapped( + pc, this.nextConf.getNameIdAttribute(pc), migration); + + linkedClass.put(pcmBase,pcmNext); + for (MapAdapterAdmin m : data) { + migration.migrate(m, this); + m.setOuterClass(parent); + //m.setOuterVersion(this.nextConf.getVersion()); + } + } catch (ClassNotFoundException e) { + log.error("Cannot found migration class " + + mc.getNameMigrationClass()); + } catch (InstantiationException e) { + log.error("Cannot initalize migration class " + + mc.getNameMigrationClass()); + } catch (IllegalAccessException e) { + log.error("IllegalAccessException while loading migration class " + + mc.getNameMigrationClass()); + } + + alreadyMigrated.add(pc); + this.switchMaps(data); + log.debug("Saving '" + pc.getCanonicalName() + "' in version " + this.nextConf.getVersion()); + save(data); + clearLoadedClasses(); + + } + + private void switchMaps(Collection data) { + for (MapAdapterAdmin m : data) { + m.switchVersion(); + } + } + + private void clearLoadedClasses() { + this.loadedClass.clear(); + } + + private void save(Collection data) { + nextConf.saveMaps(data, linkedClass); + } + + private Map findCurrentMigrations( + Collection classes) { + + Map result = new HashMap(); + for (ProxyClass klass : classes) { + MigrationClass mig = this.classFinder.getMigrationClass(klass, + this.baseConf.getVersion(), this.nextConf.getVersion()); + if (mig == null) { + mig = new NullMigrationClass(MigrationNull.class + .getCanonicalName(), klass, this.baseConf.getVersion(), + this.nextConf.getVersion()); + } + result.put(klass, mig); + } + return result; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.transformation.MapHelper#getNewMap(org.codelutin.topia.migration.common.ProxyClass, java.io.Serializable) + */ + public MapAdapter getNewMap(ProxyClass clazz, Serializable idVal) throws ObjectNotFound { + return getMap(this.nextConf, clazz, idVal); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.transformation.MapHelper#getNewMap(java.lang.String, java.io.Serializable) + */ + public MapAdapter getNewMap(String clazz, Serializable idVal) throws ObjectNotFound { + return getMap(this.nextConf, new SimpleProxyClass(clazz), idVal); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.transformation.MapHelper#getOldMap(org.codelutin.topia.migration.common.ProxyClass, java.io.Serializable) + */ + public MapAdapter getOldMap(ProxyClass clazz, Serializable idVal) throws ObjectNotFound { + return getMap(this.baseConf, clazz, idVal); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.transformation.MapHelper#getOldMap(java.lang.String, java.io.Serializable) + */ + public MapAdapter getOldMap(String clazz, Serializable idVal) throws ObjectNotFound { + return getMap(this.baseConf, new SimpleProxyClass(clazz), idVal); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.transformation.MapHelper#getNewMaps(java.lang.String) + */ + public Collection getNewMaps(String clazz) { + return getMaps(this.nextConf, new SimpleProxyClass(clazz)); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.transformation.MapHelper#getNewMaps(org.codelutin.topia.migration.common.ProxyClass) + */ + public Collection getNewMaps(ProxyClass clazz) { + return getMaps(this.nextConf, clazz); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.transformation.MapHelper#getOldMaps(java.lang.String) + */ + public Collection getOldMaps(String clazz) { + return getMaps(this.baseConf, new SimpleProxyClass(clazz)); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.transformation.MapHelper#getOldMaps(org.codelutin.topia.migration.common.ProxyClass) + */ + public Collection getOldMaps(ProxyClass clazz) { + return getMaps(this.baseConf, clazz); + } + + private MapAdapter getMap(ConfigurationAdapter cfg, ProxyClass pc, + Serializable idVal) throws ObjectNotFound { + MapAdapter result = cfg.getData(pc, idVal); + if (result==null) throw new ObjectNotFound(); + return result; + } + + private Collection getMaps(ConfigurationAdapter cfg, ProxyClass clazz) { + Collection tmp = cfg.getData(clazz); + Collection result = new HashSet(); + for(MapAdapterAdmin m : tmp) { + result.add(m); + } + return result; + } + +} Index: topia-service/src/java/org/codelutin/topia/migration/kernel/DependenciesHelper.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/kernel/DependenciesHelper.java:1.1 --- /dev/null Mon Apr 2 14:24:43 2007 +++ topia-service/src/java/org/codelutin/topia/migration/kernel/DependenciesHelper.java Mon Apr 2 14:24:37 2007 @@ -0,0 +1,190 @@ +/* *##% + * Copyright (C) 2007 Code Lutin + * + * 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. + *##%*/ + +package org.codelutin.topia.migration.kernel; + +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.codelutin.topia.migration.common.ProxyClass; +import org.codelutin.topia.migration.common.SimpleProxyClass; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.engine.SessionFactoryImplementor; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.mapping.RootClass; +import org.hibernate.type.CollectionType; +import org.hibernate.type.Type; + +/** + * DependenciesHelper.java + * + * Permet de charger un fichier de mapping et de former les dependances entre les classes mappees + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007/04/02 14:24:37 $ + */ + +public class DependenciesHelper { + + /** + * la sesion factory, hibernate en a besoin pour determiner les liens + */ + private SessionFactory sessionFactory; + /** + * La configuration contenant le schema + */ + private Configuration configuration; + + /** + * Logger (common-logging) + */ + //private Log logger = LogFactory.getLog(DependenciesHelper.class); + + /** + * Liste des classes dépendantes pour chaque classe + */ + private Map> mDependencies; + + /** + * Liste des classes dépendantes d'une classe + */ + private Map> mInvertDependencies; + + /** + * Constructeur + * @param configuration une Configuration + */ + public DependenciesHelper(SessionFactory sessionFactory, Configuration configuration) { + super(); + this.configuration = configuration; + this.sessionFactory = sessionFactory; + + calculateDependencies(); + } + + /** + * Calcules les dependances + */ + private void calculateDependencies() { + + mDependencies = new Hashtable>(); + mInvertDependencies = new Hashtable>(); + + //FIXED apparement on est obilger d'avoir un sessionFactory + // pour que hibernate nous fournisse les relations (type) + SessionFactoryImplementor sfi = (SessionFactoryImplementor)this.sessionFactory; + + //on parcourt les entites + Iterator im = configuration.getClassMappings(); + while(im.hasNext()) { + RootClass m = (RootClass)im.next(); + + //on recupere le nom de la classe courante + String currentClass = m.getEntityName(); + ProxyClass pcCurrentClass = new SimpleProxyClass(currentClass); + + //on initialise les listes correspondantes si ce n'est pas deja fait + List lDependecies = new LinkedList(); + List lOldDependencies = mDependencies.get(pcCurrentClass); + if(lOldDependencies != null) { + lDependecies.addAll(lOldDependencies); + } + mDependencies.put(pcCurrentClass, lDependecies); + + if(mInvertDependencies.get(pcCurrentClass) == null) { + mInvertDependencies.put(pcCurrentClass, new LinkedList()); + } + + PersistentClass clazz = configuration.getClassMapping(currentClass); + Iterator i = clazz.getPropertyIterator(); + //on parcourt les proprietes de la classe courante + while (i.hasNext()) { + Property p = (Property)i.next(); + Type t = p.getType(); + // si c'est une association + if(t.isAssociationType()){ + String dependentClassName; + // on recupere le nom de la classe dont depend la classe courante + if(t.isCollectionType()){ + // + // + // + // + // + dependentClassName = ((CollectionType)t).getAssociatedEntityName(sfi); + } + else { + // + dependentClassName = t.getName(); + } + //on l'ajoute dans les listes correspondantes + ProxyClass pcDependentClass = new SimpleProxyClass(dependentClassName); + //classe courante -> classe dont elle depend + lDependecies.add(pcDependentClass); + //classe dont depend la classe courante -> classe courante + List lOldDependents = mInvertDependencies.get(pcDependentClass); + if(lOldDependents == null) { + lOldDependents = new LinkedList(); + mInvertDependencies.put(pcDependentClass, lOldDependents); + } + lOldDependents.add(new SimpleProxyClass(currentClass)); + } + } + } + } + + /** + * Permet de recuperer la liste des classes dont depend la Classe passee en parametre + * @param pc une Classe + * @return la liste des classes dont depend la Classe passee en parametre + */ + public Set getDependencies(ProxyClass pc) { + if(mDependencies.get(pc) == null) return null; + Set lRecursiveDependencies = new HashSet(mDependencies.get(pc)); + Set result = new HashSet(lRecursiveDependencies); + //IL NE FAUT PAS QU'IL Y AIT DE CYCLE + for(ProxyClass dependency : lRecursiveDependencies) { + if(mInvertDependencies.get(dependency) == null) + result.addAll(getDependencies(dependency)); + } + + return result; + } + + /** + * Permet de recuperer la liste des classes qui dependent de la Classe passee en parametre + * @param pc une Classe + * @return la liste des classes qui dependent de la Classe passee en parametre + */ + public Set getInvertDependencies(ProxyClass pc) { + if(mInvertDependencies.get(pc) == null) return null; + return new HashSet(mInvertDependencies.get(pc)); + } +} \ No newline at end of file Index: topia-service/src/java/org/codelutin/topia/migration/kernel/BaseException.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/kernel/BaseException.java:1.1 --- /dev/null Mon Apr 2 14:24:43 2007 +++ topia-service/src/java/org/codelutin/topia/migration/kernel/BaseException.java Mon Apr 2 14:24:37 2007 @@ -0,0 +1,71 @@ +/* *##% + * Copyright (C) 2007 Code Lutin + * + * 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. + *##%*/ + +package org.codelutin.topia.migration.kernel; + +/** + * BaseException.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007/04/02 14:24:37 $ + */ +public class BaseException extends Exception { + + /** + * Version UID + */ + private static final long serialVersionUID = -6315451538306231430L; + + /** + * Constructeur + * @param string message + */ + public BaseException(String string) { + super(string); + } + + /** + * Constructeur + * + */ + public BaseException() { + super(); + } + + /** + * Constructeur + * @param arg0 message + * @param arg1 exception + */ + public BaseException(String arg0, Throwable arg1) { + super(arg0, arg1); + } + + /** + * Constructeur + * @param arg0 exception + */ + public BaseException(Throwable arg0) { + super(arg0); + } +} Index: topia-service/src/java/org/codelutin/topia/migration/kernel/ConfigurationHelper.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/kernel/ConfigurationHelper.java:1.1 --- /dev/null Mon Apr 2 14:24:45 2007 +++ topia-service/src/java/org/codelutin/topia/migration/kernel/ConfigurationHelper.java Mon Apr 2 14:24:37 2007 @@ -0,0 +1,245 @@ +/* *##% + * Copyright (C) 2007 Code Lutin + * + * 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. + *##%*/ + +package org.codelutin.topia.migration.kernel; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Attribute; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.hibernate.cfg.Configuration; +import org.hibernate.util.DTDEntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * ConfigurationHelper.java + * + * Charge des configuration en mode "dynamic-map". + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007/04/02 14:24:37 $ + */ +public class ConfigurationHelper { + + /** + * Logger (common-logging) + */ + private static Log logger = LogFactory.getLog(ConfigurationHelper.class); + + /** + * Constructeur + */ + public ConfigurationHelper() { + + } + + /** + * Retourne une configuration correspondant au chargement + * de tous les mappings du dossier specifie. + * + * @param pathDirectory Le dossier ou se trouvent les mappings + * @return La configuration associee + */ + public Configuration getConfigurationInDirectory(String pathDirectory) { + + // schema des noms de fichier a lire + final String regexFilename = ".*\\.hbm\\.xml"; + + // nouvelle configuration en retour + Configuration configuration = null; + + // parcourt du dossier + File mappingDir = new File(pathDirectory); + if(!mappingDir.isDirectory()) { + throw new IllegalArgumentException("'" + pathDirectory + "' is not a directory"); + + } + logger.debug("Loading mappings in " + mappingDir.getAbsolutePath()); + + File[] filesInIt = mappingDir.listFiles(); + + if (filesInIt != null && filesInIt.length > 0) { + // s'il y a des fichier a charger + configuration = new Configuration(); + + for(File fileInIt : filesInIt) { + if(fileInIt.getName().matches(regexFilename)){ + + String xmlmapping = ConfigurationHelper.getEntityMappingFromFile(fileInIt.getAbsolutePath()); + + // lit le fichier avec lutin xml + org.w3c.dom.Document domDoc = getDocumentResolvedByHibernate(xmlmapping); + + configuration.addDocument(domDoc); + + logger.debug("Local mapping added : " + fileInIt.getName()); + } + } + } + else { + logger.error("No mappings files found in directory " + pathDirectory); + } + + return configuration; + } + + /** + * Charge un fichier et retourne un mapping valide pour etre manipule via les map + * + * @param fichier le nom du fichier + * @return le mapping en forme xml + */ + public static String getEntityMappingFromFile(String fichier) { + String sXml = null; + + // charge le document en representation dom4j + Document domDoc = getDom4jDocument(fichier); + + // transforme en entity + domDoc = transformAsValidHibernateMapConfigFile(domDoc); + + // transforme le document en chaine + sXml = domDoc.asXML(); + + return sXml; + } + + /** + * Transforme en ajoutant des info de mappings. + * + * Ajoute un entity-name avec la valeur de name. + * Supprime name , sinon hibernate l'utilise encore. + * + * @param dom4jdoc le document + * @return un document mapping entite + */ + private static Document transformAsValidHibernateMapConfigFile(Document dom4jdoc) { + Element rootElem = dom4jdoc.getRootElement(); + + List childElements = rootElem.elements("class"); + + for(Object child : childElements) { + Element eChild = (Element)child; + + // suivant si l'attribut est deja la ou pas + String entityname = eChild.attributeValue("entity-name"); + String odlClassName = eChild.attributeValue("name"); + + if(entityname == null) { + // on l'ajoute alors + eChild.addAttribute("entity-name", odlClassName); + } + + // et supprime l'attribut name (sinon, hibernate continue de l'utiliser) + Attribute attName = eChild.attribute("name"); + if(attName != null) { + eChild.remove(attName); + } + } + + return dom4jdoc; + } + + /** + * Charge un document dom4j + * + * @param fichier le nom du fichier + * @return un document dom4j + */ + private static Document getDom4jDocument(String fichier) { + SAXReader reader = new SAXReader(); + reader.setValidation(false); + //DTDEntityResolver = celui d'hibernate + reader.setEntityResolver(new DTDEntityResolver()); + org.dom4j.Document document = null; + try { + document = reader.read(fichier); + } catch (DocumentException e) { + e.printStackTrace(); + } + return document; + } + + + // a partir d'ici, on reprend du code de lutin xml + // mais on doit le modifier pour specifier un DTDResolver , celui d'hibernate + + /** + * Construit un nouveau document a partir du flux + */ + private static org.w3c.dom.Document getDocumentResolvedByHibernate(String xmlString) { + DocumentBuilder domBuild = ConfigurationHelper.getDocumentBuilder(); + // resolver hibernate + domBuild.setEntityResolver(new DTDEntityResolver()); + + org.w3c.dom.Document domDoc = null; + try { + domDoc = domBuild.parse(new InputSource(new BufferedReader(new StringReader(xmlString)))); + } catch (IOException eee) { + logger.error("Error while reading xml mapping"); + } catch (SAXException eee) { + logger.error("Error while parsing xml mapping"); + } + + return domDoc; + } + + /** + * Recopie, elle est protected dans lutin xml + * @return un DocumentBuilder + */ + private static DocumentBuilder getDocumentBuilder() { + DocumentBuilderFactory builderFactory = + DocumentBuilderFactory.newInstance(); + + // on configure le builder + builderFactory.setCoalescing(true); + builderFactory.setExpandEntityReferences(true); + builderFactory.setIgnoringComments(true); + builderFactory.setNamespaceAware(true); + builderFactory.setValidating(false); + + DocumentBuilder builder = null; + try { + builder = builderFactory.newDocumentBuilder(); + } catch (ParserConfigurationException eee) { + logger.error("Error while parsing xml mapping"); + } + + return builder; + } +} Index: topia-service/src/java/org/codelutin/topia/migration/kernel/NullMigrationClass.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/kernel/NullMigrationClass.java:1.1 --- /dev/null Mon Apr 2 14:24:45 2007 +++ topia-service/src/java/org/codelutin/topia/migration/kernel/NullMigrationClass.java Mon Apr 2 14:24:37 2007 @@ -0,0 +1,51 @@ +/* *##% + * Copyright (C) 2007 Code Lutin + * + * 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. + *##%*/ + +package org.codelutin.topia.migration.kernel; + +import org.codelutin.topia.migration.common.ProxyClass; +import org.codelutin.topia.migration.common.Version; +import org.codelutin.topia.migration.transformation.MigrationClass; + +/** + * NullMigrationClass.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007/04/02 14:24:37 $ + */ +public class NullMigrationClass extends MigrationClass { + + /** + * Constructeur. + * + * @param nameMigrationClass + * @param nameClass + * @param begin + * @param end + */ + public NullMigrationClass(String nameMigrationClass, ProxyClass nameClass, Version begin, Version end) { + super(nameMigrationClass, nameClass, begin, end); + } + + +} Index: topia-service/src/java/org/codelutin/topia/migration/kernel/ObjectNotFound.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/kernel/ObjectNotFound.java:1.1 --- /dev/null Mon Apr 2 14:24:45 2007 +++ topia-service/src/java/org/codelutin/topia/migration/kernel/ObjectNotFound.java Mon Apr 2 14:24:37 2007 @@ -0,0 +1,53 @@ +/* *##% + * Copyright (C) 2007 Code Lutin + * + * 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. + *##%*/ + +package org.codelutin.topia.migration.kernel; + +/** + * ObjectNotFound.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007/04/02 14:24:37 $ + */ +public class ObjectNotFound extends Exception { + + /** + * Version UID + */ + private static final long serialVersionUID = -4631241291076236289L; + + public ObjectNotFound() { + } + + public ObjectNotFound(String arg0) { + super(arg0); + } + + public ObjectNotFound(Throwable arg0) { + super(arg0); + } + + public ObjectNotFound(String arg0, Throwable arg1) { + super(arg0, arg1); + } +}