Index: topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java diff -u /dev/null topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java:1.1 --- /dev/null Wed Jan 4 13:24:40 2006 +++ topia2/src/java/org/codelutin/topia/framework/TopiaContextImpl.java Wed Jan 4 13:24:35 2006 @@ -0,0 +1,445 @@ +/* + * *##% Copyright (C) 2005 Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. ##% + */ + +/******************************************************************************* + * TopiaContextImpl.java + * + * Created: 23 déc. 2005 16:58:50 + * + * @author poussin + * + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/01/04 13:24:35 $ by : $Author: bpoussin $ + */ + +package org.codelutin.topia.framework; + +import java.io.File; +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.topia.TopiaContext; +import org.codelutin.topia.TopiaException; +import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.topia.event.TopiaEntityEvent; +import org.codelutin.topia.event.TopiaEntityListener; +import org.codelutin.topia.persistence.TopiaDAO; +import org.codelutin.topia.persistence.TopiaDAODelegator; +import org.codelutin.topia.persistence.TopiaEntity; +import org.codelutin.util.CategorisedListenerSet; +import org.hibernate.EmptyInterceptor; +import org.hibernate.FlushMode; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.cfg.Configuration; +import org.hibernate.type.Type; + +/** + * Le TopiaContextImpl est le point d'entre pour acceder aux donnees. Il est + * configurer par un fichier de propriete + *

+ * List des proprietes disponible + *

+ *
topia.persistence.properties.file + *
le fichier de propriété a utiliser pour configurer hibernate + * + *
topia.persistence.directories + *
la liste des repertoires contenant les mappings hibernates (.hbm.xml) la + * liste de repertoire est separer par des virgules ',' + * + *
topia.persistence.classes + *
la liste des classes que doit géré hibernate. On peut tres bien utiliser + * topia.persistence.directories pour un ensemble d'entié du meme repertoire et + * topia.persistence.classes pour d'autres classes + *
+ * + * @author poussin + * + */ +public class TopiaContextImpl implements TopiaContext, TopiaContextImplementor { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(TopiaContextImpl.class); + + static final private String TOPIA_PERSISTENCE_DIRECTORIES = "topia.persistence.directories"; + + static final private String TOPIA_PERSISTENCE_CLASSES = "topia.persistence.classes"; + + static final private String TOPIA_PERSISTENCE_PROPERTIES_FILE = "topia.persistence.properties.file"; + + /** + * Le pere de ce context, les contexts initaux n'ont pas de context pere + */ + protected TopiaContextImplementor parentContext = null; + + /** + * la factory permettant de recuperer la session hibernate. Seul les + * TopiaContextImpl initiaux contiennent un hibernateFactory + */ + protected SessionFactory hibernateFactory = null; + + /** + * La session utilisé par le TopiaContextImpl + */ + protected Session hibernate = null; + + /** + * Propriete de configuration + */ + protected Properties config = null; + + /** + * cache des DAO deja chargé pour ce context + */ + protected Map daoCache = new HashMap(); + + /** + * Set des sous context creer avec un beginTransaction et donc sur lequel + * nous sommes listener + */ + protected Set childContext = new HashSet(); + + protected CategorisedListenerSet listeners = new CategorisedListenerSet( + TopiaEntityListener.class); + + + /** + * constructeur utilisé par la factory pour creer les contexts initiaux + * + * @param config + */ + public TopiaContextImpl(Properties config) { + this.config = config; + } + + /** + * Constructeur utilisé par le beginTransaction pour créer le context fils. + * + * @param config la configuration du + * @param parentContext + * @throws HibernateException + * @throws TopiaNotFoundException + */ + protected TopiaContextImpl(TopiaContextImplementor parentContext) + throws HibernateException, TopiaNotFoundException { + this.parentContext = parentContext; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#getParentContext() + */ + public TopiaContextImplementor getParentContext() { + return parentContext; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#getRootContext() + */ + public TopiaContextImplementor getRootContext() { + TopiaContextImplementor result = this; + if (getParentContext() != null) { + result = getParentContext().getRootContext(); + } + return result; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#getConfig() + */ + public Properties getConfig() { + if (config == null && getParentContext() != null) { + config = getParentContext().getConfig(); + } + return config; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#getHibernate() + */ + public Session getHibernate() throws TopiaException { + if (hibernate == null) { + throw new TopiaException( + "No hibernate session available, you must start transaction with beginTransaction"); + } + return hibernate; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#getHibernateFactory() + */ + public SessionFactory getHibernateFactory() + throws TopiaNotFoundException { + if (hibernateFactory == null) { + if (getParentContext() != null) { + hibernateFactory = getParentContext().getHibernateFactory(); + } else { + Configuration cfg = new Configuration(); + + // ajout des repertoires contenant les mappings hibernate + String[] dirs = getConfig().getProperty( + TOPIA_PERSISTENCE_DIRECTORIES, "").split(","); + for (String dir : dirs) { + dir = dir.trim(); + if (!"".equals(dir)) { + cfg.addDirectory(new File(dir)); + } + } + + // ajout des classes dites persistentes + String[] classes = getConfig().getProperty( + TOPIA_PERSISTENCE_CLASSES, "").split(","); + for (String classname : classes) { + classname = classname.trim(); + if (!"".equals(classname)) { + Class clazz; + try { + clazz = Class.forName(classname); + } catch (ClassNotFoundException eee) { + throw new TopiaNotFoundException( + "Persistent class " + classname + + " not found"); + } + cfg.addClass(clazz); + } + } + + Properties prop = new Properties(); + prop.putAll(cfg.getProperties()); + prop.putAll(getConfig()); + prop.putAll(TopiaUtil.getProperties(getConfig().getProperty( + TOPIA_PERSISTENCE_PROPERTIES_FILE))); + cfg.setProperties(prop); + + hibernateFactory = cfg.buildSessionFactory(); + } + } + return hibernateFactory; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#getDAO(java.lang.Class) + */ + @SuppressWarnings("unchecked") + public TopiaDAO getDAO(Class entityClass) throws TopiaException { + if (getRootContext() == this) { + throw new TopiaException("Vous êtes sur le root context vous devez ouvrir une transaction pour pouvoir accèder aux données"); + } + TopiaDAO result = (TopiaDAO) daoCache.get(entityClass); + if (result == null) { + // recherche du type de DAO a instancier pour cette entity + String defaultDAOClassname = getConfig().getProperty("topia.dao.default.class", "hibernate"); + String daoClassname = getConfig().getProperty("topia.dao." + entityClass.getName(), defaultDAOClassname); + if ("hibernate".equals(daoClassname)) { + daoClassname = "org.codelutin.topia.persistence.hibernate.TopiaDAOHibernate"; + } else if ("flatfile".equals(daoClassname)) { + daoClassname = "org.codelutin.topia.persistence.flatfile.TopiaDAOFlatFile"; + } + + try { + Class> resultClass = (Class>)Class.forName(daoClassname); + result = resultClass.newInstance(); + result.init(this, entityClass); + } catch (ClassNotFoundException eee) { + throw new TopiaException("Can't find DAO class " + daoClassname); + } catch (InstantiationException eee) { + throw new TopiaException("Can't instanciate DAO class " + daoClassname); + } catch (IllegalAccessException eee) { + throw new TopiaException("Can't access DAO class " + daoClassname); + } + + // looking for specialized DAO + // normalement il en existe un car il est généré automatiquement + // si on utilise la génération + daoClassname = entityClass.getName() + "DAO"; + try { + Class> daoClass = (Class>) Class + .forName(daoClassname); + TopiaDAODelegator spe = daoClass.newInstance(); + spe.setParentDAO(result); + result = spe; + } catch (Exception eee) { + log.warn("specialized DAO " + daoClassname + + " not found, use default TopiaDAOHibernate"); + } + daoCache.put(entityClass, result); + // si quelqu'un se met listener sur le TopiaContext, il faut qu'il + // soit prevenu des evenements de tout type d'entite + getListeners().addCategory(TopiaContext.class, entityClass); + } + return result; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#getListeners() + */ + public CategorisedListenerSet getListeners() { + return listeners; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.TopiaContext#addTopiaEntityListener(org.codelutin.topia.event.TopiaEntityListener) + */ + public void addTopiaEntityListener(TopiaEntityListener l) { + getListeners().add(this.getClass(), l); + } + + /* (non-Javadoc) + * @see org.codelutin.topia.TopiaContext#removeTopiaEntityListener(org.codelutin.topia.event.TopiaEntityListener) + */ + public void removeTopiaEntityListener(TopiaEntityListener l) { + getListeners().remove(this.getClass(), l); + } + + + /* (non-Javadoc) + * @see org.codelutin.topia.TopiaContext#beginTransaction() + */ + public TopiaContext beginTransaction() throws TopiaNotFoundException { + TopiaContextImpl result = new TopiaContextImpl(this); + childContext.add(result); + result.hibernate = getHibernateFactory().openSession(new TopiaInterceptor(result)); + // on ne synchronise jamais les données avec la base tant que + // l'utilisateur n'a pas fait de commit du context + result.hibernate.setFlushMode(FlushMode.NEVER); + + // TODO se mettre listener sur ce context + return result; + } + + /* (non-Javadoc) + * @see org.codelutin.topia.TopiaContext#commitTransaction() + */ + public void commitTransaction() throws TopiaException { + try { + for(TopiaDAO dao : daoCache.values()) { + dao.commitTransaction(); + } + Transaction tx = hibernate.beginTransaction(); + hibernate.flush(); + tx.commit(); + fireOnCommited(); + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + /* (non-Javadoc) + * @see org.codelutin.topia.TopiaContext#rollbackTransaction() + */ + public void rollbackTransaction() throws TopiaException { + try { + for(TopiaDAO dao : daoCache.values()) { + dao.rollbackTransaction(); + } + Transaction tx = hibernate.beginTransaction(); + hibernate.clear(); + tx.rollback(); + fireOnRollbacked(); + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnCreated(java.lang.Object) + */ + public void fireOnCreated(Object entity) { + log.info("onCreated " + entity); + try { + getListeners().fire(entity.getClass(), "entityCreated", new TopiaEntityEvent(this, entity)); + } catch (Exception eee) { + if (log.isWarnEnabled()) { + log.warn("Can't fire event created for entity: " + entity, eee); + } + } + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnUpdated(java.lang.Object) + */ + public void fireOnUpdated(Object entity) { + log.info("onUpdated " + entity); + try { + getListeners().fire(entity.getClass(), "entityUpdated", new TopiaEntityEvent(this, entity)); + } catch (Exception eee) { + if (log.isWarnEnabled()) { + log.warn("Can't fire event updated for entity: " + entity, eee); + } + } + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnDeleted(java.lang.Object) + */ + public void fireOnDeleted(Object entity) { + log.info("onDeleted " + entity); + try { + getListeners().fire(entity.getClass(), "entityDeleted", new TopiaEntityEvent(this, entity)); + } catch (Exception eee) { + if (log.isWarnEnabled()) { + log.warn("Can't fire event deleted for entity: " + entity, eee); + } + } + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnCommited() + */ + public void fireOnCommited() { + log.info("onCommited"); + // TODO + } + + /* (non-Javadoc) + * @see org.codelutin.topia.framework.TopiaContextImplementor#fireOnRollbacked() + */ + public void fireOnRollbacked() { + log.info("onRollbacked"); + // TODO + } + + static protected class TopiaInterceptor extends EmptyInterceptor { + + protected TopiaContextImplementor context = null; + + private static final long serialVersionUID = -6787010517746197446L; + + public TopiaInterceptor(TopiaContextImplementor context) { + this.context = context; + } + + public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types){ + super.onDelete(entity, id, state, propertyNames, types); + context.fireOnDeleted(entity); + } + public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { + boolean result = super.onSave(entity, id, state, propertyNames, types); + context.fireOnUpdated(entity); + return result; + } + } +} Index: topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java diff -u /dev/null topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java:1.1 --- /dev/null Wed Jan 4 13:24:40 2006 +++ topia2/src/java/org/codelutin/topia/framework/TopiaContextImplementor.java Wed Jan 4 13:24:35 2006 @@ -0,0 +1,104 @@ +/* *##% + * Copyright (C) 2006 + * Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + *##%*/ + +/* * + * TopiaContextImplementor.java + * + * Created: 3 janv. 2006 21:27:24 + * + * @author poussin + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/01/04 13:24:35 $ + * by : $Author: bpoussin $ + */ + +package org.codelutin.topia.framework; + +import java.util.Properties; + +import org.codelutin.topia.TopiaContext; +import org.codelutin.topia.TopiaException; +import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.topia.persistence.TopiaDAO; +import org.codelutin.topia.persistence.TopiaEntity; +import org.codelutin.util.CategorisedListenerSet; +import org.hibernate.Session; +import org.hibernate.SessionFactory; + +/** + * @author poussin + * + */ + +public interface TopiaContextImplementor extends TopiaContext { + + /** + * @return Returns the parentContext. + */ + public TopiaContextImplementor getParentContext(); + + public TopiaContextImplementor getRootContext(); + + /** + * @return Returns the config. + */ + public Properties getConfig(); + + /** + * @return Returns the hibernate. + * @throws TopiaException si aucune transaction n'est ouverte + */ + public Session getHibernate() throws TopiaException; + + /** + * @return Returns the hibernateFactory. + * @throws TopiaNotFoundException + */ + public SessionFactory getHibernateFactory() throws TopiaNotFoundException; + + /** + * Get DAO for specified class. If Specialized DAO exists then it returned + * otherwize TopiaDAO<entityClass> is returned + * + * @param + * @param entityClass + * @return + * @throws TopiaException + */ + @SuppressWarnings("unchecked") + public TopiaDAO getDAO(Class entityClass) + throws TopiaException; + + /** + * Retourne tous les listeners + */ + public CategorisedListenerSet getListeners(); + + public void fireOnCreated(Object entity); + + public void fireOnUpdated(Object entity); + + public void fireOnDeleted(Object entity); + + public void fireOnCommited(); + + public void fireOnRollbacked(); + +} Index: topia2/src/java/org/codelutin/topia/framework/TopiaUtil.java diff -u /dev/null topia2/src/java/org/codelutin/topia/framework/TopiaUtil.java:1.1 --- /dev/null Wed Jan 4 13:24:40 2006 +++ topia2/src/java/org/codelutin/topia/framework/TopiaUtil.java Wed Jan 4 13:24:35 2006 @@ -0,0 +1,95 @@ +/* + * *##% Copyright (C) 2005 Code Lutin, Cédric Pineau, Benjamin Poussin + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. ##% + */ + +/******************************************************************************* + * TopiaUtil.java + * + * Created: 28 déc. 2005 20:28:57 + * + * @author poussin + * + * @version $Revision: 1.1 $ + * + * Last update: $Date: 2006/01/04 13:24:35 $ by : $Author: bpoussin $ + */ + +package org.codelutin.topia.framework; + +import java.net.URL; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.codelutin.topia.TopiaNotFoundException; +import org.codelutin.util.RecursiveProperties; +import org.codelutin.util.Resource; + +/** + * @author poussin + * + */ +public class TopiaUtil { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(TopiaUtil.class); + + /** + * Permet de récupérer le fichier de propriété ayant le nom passé + * en argument. + * + * @param pathOrUrl le nom du fichier de propriété à charger, s'il est null + * ou vide retourne un objet Properties vide. + * @return Un nouvel objet de propriete + * @throws TopiaNotFoundException Si pathOrUrl n'est pas null ou vide et que + * le fichier devant contenir les propriétés n'est pas retrouvé. + */ + static public Properties getProperties(String pathOrUrl) throws TopiaNotFoundException { + return getProperties(null, pathOrUrl); + } + + /** + * Permet de récupérer le fichier de propriété ayant le nom passé + * en argument. + * + * @param parent l'objet properties utilisé comme parent de l'objet retourné + * @param pathOrUrl le nom du fichier de propriété à charger, s'il est null + * ou vide retourne un objet Properties vide. + * @return Un nouvel objet de propriete + * @throws TopiaNotFoundException Si pathOrUrl n'est pas null ou vide et que + * le fichier devant contenir les propriétés n'est pas retrouvé. + */ + static public Properties getProperties(Properties parent, String pathOrUrl) + throws TopiaNotFoundException { + Properties result = new RecursiveProperties(parent); + + // load properties for helper + if (pathOrUrl != null && !pathOrUrl.equals("")) { + try { + URL propURL = Resource.getURL(pathOrUrl); + log.info("Properties file used for " + pathOrUrl + " is: " + + propURL); + result.load(propURL.openStream()); + } catch (Exception eee) { + throw new TopiaNotFoundException( + "Properties file can't be found: " + pathOrUrl, eee); + } + } + return result; + } + +}