Package jp.co.ntt.oss

Source Code of jp.co.ntt.oss.RefreshCommand

package jp.co.ntt.oss;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Hashtable;

import javax.transaction.Status;
import javax.transaction.UserTransaction;

import jp.co.ntt.oss.data.DatabaseResource;
import jp.co.ntt.oss.data.Subscriber;
import jp.co.ntt.oss.data.Subscription;
import jp.co.ntt.oss.data.SyncDatabaseDAO;
import jp.co.ntt.oss.mapper.MappingData;
import jp.co.ntt.oss.utility.PropertyCtrl;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.log4j.Logger;

public class RefreshCommand implements SyncDatabaseCommand {
  private static Logger log = Logger.getLogger(RefreshCommand.class);
  private static PropertyCtrl mProperty = PropertyCtrl.getInstance();

  private boolean showHelp = false;
  private String schema = null;
  private String table = null;
  private String server = null;
  private RefreshMode mode = RefreshMode.AUTO;
  private boolean concurrent = false;

  public RefreshCommand(final CommandLine commandLine)
      throws SyncDatabaseException {
    if (commandLine.hasOption("help")) {
      showHelp = true;
      return;
    }

    /* checking necessary argument */
    if (!commandLine.hasOption("server")
        || !commandLine.hasOption("schema")
        || !commandLine.hasOption("table")) {
      showHelp();
      throw new SyncDatabaseException("error.command");
    }

    /* set argument */
    schema = commandLine.getOptionValue("schema");
    table = commandLine.getOptionValue("table");
    server = commandLine.getOptionValue("server");

    if (commandLine.hasOption("concurrent")) {
      concurrent = true;
    }

    if (commandLine.hasOption("mode")) {
      mode = getRefreshMode(commandLine.getOptionValue("mode"));
    }
  }

  public final void execute() throws Exception {
    // show refresh command help
    if (showHelp) {
      showHelp();
      return;
    }

    DatabaseResource replicaDB = null;
    DatabaseResource masterDB = null;
    Connection replicaConn = null;
    Connection masterConn = null;
    UserTransaction utx = null;
    RefreshMode executeMode;
    String result;
    boolean refreshed = false;

    try {
      Subscription subs = null;
      Subscriber suber = null;
      long maxMlogID = SyncDatabaseDAO.MLOG_RECORD_NOT_FOUND;

      // get replica server connection
      replicaDB = new DatabaseResource(server);
      replicaConn = replicaDB.getConnection();

      // begin transaction
      utx = replicaDB.getUserTransaction();
      utx.begin();

      // get subscription data
      subs = SyncDatabaseDAO.getSubscription(replicaConn, schema, table);
      if (subs.getSchema() == null || subs.getTable() == null) {
        throw new SyncDatabaseException("error.replica.dropped",
            SyncDatabaseDAO.getTablePrint(schema, table));
      }

      // get master server connection
      masterDB = new DatabaseResource(subs.getSrvname());
      masterConn = masterDB.getConnection();

      if (subs.getSubsID() != Subscription.NOT_HAVE_SUBSCRIBER) {
        Connection masterLockConn = null;

        // get subscriber data
        suber = SyncDatabaseDAO.getSubscriber(masterConn, subs
            .getSubsID());

        if (suber.getNspName() == null || suber.getRelName() == null) {
          throw new SyncDatabaseException("error.master.dropped",
              suber.getSubsID());
        }

        try {
          masterLockConn = SyncDatabaseDAO.lockTable(subs
              .getSrvname(), suber.getMasterTableName());

          // reset transaction
          utx.rollback();
          utx.setTransactionTimeout(DatabaseResource.DEFAULT_TIMEOUT);
          utx.begin();

          masterConn
              .setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

          // first query
          maxMlogID = SyncDatabaseDAO.getMaxMlogID(masterConn, suber
              .getMlogName());

          masterLockConn.rollback();
        } finally {
          // close connection
          if (masterLockConn != null) {
            masterLockConn.close();
          }
        }

        if (maxMlogID == SyncDatabaseDAO.MLOG_RECORD_NOT_FOUND) {
          if (suber.getLastMlogID() == Subscriber.NO_REFRESH) {
            maxMlogID = 0;
          } else {
            maxMlogID = suber.getLastMlogID();
          }
        }

        if (maxMlogID < suber.getLastMlogID()) {
          log.debug("max mlog ID:" + maxMlogID + " / last mlog ID:"
              + suber.getLastMlogID());
          throw new SyncDatabaseException("error.mlog_illegal");
        }
      }

      // get execute refresh mode
      executeMode = getExecuteMode(masterConn, suber, subs, mode);

      if (executeMode == RefreshMode.FULL) {
        // full-refresh
        result = fullRefresh(masterConn, replicaConn, suber, subs,
            concurrent);
      } else {
        // incremental-refresh
        result = incrementalRefresh(masterConn, replicaConn, suber,
            subs);
      }

      /* update subscription */
      SyncDatabaseDAO.setSubscription(replicaConn, subs);

      /* update subscriber */
      if (suber != null) {
        suber.setLastMlogID(maxMlogID);
        SyncDatabaseDAO.setSubscriber(masterConn, suber);
      }

      // commit transaction
      utx.commit();
      log.info(result);
      refreshed = true;

      // purge
      if (suber != null) {
        utx.setTransactionTimeout(DatabaseResource.DEFAULT_TIMEOUT);
        utx.begin();
        SyncDatabaseDAO.purgeMlog(masterConn, suber.getNspName(), suber
            .getRelName());

        // commit transaction
        utx.commit();
      }

    } catch (final Exception e) {
      // rollback transaction
      if (utx != null && utx.getStatus() != Status.STATUS_NO_TRANSACTION) {
        utx.rollback();
      }

      if (!refreshed) {
        throw e;
      }

      log.warn(e.getMessage());
    } finally {
      // release resources
      if (replicaConn != null) {
        replicaConn.close();
      }
      if (replicaDB != null) {
        replicaDB.stop();
      }
      if (masterConn != null) {
        masterConn.close();
      }
      if (masterDB != null) {
        masterDB.stop();
      }
    }
  }

