Using Selenium with TestNg, TestListenerAdapter is getting tests mixed, the driver seems to be getting shared between test classes

I am running parallel tests using selenium (selenium-server-standalone-2.47.1.jar) grid with TestNg, kicked off by ant, and using a TestListenerAdapter. Screenshots are taken in the listener's 'onTestFailure' method. The problem is that the listener seems to get crossed up about which driver it should be using, and sometimes takes a screenshot of the wrong browser window, or fails altogether if the driver that it thinks it should be using has already quit.

When the tests start, TestNg's @BeforeTest and the TestListenerAdapter's 'onTestStart' methods are running on the same thread, but when the test fails, the TestListenerAdapter's 'onTestFailure' method appears to be running on a separate thread. It seems like the threads are getting crossed/shared somehow, but I can't figure out why.

Here is some skeleton code, any assistance greatly appreciated.

Base Test Class:

public class baseClassTests{

    protected AutomationUtils au;
    protected DriverUtils du;

    @BeforeTest(alwaysRun = true)
    @Parameters({ "selenium.OS", "selenium.browser" })
    public void beforeTest(String OS, String browser) {

        //these call simple private methods to know where to set up the driver
        String port = getPort(OS, browser);
        String host = getHost(OS);

        //make a driver utility object here, this makes a driver
        du = new DriverUtils(browser, host, port);

        //pass this driver utility object to another class of utilities
        //this 'AutomationUtils' class gets a RemoteWebDriver ('driver') by calling driver=du.getDriver();
        //the 'AutomationUtils' class is then the one that does all of the 'driver.findBy...' etc
        au = new AutomationUtils(du);
    }


    @BeforeMethod(alwaysRun = true)
    public void beforeMethod(Method m, ITestResult tr) {
        du.deleteCookies();
        testNgTestName = m.getName();
        print("Method: "+testNgTestName + "   Thread: "+Thread.currentThread().hashCode());
        //set the attribute of the ITestResult object so we can use the same object in the listener
        tr.setAttribute("du", du);
        tr.setAttribute("au", au);
    }

}

Listener class

public class AmSimpleTestListener extends TestListenerAdapter {

    private DriverUtils driveU;
    private AutomationUtils AutoU;
    private RemoteWebDriver driver;
    private RemoteWebDriver augmentedDriver;
    private String methodName;
    private String browser;
    private String browserVersion;
    String testClass;




    @Override
    public void onTestStart(ITestResult tr) {
        //pick up the correct driver utility object from the test class/method we are in
        driveU = (DriverUtils) tr.getAttribute("du");
        AutoU = (AutomationUtils) tr.getAttribute("au");
        driver = du.getDriver();
        augmentedDriver = (RemoteWebDriver) new Augmenter().augment(driver);
        methodName = tr.getName();
        testClass=tr.getTestClass();  //sort of, I actually parse it up a bit
        browser = driveU.getBrowser();
        browserVersion = driveU.getBrowserVersion();
        print("Method: "+methodName + "   Thread: "+Thread.currentThread().hashCode());
    }

    @Override
    public void onTestFailure(ITestResult tr) {
       print("Method: "+tr.getName() + "   Thread: "+Thread.currentThread().hashCode());
        try{
            writeScreenshotFile();
        }
        catch (Exception e){
            Out.error("Unable to take screen shot");
            e.printStackTrace();
        }
    }


    private String writeScreenshotFile() {
        if (driver != null && driver.getSessionId() != null) {
            File scrShot = ((TakesScreenshot) augmentedDriver).getScreenshotAs(OutputType.FILE);
            File localPathToScreenShot = new File("/path/to/base/directory/"+testClass+"/"+methodName+".png");
            try {
                FileUtils.copyFile(scrShot, localPathToScreenShot);
            } catch (Exception e) {
                Out.error("Couldn't write screenshot to file");
            }
            return localPathToScreenShot.getAbsolutePath();
        }
        return "Could not get path.";
    }

}

DriverUtils class makes/supplies the driver

public class DriverUtils {

    private RemoteWebDriver driver;
    private int timeout;
    private String browserVersion;
    private String browser
    private DesiredCapabilities caps;

    public DriverUtils(String browser, String host, String port) {
        String hostUrl = "http://" + host + ":" + port + "/wd/hub";
        this.browser=browser;
        //do some stuff here to set capabilties
        driver = new RemoteWebDriver(new URL(hostUrl), caps);
        browserVersion = driver.getCapabilities().getVersion();
    }

    public RemoteWebDriver getDriver() {
        return driver;
    }

    public AmBrowser getBrowser() {
        return browser;
    }

    public String getBrowserVersion() {
        return browserVersion;
    }


    public void quitDriver() {
        driver.quit();
    }

    public void deleteCookies(){
        driver.manage().deleteAllCookies();
    }


}


public class AutomationUtils extends BaseClassUtils {

    public AutomationUtils(DriverUtils driverUtils) {
        //pass it up to the base class utils (this is different than base class tests, above)
        //do this so the driver can be accessed by other utility classes as well
        super(driverUtils);
    }

    //All sorts of methods here to find elements, login, blah blah everything that is done with a driver object
}


public class BaseClassUtils { //this is a different class than BaseClassTests

    //make the driver a protected object so all utility classes can access as nec.
    protected final RemoteWebDriver driver;

    public BaseClassUtils(DriverUtils driverUtils) {
         driver = driverUtils.getDriver();
    }

}

Tests are run via ant.

<suite name="Dev2 for debugging" parallel="tests" thread-count="10">-- tests here </suite>

After tinkering this for a while, I came to the conclusion that there were two things that seemed to help tremendously.

  1. eliminate the listener, and take all screenshots in the @AfterMethod
  2. Move the @Before/After Method/Test methods into the child classes, but simply call methods in the parent to do all the work.

Another thing I noticed is that for #2, TestNG is supposed to run the parent @Before methods then the child @Before methods; and then at the end run the child '@After' methods and then the parent @After methods.
I ran a series of simple tests, I found that all before/after methods were not being run, so for the few cases where I was using @Before and @After methods in both parent and child, I consolidated.

Things seem to run much better now, the driver does not get confused, and screenshots are being attached to the correct browser/test.