// Path: core/src/main/java/com/github/jeluard/stone/api/Listener.java
// public interface Listener {
// 
//   /**
//    * Invoked each time a {@code value} is published.
//    *
//    * @param previousTimestamp
//    * @param currentTimestamp
//    * @param value 
//    */
//   void onPublication(long previousTimestamp, long currentTimestamp, int value);
// 
// }
// 
// Path: core/src/main/java/com/github/jeluard/stone/helper/Loggers.java
// public final class Loggers {
// 
//   private Loggers() {
//   }
// 
//   private static final String BASE_LOGGER_NAME = "com.github.jeluard.stone";
//   public static final Logger BASE_LOGGER = Logger.getLogger(Loggers.BASE_LOGGER_NAME);
// 
//   /**
//    * @param suffix
//    * @return a configured {@link Logger}
//    */
//   public static Logger create(final String suffix) {
//     Preconditions.checkNotNull(suffix, "null suffix");
// 
//     return Logger.getLogger(Loggers.BASE_LOGGER_NAME+"."+suffix);
//   }
// 
// }
// 
// Path: core/src/main/java/com/github/jeluard/stone/spi/Dispatcher.java
// public abstract class Dispatcher {
// 
//   /**
//    * Intercept {@link Exception} thrown when a {@code value} is published.
//    */
//   public interface ExceptionHandler {
// 
//     /**
//      * Invoked when an {@link Exception} is thrown during accumulation/persistency process.
//      *
//      * @param exception
//      */
//     void onException(Exception exception);
// 
//   }
// 
//   static final Logger LOGGER = Loggers.create("dispatcher");
// 
//   private final ExceptionHandler exceptionHandler;
//   protected static final ExceptionHandler DEFAULT_EXCEPTION_HANDLER = new ExceptionHandler() {
//     @Override
//     public void onException(final Exception exception) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while publishing", exception);
//       }
//     }
//   };
// 
//   /**
//    * @param exceptionHandler 
//    */
//   public Dispatcher(final ExceptionHandler exceptionHandler) {
//     this.exceptionHandler = Preconditions.checkNotNull(exceptionHandler, "null exceptionHandler");
//   }
// 
//   /**
//    * Safely execute {@link ExceptionHandler#onException(java.lang.Exception)} 
//    *
//    * @param exception 
//    */
//   protected final void notifyExceptionHandler(final Exception exception) {
//     try {
//       this.exceptionHandler.onException(exception);
//     } catch (Exception e) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while executing "+ExceptionHandler.class.getSimpleName()+" <"+this.exceptionHandler+">", e);
//       }
//     }
//   }
// 
//   /**
//    * Dispatch {@link Listener}s executions.
//    *
//    * @param previousTimestamp {@linkplain com.github.jeluard.stone.api.TimeSeries#DEFAULT_LATEST_TIMESTAMP} if does not exist
//    * @param currentTimestamp
//    * @param value
//    * @param listeners
//    * @return true if dispatch has been accepted; false if rejected
//    */
//    public abstract boolean dispatch(long previousTimestamp, long currentTimestamp, int value, Listener[] listeners);
// 
// }


import com.github.jeluard.guayaba.util.concurrent.ThreadFactoryBuilders;
import com.github.jeluard.stone.api.Listener;
import com.github.jeluard.stone.helper.Loggers;
import com.github.jeluard.stone.spi.Dispatcher;
import com.google.common.base.Preconditions;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;

    public Entry(final long previousTimestamp, final long currentTimestamp, final int value, final Listener listener) {
      this.previousTimestamp = previousTimestamp;
      this.currentTimestamp = currentTimestamp;
      this.value = value;
      this.listener = listener;
    }

  }

  private final class Consumer implements Runnable {
    @Override
    public void run() {
      try {
        while (true) {
          final Entry entry = BlockingQueueDispatcher.this.queue.take();
          try {
            entry.listener.onPublication(entry.previousTimestamp, entry.currentTimestamp, entry.value);
          } catch (Exception e) {
            notifyExceptionHandler(e);
          }
        }
      } catch (InterruptedException e) {
        if (BlockingQueueDispatcher.LOGGER.isLoggable(Level.INFO)) {
          BlockingQueueDispatcher.LOGGER.log(Level.INFO, "Consumer <{0}> interrupted; letting it die", Thread.currentThread().getName());
        }
      }
    }
  }

