com.linkedin.databus.core.TestTrailFilePositionSetter.java Source code

Java tutorial

Introduction

Here is the source code for com.linkedin.databus.core.TestTrailFilePositionSetter.java

Source

/*
 * Copyright 2013 LinkedIn Corp. All rights reserved
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.linkedin.databus.core;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.linkedin.databus.core.TrailFilePositionSetter.FileFilter;
import com.linkedin.databus.core.TrailFilePositionSetter.FilePositionResult;
import com.linkedin.databus.core.TrailFilePositionSetter.FilePositionResult.Status;
import com.linkedin.databus.core.util.InvalidConfigException;
import com.linkedin.databus2.producers.db.GGXMLTrailTransactionFinder;
import com.linkedin.databus2.test.TestUtil;

public class TestTrailFilePositionSetter {
    public static final String MODULE = TestTrailFilePositionSetter.class.getName();
    public static final Logger LOG = Logger.getLogger(MODULE);

    private static final String TRAIL_FILENAME_PREFIX = "x3";
    private static final String[] _txnPattern = { "<transaction timestamp=\"2013-03-09:02:54:34.000000\">",
            " <dbupdate table=\"TASKMGR.TASKCTL_JOBS_1\" type=\"update\">", "   <columns>",
            "     <column name=\"JOB_ID\" key=\"true\">1621745</column>",
            "     <column name=\"GG_MODI_TS\">2013-03-09:02:54:33.996072000</column>",
            "     <column name=\"GG_STATUS\">o</column>", "   </columns>", "   <tokens>",
            "     <token name=\"TK-XID\">42.8.2681282</token>", "     <token name=\"TK-CSN\">${SCN}</token>",
            "   </tokens>", " </dbupdate>", " <dbupdate table=\"TASKMGR.TASKCTL_JOBS_2\" type=\"update\">",
            "   <columns>", "     <column name=\"JOB_ID\" key=\"true\">1621745</column>",
            "     <column name=\"GG_MODI_TS\">2013-03-09:02:54:33.996072000</column>",
            "     <column name=\"GG_STATUS\">o</column>", "   </columns>", "   <tokens>",
            "     <token name=\"TK-XID\">42.8.2681283</token>", "     <token name=\"TK-CSN\">${SCN}</token>",
            "   </tokens>", " </dbupdate>", "</transaction>" }; // currently 24 lines; if startLine > 0, probably want it less than this
    private static final String DONE_STRING = "done\n----------------------------------------------------------------------------\n";

    private ArrayList<File> _dirsToDelete = new ArrayList<File>(100);

    private BufferedWriter createWriter(String dir, String file) throws IOException {
        BufferedWriter w = new BufferedWriter(new FileWriter(new File(dir + "/" + file)));
        return w;
    }

    private void createTrailFiles(String dir, String prefix, long numTxns, long numLinesPerFile,
            long numLinesPerNewline, String newlineChar, int startLine, long corruptedScn, String corruption,
            boolean addAlternateCorruption, String altCorruption) throws IOException {
        HashSet<Long> corruptedScns = new HashSet<Long>(1);
        corruptedScns.add(new Long(corruptedScn));
        createTrailFiles(dir, prefix, numTxns, numLinesPerFile, numLinesPerNewline, newlineChar, startLine,
                corruptedScns, corruption, addAlternateCorruption, altCorruption);
    }

    private void createTrailFiles(String dir, String prefix, long numTxns, long numLinesPerFile,
            long numLinesPerNewline, String newlineChar, int startLine, Set<Long> corruptedScns, String corruption,
            boolean addAlternateCorruption, String altCorruption) throws IOException {
        createTrailFiles(dir, prefix, numTxns, numLinesPerFile, numLinesPerNewline, newlineChar, startLine,
                corruptedScns, corruption, addAlternateCorruption, altCorruption, _txnPattern, 1, 100);
    }

    private void createTrailFiles(String dir, String prefix, long numTxns, long numLinesPerFile,
            long numLinesPerNewline, String newlineChar, int startLine, String[] txnPattern,
            int numDbUpdatesWithSameScn, long startScn) throws IOException {
        createTrailFiles(dir, prefix, numTxns, numLinesPerFile, numLinesPerNewline, newlineChar, startLine,
                new HashSet<Long>(), "", false, "", txnPattern, numDbUpdatesWithSameScn, startScn);
    }

    private void createTrailFiles(String dir, String prefix, long numTxns, long numLinesPerFile,
            long numLinesPerNewline, String newlineChar, int startLine, Set<Long> corruptedScns, String corruption,
            boolean addAlternateCorruption, String altCorruption, String[] txnPattern, int numDbUpdatesWithSameScn,
            long currScn) throws IOException {
        long numFiles = ((numTxns * (txnPattern.length)) / numLinesPerFile) + 1;
        long numDigits = new Double(Math.log10(numFiles)).longValue() + 1;
        long currFileNum = 0;
        String currFile = prefix + toFixedLengthString(currFileNum, numDigits);
        long lineCount = 0;
        BufferedWriter w = createWriter(dir, currFile);
        int start = startLine;
        int dbUpdates = 0;
        for (long txn = 0; txn < numTxns; ++txn) {
            boolean corruptNextTokensEndTag = false;
            if (txn > 0)
                start = 0;
            for (int j = 0; j < txnPattern.length; ++j) {
                lineCount++;
                String txnLine = txnPattern[j];
                if (txnLine.contains("${SCN}")) {
                    dbUpdates++;
                    txnLine = txnLine.replace("${SCN}",
                            new Long(currScn).toString() + (corruptedScns.contains(currScn) ? corruption : ""));
                    if (addAlternateCorruption && corruptedScns.contains(currScn))
                        corruptNextTokensEndTag = true;
                    if (dbUpdates >= numDbUpdatesWithSameScn) {
                        currScn++;
                        dbUpdates = 0;
                    }
                }
                if (corruptNextTokensEndTag && txnLine.contains("</tokens>")) {
                    //txnLine = txnLine.append(newlineChar).append("   ").append(altCorruption);
                    txnLine = txnLine + newlineChar + "   " + altCorruption;
                    corruptNextTokensEndTag = false;
                }

                if (j >= start) {
                    w.append(txnLine);

                    if (lineCount % numLinesPerNewline == 0)
                        w.append(newlineChar);
                }

                if ((lineCount % numLinesPerFile) == 0) {
                    w.close();
                    currFileNum++;
                    currFile = prefix + toFixedLengthString(currFileNum, numDigits);
                    w = createWriter(dir, currFile);
                }
            }
        }

        if (w != null)
            w.close();
    }

    private static String toFixedLengthString(Long num, long numDigits) {
        String n = num.toString();
        long numDigitsToBeZeroed = numDigits - n.length();
        StringBuilder bld = new StringBuilder();
        for (int i = 0; i < numDigitsToBeZeroed; i++)
            bld.append('0');
        bld.append(n);
        return bld.toString();
    }

    @BeforeClass
    public void setUpClass() throws InvalidConfigException {
        //set up logging
        // TODO Setting the first argument to 'true' and make DDSDBUS-3577 not happen
        TestUtil.setupLoggingWithTimestampedFile(false, "/tmp/TestTrailFilePositionSetter_", ".log", Level.INFO); // FATAL
    }

    @AfterClass // optionally could do this after every test instead
    public void tearDownClass() {
        for (File dir : _dirsToDelete) {
            FileUtils.deleteQuietly(dir);
        }
        _dirsToDelete.clear();
    }

    private File createTempDir() throws IOException {
        File dir = File.createTempFile("dir_", null);

        if (!(dir.delete()))
            throw new IOException("Unable to delete temp file " + dir.getAbsolutePath());

        if (!dir.mkdir())
            throw new IOException("Unable to create tempDir: " + dir.getAbsolutePath());

        _dirsToDelete.add(dir); // deleteOnExit() deletes only if dir empty => use Apache FileUtils helper instead

        return dir;
    }

    /**
     * DDSDBUS-2603 NUllPointerException in GG Relay in Experimental CLuster
     * @throws Exception
     */
    @Test
    public void testDeletedDirectory() throws Exception {
        final Logger log = Logger.getLogger("TestTrailFilePositionSetter.testDeletedDirectory");
        log.info("starting");

        File dir = createTempDir();

        TrailFilePositionSetter posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(),
                TRAIL_FILENAME_PREFIX);
        GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();

        // Now delete the directory to make the locateFilePosition() see null for listFiles() call.
        boolean deleted = dir.delete();
        Assert.assertTrue(deleted, "Deleted the trail directory successfully");

        FilePositionResult res = posSetter.locateFilePosition(100, finder);
        Assert.assertEquals(res.getStatus(), Status.NO_TXNS_FOUND, "File Position Result Status");

        log.info(DONE_STRING);
    }

    /**
     * Verify that corruption that occurs early in the file causes USE_EARLIEST_SCN to return the
     * first SCN of the first uncorrupted transaction.
     */
    @Test
    public void testFailureMode_MalformedFirstTransactionInFirstFile() throws Exception {
        final Logger log = Logger
                .getLogger("TestTrailFilePositionSetter.testFailureMode_MalformedFirstTransactionInFirstFile");
        log.info("starting");

        File dir = createTempDir();

        // first SCN is hardcoded always to be 100
        createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, 150 /* numTxns, 24 lines each */,
                1250 /* numLinesPerFile */, 1 /* numLinesPerNewline */, "\n", 0, 100 /* corrupt first SCN */,
                "xyzzy", false, "");

        TrailFilePositionSetter posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(),
                TRAIL_FILENAME_PREFIX);
        GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();
        FilePositionResult res;

        // SCN 100 is corrupted, so 101 is the effective oldest SCN => 100 treated as error:
        res = posSetter.locateFilePosition(100, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                "expected error for exact-match SCN that's corrupted and oldest in all trail files.");

        // SCN 101 is OK (regexQuery() doesn't fully validate XML):
        finder.reset();
        res = posSetter.locateFilePosition(TrailFilePositionSetter.USE_EARLIEST_SCN, finder);
        assertFilePositionResult(res, dir, 101, FilePositionResult.Status.FOUND);

        log.info(DONE_STRING);
    }

    /**
     * Verify that corruption that occurs after the desired SCN/transaction doesn't cause errors
     * when requesting an exact SCN.
     */
    @Test
    public void testFailureMode_MalformedLastTransactionInMiddleFile() throws Exception {
        final Logger log = Logger
                .getLogger("TestTrailFilePositionSetter.testFailureMode_MalformedLastTransactionInMiddleFile");
        log.info("starting");

        File dir = createTempDir();

        // first SCN is hardcoded always to be 100
        createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, 150 /* numTxns, 24 lines each */,
                1250 /* numLinesPerFile */, 1 /* numLinesPerNewline */, "\n", 0,
                307 /* corrupt last SCN in 2nd file */, "plugh", false, "");

        TrailFilePositionSetter posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(),
                TRAIL_FILENAME_PREFIX);
        GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();
        FilePositionResult res;

        // corruption at SCN 307 occurs after SCN 299, so latter should be found OK:
        res = posSetter.locateFilePosition(299, finder);
        assertFilePositionResult(res, dir, 299, FilePositionResult.Status.FOUND);

        // SCN 306 is in same transaction as 307, but regexQuery() doesn't fully validate XML => OK
        finder.reset();
        res = posSetter.locateFilePosition(306, finder);
        assertFilePositionResult(res, dir, 306, FilePositionResult.Status.FOUND);

        log.info(DONE_STRING);
    }

    /**
     * Verify that corruption in the very last transaction causes USE_LATEST_SCN to return
     * the last SCN from the previous transaction.
     */
    @Test
    public void testFailureMode_MalformedLastTransactionInLastFile() throws Exception {
        final Logger log = Logger
                .getLogger("TestTrailFilePositionSetter.testFailureMode_MalformedLastTransactionInLastFile");
        log.info("starting");

        File dir = createTempDir();

        // first SCN is hardcoded always to be 100
        createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, 150 /* numTxns, 24 lines and 2 SCNs each */,
                1250 /* numLinesPerFile */, 1 /* numLinesPerNewline */, "\n", 0,
                399 /* corrupt last SCN in 3rd file */, "quux", false, "");

        TrailFilePositionSetter posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(),
                TRAIL_FILENAME_PREFIX);
        GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();
        FilePositionResult res;

        // corruption at SCN 399 => should get 398 back (same transaction, but again, regexQuery() doesn't fully validate)
        res = posSetter.locateFilePosition(TrailFilePositionSetter.USE_LATEST_SCN, finder);
        assertFilePositionResult(res, dir, 398, FilePositionResult.Status.FOUND);

        log.info(DONE_STRING);
    }

    /**
     * Verify that a non-corrupted SCN value within a corrupted transaction causes an exception to be thrown
     * when the exact SCN is requested.
     */
    @Test
    public void testFailureMode_MalformedTransactionForExactScn() throws Exception {
        final Logger log = Logger
                .getLogger("TestTrailFilePositionSetter.testFailureMode_MalformedTransactionForExactScn");
        log.info("starting");

        File dir = createTempDir();

        // first SCN is hardcoded always to be 100
        // this adds a non-XML string near the end of the dbupdate for SCN 377, but the SCN value itself is clean:
        createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, 150 /* numTxns, 24 lines each */,
                1250 /* numLinesPerFile */, 1 /* numLinesPerNewline */, "\n", 0,
                377 /* corrupt a txn in 3rd file */, "", true, "kilroy");

        TrailFilePositionSetter posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(),
                TRAIL_FILENAME_PREFIX);
        GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();
        FilePositionResult res;

        // with a full XML parser, this should throw an exception; with regexQuery(), it's fine:
        finder.reset();
        res = posSetter.locateFilePosition(377, finder);
        assertFilePositionResult(res, dir, 377, FilePositionResult.Status.FOUND);

        log.info(DONE_STRING);
    }

    /**
     * Verify that a transaction with corruption in all of its SCNs doesn't cause problems.
     * Case 1:  bad transaction at the beginning of the first trail file.
     * Case 2:  bad transaction at the beginning of the middle trail file (partial transaction).
     * Case 3:  bad transaction at the beginning of the middle trail file (first full transaction; preceding partial bad).
     * Case 4:  bad transaction in the middle of the middle trail file.
     * Case 5:  bad transaction at the beginning of the last trail file (first full transaction; preceding partial is OK).
     * Case 6:  bad transaction at the end of the last trail file.
     */
    @Test
    public void testFailureMode_MalformedTransactionNoValidScnsInMiddleOfMiddleFile() throws Exception {
        final Logger log = Logger
                .getLogger("TestTrailFilePositionSetter.testFailureMode_MalformedTransactionNoValidScns");
        log.info("starting");

        File dir = createTempDir();

        // corrupt both SCNs in each of six transactions:
        HashSet<Long> corruptedScns = new HashSet<Long>(10);
        corruptedScns.add(new Long(100)); // case 1
        corruptedScns.add(new Long(101)); // case 1
        corruptedScns.add(new Long(204)); // case 2
        corruptedScns.add(new Long(205)); // case 2
        corruptedScns.add(new Long(206)); // case 3
        corruptedScns.add(new Long(207)); // case 3
        corruptedScns.add(new Long(250)); // case 4
        corruptedScns.add(new Long(251)); // case 4
        corruptedScns.add(new Long(310)); // case 5
        corruptedScns.add(new Long(311)); // case 5
        corruptedScns.add(new Long(398)); // case 6
        corruptedScns.add(new Long(399)); // case 6
        createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, 150 /* numTxns, 24 lines each */,
                1250 /* numLinesPerFile */, 1 /* numLinesPerNewline */, "\n", 0, corruptedScns, "blargh", false,
                "");

        TrailFilePositionSetter posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(),
                TRAIL_FILENAME_PREFIX);
        GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();
        FilePositionResult res;

        // SCN 101 is before the earliest (valid) SCN present, so expect ERROR:
        res = posSetter.locateFilePosition(101, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                "expected error for exact-match SCN that's 'too old'.");

        // For SCN <= the earliest transactions maxSCN, we throw error
        finder.reset();
        res = posSetter.locateFilePosition(102, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                "expected error for exact-match SCN that's 'too old'.");

        // expect first non-corrupted SCN here, not first "transaction SCN":
        finder.reset();
        res = posSetter.locateFilePosition(TrailFilePositionSetter.USE_EARLIEST_SCN, finder);
        assertFilePositionResult(res, dir, 102, FilePositionResult.Status.FOUND);

        // 107 = max SCN of its transaction = "transaction SCN" => should get FOUND
        finder.reset();
        res = posSetter.locateFilePosition(107, finder);
        assertFilePositionResult(res, dir, 107, FilePositionResult.Status.FOUND);

        // 203 = last valid SCN in first file = max SCN of its transaction = "transaction SCN"
        // => should be FOUND
        finder.reset();
        res = posSetter.locateFilePosition(203, finder);
        assertFilePositionResult(res, dir, 203, FilePositionResult.Status.FOUND);

        // SCN 204 is invalid and is part of a transaction split across first/second files;
        // 209 = next "transaction SCN" and is near the top of the middle file
        finder.reset();
        res = posSetter.locateFilePosition(204, finder);
        assertFilePositionResult(res, dir, 209, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // SCN 250 is invalid (as is 251); expect 253 since max SCN of following transaction
        finder.reset();
        res = posSetter.locateFilePosition(250, finder);
        assertFilePositionResult(res, dir, 253, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // SCN 251 is invalid (as is 250); expect 253 since max SCN of following transaction
        finder.reset();
        res = posSetter.locateFilePosition(251, finder);
        assertFilePositionResult(res, dir, 253, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // SCN 252 is valid and present, but weird corner case => still EXACT_SCN_NOT_FOUND
        finder.reset();
        res = posSetter.locateFilePosition(252, finder);
        assertFilePositionResult(res, dir, 252, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // SCN 253 is valid and present and max SCN of its transaction => FOUND
        finder.reset();
        res = posSetter.locateFilePosition(253, finder);
        assertFilePositionResult(res, dir, 253, FilePositionResult.Status.FOUND);

        // SCN 309 is valid and present and max SCN of its transaction => FOUND (even though
        // split across second/third files, and following transaction is corrupted)
        finder.reset();
        res = posSetter.locateFilePosition(309, finder);
        assertFilePositionResult(res, dir, 309, FilePositionResult.Status.FOUND);

        // SCN 310 is invalid (as is 311); expect 313
        finder.reset();
        res = posSetter.locateFilePosition(310, finder);
        assertFilePositionResult(res, dir, 313, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // SCN 311 is invalid (as is 310); expect 313
        finder.reset();
        res = posSetter.locateFilePosition(311, finder);
        assertFilePositionResult(res, dir, 313, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // SCN 398 is invalid (as is 399) and is in last transaction of last file, but since
        // trail file is expected to continue growing (i.e., eventually to have a valid SCN
        // that's larger than the request), expect EXACT_SCN_NOT_FOUND rather than ERROR.  SCN
        // returned will be that of last valid transaction, i.e., 397.
        // [checks beginning of last valid transaction == 396/397 one at byte offset 35650]
        finder.reset();
        res = posSetter.locateFilePosition(398, finder);
        assertFilePositionResult(res, dir, 397, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // SCN 405 is completely missing (would be after last transaction of last file); expect
        // same behavior as previous case
        finder.reset();
        res = posSetter.locateFilePosition(405, finder);
        assertFilePositionResult(res, dir, 397, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // last valid transaction-SCN is 397
        finder.reset();
        res = posSetter.locateFilePosition(TrailFilePositionSetter.USE_LATEST_SCN, finder);
        assertFilePositionResult(res, dir, 397, FilePositionResult.Status.FOUND);

        log.info(DONE_STRING);
    }

    /**
     * Verify that requesting an SCN that's older than the oldest trail file causes ERROR
     * to be returned.  Also check various valid states:  SCN that isn't the max for its
     * transaction; SCN that is the max for its transaction; earliest SCN; etc.
     */
    @Test
    public void testRequestedScnOlderThanOldestTransaction() throws Exception {
        final Logger log = Logger
                .getLogger("TestTrailFilePositionSetter.testRequestedScnOlderThanOldestTransaction");
        //log.setLevel(Level.DEBUG);
        log.info("starting");

        File dir = createTempDir();

        // first SCN is hardcoded always to be 100:
        createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, 150 /* numTxns, 24 lines each */,
                1250 /* numLinesPerFile */, 1 /* numLinesPerNewline */, "\n", 0, -1 /* no corruption */, "", false,
                "");

        TrailFilePositionSetter posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(),
                TRAIL_FILENAME_PREFIX);
        GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();
        FilePositionResult res;

        // SCN 99 is before the earliest SCN present in the trail files (100), so expect ERROR:
        res = posSetter.locateFilePosition(99, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                "expected error for exact-match SCN that's too old.");

        // "Transaction-SCN" semantics:  max SCN is 121 in this transaction and 119 in the
        // previous one, so those are the values used for most of the "is it found?" logic.
        // Since 120 falls between the two but doesn't equal either one, it's not considered
        // to be found exactly (even though it's actually present), but the state is still
        // valid, so processing can continue (i.e., it doesn't matter).
        finder.reset();
        res = posSetter.locateFilePosition(120, finder);
        assertFilePositionResult(res, dir, 120, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);

        // For SCN <= the earliest transactions maxSCN, we throw error
        finder.reset();
        res = posSetter.locateFilePosition(100, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                "expected error for exact-match SCN that's too old.");

        // Related, weird corner case:  USE_EARLIEST_SCN uses the min SCN in the oldest transaction,
        // so even though an exact match for 100 doesn't return FOUND, this does:
        finder.reset();
        res = posSetter.locateFilePosition(TrailFilePositionSetter.USE_EARLIEST_SCN, finder);
        assertFilePositionResult(res, dir, 100, FilePositionResult.Status.FOUND);

        // For SCN <= the earliest transactions maxSCN, we throw error
        finder.reset();
        res = posSetter.locateFilePosition(101, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                "expected error for exact-match SCN that's too old.");

        log.info(DONE_STRING);
    }

    @Test
    public void testScnPositionLocator() throws Exception {
        final Logger log = Logger.getLogger("TestTrailFilePositionSetter.testScnPositionLocator");
        log.info("starting");

        testScnPositionLocator(0, 100, 200, 102, log); // case where all txn are complete

        log.info(DONE_STRING);
    }

    private void testScnPositionLocator(int startLine, int startScn, int endScn, int beginFoundScn, Logger log)
            throws Exception {
        File dir = createTempDir();
        // TODO/FIXME:  why are startScn and endScn being used for numTxns and numLinesPerFile, respectively?
        createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, startScn, endScn, 1, "\n", startLine, -1, "",
                false, "");
        log.info("Directory is: " + dir);

        TrailFilePositionSetter posSetter = null;
        //GoldenGateTransactionSCNFinder finder;
        GGXMLTrailTransactionFinder finder;

        //less than minScn
        for (long i = 0; i < beginFoundScn; i++) {
            posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
            //finder = new GoldenGateTransactionSCNFinder();
            finder = new GGXMLTrailTransactionFinder();

            FilePositionResult res = posSetter.locateFilePosition(i, finder);
            Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                    "Result Status for SCN: " + i + ", Result: " + res);
        }

        //Found Case
        for (long i = beginFoundScn; i < (startScn + endScn); i++) {
            posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
            //finder = new GoldenGateTransactionSCNFinder();
            finder = new GGXMLTrailTransactionFinder();

            FilePositionResult res = posSetter.locateFilePosition(i, finder);
            log.info("For scn (" + i + "):  the result is:  " + res);
            if (i % 2 == 0)
                assertFilePositionResult(res, dir, i + 1, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);
            else
                assertFilePositionResult(res, dir, i, FilePositionResult.Status.FOUND);
        }

        //Found Case
        for (long i = (startScn + endScn); i < (startScn + endScn) + 20; i++) {
            posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
            //finder = new GoldenGateTransactionSCNFinder();
            finder = new GGXMLTrailTransactionFinder();

            FilePositionResult res = posSetter.locateFilePosition(i, finder);
            log.info("For scn (" + i + "):  the result is:  " + res);
            assertFilePositionResult(res, dir, 299, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);
        }
    }

    @Test
    public void testRepeatSCN() throws Exception {
        final Logger log = Logger.getLogger("TestTrailFilePositionSetter.testRepeatSCN");
        File dir = createTempDir();
        long startScn = 100;
        int numTxnsPerFile = 10;
        /**
         * Create trail files with the following SCN range
         *
         * Trail-File       Number of Txns          SCN Range
         *   x300               10                  100-100
         *   x301               10                  100-100
         *   x302               10                  101-101
         *   x303               10                  101-101
         *   x304               10                  102-102
         *   x305               10                  102-102
         *   x306               10                  103-103
         *   x307               10                  103-103
         *   x308               10                  104-104
         *   x309               10                  104-104
         *
         */
        createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, 100L, _txnPattern.length * numTxnsPerFile,
                1L, "\n", 0, _txnPattern, 2 * numTxnsPerFile * 2, // Transactions in 2 files have the same SCN
                startScn);
        log.info("Directory is: " + dir);

        TrailFilePositionSetter posSetter = null;
        // GoldenGateTransactionSCNFinder finder = new GoldenGateTransactionSCNFinder();
        GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();
        posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);

        // SCN 100 is not found because this is the first SCN in the trail file.
        FilePositionResult res = posSetter.locateFilePosition(100, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                "Result Status for SCN: " + 100 + ", Result: " + res);

        // SCN 101 is found
        res = posSetter.locateFilePosition(101, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.FOUND, "Result Status");
        Assert.assertEquals(res.getTxnPos().getFile(), "x302", "File found");
        Assert.assertEquals(res.getTxnPos().getFileOffset(), 0, "File offset found");
        Assert.assertEquals(res.getTxnPos().getLineNumber(), 1, "File line number found");
        Assert.assertEquals(res.getTxnPos().getMinScn(), 101, "MinScn check");
        Assert.assertEquals(res.getTxnPos().getMaxScn(), 101, "MinScn check");
        Assert.assertEquals(res.getTxnPos().getTxnRank(), 10, "Txn Rank");

        // SCN 102 is found
        res = posSetter.locateFilePosition(102, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.FOUND, "Result Status");
        Assert.assertEquals(res.getTxnPos().getFile(), "x304", "File found");
        Assert.assertEquals(res.getTxnPos().getFileOffset(), 0, "File offset found");
        Assert.assertEquals(res.getTxnPos().getLineNumber(), 1, "File line number found");
        Assert.assertEquals(res.getTxnPos().getMinScn(), 102, "MinScn check");
        Assert.assertEquals(res.getTxnPos().getMaxScn(), 102, "MinScn check");
        Assert.assertEquals(res.getTxnPos().getTxnRank(), 10, "Txn Rank");

        // SCN 103 is found
        res = posSetter.locateFilePosition(103, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.FOUND, "Result Status");
        Assert.assertEquals(res.getTxnPos().getFile(), "x306", "File found");
        Assert.assertEquals(res.getTxnPos().getFileOffset(), 0, "File offset found");
        Assert.assertEquals(res.getTxnPos().getLineNumber(), 1, "File line number found");
        Assert.assertEquals(res.getTxnPos().getMinScn(), 103, "MinScn check");
        Assert.assertEquals(res.getTxnPos().getMaxScn(), 103, "MinScn check");
        Assert.assertEquals(res.getTxnPos().getTxnRank(), 10, "Txn Rank");

        // SCN 104 is found
        res = posSetter.locateFilePosition(104, finder);
        Assert.assertEquals(res.getStatus(), FilePositionResult.Status.FOUND, "Result Status");
        Assert.assertEquals(res.getTxnPos().getFile(), "x308", "File found");
        Assert.assertEquals(res.getTxnPos().getFileOffset(), 0, "File offset found");
        Assert.assertEquals(res.getTxnPos().getLineNumber(), 1, "File line number found");
        Assert.assertEquals(res.getTxnPos().getMinScn(), 104, "MinScn check");
        Assert.assertEquals(res.getTxnPos().getMaxScn(), 104, "MinScn check");
        Assert.assertEquals(res.getTxnPos().getTxnRank(), 10, "Txn Rank");
    }

    @Test
    public void testScnPositionSetter1() throws Exception {
        final Logger log = Logger.getLogger("TestTrailFilePositionSetter.testScnPositionSetter1");
        log.info("starting");

        testScnPositionSetter(0, 100, 200, 100, log); // case where all txn are complete
        testScnPositionSetter(10, 100, 200, 102, log); // case where initial txn is incomplete

        log.info(DONE_STRING);
    }

    private void testScnPositionSetter(int startLine, int startScn, int endScn, int beginFoundScn, Logger log)
            throws Exception {
        String[] newLines = { "\n", "\r", "\r\n" };

        for (String newLine : newLines) {
            File dir = createTempDir();
            createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, startScn, endScn, 1, newLine, startLine,
                    -1, "", false, "");
            log.info("Directory is: " + dir);

            TrailFilePositionSetter posSetter = null;
            //GoldenGateTransactionSCNFinder finder = new GoldenGateTransactionSCNFinder();
            GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();

            //less than minScn
            for (long i = 0; i < beginFoundScn; i++) {
                posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
                Logger log2 = posSetter._log;
                log2.setLevel(Level.INFO);
                log2.info("Created a TrailFilePositionSetter with suffix x3");
                //finder = new GoldenGateTransactionSCNFinder();
                finder = new GGXMLTrailTransactionFinder();

                FilePositionResult res = posSetter.getFilePosition(i, finder);
                Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR,
                        "Result Status for SCN: " + i + ", Result: " + res);
            }

            //Found Case
            for (long i = beginFoundScn; i < (startScn + endScn); i++) {
                posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
                //finder = new GoldenGateTransactionSCNFinder();
                finder = new GGXMLTrailTransactionFinder();

                FilePositionResult res = posSetter.getFilePosition(i, finder);
                log.info("For scn (" + i + "):  the result is:  " + res);
                if (i % 2 == 0)
                    assertFilePositionResult(res, dir, i + 1, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);
                else
                    assertFilePositionResult(res, dir, i, FilePositionResult.Status.FOUND);
            }

            //Found Case
            for (long i = (startScn + endScn); i < (startScn + endScn) + 20; i++) {
                posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
                //finder = new GoldenGateTransactionSCNFinder();
                finder = new GGXMLTrailTransactionFinder();

                FilePositionResult res = posSetter.getFilePosition(i, finder);
                log.info("For scn (" + i + "):  the result is:  " + res);
                assertFilePositionResult(res, dir, 299, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);
            }
        }
    }

    /**
     * Case where XML snippets have newlines at different positions.
     * NOTE:  This test is EXCEEDINGLY slow...
     *
     * @throws Exception
     */
    @Test
    public void testScnPositionSetter2() throws Exception {
        final Logger log = Logger.getLogger("TestTrailFilePositionSetter.testScnPositionSetter2");
        log.info("starting");

        String[] newLines = { "\r", "\n", "\r\n" };

        for (String newLine : newLines) {
            // Iterate by Number of Tags per line
            for (int j = 10; j < 32; j++) {
                log.info("NumPerLine: " + j);
                File dir = createTempDir();
                createTrailFiles(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX, 100, 200, j, newLine, 0, -1, "",
                        false, "");
                log.info("Directory is: " + dir);

                TrailFilePositionSetter posSetter = null;
                //GoldenGateTransactionSCNFinder finder = new GoldenGateTransactionSCNFinder();
                GGXMLTrailTransactionFinder finder = new GGXMLTrailTransactionFinder();

                //less than minScn
                log.info("less than MinScn case started !!");
                for (long i = 0; i < 100; i++) {
                    if (true)
                        break;
                    posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
                    //finder = new GoldenGateTransactionSCNFinder();
                    finder = new GGXMLTrailTransactionFinder();

                    FilePositionResult res = posSetter.getFilePosition(i, finder);
                    Assert.assertEquals(res.getStatus(), FilePositionResult.Status.ERROR, "Result Status");
                }

                log.info("less than MinScn case passed !!");

                //Found Case
                for (long i = 100; i < 300; i++) {
                    posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
                    finder = new GGXMLTrailTransactionFinder();

                    log.info("SCN:  " + i);
                    FilePositionResult res = posSetter.getFilePosition(i, finder);
                    log.info("For scn (" + i + "):  the result is:  " + res);
                    if (i % 2 == 0)
                        assertFilePositionResult(res, dir, i + 1, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);
                    else
                        assertFilePositionResult(res, dir, i, FilePositionResult.Status.FOUND);

                }

                //Found Case
                FilePositionResult res = null;
                for (long i = 300; i < 320; i++) {
                    posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
                    //finder = new GoldenGateTransactionSCNFinder();
                    finder = new GGXMLTrailTransactionFinder();

                    res = posSetter.getFilePosition(i, finder);
                    //log.info("For scn (" + i + "):  the result is:  "  + res);
                    assertFilePositionResult(res, dir, 299, FilePositionResult.Status.EXACT_SCN_NOT_FOUND);
                }

                // USE Latest SCN (-1)
                posSetter = new TrailFilePositionSetter(dir.getAbsolutePath(), TRAIL_FILENAME_PREFIX);
                //finder = new GoldenGateTransactionSCNFinder();
                finder = new GGXMLTrailTransactionFinder();

                res = posSetter.getFilePosition(-1, finder);
                //log.info("For scn (" + i + "):  the result is:  "  + res);
                assertFilePositionResult(res, dir, 299, FilePositionResult.Status.FOUND);
            }
        }

        log.info(DONE_STRING);
    }

    private void assertFilePositionResult(FilePositionResult res, File dir, long expScn,
            FilePositionResult.Status expStatus) throws Exception {
        Assert.assertEquals(res.getStatus(), expStatus, "Result Status for scn: " + expScn);

        ConcurrentAppendableCompositeFileInputStream c = new ConcurrentAppendableCompositeFileInputStream(
                dir.getAbsolutePath(), res.getTxnPos().getFile(), res.getTxnPos().getFileOffset(),
                new TrailFilePositionSetter.FileFilter(dir, TRAIL_FILENAME_PREFIX), true);

        ReadFirstSCN scnFetcher = new ReadFirstSCN(new BufferedReader(new InputStreamReader(c)), expScn);
        scnFetcher.start();

        scnFetcher.awaitShutdown();
        if (c != null)
            c.close();
        LOG.info("Result is: " + res);
        Assert.assertEquals(scnFetcher.isPatternMatched(), true,
                "expected SCN " + expScn + " not found at byte offset " + res.getTxnPos().getFileOffset()
                        + " of file " + res.getTxnPos().getFile() + ".");
        LOG.info("Finding result txn (" + res + ") passed !!");
    }

    public static class ReadFirstSCN extends DatabusThreadBase {
        private final BufferedReader _reader;
        private boolean _patternMatched = false;
        private String _errorMessage = null;
        private long _expScn = -1;

        public ReadFirstSCN(BufferedReader reader, long expScn) {
            super("ReadFirSCN");
            _reader = reader;
            _expScn = expScn;
        }

        @Override
        public void run() {
            boolean beginSeen = false;
            String line = null;
            try {
                String pattern = "<token name=\"tk-csn\">" + _expScn + "</token>";

                while ((line = _reader.readLine()) != null) {
                    //LOG.info("Line:  (" + line + ")");

                    line = line.toLowerCase();
                    if (!beginSeen) {
                        beginSeen = true;

                        if (!line.startsWith(GGXMLTrailTransactionFinder.TRANSACTION_BEGIN_PREFIX)) {
                            _errorMessage = "Stream not starting with transaction begin string !!  Line is: "
                                    + line;
                            return;
                        }
                    }

                    if (line.contains(pattern)
                            && line.contains(GGXMLTrailTransactionFinder.TRANSACTION_END_PREFIX)) {
                        int index1 = line.indexOf(pattern);
                        int index2 = line.indexOf(GGXMLTrailTransactionFinder.TRANSACTION_END_PREFIX);

                        if (index1 < index2) {
                            _patternMatched = true;
                        }
                        break;
                    }

                    if (line.contains(pattern))
                        _patternMatched = true;

                    if (line.contains(GGXMLTrailTransactionFinder.TRANSACTION_END_PREFIX))
                        break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != _reader)
                    try {
                        _reader.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                doShutdownNotify();
            }
        }

        public boolean isPatternMatched() {
            return _patternMatched;
        }
    }

    @Test
    public void testSplitBytesByNewLines() {
        final Logger log = Logger.getLogger("TestTrailFilePositionSetter.testSplitBytesByNewLines");
        log.info("starting");

        String s = "abc\r\ndef\r\n";
        List<String> l = new ArrayList<String>();
        List<Integer> endPos = new ArrayList<Integer>();
        String lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()),
                s.length(), false, -1, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(2, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 10, "Second in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 0, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(3, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 3, "Length");
        Assert.assertEquals(l.get(0), "", "First in " + l);
        Assert.assertEquals(l.get(1), "abc", "Second in " + l);
        Assert.assertEquals(l.get(2), "def", "Third in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), -1, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(2).intValue(), 10, "Second in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 1, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(3, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 3, "Length");
        Assert.assertEquals(l.get(0), "a", "First in " + l);
        Assert.assertEquals(l.get(1), "bc", "Second in " + l);
        Assert.assertEquals(l.get(2), "def", "Third in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), -1, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(2).intValue(), 10, "Second in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 2, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(3, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 3, "Length");
        Assert.assertEquals(l.get(0), "ab", "First in " + l);
        Assert.assertEquals(l.get(1), "c", "Second in " + l);
        Assert.assertEquals(l.get(2), "def", "Third in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), -1, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(2).intValue(), 10, "Second in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 3, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(2, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 10, "Second in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 5, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(l.size(), 3, "Length");
        Assert.assertEquals(endPos.size(), 3, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "", "Second in " + l);
        Assert.assertEquals(l.get(2), "def", "Third in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), -1, "First in " + endPos);
        Assert.assertEquals(endPos.get(2).intValue(), 10, "Second in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 6, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(l.size(), 3, "Length");
        Assert.assertEquals(endPos.size(), 3, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "d", "Second in " + l);
        Assert.assertEquals(l.get(2), "ef", "Third in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), -1, "First in " + endPos);
        Assert.assertEquals(endPos.get(2).intValue(), 10, "Second in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        //log.info("Starting !!");
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 7, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(l.size(), 3, "Length");
        Assert.assertEquals(endPos.size(), 3, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "de", "Second in " + l);
        Assert.assertEquals(l.get(2), "f", "Third in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), -1, "Second in " + endPos);
        Assert.assertEquals(endPos.get(2).intValue(), 10, "Third in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 4, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(2, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 10, "Second in " + endPos);

        s = "abc\r\ndef\r\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 8, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(2, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 10, "Second in " + endPos);

        s = "abc\ndef\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 3, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(2, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 4, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 8, "Second in " + endPos);

        s = "abc\ndef\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(2, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 4, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 8, "Second in " + endPos);

        s = "abc\r\ndef\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);

        Assert.assertNull(lastLineStr, "Returned String should be null");
        Assert.assertEquals(2, l.size(), "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 9, "Second in " + endPos);

        s = "abc\r\ndef";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);

        Assert.assertEquals(lastLineStr, "def", "Returned String");
        Assert.assertEquals(l.size(), 1, "Length");
        Assert.assertEquals(endPos.size(), 1, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);

        s = "abc\r\ndef\r";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);
        //log.info("Length: " + lastLineStr.length());
        Assert.assertEquals(lastLineStr, "def\r", "Returned String");
        Assert.assertEquals(l.size(), 1, "Length");
        Assert.assertEquals(endPos.size(), 1, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);

        lastLineStr = "abc";
        s = "def\r\nghi";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, lastLineStr, l, endPos);

        Assert.assertEquals(lastLineStr, "ghi", "Returned String");
        Assert.assertEquals(l.size(), 1, "Length");
        Assert.assertEquals(endPos.size(), 1, "Length");
        Assert.assertEquals(l.get(0), "abcdef", "First in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 8, "First in " + endPos);

        lastLineStr = "abc\r";
        s = "def\r\nghi";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, lastLineStr, l, endPos);

        Assert.assertEquals(lastLineStr, "ghi", "Returned String");
        Assert.assertEquals(l.size(), 2, "Length: " + l);
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "First in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 4, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 9, "Second in " + endPos);

        lastLineStr = "abc\r";
        s = "\ndef\r\nghi";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, lastLineStr, l, endPos);
        Assert.assertEquals(lastLineStr, "ghi", "Returned String");
        Assert.assertEquals(l.size(), 2, "Length: " + l);
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "First in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 5, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 10, "Second in " + endPos);

        lastLineStr = "abc";
        s = "\ndef\r\nghi";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, lastLineStr, l, endPos);

        Assert.assertEquals(lastLineStr, "ghi", "Returned String");
        Assert.assertEquals(l.size(), 2, "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "First in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 4, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 9, "Second in " + endPos);

        lastLineStr = "abc";
        s = "\rdef\r\nghi";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, lastLineStr, l, endPos);

        Assert.assertEquals(lastLineStr, "ghi", "Returned String");
        Assert.assertEquals(l.size(), 2, "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");

        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "def", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 4, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 9, "Second in " + endPos);

        lastLineStr = "xyz";
        s = "abcdef";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, lastLineStr, l, endPos);

        Assert.assertEquals(lastLineStr, "xyzabcdef", "Returned String");
        Assert.assertEquals(l.size(), 0, "Length");
        Assert.assertEquals(endPos.size(), 0, "Length");

        s = "abcdef";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);

        Assert.assertEquals(lastLineStr, "abcdef", "Returned String");
        Assert.assertEquals(l.size(), 0, "Length");
        Assert.assertEquals(endPos.size(), 0, "Length");

        s = "abc\n\n  def";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);

        Assert.assertEquals(lastLineStr, "  def", "Returned String");
        Assert.assertEquals(l.size(), 2, "Length");
        Assert.assertEquals(endPos.size(), 2, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "", "Second in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 4, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 5, "Second in " + endPos);

        s = "abc\n\n  def\n   ghi";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                true, 4, null, l, endPos);

        Assert.assertEquals(lastLineStr, "   ghi", "Returned String");
        Assert.assertEquals(l.size(), 3, "Length");
        Assert.assertEquals(endPos.size(), 3, "Length");
        Assert.assertEquals(l.get(0), "abc", "First in " + l);
        Assert.assertEquals(l.get(1), "", "Second in " + l);
        Assert.assertEquals(l.get(2), "  def", "Third in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 4, "First in " + endPos);
        Assert.assertEquals(endPos.get(1).intValue(), 5, "Second in " + endPos);
        Assert.assertEquals(endPos.get(2).intValue(), 11, "Third in " + endPos);

        s = "\n";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);

        Assert.assertEquals(lastLineStr, null, "Returned String");
        Assert.assertEquals(l.size(), 1, "Length");
        Assert.assertEquals(endPos.size(), 1, "Length");
        Assert.assertEquals(l.get(0), "", "First in " + l);
        Assert.assertEquals(endPos.get(0).intValue(), 1, "First in " + endPos);

        s = " ";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);

        Assert.assertEquals(lastLineStr, " ", "Returned String");
        Assert.assertEquals(l.size(), 0, "Length");
        Assert.assertEquals(endPos.size(), 0, "Length");

        s = "";
        l = new ArrayList<String>();
        endPos = new ArrayList<Integer>();
        lastLineStr = TrailFilePositionSetter.splitBytesByNewLines(s.getBytes(Charset.defaultCharset()), s.length(),
                false, -1, null, l, endPos);

        Assert.assertEquals(lastLineStr, null, "Returned String");
        Assert.assertEquals(l.size(), 0, "Length");
        Assert.assertEquals(endPos.size(), 0, "Length");

        log.info(DONE_STRING);
    }

    @Test
    public void testTrailFileComparator() {
        final Logger log = Logger.getLogger("TestTrailFilePositionSetter.testTrailFileComparator");
        log.info("starting");

        String prefix = "x4";
        File dir = new File("/tmp");
        FileFilter f = new TrailFilePositionSetter.FileFilter(dir, prefix);

        //isTrailFile Check
        Assert.assertFalse(f.isTrailFile(null), "Null trail file");
        Assert.assertFalse(f.isTrailFile(dir), "Dir as  trail file");
        Assert.assertFalse(f.isTrailFile(new File("")), "Empty trail file name");
        Assert.assertFalse(f.isTrailFile(new File("/tmp/x12222")), "trail file with different prefix");
        Assert.assertFalse(f.isTrailFile(new File("/tmp/42222")), "trail file with different prefix");
        Assert.assertFalse(f.isTrailFile(new File("/tmp/x412222.xml")), "trail file with bad suffix");
        Assert.assertFalse(f.isTrailFile(new File("/tmp/x412222x")), "trail file with bad suffix");
        Assert.assertTrue(f.isTrailFile(new File("/tmp/x4122222")), "correct trail file");
        Assert.assertTrue(f.isTrailFile(new File("x4122222")), "correct trail file");

        //compareTo Check
        Assert.assertEquals(-1, f.compareFileName(new File("/tmp/x400001"), new File("/tmp/x400002")));
        Assert.assertEquals(-1, f.compareFileName(new File("/tmp/x400001"), new File("/tmp/x400002"))); // DUPLICATE?
        Assert.assertEquals(1, f.compareFileName(new File("/tmp/x410001"), new File("/tmp/x400002")));
        Assert.assertEquals(1, f.compareFileName(new File("/tmp/x410009"), new File("/tmp/x410003")));
        Assert.assertEquals(0, f.compareFileName(new File("/tmp/x410009"), new File("/tmp/x410009")));

        //isNextFileInSeq
        Assert.assertFalse(f.isNextFileInSequence(new File("/tmp/x400000"), new File("/tmp/x400000")));
        Assert.assertFalse(f.isNextFileInSequence(new File("/tmp/x410000"), new File("/tmp/x400000")));
        Assert.assertFalse(f.isNextFileInSequence(new File("/tmp/x400001"), new File("/tmp/x400000")));
        Assert.assertTrue(f.isNextFileInSequence(new File("/tmp/x400000"), new File("/tmp/x400001")));

        log.info(DONE_STRING);
    }
}