Package jfix.db4o

Source Code of jfix.db4o.ObjectDatabase

/*
    Copyright (C) 2010 maik.jablonski@gmail.com

    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 3 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, see <http://www.gnu.org/licenses/>.
*/
package jfix.db4o;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import jfix.db4o.engine.PersistenceEngine;
import jfix.functor.Command;
import jfix.functor.Functors;
import jfix.functor.Predicate;
import jfix.functor.Procedure;
import jfix.functor.Supplier;
import jfix.util.Reflections;

public class ObjectDatabase {

  public static int MAINTENANCE_INTERVAL = 1 * 3600 * 1000;

  private ReadWriteLock lock;
  private ObjectRepository objectRepository;
  private PersistenceEngine persistenceEngine;
  private Timer maintenanceTimer;
  private boolean maintenanceScheduled;
  private boolean transactionInProgress;
  private Map<Supplier, Object> supplierCache;
  private List<Blob> blobsToSave;
  private List<Blob> blobsToDelete;

  public ObjectDatabase(PersistenceEngine persistenceEngine) {
    this.lock = new ReentrantReadWriteLock();
    this.objectRepository = new ObjectRepository();
    this.supplierCache = new HashMap();
    this.persistenceEngine = persistenceEngine;
  }

  public void open() {
    lock.writeLock().lock();
    try {
      maintenanceScheduled = false;
      transactionInProgress = false;
      populateObjectRepository();
      startMaintenanceTimer(MAINTENANCE_INTERVAL);
    } finally {
      lock.writeLock().unlock();
    }
  }

  private void populateObjectRepository() {
    String blobDirectory = getBlobDirectory();
    for (Object obj : persistenceEngine.query()) {
      if (obj instanceof Persistent) {
        objectRepository.put((Persistent) obj);
        if (obj instanceof Blob) {
          ((Blob) obj).initPath(blobDirectory);
        }
      }
    }
  }

  private void startMaintenanceTimer(int period) {
    maintenanceTimer = new Timer(true);
    maintenanceTimer.schedule(new TimerTask() {
      public void run() {
        if (maintenanceScheduled) {
          gc();
          // need to lock for consistent backup
          lock.readLock().lock();
          try {
            persistenceEngine.backup();
          } finally {
            lock.readLock().unlock();
          }
          maintenanceScheduled = false;
        }
      }
      // Minimize IO by distributing backups for different databases
    }, (int) (Math.random() * period), period);
  }

  private void stopMaintenanceTimer() {
    if (maintenanceTimer != null) {
      maintenanceTimer.cancel();
      maintenanceTimer = null;
    }
  }

  public String getBlobDirectory() {
    return persistenceEngine.getBlobDirectory();
  }

  public void close() {
    lock.writeLock().lock();
    try {
      stopMaintenanceTimer();
      if (persistenceEngine != null) {
        persistenceEngine.close();
      }
      persistenceEngine = null;
      objectRepository = null;
      supplierCache = null;
    } finally {
      lock.writeLock().unlock();
    }
  }

  public void gc() {
    // No lock so other threads can proceed with their work.
    // If we miss an orphaned value this time, we will collect it on the
    // next run.
    for (Object orphanedValue : objectRepository
        .getGarbage(Persistent.Value.class)) {
      if (orphanedValue instanceof Persistent.Value) {
        deleteDeliberately((Persistent) orphanedValue);
      }
    }
  }

  public <E> List<E> query(Class<E> clazz) {
    lock.readLock().lock();
    try {
      return (List<E>) objectRepository.get(clazz);
    } finally {
      lock.readLock().unlock();
    }
  }

  public <E> List<E> query(Class<E> clazz, Predicate<E> predicate) {
    lock.readLock().lock();
    try {
      return Functors.filter((List<E>) objectRepository.get(clazz),
          predicate);
    } finally {
      lock.readLock().unlock();
    }
  }

  public <E> E queryUnique(Class<E> clazz, Predicate<E> predicate) {
    lock.readLock().lock();
    try {
      List<E> result = query(clazz, predicate);
      if (result == null || result.size() != 1) {
        return null;
      }
      return result.get(0);
    } finally {
      lock.readLock().unlock();
    }
  }

  public <E> boolean isUnique(E entity, Predicate<E> predicate) {
    lock.readLock().lock();
    try {
      List<E> result = query((Class<E>) entity.getClass(), predicate);
      if (result == null) {
        return true;
      }
      if (result.size() == 1 && result.get(0) != entity) {
        return false;
      }
      if (result.size() > 1) {
        return false;
      }
      return true;
    } finally {
      lock.readLock().unlock();
    }
  }

  public boolean isStored(Persistent object) {
    lock.readLock().lock();
    try {
      return objectRepository.get(object.getClass()).contains(object);
    } finally {
      lock.readLock().unlock();
    }
  }

