package com.ikokoon.serenity.persistence; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.apache.log4j.Logger; import org.neodatis.odb.ODB; import org.neodatis.odb.ODBFactory; import org.neodatis.odb.Objects; import org.neodatis.odb.core.query.IQuery; import org.neodatis.odb.core.query.criteria.Where; import org.neodatis.odb.impl.core.query.criteria.CriteriaQuery; import com.ikokoon.serenity.model.Composite; import com.ikokoon.toolkit.Toolkit; /** * This is the database class using Neodatis as the persistence tool. * * @author Michael Couck * @since 01.12.09 * @version 01.00 */ public class DataBaseOdb extends DataBase { private Logger logger = Logger.getLogger(this.getClass()); /** The Neodatis object database for persistence. */ private ODB odb = null; /** The database file for Neodatis. */ private String dataBaseFile; /** The closed flag. */ private boolean closed = true; /** * Constructor initialises a {@link DataBaseOdb} object. * * @param dataBaseFile * the file to open the database with * @param create * whether to create a new database essentially deleting the database file and creating a new one or to use the data in the existing * database file */ public DataBaseOdb(String dataBaseFile) { synchronized (DataBaseOdb.class) { this.dataBaseFile = dataBaseFile; logger.info("Opening ODB database on file : " + new File(dataBaseFile).getAbsolutePath()); try { odb = ODBFactory.open(this.dataBaseFile); closed = false; } catch (Exception e) { logger.error("Exception initialising the database : " + dataBaseFile + ", " + this, e); } } } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public synchronized <E extends Composite<?, ?>> E find(Class<E> klass, Long id) { IQuery query = new CriteriaQuery(klass, Where.equal("id", id)); return (E) find(query); } /** * {@inheritDoc} */ public synchronized <E extends Composite<?, ?>> E find(Class<E> klass, List<?> parameters) { Long id = Toolkit.hash(parameters.toArray()); return find(klass, id); } /** * {@inheritDoc} */ public synchronized <E extends Composite<?, ?>> List<E> find(Class<E> klass) { return find(klass, 0, Integer.MAX_VALUE); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public <E extends Composite<?, ?>> List<E> find(Class<E> klass, int start, int end) { List<E> list = new ArrayList<E>(); try { Objects objects = odb.getObjects(klass, false, start, end); while (objects.hasNext()) { Object object = objects.next(); list.add((E) object); } } catch (Exception e) { logger.error("Exception selecting objects with class : " + klass + ", " + this, e); } return list; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public <E extends Composite<?, ?>> List<E> find(final Class<E> klass, final Map<String, ?> parameters) { Set<E> set = new TreeSet<E>(); for (String field : parameters.keySet()) { Object value = parameters.get(field); logger.debug("Field : " + field + ", " + value); IQuery query = new CriteriaQuery(klass, Where.like(field, "%" + value.toString() + "%")); try { Objects objects = odb.getObjects(query); logger.debug("Objects : " + objects); if (set.size() == 0) { set.addAll(objects); } set.retainAll(objects); logger.debug("Set : " + set); } catch (Exception e) { logger.error("Exception selecting objects with class : " + klass + ", parameters : " + parameters + ", " + this, e); } } List<E> list = new ArrayList<E>(); list.addAll(set); logger.debug("List : " + list); return list; } private synchronized void commit() { try { if (!isClosed()) { odb.commit(); } } catch (IOException e) { logger.error("Exception comitting the ODB database : " + this, e); } catch (Exception e) { logger.error("Exception comitting the ODB database : " + this, e); } } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") public synchronized <E extends Composite<?, ?>> E persist(E composite) { try { setIds(composite); E duplicate = (E) find(composite.getClass(), composite.getId()); if (duplicate != null) { if (duplicate != composite) { logger.warn("Attempted to persist a duplicate composite : " + composite + ", " + this); return composite; } } logger.debug("Persisting composite : " + composite); odb.store(composite); } catch (Exception e) { logger.error("Exception persisting object : " + composite + ", " + this, e); } commit(); return composite; } /** * {@inheritDoc} */ public synchronized <E extends Composite<?, ?>> E remove(Class<E> klass, Long id) { E composite = find(klass, id); try { if (composite != null) { odb.delete(composite); } } catch (Exception e) { logger.error("Exception deleting object : " + id + ", " + this, e); } commit(); return composite; } /** * {@inheritDoc} */ public synchronized boolean isClosed() { return closed; } /** * {@inheritDoc} */ public synchronized void close() { try { if (isClosed()) { logger.warn("Attempted to close the database again : " + this); return; } if (logger.isInfoEnabled()) { logger.info("Closing database : " + dataBaseFile + ", " + this); ByteArrayOutputStream bos = new ByteArrayOutputStream(); new Exception().printStackTrace(new PrintStream(bos)); logger.info("Stack dump : " + bos.toString()); } commit(); odb.close(); IDataBaseEvent dataBaseEvent = new DataBaseEvent(this, IDataBaseEvent.Type.DATABASE_CLOSE); IDataBase.DataBaseManager.fireDataBaseEvent(dataBaseFile, dataBaseEvent); } catch (Exception e) { logger.error("Exception closing the ODB database : " + this, e); } closed = true; } @SuppressWarnings("unchecked") private synchronized <E extends Composite<?, ?>> E find(IQuery query) { E e = null; try { Objects objects = odb.getObjects(query); if (objects.size() == 1) { e = (E) objects.getFirst(); } else if (objects.size() > 1) { logger.warn("Id for object must be unique : " + query); } } catch (Exception ex) { logger.error("Exception selecting object on ODB database : " + query + ", " + this.dataBaseFile + ", " + this, ex); } return e; } /** * This method sets the ids in a graph of objects. * * @param object * the object to set the ids for */ @SuppressWarnings("unchecked") synchronized final void setIds(Composite<?, ?> composite) { if (composite == null) { return; } super.setId(composite); logger.debug("Persisted object : " + composite); List<Composite<?, ?>> children = (List<Composite<?, ?>>) composite.getChildren(); if (children != null) { for (Composite<?, ?> child : children) { setIds(child); } } } }