// Path: core/src/main/java/com/github/jeluard/stone/api/Listener.java
// public interface Listener {
// 
//   /**
//    * Invoked each time a {@code value} is published.
//    *
//    * @param previousTimestamp
//    * @param currentTimestamp
//    * @param value 
//    */
//   void onPublication(long previousTimestamp, long currentTimestamp, int value);
// 
// }
// 
// Path: core/src/main/java/com/github/jeluard/stone/helper/Loggers.java
// public final class Loggers {
// 
//   private Loggers() {
//   }
// 
//   private static final String BASE_LOGGER_NAME = "com.github.jeluard.stone";
//   public static final Logger BASE_LOGGER = Logger.getLogger(Loggers.BASE_LOGGER_NAME);
// 
//   /**
//    * @param suffix
//    * @return a configured {@link Logger}
//    */
//   public static Logger create(final String suffix) {
//     Preconditions.checkNotNull(suffix, "null suffix");
// 
//     return Logger.getLogger(Loggers.BASE_LOGGER_NAME+"."+suffix);
//   }
// 
// }
// 
// Path: core/src/main/java/com/github/jeluard/stone/spi/Dispatcher.java
// public abstract class Dispatcher {
// 
//   /**
//    * Intercept {@link Exception} thrown when a {@code value} is published.
//    */
//   public interface ExceptionHandler {
// 
//     /**
//      * Invoked when an {@link Exception} is thrown during accumulation/persistency process.
//      *
//      * @param exception
//      */
//     void onException(Exception exception);
// 
//   }
// 
//   static final Logger LOGGER = Loggers.create("dispatcher");
// 
//   private final ExceptionHandler exceptionHandler;
//   protected static final ExceptionHandler DEFAULT_EXCEPTION_HANDLER = new ExceptionHandler() {
//     @Override
//     public void onException(final Exception exception) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while publishing", exception);
//       }
//     }
//   };
// 
//   /**
//    * @param exceptionHandler 
//    */
//   public Dispatcher(final ExceptionHandler exceptionHandler) {
//     this.exceptionHandler = Preconditions.checkNotNull(exceptionHandler, "null exceptionHandler");
//   }
// 
//   /**
//    * Safely execute {@link ExceptionHandler#onException(java.lang.Exception)} 
//    *
//    * @param exception 
//    */
//   protected final void notifyExceptionHandler(final Exception exception) {
//     try {
//       this.exceptionHandler.onException(exception);
//     } catch (Exception e) {
//       if (Dispatcher.LOGGER.isLoggable(Level.WARNING)) {
//         Dispatcher.LOGGER.log(Level.WARNING, "Got exception while executing "+ExceptionHandler.class.getSimpleName()+" <"+this.exceptionHandler+">", e);
//       }
//     }
//   }
// 
//   /**
//    * Dispatch {@link Listener}s executions.
//    *
//    * @param previousTimestamp {@linkplain com.github.jeluard.stone.api.TimeSeries#DEFAULT_LATEST_TIMESTAMP} if does not exist
//    * @param currentTimestamp
//    * @param value
//    * @param listeners
//    * @return true if dispatch has been accepted; false if rejected
//    */
//    public abstract boolean dispatch(long previousTimestamp, long currentTimestamp, int value, Listener[] listeners);
// 
// }


// Path: implementations/dispatchers/blocking-queue/src/main/java/com/github/jeluard/stone/dispatcher/blocking_queue/BlockingQueueDispatcher.java
import com.github.jeluard.guayaba.util.concurrent.ThreadFactoryBuilders;
import com.github.jeluard.stone.api.Listener;
import com.github.jeluard.stone.helper.Loggers;
import com.github.jeluard.stone.spi.Dispatcher;
import com.google.common.base.Preconditions;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;


    public Entry(final long previousTimestamp, final long currentTimestamp, final int value, final Listener listener) {
      this.previousTimestamp = previousTimestamp;
      this.currentTimestamp = currentTimestamp;
      this.value = value;
      this.listener = listener;
    }

  }

  private final class Consumer implements Runnable {
    @Override
    public void run() {
      try {
        while (true) {
          final Entry entry = BlockingQueueDispatcher.this.queue.take();
          try {
            entry.listener.onPublication(entry.previousTimestamp, entry.currentTimestamp, entry.value);
          } catch (Exception e) {
            notifyExceptionHandler(e);
          }
        }
      } catch (InterruptedException e) {
        if (BlockingQueueDispatcher.LOGGER.isLoggable(Level.INFO)) {
          BlockingQueueDispatcher.LOGGER.log(Level.INFO, "Consumer <{0}> interrupted; letting it die", Thread.currentThread().getName());
        }
      }
    }
  }

  private static final Logger LOGGER = Loggers.create("dispatcher.blocking-queue");