/** Filter the event if it has already been executed. */
  public ReplDBMSEvent filter(ReplDBMSEvent event)
      throws ReplicatorException, InterruptedException {
    totalEvents++;

    if (appliedTimes == null) appliedTimes = new TreeMap<Long, Timestamp>();

    Timestamp sourceTstamp = event.getDBMSEvent().getSourceTstamp();
    appliedTimes.put(event.getSeqno(), sourceTstamp);

    long currentTime = System.currentTimeMillis();
    prefetchLatency = currentTime - sourceTstamp.getTime();

    if (interval == 0 || lastChecked == 0 || (currentTime - lastChecked >= interval)) {
      // It is now time to check CommitSeqnoTable again
      checkSlavePosition(currentTime);
    }

    if (initTime == 0) initTime = sourceTstamp.getTime();

    if (event.getSeqno() <= currentSeqno) {
      if (logger.isDebugEnabled())
        logger.debug("Discarding event " + event.getSeqno() + " as it is already applied");
      return null;
    } else
      while (sourceTstamp.getTime() - initTime > (aheadMaxTime * 1000)) {
        if (logger.isDebugEnabled())
          logger.debug("Event is too far ahead of current slave position... sleeping");
        // this event is too far ahead of the CommitSeqnoTable position:
        // sleep some time and continue
        long sleepStartMillis = System.currentTimeMillis();
        try {

          prefetchState = PrefetchState.sleeping;
          Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
          return null;
        } finally {
          prefetchState = PrefetchState.active;
          sleepTimeMillis += (System.currentTimeMillis() - sleepStartMillis);
        }
        // Check again CommitSeqnoTable
        checkSlavePosition(System.currentTimeMillis());
        // and whereas the event got applied while sleeping
        if (event.getSeqno() <= currentSeqno) {
          if (logger.isDebugEnabled())
            logger.debug("Discarding event " + event.getSeqno() + " as it is already applied");
          return null;
        }
      }

    prefetchEvents++;
    if (logger.isDebugEnabled() && totalEvents % 20000 == 0)
      logger.debug(
          "Prefetched "
              + prefetchEvents
              + " events - Ratio "
              + (100 * prefetchEvents / totalEvents)
              + "%");
    return event;
  }
 // Confirms that a particular event is ignored.
 private void verifyIgnore(FilterVerificationHelper filterHelper, ReplDBMSEvent e)
     throws ReplicatorException, InterruptedException {
   ReplDBMSEvent e2 = filterHelper.filter(e);
   assertNull("Should ignore event: seqno=" + e.getSeqno(), e2);
 }
  /**
   * {@inheritDoc}
   *
   * @see
   *     com.continuent.tungsten.replicator.filter.Filter#filter(com.continuent.tungsten.replicator.event.ReplDBMSEvent)
   */
  public ReplDBMSEvent filter(ReplDBMSEvent event) throws ReplicatorException {
    ArrayList<DBMSData> data = event.getData();
    for (Iterator<DBMSData> iterator = data.iterator(); iterator.hasNext(); ) {
      DBMSData dataElem = iterator.next();
      if (dataElem instanceof RowChangeData) {
        RowChangeData rdata = (RowChangeData) dataElem;
        for (Iterator<OneRowChange> iterator2 = rdata.getRowChanges().iterator();
            iterator2.hasNext(); ) {
          OneRowChange orc = iterator2.next();

          // Don't analyze tables from Tungsten schema.
          if (orc.getSchemaName().compareToIgnoreCase(tungstenSchema) == 0) {
            if (logger.isDebugEnabled()) logger.debug("Ignoring " + tungstenSchema + " schema");
            continue;
          }

          // Loop through defined transformations (usually one).
          Iterator<String> it = definitions.keySet().iterator();
          while (it.hasNext()) {
            String transformation = it.next();

            JSONArray array = definitions.get(transformation);

            // Not using any hash, because simple loop should be
            // fast enough for a few expected entries.
            for (Object o : array) {
              JSONObject jo = (JSONObject) o;
              String defSchema = (String) jo.get("schema");
              String defTable = (String) jo.get("table");

              // Found a filter request for this schema & table?
              if ((defSchema.equals("*") || defSchema.equals(orc.getSchemaName()))
                  && (defTable.equals("*") || defTable.equals(orc.getTableName()))) {
                // Defined columns to filter.
                JSONArray defColumns = (JSONArray) jo.get("columns");

                // Filter column values.
                ArrayList<ColumnSpec> colSpecs = orc.getColumnSpec();
                ArrayList<ArrayList<OneRowChange.ColumnVal>> colValues = orc.getColumnValues();
                for (int c = 0; c < colSpecs.size(); c++) {
                  ColumnSpec colSpec = colSpecs.get(c);
                  if (colSpec.getName() != null) {
                    // Have this column in definitions?
                    if (defColumns.contains(colSpec.getName())) {
                      // Iterate through all rows in the
                      // column.
                      for (int row = 0; row < colValues.size(); row++) {
                        ColumnVal colValue = colValues.get(row).get(c);
                        if (colValue.getValue() != null) {
                          if (logger.isDebugEnabled())
                            logger.debug("Sending value: " + colValue.getValue());

                          // Send to server.
                          Object newValue =
                              sendToFilter(
                                  transformation,
                                  event.getSeqno(),
                                  row,
                                  orc.getSchemaName(),
                                  orc.getTableName(),
                                  colSpec.getName(),
                                  colValue.getValue());
                          colValue.setValue((Serializable) newValue);

                          if (logger.isDebugEnabled()) logger.debug("Received value: " + newValue);
                        }
                      }
                    }
                  } else {
                    if (logger.isDebugEnabled()) {
                      logger.debug(
                          "Expected to filter column, but column name is undefined: "
                              + orc.getSchemaName()
                              + "."
                              + orc.getTableName()
                              + "["
                              + colSpec.getIndex()
                              + "]");
                    }
                  }
                }

                // Filter key values.
                ArrayList<ColumnSpec> keySpecs = orc.getKeySpec();
                ArrayList<ArrayList<OneRowChange.ColumnVal>> keyValues = orc.getKeyValues();
                for (int k = 0; k < keySpecs.size(); k++) {
                  ColumnSpec keySpec = keySpecs.get(k);
                  if (keySpec.getName() != null) {
                    if (defColumns.contains(keySpec.getName())) {
                      // Iterate through all rows in the
                      // key.
                      for (int row = 0; row < keyValues.size(); row++) {
                        ColumnVal keyValue = keyValues.get(row).get(k);
                        if (keyValue.getValue() != null) {
                          if (logger.isDebugEnabled())
                            logger.debug("Sending value: " + keyValue.getValue());

                          // Send to server.
                          Object newValue =
                              sendToFilter(
                                  transformation,
                                  event.getSeqno(),
                                  row,
                                  orc.getSchemaName(),
                                  orc.getTableName(),
                                  keySpec.getName(),
                                  keyValue.getValue());
                          keyValue.setValue((Serializable) newValue);

                          if (logger.isDebugEnabled()) logger.debug("Received value: " + newValue);
                        }
                      }
                    }
                  } else {
                    if (logger.isDebugEnabled()) {
                      logger.debug(
                          "Expected to filter key, but column name is undefined: "
                              + orc.getSchemaName()
                              + "."
                              + orc.getTableName()
                              + "["
                              + keySpec.getIndex()
                              + "]");
                    }
                  }
                }
              }
            }
          }
        }
      } else if (dataElem instanceof StatementData) {
        // Not supported.
      }
    }
    return event;
  }
 // Confirms that a particular event is accepted.
 private void verifyAccept(FilterVerificationHelper filterHelper, ReplDBMSEvent e)
     throws ReplicatorException, InterruptedException {
   ReplDBMSEvent e2 = filterHelper.filter(e);
   assertTrue("Should allow event, seqno=" + e.getSeqno(), e == e2);
 }