Index: topia-service/src/java/org/codelutin/topia/migration/DatabaseManager.java diff -u topia-service/src/java/org/codelutin/topia/migration/DatabaseManager.java:1.4 topia-service/src/java/org/codelutin/topia/migration/DatabaseManager.java:1.5 --- topia-service/src/java/org/codelutin/topia/migration/DatabaseManager.java:1.4 Fri Nov 9 17:18:03 2007 +++ topia-service/src/java/org/codelutin/topia/migration/DatabaseManager.java Tue Nov 20 15:16:35 2007 @@ -18,6 +18,8 @@ package org.codelutin.topia.migration; +import java.sql.Connection; +import java.sql.SQLException; import java.util.Iterator; import java.util.List; import java.util.Properties; @@ -29,13 +31,19 @@ import org.hibernate.SQLQuery; import org.hibernate.Session; import org.hibernate.SessionFactory; +import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; +import org.hibernate.connection.ConnectionProvider; +import org.hibernate.connection.ConnectionProviderFactory; +import org.hibernate.dialect.Dialect; import org.hibernate.exception.JDBCConnectionException; import org.hibernate.exception.SQLGrammarException; import org.hibernate.mapping.ForeignKey; import org.hibernate.mapping.Table; +import org.hibernate.tool.hbm2ddl.DatabaseMetadata; import org.hibernate.tool.hbm2ddl.SchemaExport; +import org.hibernate.tool.hbm2ddl.TableMetadata; /** * DatabaseManager.java @@ -47,9 +55,9 @@ * @author Chevallereau Benjamin * @author Eon Sébastien * @author Trève Vincent - * @version $Revision: 1.4 $ + * @version $Revision: 1.5 $ * - * Last update : $Date: 2007-11-09 17:18:03 $ + * Last update : $Date: 2007-11-20 15:16:35 $ */ public class DatabaseManager { @@ -104,17 +112,18 @@ * Retourne la version de la base * @return la version present en base, ou null si la version ne peut pas etre determinee */ - public Version getDataBaseVersion() throws TopiaMigrationServiceException { + public Version getDataBaseVersion() throws MigrationServiceException { Version version = null; logger.debug("Begin transaction to get version in database"); + // get session + Session session = sessionFactory.openSession(); + try { - // get session - Session session = sessionFactory.getCurrentSession(); - + // debut d'une transaction - session.beginTransaction(); + Transaction tx = session.beginTransaction(); // execute query TMSVersion result = (TMSVersion)session.createCriteria(TMSVersion.class).uniqueResult(); @@ -127,10 +136,10 @@ } // commit - session.getTransaction().commit(); + tx.commit(); } catch(JDBCConnectionException e) { - throw new TopiaMigrationServiceException("Connection to database refused, check your configuration !"); + throw new MigrationServiceException("Connection to database refused, check your configuration !"); } catch(SQLGrammarException e) { // si la table n'existe pas, on obtient une exception : base non versionnee @@ -139,6 +148,9 @@ // on retourn null version = null; } + finally { + session.close(); + } return version; } @@ -154,10 +166,10 @@ logger.debug("Renaming tables in configuration and database for version " + vdbVersion.getVersion()); // get session - Session session = sessionFactory.getCurrentSession(); + Session session = sessionFactory.openSession(); // debut d'une transaction - session.beginTransaction(); + Transaction tx = session.beginTransaction(); // DONE : voir si les relation *<->* sont listees par l'iterateur // -> ok, normalement c bon, c gere @@ -193,7 +205,10 @@ } // commit - session.getTransaction().commit(); + tx.commit(); + + // close + session.close(); } /** @@ -293,17 +308,18 @@ /** * Introduit la version du nouveau schema dans la base - * @throws TopiaMigrationServiceException + * @param newVersion la version */ - public void putVersionInDatabase(Version nouvelleVersion) { + public void putVersionInDatabase(Version newVersion) { // get session - Session session = sessionFactory.getCurrentSession(); + Session session = sessionFactory.openSession(); // debut d'une transaction - session.beginTransaction(); + Transaction tx = session.beginTransaction(); logger.debug("Deleting existing versions"); + // supprime les versions existants // au pire, on pourrait remplacer la premiere // execute query @@ -315,56 +331,24 @@ } // positionne la version - logger.debug("Setting database version to " + nouvelleVersion.getVersion()); - TMSVersion version = new TMSVersion(nouvelleVersion.getVersion()); + logger.debug("Setting database version to " + newVersion.getVersion()); + TMSVersion version = new TMSVersion(newVersion.getVersion()); session.save(version); // commit - session.getTransaction().commit(); + tx.commit(); + + // session close + session.close(); } /** * Supprime les tables des l'ancien mapping * - * ATTENTION : pour H2 apparement, les version anterieures à 'Version 1.0 / 2007-03-04' - * bug pour la suppression des tables (erreur de lock) - * - * Postgres gere les dépendances entres les tables - * il faut les supprimer dans l'ordre... - * * @param oldConfiguration configuration contenant le schema */ public void removeTablesFromOldMapping(Configuration oldConfiguration) { - - /*logger.debug("Removing tables from old mapping"); - // get session - Session session = sessionFactory.getCurrentSession(); - - // debut d'une transaction - session.beginTransaction(); - - // les relations *<->* sont listees par l'iterateur - Iterator i = oldConfiguration.getTableMappings(); - while(i.hasNext()) { - Table table = (Table)i.next(); - - String tableName = table.getName(); - // en fait, le nom de la table dans la configuration est deja versionne - - // TODO table existe pas - logger.debug("Deleting table " + tableName); - String sQuery = "DROP TABLE " + tableName + " cascade"; - //logger.debug("Query : " + sQuery); - - // TODO utilsation des dialect pour etre sur d'avoir des requete correctes ? - - SQLQuery sqlq = session.createSQLQuery(sQuery); - sqlq.executeUpdate(); - } - - // commit - session.getTransaction().commit();*/ if(logger.isDebugEnabled()) { logger.debug("Removing schema"); @@ -372,16 +356,7 @@ dropSchema(oldConfiguration); } - /** - * Se deconnecte - */ - public void disconnect() { - //nettoye - sessionFactory.getCurrentSession().close(); - sessionFactory.close(); - } - - /** + /** * Return table suffix name * * @param version version @@ -391,4 +366,52 @@ String suffix = VERSIONNED_TABLES_SUFFIX + version.getValidName(); return suffix; } + + /** + * Test si les tables correspondant a une configuration existent. + * + * Test si au moins une table de la configuration existe. + * + * @param configuration la configuration + * @return true si le schema existe + */ + public boolean isSchemaExist(Configuration configuration) { + + boolean exist = false; + + Dialect dialect = Dialect.getDialect(configuration.getProperties()); + ConnectionProvider connectionProvider = ConnectionProviderFactory.newConnectionProvider( configuration.getProperties() ); + + try { + + Iterator tables = configuration.getTableMappings(); + + if(tables.hasNext()) { + Table testTable = tables.next(); + + Connection connection = connectionProvider.getConnection(); + + DatabaseMetadata meta = new DatabaseMetadata( connection, dialect ); + + TableMetadata tmd = meta.getTableMetadata(testTable.getName(), testTable.getSchema(), testTable.getCatalog(), testTable.isQuoted()); + + if(tmd!=null) { //table not found + exist = true; + } + } + + } catch (SQLException e) { + logger.error("Cant connect to database",e); + } + + return exist; + } + + /** + * Se deconnecte + */ + public void disconnect() { + //nettoye + sessionFactory.close(); + } } Index: topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationService.java diff -u topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationService.java:1.2 topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationService.java:1.3 --- topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationService.java:1.2 Wed Nov 14 17:36:11 2007 +++ topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationService.java Tue Nov 20 15:16:36 2007 @@ -18,9 +18,8 @@ package org.codelutin.topia.migration; +import org.codelutin.topia.event.TopiaContextListener; import org.codelutin.topia.framework.TopiaService; -import org.codelutin.topia.migration.callback.MigrationCallbackHandler; -import org.hibernate.cfg.Configuration; /** * TopiaMigrationService.java @@ -29,71 +28,14 @@ * @author Chevallereau Benjamin * @author Eon Sébastien * @author Trève Vincent - * @version $Revision: 1.2 $ + * @version $Revision: 1.3 $ * - * Last update : $Date: 2007-11-14 17:36:11 $ + * Last update : $Date: 2007-11-20 15:16:36 $ */ -public interface TopiaMigrationService extends TopiaService { +public interface TopiaMigrationService extends TopiaService, TopiaContextListener { /** * Nom du service */ public static final String SERVICE_NAME = "migration"; - - /** - * Renvoie le chemin du ficier de configuration utilise - * @return Chemin du fichier de configuration - */ - public String getConfigurationFile(); - - /** - * Modifie le fichier de configuration - * @param configurationFile Chemin du fichier de configuration d'hibernate - */ - public void setConfigurationFile(String configurationFile); - - /** - * Retourne la configuration - * @return la configuration - * @see org.hibernate.cfg.Configuration - */ - public Configuration getConfiguration(); - - /** - * Renseigne la configuration - * @param configuration la configuration - * @see org.hibernate.cfg.Configuration - */ - public void setConfiguration(Configuration configuration); - - /** - * Retourne le repertoire des anciens schemas - * @return Le repertoire des anciens schemas - */ - public String getMappingsDirectory(); - - /** - * Modifie le chemin du dossier des anciens schemas - * @param mappingsDirectory Le chemin du dossier des anciens schemas - */ - public void setMappingsDirectory(String mappingsDirectory); - - /** - * Change la version courante - * @param version la version - */ - public void setApplicationVersion(String version); - - /** - * Ajoute un callbackhandler pour la migration - */ - public void addMigrationCallbackHandler(MigrationCallbackHandler callbackHandler); - - /** - * Migrate the schema - * - * @return true si la migration a ete effectuee et s'est bien passee, false sinon - * @throws TopiaMigrationServiceException dans le cas ou le schema ne peut pas etre mis a jour - */ - public boolean migrateSchema() throws TopiaMigrationServiceException; } \ No newline at end of file Index: topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationServiceImpl.java diff -u topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationServiceImpl.java:1.8 topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationServiceImpl.java:1.9 --- topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationServiceImpl.java:1.8 Wed Nov 14 17:36:11 2007 +++ topia-service/src/java/org/codelutin/topia/migration/TopiaMigrationServiceImpl.java Tue Nov 20 15:16:36 2007 @@ -19,27 +19,16 @@ package org.codelutin.topia.migration; import java.io.File; -import java.net.URL; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; import java.util.Properties; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.codelutin.topia.TopiaRuntimeException; +import org.codelutin.topia.event.TopiaContextEvent; +import org.codelutin.topia.framework.TopiaContextImpl; import org.codelutin.topia.framework.TopiaContextImplementor; import org.codelutin.topia.migration.callback.MigrationCallbackHandler; import org.codelutin.topia.migration.common.Version; -import org.codelutin.topia.migration.kernel.ConfigurationAdapter; -import org.codelutin.topia.migration.kernel.ConfigurationHelper; -import org.codelutin.topia.migration.kernel.Transformer; -import org.codelutin.util.Resource; import org.hibernate.cfg.Configuration; /** @@ -51,11 +40,11 @@ * @author Chevallereau Benjamin * @author Eon S�bastien * @author Tr�ve Vincent - * @version $Revision: 1.8 $ + * @version $Revision: 1.9 $ * - * Last update : $Date: 2007-11-14 17:36:11 $ + * Last update : $Date: 2007-11-20 15:16:36 $ */ -public class TopiaMigrationServiceImpl implements TopiaMigrationService { +public class TopiaMigrationServiceImpl extends MigrationServiceImpl implements TopiaMigrationService { /** * Nom des proprietes @@ -72,51 +61,15 @@ static final protected String TOPIA_PERSISTENCE_CLASSES = "topia.persistence.classes"; /** - * Nom courant du fichier de configuration. - */ - protected String currentHibernateConfigurationFile; - - /** - * Configuration hibernate courante utilisee par l'application - */ - protected Configuration currentApplicationConfiguration; - - /** - * Chemin du dossier contenant les schema de touts les versions - */ - protected String mappingsDirectory; - - /** - * Version courante de l'application - */ - protected Version currentApplicationVersion; - - /** - * CallbackHandler list - */ - protected List migrationCallBackHandlers; - - /** * Logger (common-logging) */ private static Log logger = LogFactory.getLog(TopiaMigrationServiceImpl.class); /** * Constructeur vide. - * - * Visibilite package (contruite par la factory). - * */ public TopiaMigrationServiceImpl() { super(); - - // init the configuration file - currentHibernateConfigurationFile = null; - // init configuration - currentApplicationConfiguration = null; - - // init callbask list - migrationCallBackHandlers = new LinkedList(); } /* (non-Javadoc) @@ -136,7 +89,7 @@ /* (non-Javadoc) * @see org.codelutin.topia.framework.TopiaService#preInit(org.codelutin.topia.framework.TopiaContextImplementor) */ - public void preInit(TopiaContextImplementor context) { + public boolean preInit(TopiaContextImplementor context) { Properties config = context.getConfig(); String mappingdir = config.getProperty(MIGRATION_PREVIOUS_MAPPING_DIRECTORY, null); @@ -230,6 +183,8 @@ } } + // add topia context listener + context.addTopiaContextListener(this); // test mappingdir null if(mappingdir == null) { @@ -247,361 +202,70 @@ for(String modelname : modelnames) { this.setMappingsDirectory(mappingdir + File.separator + modelname); + boolean complete = false; + try { // migration - migrateSchema(); + complete = migrateSchema(); } - catch(TopiaMigrationServiceException e) { + catch(MigrationServiceException e) { logger.error("Can't migrate schema", e); } + + + if(!complete) { + if(logger.isDebugEnabled()) { + logger.error("Database migration not complete"); + } + throw new TopiaRuntimeException("Database migration not succesfully ended !"); + } } } } + + return true; } /* (non-Javadoc) * @see org.codelutin.topia.framework.TopiaService#postInit(org.codelutin.topia.framework.TopiaContextImplementor) */ - public void postInit(TopiaContextImplementor context) { - - } - - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#getConfigurationFile() - */ - public String getConfigurationFile() { - return currentHibernateConfigurationFile; - } - - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#setConfigurationFile(java.lang.String) - */ - public void setConfigurationFile(String hibernateConfigurationFile) { - this.currentHibernateConfigurationFile = hibernateConfigurationFile; + public boolean postInit(TopiaContextImplementor context) { + return true; } - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#getConfiguration() - */ - public Configuration getConfiguration() { - return currentApplicationConfiguration; - } - - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#setConfiguration(org.hibernate.cfg.Configuration) - */ - public void setConfiguration(Configuration configuration) { - this.currentApplicationConfiguration = configuration; - } - - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#getMappingsDirectory() - */ - public String getMappingsDirectory() { - return mappingsDirectory; - } - - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#setMappingsDirectory(java.lang.String) - */ - public void setMappingsDirectory(String mappingsDirectory) { - this.mappingsDirectory = mappingsDirectory; - } - - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#setApplicationVersion(java.lang.String) - */ - public void setApplicationVersion(String version) { - currentApplicationVersion = new Version(version); - } - - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#addMigrationCallbackHandler(org.codelutin.topia.migration.callback.MigrationCallbackHandler) + /* (non-Javadoc) + * @see org.codelutin.topia.event.TopiaContextListener#postCreateSchema(org.codelutin.topia.event.TopiaContextEvent) */ - public void addMigrationCallbackHandler( - MigrationCallbackHandler callbackHandler) { - migrationCallBackHandlers.add(callbackHandler); + public void postCreateSchema(TopiaContextEvent event) { + + if(logger.isDebugEnabled()) { + logger.debug("postCreateSchema event called : put version in database"); + } + + TopiaContextImplementor context = (TopiaContextImplementor)event.getSource(); + + Version version = new Version(context.getConfig().getProperty(MIGRATION_APPLICATION_VERSION, null)); + putVersionInDatabase(context.getConfig(), version , true); } - - /** - * Charge la configuration locale si elle n'est pas deja ete fournit - */ - protected void loadApplicationConfiguration() { - - // configuration pas deja fournit - if(this.currentApplicationConfiguration == null) { - // creation - this.currentApplicationConfiguration = new Configuration(); - - if ( this.currentHibernateConfigurationFile != null ) { - logger.debug("Loading configuration file : " + this.currentHibernateConfigurationFile); - - // chargement via l'objet configuration dhibernate - currentApplicationConfiguration.configure(this.currentHibernateConfigurationFile); - } - else { - logger.debug("Loading configuration file : default hibernate configuration file"); - - // chargement via l'objet configuration dhibernate - currentApplicationConfiguration.configure(); - } - } - else { - // log - logger.debug("Configuration given, nothing to load"); - } - } - - /* (non-Javadoc) - * @see org.codelutin.topia.migration.TopiaMigrationService#migrateSchema() - */ - public boolean migrateSchema() throws TopiaMigrationServiceException { - /*temp*/long timeBegin = System.currentTimeMillis(); + /* (non-Javadoc) + * @see org.codelutin.topia.event.TopiaContextListener#postUpdateSchema(org.codelutin.topia.event.TopiaContextEvent) + */ + public void postUpdateSchema(TopiaContextEvent event) { - // return flag - boolean schemaMigrated = false; - - // log - logger.info("Starting Topia Migration Service"); - - // check that version is set - if(this.currentApplicationVersion == null) { - throw new TopiaMigrationServiceException("No version set"); - } - - // check that shema location is set - if(this.mappingsDirectory == null) { - throw new TopiaMigrationServiceException("No old mapping directory set"); - } - - // chargement de la configuration de l'application - loadApplicationConfiguration(); - - // initie un DatabaseManager - // fournit les propietes de connection a la base (properties) - DatabaseManager dbManager = new DatabaseManager(this.currentApplicationConfiguration.getProperties()); - - // recupere la version de la base - Version vdbVersion = dbManager.getDataBaseVersion(); - - // si la version n'a pas ete trouvee - if(vdbVersion == null) { - // la base dans ce cas n'est pas versionee. - // On dit que la version de la base est 0 - // et les schema de cette version 0 doivent - // etre detenu en local - vdbVersion = Version.VZERO; - - logger.info("Database version not found, so database schema is considered as V0"); - - // si la base n'etait pas versionnee, la table version n'existe pas - // creation - dbManager.createVersionTable(); - } - - logger.info("Application version : " + currentApplicationVersion.getVersion() + ", database version : " + vdbVersion.getVersion()); - - /*temp*/long timeEnd = System.currentTimeMillis(); - - /*temp*/logger.error("[Time] get db version took : " + (timeEnd - timeBegin) + " ms"); - - // ask handler for migration - boolean doMigration; - // vdbVersion < currentApplicationVersion - if(vdbVersion.compareTo(currentApplicationVersion) < 0) { - logger.info("Database need update"); - - doMigration = askHandlerForMigration(vdbVersion.getVersion(),currentApplicationVersion.getVersion()); - - schemaMigrated = doMigration; - } - else { - doMigration = false; - schemaMigrated = true; // normal behaviour - logger.info("Database is up to date, no migration needed."); - } - - // si la migration doit etre faite - if(doMigration) { - - /*temp*/timeBegin = System.currentTimeMillis(); - - logger.info("Database need update"); - - // ici, on charge toutes les configuration, entre >= vdbVersion et < currentApplicationVersion - Map mVersionAndConfigurationMap = loadOldConfigurations(vdbVersion); - - // Les configurationAdpater pour le kernel - SortedMap smVersionAndConfigurationAdapterMap = new TreeMap(); - - // les configurations sont chargees - // on doit : - // - pour la version vdbVersion, on utilise les tables deja en base - // - pour les autres, creer les tables (suffixees avec la version) - // - creation du schema courant - - logger.debug("Set old database from old mappings"); - - // en meme temps, on construit les ConfigurationAdapter pour le noyau - for(Map.Entry entry : mVersionAndConfigurationMap.entrySet()) { - Version vVersion = entry.getKey(); - Configuration cConfiguration = entry.getValue(); - - //ConfigurationHelper.getConfigurationForVersion(v) - // ne positionne pas les properties parce qu'elle n'en a pas connaissance - // on les met ici - cConfiguration.setProperties(this.currentApplicationConfiguration.getProperties()); - - // renommage des table - // et creation des schema intermediaires - if(!vVersion.equals(vdbVersion)) { - cConfiguration = dbManager.setRenamedTableSchema(cConfiguration,vVersion); - logger.debug("Creating schema for version : " + vVersion.getVersion()); - dbManager.setApplicationSchemaInDatabase(cConfiguration); - } - - // on construit les ConfigurationAdpater - ConfigurationAdapter cfgAdpater = new ConfigurationAdapter(cConfiguration,vVersion); - smVersionAndConfigurationAdapterMap.put(vVersion, cfgAdpater); - } - - // enfin, il reste la configuration de l'application - // on va instancier le nouveau schema (le creer) - - // on renomme le nom des table d'abord - this.currentApplicationConfiguration = dbManager.setRenamedTableSchema(this.currentApplicationConfiguration,this.currentApplicationVersion); - - logger.debug("Creating current application schema"); - dbManager.setApplicationSchemaInDatabase(this.currentApplicationConfiguration); - - ConfigurationAdapter appCfgAdpater = new ConfigurationAdapter(this.currentApplicationConfiguration,this.currentApplicationVersion); - smVersionAndConfigurationAdapterMap.put(this.currentApplicationVersion, appCfgAdpater); - - logger.info("Data migration"); - - // Ici, on a l'ancien schema deja present en base - // les schemas intermediaires creer et vides - // et le nouveau schema cree et vide - // on doit maintenant migrer les donnees - - // execute la transformation - Transformer trans = new Transformer(smVersionAndConfigurationAdapterMap); - - // migrate data - trans.execute(); - - // log - logger.info("Data migrated"); - - // Changement de la version en base - dbManager.putVersionInDatabase(currentApplicationVersion); - - logger.debug("Deleting old database"); - - // suppresion des anciennes tables de toutes les configuration, sauf - // currentApplicationVersion - // (elle n'est pas dans mVersionAndConfigurationMap) - for(Configuration cfg : mVersionAndConfigurationMap.values()) { - dbManager.removeTablesFromOldMapping(cfg); - } - - // renommage correct du schema courant - dbManager.renameTables(this.currentApplicationConfiguration, this.currentApplicationVersion); - - // il faudrait ici valider les transactions et fermer les sessions - // vmvManager a sa propre gestion des transactions/session - // this.remoteConfiguration doit en avoir ouverte - // this.localConfiguration aussi - - // all done - logger.info("All done, migration complete"); - - /*temp*/timeEnd = System.currentTimeMillis(); - /*temp*/logger.error("[Time] migration took : " + (timeEnd - timeBegin) + " ms"); - - } - - // ferme la connexion a la base - dbManager.disconnect(); - - return schemaMigrated; - } + } - /** - * Ask handler for migration. - * - * Return true if all handler return true, or if there is no handler - * - * @return true or false + /* (non-Javadoc) + * @see org.codelutin.topia.event.TopiaContextListener#preCreateSchema(org.codelutin.topia.event.TopiaContextEvent) */ - protected boolean askHandlerForMigration(String databaseVersion, String applicationVersion) { + public void preCreateSchema(TopiaContextEvent event) { - // true par defaut, s'il n'y a pas de handlers - boolean result = true; - - for(MigrationCallbackHandler callback : migrationCallBackHandlers) { - result &= callback.doMigration(databaseVersion, applicationVersion); - } - - return result; } - /** - * Charge les configurations de version a partir de vdbVersion - * jusqu'a currentApplicationVersion "non compris" - * @param vdbVersion la version de depart - */ - protected Map loadOldConfigurations(Version vdbVersion) { - // schema des noms de dossier de version - final Pattern pattern = Pattern.compile(mappingsDirectory + File.separator + "([0-9]+(\\.[0-9]+)*)"); - - // instancie la map ordonee - Map mVersionAndConfigurationMap = null; - - List urls = null; - urls = Resource.getURLs(".*" + mappingsDirectory + File.separator + ".*"); - - if (urls != null && urls.size() > 0) { - - mVersionAndConfigurationMap = new HashMap(); - - // ensemble ordonnee des version a charger apres - TreeSet tsEnsembleVersionACharger = new TreeSet(); - - for(URL url : urls) { - Matcher matcher = pattern.matcher(url.getFile()); - - if(matcher.find()) { - // group(1) est ce qui match entre le premier niveau de parentheses - String sVersion = matcher.group(1); - //logger.debug("Directory " + fileInIt.getName() + " matches, version = " + sVersion); - - tsEnsembleVersionACharger.add(new Version(sVersion)); - } - } - - ConfigurationHelper cfgHelper = new ConfigurationHelper(); - - // charge les version qui conviennent - for(Version v : tsEnsembleVersionACharger) { - if(v.compareTo(vdbVersion) < 0) { - logger.debug("No load needed for version " + v.getVersion()); - } - else { - logger.debug("Loading mapping for version " + v.getVersion()); - - String mappingVersionDir = mappingsDirectory + File.separator + v.getVersion(); - - Configuration cfgForVersion = cfgHelper.getConfigurationInDirectory(mappingVersionDir); - mVersionAndConfigurationMap.put(v, cfgForVersion); - } - } - } - else { - logger.error("No mapping found in classpath '" + mappingsDirectory + "'; can't load old mappings"); - } - - return mVersionAndConfigurationMap; - } + /* (non-Javadoc) + * @see org.codelutin.topia.event.TopiaContextListener#preUpdateSchema(org.codelutin.topia.event.TopiaContextEvent) + */ + public void preUpdateSchema(TopiaContextEvent event) { + + } } Index: topia-service/src/java/org/codelutin/topia/migration/MigrationService.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/MigrationService.java:1.1 --- /dev/null Tue Nov 20 15:16:42 2007 +++ topia-service/src/java/org/codelutin/topia/migration/MigrationService.java Tue Nov 20 15:16:35 2007 @@ -0,0 +1,93 @@ +/* *##% + * 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; + +import org.codelutin.topia.migration.callback.MigrationCallbackHandler; +import org.hibernate.cfg.Configuration; + +/** + * MigrationService.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007-11-20 15:16:35 $ + */ +public interface MigrationService { + + /** + * Renvoie le chemin du ficier de configuration utilise + * @return Chemin du fichier de configuration + */ + public String getConfigurationFile(); + + /** + * Modifie le fichier de configuration + * @param configurationFile Chemin du fichier de configuration d'hibernate + */ + public void setConfigurationFile(String configurationFile); + + /** + * Retourne la configuration + * @return la configuration + * @see org.hibernate.cfg.Configuration + */ + public Configuration getConfiguration(); + + /** + * Renseigne la configuration + * @param configuration la configuration + * @see org.hibernate.cfg.Configuration + */ + public void setConfiguration(Configuration configuration); + + /** + * Retourne le repertoire des anciens schemas + * @return Le repertoire des anciens schemas + */ + public String getMappingsDirectory(); + + /** + * Modifie le chemin du dossier des anciens schemas + * @param mappingsDirectory Le chemin du dossier des anciens schemas + */ + public void setMappingsDirectory(String mappingsDirectory); + + /** + * Change la version courante + * @param version la version + */ + public void setApplicationVersion(String version); + + /** + * Ajoute un callbackhandler pour la migration + */ + public void addMigrationCallbackHandler(MigrationCallbackHandler callbackHandler); + + /** + * Migrate the schema + * + * @return true si la migration a ete effectuee et s'est bien passee, false sinon + * @throws MigrationServiceException dans le cas ou le schema ne peut pas etre mis a jour + */ + public boolean migrateSchema() throws MigrationServiceException; +} \ No newline at end of file Index: topia-service/src/java/org/codelutin/topia/migration/MigrationServiceException.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/MigrationServiceException.java:1.1 --- /dev/null Tue Nov 20 15:16:44 2007 +++ topia-service/src/java/org/codelutin/topia/migration/MigrationServiceException.java Tue Nov 20 15:16:36 2007 @@ -0,0 +1,70 @@ +/* *##% + * 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; + +/** + * TopiaMigrationServiceException.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007-11-20 15:16:36 $ + */ +public class MigrationServiceException extends Exception { + + /** + * Version + */ + private static final long serialVersionUID = -8900901171551405745L; + + /** + * Constructeur par defaut + */ + public MigrationServiceException() { + super(); + } + + /** + * Constructeur avec message + * @param message Le message d'erreur + */ + public MigrationServiceException(String message) { + super(message); + } + + /** + * Constructeur avec message et exception + * @param message Le message d'erreur + * @param exception l'exception + */ + public MigrationServiceException(String message, Throwable exception) { + super(message, exception); + } + + /** + * Constructeur avec exception + * @param exception l'exception + */ + public MigrationServiceException(Throwable exception) { + super(exception); + } +} Index: topia-service/src/java/org/codelutin/topia/migration/MigrationServiceImpl.java diff -u /dev/null topia-service/src/java/org/codelutin/topia/migration/MigrationServiceImpl.java:1.1 --- /dev/null Tue Nov 20 15:16:44 2007 +++ topia-service/src/java/org/codelutin/topia/migration/MigrationServiceImpl.java Tue Nov 20 15:16:36 2007 @@ -0,0 +1,499 @@ +/* *##% + * 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; + +import java.io.File; +import java.net.URL; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.topia.migration.callback.MigrationCallbackHandler; +import org.codelutin.topia.migration.common.Version; +import org.codelutin.topia.migration.kernel.ConfigurationAdapter; +import org.codelutin.topia.migration.kernel.ConfigurationHelper; +import org.codelutin.topia.migration.kernel.Transformer; +import org.codelutin.util.Resource; +import org.hibernate.cfg.Configuration; + +/** + * MigrationServiceImpl.java + * + * Classe principale du projet. + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon S�bastien + * @author Tr�ve Vincent + * @version $Revision: 1.1 $ + * + * Last update : $Date: 2007-11-20 15:16:36 $ + */ +public class MigrationServiceImpl implements MigrationService { + + /** + * Nom courant du fichier de configuration. + */ + protected String currentHibernateConfigurationFile; + + /** + * Configuration hibernate courante utilisee par l'application + */ + protected Configuration currentApplicationConfiguration; + + /** + * Chemin du dossier contenant les schema de touts les versions + */ + protected String mappingsDirectory; + + /** + * Version courante de l'application + */ + protected Version currentApplicationVersion; + + /** + * CallbackHandler list + */ + protected List migrationCallBackHandlers; + + /** + * Logger (common-logging) + */ + private static Log logger = LogFactory.getLog(MigrationServiceImpl.class); + + /** + * Constructeur vide. + */ + public MigrationServiceImpl() { + super(); + + // init the configuration file + currentHibernateConfigurationFile = null; + // init configuration + currentApplicationConfiguration = null; + + // init callbask list + migrationCallBackHandlers = new LinkedList(); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#getConfigurationFile() + */ + public String getConfigurationFile() { + return currentHibernateConfigurationFile; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#setConfigurationFile(java.lang.String) + */ + public void setConfigurationFile(String hibernateConfigurationFile) { + this.currentHibernateConfigurationFile = hibernateConfigurationFile; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#getConfiguration() + */ + public Configuration getConfiguration() { + return currentApplicationConfiguration; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#setConfiguration(org.hibernate.cfg.Configuration) + */ + public void setConfiguration(Configuration configuration) { + this.currentApplicationConfiguration = configuration; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#getMappingsDirectory() + */ + public String getMappingsDirectory() { + return mappingsDirectory; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#setMappingsDirectory(java.lang.String) + */ + public void setMappingsDirectory(String mappingsDirectory) { + this.mappingsDirectory = mappingsDirectory; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#setApplicationVersion(java.lang.String) + */ + public void setApplicationVersion(String version) { + currentApplicationVersion = new Version(version); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#addMigrationCallbackHandler(org.codelutin.topia.migration.callback.MigrationCallbackHandler) + */ + public void addMigrationCallbackHandler( + MigrationCallbackHandler callbackHandler) { + migrationCallBackHandlers.add(callbackHandler); + } + + /** + * Charge la configuration locale si elle n'est pas deja ete fournit + */ + protected void loadApplicationConfiguration() { + + // configuration pas deja fournit + if(this.currentApplicationConfiguration == null) { + // creation + this.currentApplicationConfiguration = new Configuration(); + + if ( this.currentHibernateConfigurationFile != null ) { + logger.debug("Loading configuration file : " + this.currentHibernateConfigurationFile); + + // chargement via l'objet configuration dhibernate + currentApplicationConfiguration.configure(this.currentHibernateConfigurationFile); + } + else { + logger.debug("Loading configuration file : default hibernate configuration file"); + + // chargement via l'objet configuration dhibernate + currentApplicationConfiguration.configure(); + } + } + else { + // log + logger.debug("Configuration given, nothing to load"); + } + } + + /** + * Verifie si les information indispensable à la migration ont été + * renseignee. + * + * @throws MigrationServiceException + */ + protected void checkInformation() throws MigrationServiceException { + // check that version is set + if(this.currentApplicationVersion == null) { + throw new MigrationServiceException("No version set"); + } + + // check that shema location is set + if(this.mappingsDirectory == null) { + throw new MigrationServiceException("No old mapping directory set"); + } + } + + /* (non-Javadoc) + * @see org.codelutin.topia.migration.TopiaMigrationService#migrateSchema() + */ + public boolean migrateSchema() throws MigrationServiceException { + + // log + logger.info("Starting Topia Migration Service"); + + // check informations + checkInformation(); + + // chargement de la configuration de l'application + loadApplicationConfiguration(); + + // initie un DatabaseManager + // fournit les propietes de connection a la base (properties) + DatabaseManager dbManager = new DatabaseManager(this.currentApplicationConfiguration.getProperties()); + + // recupere la version de la base + Version vdbVersion = dbManager.getDataBaseVersion(); + + // si la version n'a pas ete trouvee + if(vdbVersion == null) { + // la base dans ce cas n'est pas versionee. + // On dit que la version de la base est 0 + // et les schema de cette version 0 doivent + // etre detenu en local + vdbVersion = Version.VZERO; + + logger.info("Database version not found, so database schema is considered as V0"); + } + + logger.info("Application version : " + currentApplicationVersion.getVersion() + ", database version : " + vdbVersion.getVersion()); + + // tel if migration is needed + boolean bMigrationNeeded = false; + // tel if migration is wanted + boolean bMigrationWanted = true; + + // test if schema exist in database... + // if not, the schema must be created + // and it will be created in version this.currentApplicationConfiguration + Configuration vdbConfiguration = getSingleConfiguration(vdbVersion); + vdbConfiguration.setProperties(currentApplicationConfiguration.getProperties()); + bMigrationNeeded = dbManager.isSchemaExist(vdbConfiguration); + if(logger.isDebugEnabled()) { + if(bMigrationNeeded) { + logger.debug("Schema for version " + vdbVersion.getVersion() + " found. Can do migration."); + } else { + logger.debug("Schema for version " + vdbVersion.getVersion() + " not found. No migration needed."); + } + } + + // vdbVersion < currentApplicationVersion + if(bMigrationNeeded && vdbVersion.compareTo(currentApplicationVersion) < 0) { + + logger.info("Database need update"); + + bMigrationNeeded = true; + // ask handler for migration + bMigrationWanted = askHandlerForMigration(vdbVersion.getVersion(),currentApplicationVersion.getVersion()); + } + else { + logger.info("Database is up to date, no migration needed."); + } + + // si la migration doit etre faite + if(bMigrationNeeded && bMigrationWanted) { + + logger.info("Beginning database migration"); + + // ici, on charge toutes les configuration, entre > vdbVersion et < currentApplicationVersion + Map mVersionAndConfigurationMap = loadIntermediateConfigurations(vdbVersion); + + // vdbVersion mapping has been loaded earlier + // on construit les ConfigurationAdpater + mVersionAndConfigurationMap.put(vdbVersion, vdbConfiguration); + + // Les configurationAdpater pour le kernel + SortedMap smVersionAndConfigurationAdapterMap = new TreeMap(); + + // les configurations sont chargees + // on doit : + // - pour la version vdbVersion, on utilise les tables deja en base + // - pour les autres, creer les tables (suffixees avec la version) + // - creation du schema courant + logger.debug("Set old database for old mappings"); + + // en meme temps, on construit les ConfigurationAdapter pour le noyau + for(Map.Entry entry : mVersionAndConfigurationMap.entrySet()) { + Version vVersion = entry.getKey(); + Configuration cConfiguration = entry.getValue(); + + // la version vdbVersion a deja ses proprietes et ne doit pas etre renommee + if(!vdbVersion.equals(vVersion)) { + //ConfigurationHelper.getConfigurationForVersion(v) + // ne positionne pas les properties parce qu'elle n'en a pas connaissance + // on les met ici + cConfiguration.setProperties(this.currentApplicationConfiguration.getProperties()); + + // renommage des table + // et creation des schema intermediaires + cConfiguration = dbManager.setRenamedTableSchema(cConfiguration,vVersion); + logger.debug("Creating schema for version : " + vVersion.getVersion()); + dbManager.setApplicationSchemaInDatabase(cConfiguration); + } + + // on construit les ConfigurationAdpater + ConfigurationAdapter cfgAdpater = new ConfigurationAdapter(cConfiguration,vVersion); + smVersionAndConfigurationAdapterMap.put(vVersion, cfgAdpater); + } + + // enfin, il reste la configuration de l'application + // on va instancier le nouveau schema (le creer) + + // on renomme le nom des tables d'abord + this.currentApplicationConfiguration = dbManager.setRenamedTableSchema(this.currentApplicationConfiguration,this.currentApplicationVersion); + + logger.debug("Creating current application schema"); + dbManager.setApplicationSchemaInDatabase(this.currentApplicationConfiguration); + + ConfigurationAdapter appCfgAdpater = new ConfigurationAdapter(this.currentApplicationConfiguration,this.currentApplicationVersion); + smVersionAndConfigurationAdapterMap.put(this.currentApplicationVersion, appCfgAdpater); + + logger.info("Data migration"); + + // Ici, on a l'ancien schema deja present en base + // les schemas intermediaires creer et vides + // et le nouveau schema cree et vide + // on doit maintenant migrer les donnees + + // execute la transformation + Transformer trans = new Transformer(smVersionAndConfigurationAdapterMap); + + // migrate data + trans.execute(); + + // log + logger.info("Data migrated"); + + logger.debug("Deleting old database"); + + // suppresion des anciennes tables de toutes les configuration, sauf + // currentApplicationVersion + // (elle n'est pas dans mVersionAndConfigurationMap) + for(Configuration cfg : mVersionAndConfigurationMap.values()) { + dbManager.removeTablesFromOldMapping(cfg); + } + + // renommage correct du schema courant + dbManager.renameTables(this.currentApplicationConfiguration, this.currentApplicationVersion); + + // il faudrait ici valider les transactions et fermer les sessions + // vmvManager a sa propre gestion des transactions/session + // this.remoteConfiguration doit en avoir ouverte + // this.localConfiguration aussi + + // all done + logger.info("All done, migration complete"); + + // ferme la connexion a la base + dbManager.disconnect(); + + // put version in databse + putVersionInDatabase(currentApplicationConfiguration.getProperties(),currentApplicationVersion,vdbVersion.equals(Version.VZERO)); + } + else { + // ferme la connexion a la base + dbManager.disconnect(); + } + + // return succes flag + // - no migration needed + // - or migration needed and accepted + return !bMigrationNeeded || (bMigrationNeeded && bMigrationWanted); + } + + /** + * Put version in database + * + * Single method because, version can be created alone... + * + * @param properties proprietes de connexion + * @param version version + */ + protected void putVersionInDatabase(Properties properties, Version version, boolean createTable) { + + DatabaseManager dbManager = new DatabaseManager(properties); + + // update version even if database has not been migrated + // only case that database doesn't exist match this + if(createTable) { + // si la base n'etait pas versionnee, la table version n'existe pas + // creation + dbManager.createVersionTable(); + } + + // Changement de la version en base + dbManager.putVersionInDatabase(version); + + dbManager.disconnect(); + + } + + /** + * Ask handler for migration. + * + * Return true if all handler return true, or if there is no handler + * + * @return true or false + */ + protected boolean askHandlerForMigration(String databaseVersion, String applicationVersion) { + + // true par defaut, s'il n'y a pas de handlers + boolean result = true; + + for(MigrationCallbackHandler callback : migrationCallBackHandlers) { + result &= callback.doMigration(databaseVersion, applicationVersion); + } + + return result; + } + + /** + * Charge les configurations de version a partir de vdbVersion "non compris" + * jusqu'a currentApplicationVersion "non compris" + * @param vdbVersion la version de depart + */ + protected Map loadIntermediateConfigurations(Version vdbVersion) { + // schema des noms de dossier de version + final Pattern pattern = Pattern.compile(mappingsDirectory + File.separator + "([0-9]+(\\.[0-9]+)*)"); + + // instancie la map ordonee + Map mVersionAndConfigurationMap = null; + + List urls = null; + urls = Resource.getURLs(".*" + mappingsDirectory + File.separator + ".*"); + + if (urls != null && urls.size() > 0) { + + mVersionAndConfigurationMap = new HashMap(); + + // ensemble ordonnee des version a charger apres + TreeSet tsEnsembleVersionACharger = new TreeSet(); + + for(URL url : urls) { + Matcher matcher = pattern.matcher(url.getFile()); + + if(matcher.find()) { + // group(1) est ce qui match entre le premier niveau de parentheses + String sVersion = matcher.group(1); + //logger.debug("Directory " + fileInIt.getName() + " matches, version = " + sVersion); + + tsEnsembleVersionACharger.add(new Version(sVersion)); + } + } + + // charge les version qui conviennent + for(Version v : tsEnsembleVersionACharger) { + if(v.compareTo(vdbVersion) <= 0) { + logger.debug("No load needed for version " + v.getVersion()); + } + else { + logger.debug("Loading mapping for version " + v.getVersion()); + + Configuration cfgForVersion = getSingleConfiguration(v); + mVersionAndConfigurationMap.put(v, cfgForVersion); + } + } + } + else { + logger.error("No mapping found in classpath '" + mappingsDirectory + "'; can't load old mappings"); + } + + return mVersionAndConfigurationMap; + } + + /** + * Recupere une configuration sur disque pour une version. + * + * @param version version + * @return une configuration hibernate + */ + protected Configuration getSingleConfiguration(Version version) { + String mappingVersionDir = mappingsDirectory + File.separator + version.getVersion(); + + ConfigurationHelper cfgHelper = ConfigurationHelper.getInstance(); + Configuration cfgForVersion = cfgHelper.getConfigurationInDirectory(mappingVersionDir); + + return cfgForVersion; + } +}