  public List<Persistent> queryReferrers(Persistent reference) {
    lock.readLock().lock();
    try {
      return new ArrayList<Persistent>(objectRepository
          .getReferrers(reference));
    } finally {
      lock.readLock().unlock();
    }
  }

  public <E> E query(Supplier<E> supplier) {
    lock.readLock().lock();
    try {
      E result = (E) supplierCache.get(supplier);
      if (result == null) {
        result = supplier.get();
        supplierCache.put(supplier, result);
      }
      return result;
    } finally {
      lock.readLock().unlock();
    }
  }

  public void read(Command transaction) {
    lock.readLock().lock();
    try {
      transaction.run();
    } finally {
      lock.readLock().unlock();
    }
  }

  public void write(Command transaction) {
    lock.writeLock().lock();
    try {
      transactionInProgress = true;
      transaction.run();
      if (blobsToSave != null) {
        String blobDirectory = getBlobDirectory();
        for (Blob blob : blobsToSave) {
          blob.initPath(blobDirectory);
        }
      }
      if (blobsToDelete != null) {
        for (Blob blob : blobsToDelete) {
          blob.getFile().delete();
        }
      }
      persistenceEngine.commit();
    } catch (Throwable e) {
      persistenceEngine.rollback();
      close();
      e.printStackTrace();
      throw new RuntimeException(e);
    } finally {
      blobsToSave = null;
      blobsToDelete = null;
      maintenanceScheduled = true;
      transactionInProgress = false;
      if (supplierCache != null) {
        supplierCache.clear();
      }
      lock.writeLock().unlock();
    }
  }

  public void save(final Persistent persistent) {
    lock.writeLock().lock();
    try {
      if (transactionInProgress) {
        traverseAndSave(persistent);
      } else {
        write(new Command() {
          public void run() {
            save(persistent);
          }
        });
      }
    } finally {
      lock.writeLock().unlock();
    }
  }

  public void delete(final Persistent persistent) {
    lock.writeLock().lock();
    try {
      int refcount = queryReferrers(persistent).size();
      if (refcount != 0) {
        // Garbage collection to remove possible dangling references by
        // values.
        gc();
        refcount = queryReferrers(persistent).size();
        if (refcount != 0) {
          throw new RuntimeException("Deletion not possible: \""
              + String.valueOf(persistent)
              + "\" is still referenced by " + refcount
              + " referrers.");
        }
      }
      if (transactionInProgress) {
        traverseAndDelete(persistent);
      } else {
        write(new Command() {
          public void run() {
            delete(persistent);
          }
        });
      }
    } finally {
      lock.writeLock().unlock();
    }
  }

  public void deleteDeliberately(final Persistent persistent) {
    lock.writeLock().lock();
    try {
      if (transactionInProgress) {
        traverseAndDelete(persistent);
      } else {
        write(new Command() {
          public void run() {
            deleteDeliberately(persistent);
          }
        });
      }
    } finally {
      lock.writeLock().unlock();
    }
  }

  private void traverseAndSave(Object candidate) {
    if (candidate != null) {
      traverseAndExecute(candidate, new Procedure() {
        public void execute(Object object) {
          traverseAndSave(object);
        }
      });
      if (candidate instanceof Persistent) {
        objectRepository.put(candidate);
        persistenceEngine.save(candidate);
        if (candidate instanceof Blob) {
          if (blobsToSave == null) {
            blobsToSave = new ArrayList();
          }
          blobsToSave.add((Blob) candidate);
        }
        return;
      }
    }
  }

  private void traverseAndDelete(Object candidate) {
    if (candidate != null) {
      // Don't cascade delete on values.
      if (!(candidate instanceof Persistent.Value)) {
        traverseAndExecute(candidate, new Procedure() {
          public void execute(Object object) {
            traverseAndDelete(object);
          }
        });
      }
      if (candidate instanceof Persistent) {
        objectRepository.remove(candidate);
        persistenceEngine.delete(candidate);
        if (candidate instanceof Blob) {
          if (blobsToDelete == null) {
            blobsToDelete = new ArrayList();
          }
          blobsToDelete.add((Blob) candidate);
        }
      }
    }
  }

  private void traverseAndExecute(Object candidate, Procedure procedure) {
    try {
      for (Field field : Reflections.getFields(candidate.getClass())) {
        executeOnValues(field.get(candidate), procedure);
      }
    } catch (Exception e) {
      e.printStackTrace();
      throw new RuntimeException(e);
    }
  }

  private void executeOnValues(Object candidate, Procedure procedure) {
    if (candidate == null) {
      return;
    }
    if (candidate instanceof Persistent.Value || candidate instanceof Blob) {
      procedure.execute(candidate);
      return;
    }
    if (candidate instanceof Persistent.Value[]
        || candidate instanceof Blob[]) {
      for (Object arrayItem : (Object[]) candidate) {
        executeOnValues(arrayItem, procedure);
      }
      return;
    }
  }
}
TOP

Related Classes of jfix.db4o.ObjectDatabase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.