package com.ikokoon.serenity.persistence; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Logger; import com.ikokoon.serenity.model.Composite; import com.ikokoon.toolkit.ObjectFactory; /** * This is the persistence interface for the Serenity code coverage and dependency functionality. * * @author Michael Couck * @since 12.08.09 * @version 01.00 * * -------------------------------------------------------------<br> * @version 01.1 <br> * Removed the JPA oriented methods in favour of in memory as JPA was hopelessly too slow. * -------------------------------------------------------------<br> * @version 01.2 <br> * Added the listeners to the databases so the manager can release them from the map. Changed the IDataBase interface to be generic so * Neodatis can be integrated easier and modified several methods to include the class that is being selected as most persistence libraries * will need this for their selection. * @version 01.3 <br> * Added a find method that can be implemented as a fuzzy search method by the implementation with multiple search criteria. For example * 'where x like n and y in m'. */ public interface IDataBase { /** * This class manages the databases if there are more than one. It also listens to events that the databases might throw like a close and so on. * * @author Michael Couck * @since 12.08.09 * @version 01.00 */ public static class DataBaseManager { private static Logger logger = Logger.getLogger(DataBaseManager.class); /** The map of open databases keyed on the database file name. */ private static Map<String, IDataBase> dataBases = new HashMap<String, IDataBase>(); /** The map of database listeners keyed on the database file name. */ private static Map<String, List<IDataBaseListener>> dataBaseListeners = new HashMap<String, List<IDataBaseListener>>(); /** * Access to all the databases in the current VM. * * @return all the databases */ public static synchronized Map<String, IDataBase> getDataBases() { return dataBases; } /** * Accesses a database. In the case the database is not open one will be instantiated on the database file specified. In the case the database * is open but not the right type of database, the old one will be closed and the new one will be opened on the database file, otherwise the * database is returned. * * @param <E> * the database type * @param klass * the class of database to initialise * @param dataBaseFile * the database file to open the database on * @param create * whether to create a new database, i.e. deleting the old database file and creating a new one * @return the database */ public static synchronized final <E extends IDataBase> IDataBase getDataBase(Class<E> klass, String dataBaseFile, IDataBase internalDataBase) { IDataBase dataBase = dataBases.get(dataBaseFile); if (dataBase == null || dataBase.isClosed()) { getDataBaseListener(dataBaseFile); dataBase = ObjectFactory.getObject(klass, dataBaseFile, internalDataBase); logger.info("Adding database : " + dataBase); dataBases.put(dataBaseFile, dataBase); } logger.info("Returned database : " + klass + ", data base : " + dataBase + ", file : " + dataBaseFile); return dataBase; } public static synchronized void addDataBaseListener(String dataBaseFile, IDataBaseListener dataBaseListener) { List<IDataBaseListener> dataBaseListeners = DataBaseManager.dataBaseListeners.get(dataBaseFile); if (dataBaseListeners == null) { dataBaseListeners = new ArrayList<IDataBaseListener>(); DataBaseManager.dataBaseListeners.put(dataBaseFile, dataBaseListeners); } dataBaseListeners.add(dataBaseListener); } public static synchronized void removeDataBaseListener(String dataBaseFile, IDataBaseListener dataBaseListener) { List<IDataBaseListener> dataBaseListeners = DataBaseManager.dataBaseListeners.get(dataBaseFile); if (dataBaseListeners != null) { dataBaseListeners.remove(dataBaseListener); } } public static synchronized void fireDataBaseEvent(String dataBaseFile, IDataBaseEvent dataBaseEvent) { List<IDataBaseListener> dataBaseListeners = DataBaseManager.dataBaseListeners.get(dataBaseFile); if (dataBaseListeners != null) { IDataBaseListener[] array = dataBaseListeners.toArray(new IDataBaseListener[dataBaseListeners.size()]); for (IDataBaseListener dataBaseListener : array) { dataBaseListener.fireDataBaseEvent(dataBaseEvent); } } } private synchronized static IDataBaseListener getDataBaseListener(final String dataBaseFile) { IDataBaseListener dataBaseListener = new IDataBaseListener() { { DataBaseManager.addDataBaseListener(dataBaseFile, this); } public void fireDataBaseEvent(IDataBaseEvent dataBaseEvent) { if (dataBaseEvent.getEventType().equals(IDataBaseEvent.Type.DATABASE_CLOSE)) { IDataBase dataBase = dataBaseEvent.getDataBase(); if (!dataBases.values().remove(dataBase)) { logger.info("Database not removed : " + dataBase); } else { logger.info("Removed database : " + dataBase); } DataBaseManager.removeDataBaseListener(dataBaseFile, this); } } }; return dataBaseListener; } } /** * Persists an object to a persistent store. * * @param composite * the composite to persist * @return the composite that is persisted, typically the id will be set by the underlying implementation */ public <E extends Composite<?, ?>> E persist(E composite); /** * Selects a composite based on the class type and the id of the class. * * @param <E> * the return type of the class * @param klass * the type of class to select * @param id * the unique id of the class * @return the composite with the specified id */ public <E extends Composite<?, ?>> E find(Class<E> klass, Long id); /** * Selects a class based on the combination of field values in the parameter list. * * @param <E> * the return type of the class * @param klass * the type of class to select * @param parameters * the unique combination of field values to select the class with * @return the composite with the specified unique field combination */ public <E extends Composite<?, ?>> E find(Class<E> klass, List<?> parameters); /** * Selects a list of objects based on the values in the objects and the class of the object. The implementations can implement multiple search * criteria including fuzzy selection etc. * * @param <E> * the return type of the class * @param klass * the type of class to select * @param parameters * the parameters to do the selection with. These are the fields in the objects and the values * @return the list of composites that match the selection criteria */ public <E extends Composite<?, ?>> List<E> find(Class<E> klass, Map<String, ?> parameters); /** * Selects all the classes of a particular type. Note this could potentially return the whole database. * * @param <E> * the return type of the class * @param klass * the type of class to select * @return a list of all the objects in the database that have the specified class type */ public <E extends Composite<?, ?>> List<E> find(Class<E> klass); /** * Selects all the classes of a particular type starting from an index and going to an index. * * @param <E> * the return type of the class * @param klass * the type of class to select * @param start * the beginning index to retrieve objects from * @param end * the end index for the list of objects * @return a list of all the objects in the database that have the specified class type */ public <E extends Composite<?, ?>> List<E> find(Class<E> klass, int start, int end); /** * Removes an object from the database and returns the removed object as a convenience. * * @param <E> * the return type of the class * @param klass * the type of class to select * @param id * the unique id of the class * @return the composite with the specified id */ public <E extends Composite<?, ?>> E remove(Class<E> klass, Long id); /** * Checks the open status of the database. * * @return whether the database is open or closed */ public boolean isClosed(); /** * Closes the database. */ public void close(); }