package forklift.consumer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Consumers need to run in their own thread so that they can spawn more
 * threads to process messages. This also allows the system to isolate
 * different processes.
 * @author mconroy
 *
 */
public class ConsumerThread extends Thread {
    // Hold a lock object to allow for shutdowns during exponential backoff of reconnects.
    private Object lock = new Object();

    // Store a basic exponential backoff sequence.
    private static final long[] expoSeq = {3, 5, 8, 13, 21, 34, 55};

    private AtomicBoolean running;
    private Consumer consumer;
    private Logger log;

    public ConsumerThread(Consumer consumer) {
        super(consumer.getName());
        this.running = new AtomicBoolean(false);
        this.consumer = consumer;
        this.log = LoggerFactory.getLogger("consumer-thread-" + consumer.getName());
    }

    @Override
    public void run() {
        running.set(true);

        LocalDateTime lastConnectAttemptTime;
        int connectAttempt = 0;
        do {
            lastConnectAttemptTime = LocalDateTime.now();

            log.info("starting consumer");
            try {
                consumer.listen();
            } catch (Exception e) {
                log.debug("Couldn't get connection", e);
            }

            synchronized (lock) {
                // If we are still running let's wait a little while and attempt to reconnect.
                if (running.get()) {
                    // Reset connection attempts if we have been connected for longer than the max wait time.
                    if (LocalDateTime.now().isAfter(lastConnectAttemptTime.plus(expoSeq[expoSeq.length - 1], ChronoUnit.SECONDS)))
                        connectAttempt = 0;

                    try {
                        log.info("unexpected consumer shutdown - trying reconnect in {} seconds", expoSeq[connectAttempt]);
                        lock.wait(expoSeq[connectAttempt] * 1000);

                        // Never let the attempt number get bigger than the greatest backoff sequence number.
                        connectAttempt = Math.min(connectAttempt + 1, expoSeq.length - 1);
                    } catch (InterruptedException ignored) {
                        log.error("", ignored);
                    }
                }
            }
        } while (running.get());
    }

    public void shutdown() {
        // Jump us out of any exponential backoff wait we might be in.
        synchronized (lock) {
            lock.notify();
            consumer.shutdown();
            running.set(false);
        }
    }
}

@@ -1078,24 +1078,63 @@
 oid run() %7B%0A
+        log.info(%22starting consumer%22);%0A
         runn
@@ -1148,16 +1148,16 @@
 (true);%0A
-
 %0A       
@@ -1303,51 +1303,8 @@
 );%0A%0A
-            log.info(%22starting consumer%22);%0A
     
@@ -1408,48 +1408,230 @@
     
-log.debug(%22Couldn't get connection%22, e);
+if (connectAttempt == 0) %7B%0A                    log.error(%22Couldn't get connection%22, e);%0A                %7D else %7B%0A                    log.warn(%22Couldn't get connection, attempt %7B%7D%22, connectAttempt + 1, e);%0A                %7D
 %0A   

Improve logging in ConsumerThread a little