Package org.jugile.daims

Source Code of org.jugile.daims.DomainCore

package org.jugile.daims;

/*

Copyright (C) 2011-2011 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library 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
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.log4j.Logger;
import org.jugile.util.Buffer;
import org.jugile.util.CsvTokenizer;
import org.jugile.util.DBConnection;
import org.jugile.util.DBPool;
import org.jugile.util.DBQueue;
import org.jugile.util.DOH;
import org.jugile.util.HiLo;
import org.jugile.util.Jugile;
import org.jugile.util.Props;
import org.jugile.util.Timer;


/**
* <i>"this is verse"</i> <b>()</b>
*
* <br/>==========<br/>
*
* here is doc
* @author jukka.rahkonen@iki.fi
*
*/
public abstract class DomainCore extends Jugile {
  static Logger log = Logger.getLogger(DomainCore.class);

  // ========= core data and instance =======
  protected static DomainCore domain;
 
  private static volatile DomainData coreData;
  protected static DomainData cd() {
    if (domain == null) fail("tried to call cd() on empty domain");
    if (coreData == null) coreData = new DomainData(domain);
    return coreData;
  }
  public static void reset() {
    if (domain == null) return;
    coreData = null; uow.remove();
  }

  // ========= locks ========================
  private final static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  private final static Lock readLock = readWriteLock.readLock();
  private final static Lock writeLock = readWriteLock.writeLock();

  // ========= thread local unit of work ==========
  private static ThreadLocal<UnitOfWork> uow =
    new ThreadLocal<UnitOfWork>() {
    @Override protected UnitOfWork initialValue() {
      log.debug("create UnitOfWork");
      UnitOfWork uow = new UnitOfWork(cd(),readLock,writeLock);
      return uow;
    }
    public void remove() {
      get().release();
      super.remove();
    }
  };

  // Bo needs this
  protected static UnitOfWork getUnitOfWork() {
    return uow();
  }

  private static UnitOfWork uow() {
    // return uow with ReadTx activated
    UnitOfWork u = uow.get();
    u.assertAtLeastReadTx();
    return u;
  }
 
  private static void uowRemove() {
    uow.remove();
  }
 
 
  // TODO: shoud these be moved to another threadlocal?
  public Object get(String key) { return uow().get(key); }
  public void put(String key, Object value) { uow().put(key,value); }

 
  // ========= object interface ==========
  protected Bo get(Class cl, long id) { return uow().get(cl, id); }
  protected Bo createNew(Class cl) { return uow().createNewBo(cl); }
  protected BoMapDelta getAll(Class cl) {
    BoCollection bc = (BoCollection)uow().getAll(cl);
    bc.reset();
    return bc;
  }


  // ========= transactions ==============

  public void commit() {
    if (uow().getCommitSize() == 0) {
      log.debug("commit size == 0");
      uowRemove();
      return;
    }
    if (!Props.isAdminNode()) {
      log.debug("not adminNode - commit aborted");
      uowRemove();
      return;
    }
    String delta = uow().getDelta(classes());
    log.debug("delta:\n"+delta);
   
    // TODO: optimize later: no read nor write lock when external I/O,
    //       requires db writer from dd().delta() String
   
    // 1. write to db - use optimistic locking
    // 2. write to message queue
    // 1. & 2. in one transaction
    if (hasdb()) modifyDbAndSendMsg(delta);
    uow.get().endReadTx(); // TODO: optimize later: modifyDb outside of locks
   
    // 3. merge to domain
    uow.get().startWriteTx();
    try {
      modifyDomain(delta);
    } catch (Exception e) {
      log.fatal("could not modify domain",e);
    } finally {
      uowRemove(); // frees locks
    }
  }

  /**
   * Commit only to memory.
   */
  public void commitMem() {
    if (uow().getCommitSize() == 0) {
      log.debug("commit size == 0");
      uowRemove();
      return;
    }
    String delta = uow().getDelta(classes());
    log.debug("delta:\n"+delta);
    uow.get().endReadTx();
    uow.get().startWriteTx();
    try {
      modifyDomain(delta);
    } catch (Exception e) {
      log.fatal("could not modify domain",e);
    } finally {
      uowRemove(); // frees locks
    }
  }
 
  public int getCommitSize() { return uow().getCommitSize(); }

  public void rollback() {
    uowRemove(); // frees locks
  }
 
  public void startTx() {}
  public void close() { }
  public void setReadTx() {
    uow().setReadOnly(true);
  }
  public void setWriteTx() {
    uow().setReadOnly(false);
  }

  // ========= domain core data modifiers ============
 
  protected int modifyDomain(String delta) {
    CsvTokenizer t = CsvTokenizer.parseString(delta,Bo.CSVDELIMITER)
    try {
      return cd().modifyDomain(t);
    } catch (Exception e) {
      log.fatal("could not modify domain", e);
      fail(e); return 0;
    } finally {
      t.close();
    }
  }
 
 
  // ========= csv write and read ============

  public void saveToCsvZip(String fname) { saveToCsv(zipWriter(fname)); }
  public void saveToCsv(String fname) { saveToCsv(writer(fname)); }
  public void saveToCsv(Writer out) {
    try {
      uow().delta(out,classes());
    } catch (Exception e) {
      log.fatal("could not write domain to csv file", e);
      fail(e);
    } finally {
      try {out.close();}catch(Exception e){}
      uowRemove();
    }
  }
 
