//
/////////////////////////////////////////////////////////////////
//                 C O P Y R I G H T  (c) 2010
//             A G F A - G E V A E R T  G R O U P
//                    All Rights Reserved
/////////////////////////////////////////////////////////////////
//
//       THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF
//                    Agfa-Gevaert Group
//      The copyright notice above does not evidence any
//     actual or intended publication of such source code.
//
/////////////////////////////////////////////////////////////////
//
//
/**
 *
 */

import org.apache.log4j.*;
import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;

/**
 * @author Louis Jacomet (axjuz)
 */
public class Log4JAsyncAppenderDeadLock {

    private static int BUFFER_SIZE = 128;

    private Logger logger;

    @Before
    public void setUp() {
        AsyncAppender asyncAppender = new AsyncAppender();
        asyncAppender.setBufferSize(BUFFER_SIZE);
        asyncAppender.addAppender(new ConsoleAppender(new SimpleLayout()));
        LogManager.getRootLogger().addAppender(asyncAppender);

        logger = Logger.getLogger(Log4JAsyncAppenderDeadLock.class);

    }

    @Test
    public void dispatcherDeathDoesNotCauseDeadlock() throws InterruptedException {
        final CountDownLatch dispatcherLatch = new CountDownLatch(1);
        final CountDownLatch endLatch = new CountDownLatch(1);

        logToFillBuffer(dispatcherLatch, endLatch);

        logToKillDispatcher(dispatcherLatch);

        // Wait for logging thread to be done
        endLatch.await(10L, TimeUnit.SECONDS);

        assertThat("Logging should have been done", endLatch.getCount(), is(0L));
    }

    private void logToFillBuffer(final CountDownLatch dispatcherLatch, final CountDownLatch endLatch) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // Wait for the dispatcher thread to be processing events
                    dispatcherLatch.await();
                    // Log above buffer capacity, very fast to fill AsyncAppender buffer up
                    for (int i = 0; i < BUFFER_SIZE + 30; i++) {
                        logger.error("Locking me up");
                    }
                    // Indicate thread done
                    endLatch.countDown();
                } catch (InterruptedException e) {
                    //Ignore
                }
            }
        }).start();
    }

    private void logToKillDispatcher(final CountDownLatch dispatcherLatch) {
        logger.error(new Object() {
            @Override
            public String toString() {
                // Start logging loop
                dispatcherLatch.countDown();
                try {
                    // Wait for buffer to fill
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // Ignore
                }
                // Kill dispatcher thread
                throw new RuntimeException();
            }
        });
    }
}
