title: NPE exception when reading from queue when old queue data files are deleted
body: <p>I opened an issue for this: <a href="https://github.com/OpenHFT/Chronicle-Queue/issues/534" rel="nofollow noreferrer">https://github.com/OpenHFT/Chronicle-Queue/issues/534</a></p>
  
  <p>I am trying to implement periodic old queue file purge logic using StoreFileListener. I am using latest release net.openhft:chronicle-queue:5.16.13. The issue I am having is: after the roll cycle file gets rolled because of next cycle is active, I delete the queue file which just got released in StoreFileListener, then I create a new tailer and try to read message. It throws below NPE:</p>
  
  <p>The same happens if I create a brand new queue pointing to same queue dir and create a tailer.</p>
  
  <p>seeing below NPE exception when trying to implement queue file purging logic:</p>
  
  <pre><code>java.lang.NullPointerException
  at net.openhft.chronicle.queue.impl.single.SingleChronicleQueueExcerpts$StoreTailer.inACycle(SingleChronicleQueueExcerpts.java:1198)
  at net.openhft.chronicle.queue.impl.single.SingleChronicleQueueExcerpts$StoreTailer.readingDocument(SingleChronicleQueueExcerpts.java:1000)
  at net.openhft.chronicle.queue.impl.single.SingleChronicleQueueExcerpts$StoreTailer.readingDocument(SingleChronicleQueueExcerpts.java:942)
  at net.openhft.chronicle.wire.MarshallableIn.readText(MarshallableIn.java:95)
  at com.test.edge.api.queue.TestDeleteQueueFile.testQueueFileDeletionWhileInUse(TestDeleteQueueFile.java:133)
  </code></pre>
  
  <p>Test case to reproduce is below:</p>
  
  <pre><code>package com.test.edge.api.queue;
  
  import net.openhft.chronicle.core.time.SetTimeProvider;
  import net.openhft.chronicle.queue.ChronicleQueue;
  import net.openhft.chronicle.queue.ExcerptAppender;
  import net.openhft.chronicle.queue.ExcerptTailer;
  import net.openhft.chronicle.queue.RollCycles;
  import net.openhft.chronicle.queue.impl.StoreFileListener;
  import net.openhft.chronicle.queue.impl.single.SingleChronicleQueueBuilder;
  import org.apache.commons.io.FileUtils;
  import org.junit.After;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
  
  import java.io.File;
  import java.nio.file.Files;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  public class TestDeleteQueueFile {
  
      private Path tempQueueDir;
  
      @Before
      public void setUp() throws Exception {
          tempQueueDir = Files.createTempDirectory("unitTestQueueDir");
      }
  
      @After
      public void tearDown() throws Exception {
          FileUtils.deleteDirectory(tempQueueDir.toFile());
          Assert.assertFalse(tempQueueDir.toFile().exists());
          System.out.println("Deleted " + tempQueueDir.toFile().getAbsolutePath());
      }
  
      @Test
      public void testQueueFileDeletionWhileInUse() throws Exception {
          SetTimeProvider timeProvider = new SetTimeProvider();
  
          String queueName = "unitTestQueue";
  
          QueueStoreFileListener listener = new QueueStoreFileListener(queueName);
  
          try (ChronicleQueue queue = SingleChronicleQueueBuilder.binary(tempQueueDir + "/" + queueName).
                  timeProvider(timeProvider).storeFileListener(listener)
                  .build()) {
  
              ExcerptAppender appender = queue.acquireAppender();
  
              System.out.println("first index : " + queue.firstIndex());
              Assert.assertEquals(Long.MAX_VALUE, queue.firstIndex());
  
              //write 10 records should go to first day file
              for (int i = 0; i &lt; 10; i++) {
                  appender.writeText("test");
              }
  
              long indexAfter10Records = appender.lastIndexAppended();
              System.out.println("index after writing 10 records: " + indexAfter10Records);
  
              //roll to next day file
              timeProvider.advanceMillis(24 * 60 * 60 * 1000);
  
  
              //write 5 records in next file
              for (int i = 0; i &lt; 5; i++) {
                  appender.writeText("test2");
              }
  
              Map&lt;String, List&gt; queueToRollFilesOnAcquireMap = listener.getQueueToRollFilesOnAcquireMap();
              Map&lt;String, List&gt; queueToRollFilesOnReleaseMap = listener.getQueueToRollFilesOnReleaseMap();
  
              Assert.assertEquals(1, queueToRollFilesOnAcquireMap.size());
              List&lt;String&gt; files = queueToRollFilesOnAcquireMap.get(queueName);
              Assert.assertEquals(1, files.size());
              String secondFile = files.get(0);
  
              //other will have 1 as only first file is released
              files = queueToRollFilesOnReleaseMap.get(queueName);
              Assert.assertEquals(1, files.size());
              String firstFile = files.get(0);
  
              Assert.assertNotEquals(firstFile, secondFile);
  
  
              long firstIndex = queue.firstIndex();
  
  
              long indexAfter5Records = appender.lastIndexAppended();
              System.out.println("index after writing 5 records: " + indexAfter5Records);
  
              //now lets create one reader which will read all content
              ExcerptTailer excerptTailer = queue.createTailer();
              for (int i = 0; i &lt; 10; i++) {
                  Assert.assertEquals("test", excerptTailer.readText());
              }
  
              System.out.println("index after reading 10 records: " + excerptTailer.index());
              Assert.assertEquals(firstIndex, excerptTailer.index() - 10);
              for (int i = 0; i &lt; 5; i++) {
                  Assert.assertEquals("test2", excerptTailer.readText());
              }
  
              System.out.println("index after reading 5 records: " + excerptTailer.index());
              Assert.assertEquals(indexAfter5Records, excerptTailer.index() - 1);
  
              //lets delete first file
              System.out.println("Deleting first release file: " + firstFile);
              Files.delete(Paths.get(firstFile));
  
  
              long firstIndex3 = queue.firstIndex();
              Assert.assertEquals(firstIndex, firstIndex3);
  
              // and create a tailer it should only read
              //data in second file
              ExcerptTailer excerptTailer2 = queue.createTailer();
              System.out.println("index before reading 5: " + excerptTailer2.index());
  
              //AFTER CREATING A BRAND NEW TAILER, BELOW ASSERTION ALSO FAILS
              //WAS EXPECTING THAT TAILER CAN READ FROM START OF QUEUE BUT INDEX IS LONG.MAX
  
              //Assert.assertEquals(indexAfter5Records -5, excerptTailer2.index() -1);
  
              //BELOW THROWS NPE, WAS EXPECTING THAT WE CAN READ FROM SECOND DAILY QUEUE FILE
              System.out.println("excerptTailer2: " + excerptTailer2.peekDocument());
              for (int i = 0; i &lt; 5; i++) {
                  Assert.assertEquals("test2", excerptTailer2.readText());
              }
  
              //SAME ERROR WHEN CREATING A BRAND NEW QUEUE AND TRYING TO READ IT
  
  //            // create brand new queue and read see how it behaves
  //            ChronicleQueue queue2 =  SingleChronicleQueueBuilder.binary(tempQueueDir + "/" + queueName).
  //                    timeProvider(timeProvider).storeFileListener(listener)
  //                    .build();
  //
  //            long firstIndex2 = queue2.firstIndex();
  //            Assert.assertNotEquals(firstIndex, firstIndex2);
  //            Assert.assertNotEquals(indexAfter5Records, firstIndex2);
  //
  //            ExcerptTailer excerptTailer3 = queue.createTailer();
  //            for (int i = 0; i &lt; 5; i++) {
  //                Assert.assertEquals("test2", excerptTailer3.readText());
  //            }
  
          }
  
      }
  
      final class QueueStoreFileListener implements StoreFileListener {
  
          private String queueName;
          private Map&lt;String, List&gt; queueToRollFilesOnReleaseMap = new HashMap&lt;&gt;();
          private Map&lt;String, List&gt; queueToRollFilesOnAcquireMap = new HashMap&lt;&gt;();
  
          public QueueStoreFileListener(String queueName) {
              this.queueName = queueName;
          }
  
          @Override
          public void onReleased(int cycle, File file) {
              System.out.println("onReleased called cycle: " + cycle + "file: " + file);
  
              List&lt;String&gt; files = queueToRollFilesOnReleaseMap.get(queueName);
              if (files == null) {
                  files = new ArrayList&lt;&gt;();
              }
  
              String fileAbsPath = file.getAbsolutePath();
              if (!files.contains(fileAbsPath)) {
                  files.add(fileAbsPath);
              }
              queueToRollFilesOnReleaseMap.put(queueName, files);
  
              //update acquire file map
              List&lt;String&gt; acqfiles = queueToRollFilesOnAcquireMap.get(queueName);
              acqfiles.remove(file.getAbsolutePath());
              queueToRollFilesOnAcquireMap.put(queueName, acqfiles);
  
          }
  
          @Override
          public void onAcquired(int cycle, File file) {
              System.out.println("onAcquired called cycle: " + cycle + "file: " + file);
  
              List&lt;String&gt; files = queueToRollFilesOnAcquireMap.get(queueName);
              if (files == null) {
                  files = new ArrayList&lt;&gt;();
              }
  
              String fileAbsPath = file.getAbsolutePath();
              if (!files.contains(fileAbsPath)) {
                  files.add(fileAbsPath);
              }
  
              queueToRollFilesOnAcquireMap.put(queueName, files);
  
  
          }
  
          public Map&lt;String, List&gt; getQueueToRollFilesOnAcquireMap() {
              return queueToRollFilesOnAcquireMap;
          }
  
          public Map&lt;String, List&gt; getQueueToRollFilesOnReleaseMap() {
              return queueToRollFilesOnReleaseMap;
          }
      }
  }
  </code></pre>