  public int loadFromCsvZip(String fname) { return loadFromCsv(zipInputStream(fname));}
  public int loadFromCsv(String fname) { return loadFromCsv(inputStream(fname));}
  public int loadFromCsv(InputStream is) {
    CsvTokenizer t = new CsvTokenizer(is);
    t.setDelimiter(Bo.CSVDELIMITER)
    uow.get().startWriteTx();
    try {
      int count = cd().modifyDomain(t);
      HiLo.setNextid(count + 100L);
      log.info("loaded " + count + " items. updated HiLo, nextid now: " + HiLo.nextid());
      log.info("stats: " + stats());
      return count;
    } catch (Exception e) {
      log.fatal("could not modify domain", e);
      fail(e); return 0;
    } finally {
      uowRemove();
    }
  }

 
  protected int saveAllToDB() {
    uow(); // starts readTx
    try {
      DBPool db = DBPool.getPool();
      DBConnection c = db.getConnection();
      try {
        int res = cd().saveToDB(c); // whole core domain cd()
        c.commit();
        return res;
      } catch (Exception e) {
        try { c.rollback(); fail(e); } catch (Exception e2) { fail(e2); }
      } finally {
        try { c.free(); } catch (Exception e2) { fail(e2); }
      }
    } catch (Exception e) {
      log.fatal("could not write domain to db", e);
      fail(e);
    } finally {
      uowRemove();
    }
    return 0;
  }

 
  protected int loadFromDB() {
    // flush queue
    //DBQueue.flush(node()); // NOTE:
    // flushing queue here looses some messages if multiple nodes are
    // started simultaneously
    // but: old messages must be removed and especially messages that
    // are not read but which are older than when all items were loaded from db
    uow.get().startWriteTx();
    try {
      log.debug("loadFromDB");
      int res = doLoadFromDB();
      // read messages from queue
      readDeltasFromQueue(1000);
      log.debug("loadFromDB done");
      log.info("stats: " + stats());
      return res;
    } catch (Exception e) {
      log.fatal("could not load domain from db", e);
      fail(e); return 0;
    } finally {
      uowRemove();
    }
  } 
 
  protected abstract Class<Bo>[] classes();
 
  private int doLoadFromDB() throws Exception {
    DBPool db = DBPool.getPool();
    DBConnection c = db.getConnection();
    int count = 0;
    try {
      //reset();
      count = cd().loadFromDB(c,classes());
    } catch (Exception e) {
      log.error("dbread failed",e);
      try { c.rollback(); } catch (Exception e2) { fail(e2);}
    } finally {
      try { c.free(); } catch (Exception e) {}
    }
    return count;   
  }

  public int update() {
    uow.get().startWriteTx();
    try {
      return cd().readDeltasFromQueue(node(),1000);
    } catch (Exception e) {
      log.fatal("could not load deltas from queue", e);
      fail(e); return 0;
    } finally {
      uowRemove(); // clean up old copy of uow domain data, frees locks
    }
  }

  protected int readDeltasFromQueue(int max) throws Exception {
    return cd().readDeltasFromQueue(node(),max);
  }

  private int modifyDbAndSendMsg(String delta) {
    DBPool db = DBPool.getPool();
    DBConnection c = db.getConnection();
    try {
      int res = uow().writeToDB(c, classes());
      DBQueue.writeMessage(delta, node(), nodes(), c);
      c.commit();
      return res;
    } catch (Exception e) {
      try { c.rollback(); } catch (Exception e2) { fail(e2); }
      log.error("could not write db changes",e);
      fail(e);
    } finally {
      try { c.free(); } catch (Exception e2) { fail(e2); }
    }
    return 0;
  }


  private static String node = null;
  protected final String node() {
    if (node == null) {
      node = Props.get("jugile.node");
      if (empty(node)) {
        node = getHostname();
      }
    }
    return node;
  }
  public static void setNode(String v) { node = v; }

  private String nodes[] = null;
  protected final String[] nodes() {
    if (nodes == null) {
      String nodesstr = Props.get("jugile.nodes");
      if (empty(nodesstr)) return null;
      nodes = nodesstr.split(",");
    }
    return nodes;
  }
 
 
  public void dump(String header) {
    Buffer buf = new Buffer("    ");
    buf.ln("");
    buf.ln("");
    buf.ln(mult("=",77));
    buf.ln("   DAIMS OO memory dump: " + now().getUiTs());
    buf.ln("     - "+ header);
    buf.ln(mult("=",77));
    buf.ln("");
    try {
      cd().dump(buf);
      uow().dump(buf);
    } catch (Exception e) { fail(e); }   
    print(buf.toString());
  }

  public String stats() {
    Stats st = new Stats(classes());
    Timer t = new Timer();
    cd().stats(st);
    return st.toString() + "\n stats took ms: " + t.stop();
  }
 
 
  // ========= utils =============
 
  private static String hostname = null;
  protected static String getHostname() {
    if (hostname != null) return hostname;
    hostname = resolveHostname();
    return hostname;
  }
  private static synchronized String resolveHostname() { return Jugile.getHostName()}

  private static String hostnum;
  public static String getHostNum() {
    if (hostnum != null) return hostnum;
    hostnum = "S"+nn(getHostname()).replaceAll("[^0-9]+", "");
    return hostnum;
  }
 
  protected final static boolean hasdb() { return HiLo.hasdb(); }
  public boolean _hasDb() { return this.hasdb(); } // for old interface

  public Bo get(DOH doh) {
    if (doh == null) return null;
    String clname = doh.getDomainClassName();
    String sid = doh.getId();
    long id = parseLongSafe(sid);
    Class<Bo> cl = getClassByName(clname);
    return uow().getAll(cl).get(id);
  }

  private static volatile Boolean isReadOnly = null;
  public static boolean isReadOnly() {
    if (isReadOnly != null) return isReadOnly;
    isReadOnly = !parseBoolSafe(Props.get("jugile.isAdminNode"));
    return isReadOnly;
  }

}

TOP

Related Classes of org.jugile.daims.DomainCore

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.