  protected static String fullRefresh(final Connection masterConn,
      final Connection replicaConn, final Subscriber suber,
      final Subscription subs, final boolean concurrent)
      throws SQLException, SyncDatabaseException {
    if (masterConn == null || replicaConn == null || subs == null) {
      throw new SyncDatabaseException("error.argument");
    }

    FullReader reader = null;
    FullWriter writer = null;
    long count = 0;

    try {
      // truncate replica table
      SyncDatabaseDAO.truncate(replicaConn, subs.getReplicaTable(),
          concurrent);

      reader = new FullReader(masterConn, subs.getQuery());
      writer = new FullWriter(replicaConn, subs.getSchema(), subs
          .getTable(), reader.getColumnCount());

      // set DataMapper
      MappingData.createDataMappers(reader.getColumnMapping(), writer
          .getColumnMapping());
      writer.prepare(replicaConn, subs.getReplicaTable());

      // Reader thread
      // - Writer thread 間は Object の配列でデータを受け渡す
      Object[] columns;

      while ((columns = reader.getNextColumns()) != null) {
        // Write row data
        writer.setColumns(columns);
        count++;
      }
    } finally {
      if (reader != null) {
        reader.close();
      }
      if (writer != null) {
        writer.close();
      }
    }

    subs.setLastType("F");
    if (suber != null) {
      suber.setLastType("F");
      suber.setLastCount(count);
    }

    return mProperty.getMessage("info.full", count);
  }

  protected static String incrementalRefresh(final Connection masterConn,
      final Connection replicaConn, final Subscriber suber,
      final Subscription subs) throws SQLException, SyncDatabaseException {
    if (masterConn == null || replicaConn == null || suber == null
        || subs == null) {
      throw new SyncDatabaseException("error.argument");
    }

    IncrementalReader reader = null;
    IncrementalWriter writer = null;

    try {
      Hashtable<Short, String> pkNames = SyncDatabaseDAO.getPKNames(
          masterConn, suber.getNspName(), suber.getRelName());
      final String incrementalQuery = IncrementalReader
          .getIncrementalQuery(suber.getMlogName(), subs.getQuery(),
              pkNames, suber.getLastMlogID());
      reader = new IncrementalReader(masterConn, incrementalQuery,
          pkNames.size());

      pkNames = SyncDatabaseDAO.getPKNames(replicaConn, subs.getSchema(),
          subs.getTable());
      writer = new IncrementalWriter(replicaConn, subs.getSchema(), subs
          .getTable(), reader.getColumnCount(), pkNames);

      // set DataMapper
      MappingData.createDataMappers(reader.getColumnMapping(), writer
          .getColumnMapping());
      writer.prepare(replicaConn, subs.getReplicaTable());

      // Reader thread
      // - Writer thread 間は Object の配列でデータを受け渡す
      Object[] columns;

      while ((columns = reader.getNextColumns()) != null) {
        // Write row data
        writer.setColumns(columns);
      }
    } finally {
      if (reader != null) {
        reader.close();
      }
      if (writer != null) {
        writer.close();
      }
    }

    subs.setLastType("I");
    suber.setLastType("I");
    suber.setLastCount(suber.getLastCount() + writer.getInsertCount()
        - writer.getDeleteCount());

    log.debug("INSERT:" + writer.getInsertCount() + "/DELETE:"
        + writer.getDeleteCount() + "/UPDATE:"
        + writer.getUpdateCount() + "/PK COUNT:"
        + writer.getExecCount());

    return mProperty.getMessage("info.incremental",
        writer.getInsertCount(), writer.getUpdateCount(), writer
            .getDeleteCount());
  }

  private RefreshMode getExecuteMode(final Connection masterConn,
      final Subscriber suber, final Subscription subs,
      final RefreshMode optMode) throws SyncDatabaseException,
      SQLException {
    if (masterConn == null || subs == null) {
      throw new SyncDatabaseException("error.argument");
    }

    if (optMode == RefreshMode.FULL) {
      return RefreshMode.FULL;
    }

    if (subs.getSubsID() == Subscription.NOT_HAVE_SUBSCRIBER) {
      /* has no subscriber info */
      if (optMode == RefreshMode.INCREMENTAL) {
        throw new SyncDatabaseException("error.full");
      }

      return RefreshMode.FULL;
    }

    /* not have subscriber info */
    if (suber == null) {
      throw new SyncDatabaseException("error.no_subscriber", subs
          .getSubsID());
    }

    /* not yet first refresh or it is refresh after TRUNCATE. */
    if (suber.getLastMlogID() == Subscriber.NO_REFRESH) {
      if (optMode == RefreshMode.INCREMENTAL) {
        throw new SyncDatabaseException("error.full");
      }

      return RefreshMode.FULL;
    }

    if (optMode == RefreshMode.INCREMENTAL) {
      return RefreshMode.INCREMENTAL;
    }

    return SyncDatabaseDAO.chooseFastestMode(masterConn, suber);
  }

  /* parse refresh mode */
  protected static RefreshMode getRefreshMode(final String mode)
      throws SyncDatabaseException {
    if (mode == null) {
      throw new SyncDatabaseException("error.argument");
    }

    if (mode.equals("full")) {
      return RefreshMode.FULL;
    } else if (mode.equals("incr") || mode.equals("incremental")) {
      return RefreshMode.INCREMENTAL;
    } else if (mode.equals("auto")) {
      return RefreshMode.AUTO;
    } else {
      showHelp();
      throw new SyncDatabaseException("error.command");
    }
  }

  /* get refresh command options */
  @SuppressWarnings("static-access")
  public static Options getOptions() {
    final Options options = new Options();
    options.addOption(null, "concurrent", false,
        "without taking exclusive lock on the replica table");
    options.addOption(null, "help", false, "show help");
    options.addOption(OptionBuilder.withLongOpt("mode").withDescription(
        "refresh mode, default is auto").hasArgs(1).withArgName(
        "full|incr|auto").create());
    options.addOption(OptionBuilder.withLongOpt("schema").withDescription(
        "replica schema name").hasArgs(1).withArgName("schema name")
        .create());
    options.addOption(OptionBuilder.withLongOpt("server").withDescription(
        "replica server name").hasArgs(1).withArgName("server name")
        .create());
    options.addOption(OptionBuilder.withLongOpt("table").withDescription(
        "replica table name").hasArgs(1).withArgName("table name")
        .create());

    return options;
  }

  /* show refresh command help */
  public static void showHelp() {
    final Options options = getOptions();
    final HelpFormatter f = new HelpFormatter();
    f.printHelp("SyncDatabase refresh", options);
  }
}
TOP

Related Classes of jp.co.ntt.oss.RefreshCommand

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.