geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kl...@apache.org
Subject [2/2] incubator-geode git commit: GEODE-2012: always write stat types to archive
Date Tue, 01 Nov 2016 17:48:24 GMT
GEODE-2012: always write stat types to archive

* refactor classes to make testing easier
* write additional tests for stat archive rolling
* expose bug GEODE-2012 in StatTypesAreRolledOverRegressionTest
* fix bug exposed by StatTypesAreRolledOverRegressionTest


Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/3bdd1049
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/3bdd1049
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/3bdd1049

Branch: refs/heads/develop
Commit: 3bdd104972489000edf5592b497d22b89c6ea307
Parents: 820f33e
Author: Kirk Lund <klund@apache.org>
Authored: Mon Oct 31 13:36:53 2016 -0700
Committer: Kirk Lund <klund@apache.org>
Committed: Tue Nov 1 10:43:09 2016 -0700

----------------------------------------------------------------------
 .../org/apache/geode/internal/NanoTimer.java    |   6 +-
 .../geode/internal/i18n/LocalizedStrings.java   |   2 +-
 .../io/MainWithChildrenRollingFileHandler.java  | 243 ++++++++++++++
 .../geode/internal/io/RollingFileHandler.java   |  36 +++
 .../internal/logging/ManagerLogWriter.java      | 322 +++----------------
 .../internal/statistics/HostStatSampler.java    |  33 +-
 .../internal/statistics/SampleCollector.java    |  23 +-
 .../internal/statistics/SimpleStatSampler.java  |   7 +-
 .../internal/statistics/StatArchiveHandler.java |  99 +++---
 .../internal/statistics/StatArchiveWriter.java  |  20 +-
 .../internal/statistics/StatMonitorHandler.java |  39 ++-
 .../concurrent/StoppableCountDownLatch.java     |   8 +-
 .../DiskSpaceLimitIntegrationTest.java          | 201 ++++++++++++
 .../FileSizeLimitIntegrationTest.java           | 170 ++++++++++
 .../statistics/SampleCollectorTest.java         |   4 +-
 .../statistics/StatMonitorHandlerTest.java      |  70 +---
 ...itorHandlerWithEnabledMonitorThreadTest.java | 140 ++++++++
 .../StatTypesAreRolledOverRegressionTest.java   | 205 ++++++++++++
 .../statistics/StatisticsMonitorTest.java       |   4 +-
 .../statistics/ValueMonitorIntegrationTest.java |  40 +--
 20 files changed, 1217 insertions(+), 455 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/NanoTimer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/NanoTimer.java b/geode-core/src/main/java/org/apache/geode/internal/NanoTimer.java
index 12e91f8..247f9a9 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/NanoTimer.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/NanoTimer.java
@@ -38,7 +38,7 @@ package org.apache.geode.internal;
  * </pre>
  * 
  */
-public final class NanoTimer {
+public class NanoTimer {
 
   public static final long NANOS_PER_MILLISECOND = 1000000;
 
@@ -73,7 +73,7 @@ public final class NanoTimer {
   /**
    * For unit testing
    */
-  NanoTimer(TimeService ts) {
+  protected NanoTimer(TimeService ts) {
     this.timeService = ts;
     this.lastResetTime = ts.getTime();
     this.constructionTime = this.lastResetTime;
@@ -164,7 +164,7 @@ public final class NanoTimer {
   /**
    * Allows unit tests to insert a deterministic clock for testing.
    */
-  interface TimeService {
+  public interface TimeService {
     /**
      * Returns the current time.
      */

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java b/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
index 210539b..7638cb3 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/i18n/LocalizedStrings.java
@@ -1265,7 +1265,7 @@ public class LocalizedStrings {
           "Could not free space in {0} directory.  The space used is {1} which exceeds the configured limit of {2}.");
 
   public static final StringId ManagerLogWriter_DELETED_INACTIVE__0___1_ =
-      new StringId(1797, "Deleted inactive  {0}  \"{1}\".");
+      new StringId(1797, "Deleted inactive {0} \"{1}\".");
   public static final StringId ManagerLogWriter_SWITCHING_TO_LOG__0 =
       new StringId(1798, "Switching to log {0}");
 

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/io/MainWithChildrenRollingFileHandler.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/io/MainWithChildrenRollingFileHandler.java b/geode-core/src/main/java/org/apache/geode/internal/io/MainWithChildrenRollingFileHandler.java
new file mode 100644
index 0000000..20d1c4f
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/io/MainWithChildrenRollingFileHandler.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.io;
+
+import org.apache.geode.i18n.LogWriterI18n;
+import org.apache.geode.internal.FileUtil;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.regex.Pattern;
+
+/**
+ * Extracted from ManagerLogWriter. MainWithChildrenRollingFileHandler is used for both rolling of
+ * log files and stat archive files.
+ */
+public class MainWithChildrenRollingFileHandler implements RollingFileHandler {
+
+  private static final Pattern MAIN_ID_PATTERN = Pattern.compile(".+-\\d+-\\d+\\..+");
+  private static final Pattern META_ID_PATTERN = Pattern.compile("meta-.+-\\d+\\..+");
+  private static final Pattern CHILD_ID_PATTERN = Pattern.compile(".+-\\d+-\\d+\\..+");
+
+  public MainWithChildrenRollingFileHandler() {}
+
+  @Override
+  public int calcNextChildId(final File file, final int mainId) {
+    int result = 0;
+    File dir = getParentFile(file.getAbsoluteFile());
+    int endIdx1 = file.getName().indexOf('-');
+    int endIdx2 = file.getName().lastIndexOf('.');
+    String baseName = file.getName();
+    if (endIdx1 != -1) {
+      baseName = file.getName().substring(0, endIdx1);
+    } else {
+      baseName = file.getName().substring(0, endIdx2);
+    }
+    File[] children = findChildren(dir, CHILD_ID_PATTERN);
+
+    /* Search child logs */
+
+    for (File child : children) {
+      String name = child.getName();
+      // only compare the child id among the same set of files.
+      if (!name.startsWith(baseName)) {
+        continue;
+      }
+      int endIdIdx = name.lastIndexOf('-');
+      int startIdIdx = name.lastIndexOf('-', endIdIdx - 1);
+      String id = name.substring(startIdIdx + 1, endIdIdx);
+
+      int startChild = name.lastIndexOf("-");
+      int endChild = name.lastIndexOf(".");
+      if (startChild > 0 && endChild > 0) {
+        String childId = name.substring(startChild + 1, endChild);
+
+        try {
+          int mainLogId = Integer.parseInt(id);
+          int childLogId = Integer.parseInt(childId);
+          if (mainLogId == mainId && childLogId > result) {
+            result = childLogId;
+          }
+        } catch (NumberFormatException ignore) {
+        }
+      }
+    }
+    result++;
+    return result;
+  }
+
+  @Override
+  public int calcNextMainId(final File dir, final boolean toCreateNew) {
+    int result = 0;
+    File[] children = findChildren(dir, MAIN_ID_PATTERN);
+
+    /* Search child logs */
+    for (File child : children) {
+      String name = child.getName();
+      int endIdIdx = name.lastIndexOf('-');
+      int startIdIdx = name.lastIndexOf('-', endIdIdx - 1);
+      String id = name.substring(startIdIdx + 1, endIdIdx);
+      try {
+        int mid = Integer.parseInt(id);
+        if (mid > result) {
+          result = mid;
+        }
+      } catch (NumberFormatException ignore) {
+      }
+    }
+
+    /* And search meta logs */
+    if (toCreateNew) {
+      File[] metaFiles = findChildren(dir, META_ID_PATTERN);
+      for (File metaFile : metaFiles) {
+        String name = metaFile.getName();
+        int endIdIdx = name.lastIndexOf('.');
+        int startIdIdx = name.lastIndexOf('-', endIdIdx - 1);
+        String id = name.substring(startIdIdx + 1, endIdIdx);
+        try {
+          int mid = Integer.parseInt(id);
+          if (mid > result) {
+            result = mid;
+          }
+        } catch (NumberFormatException ignore) {
+        }
+      }
+      result++;
+    }
+
+    return result;
+  }
+
+  @Override
+  public void checkDiskSpace(final String type, final File newFile, final long spaceLimit,
+      final File dir, final LogWriterI18n logger) {
+    checkDiskSpace(type, newFile, spaceLimit, dir, getFilePattern(newFile.getName()), logger);
+  }
+
+  private void checkDiskSpace(final String type, final File newFile, final long spaceLimit,
+      final File dir, final Pattern pattern, final LogWriterI18n logger) {
+    if (spaceLimit == 0 || pattern == null) {
+      return;
+    }
+    File[] children = findChildrenExcept(dir, pattern, newFile);
+    if (children == null) {
+      if (dir.isDirectory()) {
+        logger.warning(
+            LocalizedStrings.ManagerLogWriter_COULD_NOT_CHECK_DISK_SPACE_ON_0_BECAUSE_JAVAIOFILELISTFILES_RETURNED_NULL_THIS_COULD_BE_CAUSED_BY_A_LACK_OF_FILE_DESCRIPTORS,
+            dir);
+      }
+      return;
+    }
+    Arrays.sort(children, new Comparator() {
+      @Override
+      public int compare(Object o1, Object o2) {
+        File f1 = (File) o1;
+        File f2 = (File) o2;
+        long diff = f1.lastModified() - f2.lastModified();
+        if (diff < 0) {
+          return -1;
+        } else if (diff > 0) {
+          return 1;
+        } else {
+          return 0;
+        }
+      }
+    });
+    long spaceUsed = 0;
+    for (File child : children) {
+      spaceUsed += child.length();
+    }
+    int idx = 0;
+    while (spaceUsed >= spaceLimit && idx < children.length) { // check array index to 37388
+      long childSize = children[idx].length();
+      if (delete(children[idx])) {
+        spaceUsed -= childSize;
+        logger.info(LocalizedStrings.ManagerLogWriter_DELETED_INACTIVE__0___1_,
+            new Object[] {type, children[idx]});
+      } else {
+        logger.warning(LocalizedStrings.ManagerLogWriter_COULD_NOT_DELETE_INACTIVE__0___1_,
+            new Object[] {type, children[idx]});
+      }
+      idx++;
+    }
+    if (spaceUsed > spaceLimit) {
+      logger.warning(
+          LocalizedStrings.ManagerLogWriter_COULD_NOT_FREE_SPACE_IN_0_DIRECTORY_THE_SPACE_USED_IS_1_WHICH_EXCEEDS_THE_CONFIGURED_LIMIT_OF_2,
+          new Object[] {type, Long.valueOf(spaceUsed), Long.valueOf(spaceLimit)});
+    }
+  }
+
+  protected boolean delete(final File file) {
+    return file.delete();
+  }
+
+  @Override
+  public String formatId(final int id) {
+    StringBuffer result = new StringBuffer(10);
+    result.append('-');
+    if (id < 10) {
+      result.append('0');
+    }
+    result.append(id);
+    return result.toString();
+  }
+
+  @Override
+  public File getParentFile(final File file) {
+    File tmp = file.getAbsoluteFile().getParentFile();
+    if (tmp == null) {
+      tmp = new File("."); // as a fix for bug #41474 we use "." if getParentFile returns null
+    }
+    return tmp;
+  }
+
+  private Pattern getFilePattern(String name) {
+    int extIdx = name.lastIndexOf('.');
+    String ext = "";
+    if (extIdx != -1) {
+      ext = "\\Q" + name.substring(extIdx) + "\\E";
+      name = name.substring(0, extIdx);
+    }
+    name = "\\Q" + name + "\\E" + "-\\d+-\\d+" + ext;
+    return Pattern.compile(name);
+  }
+
+  private File[] findChildren(final File dir, final Pattern pattern) {
+    return FileUtil.listFiles(dir, new FilenameFilter() {
+      @Override
+      public boolean accept(File dir, String name) {
+        return pattern.matcher(name).matches();
+      }
+    });
+  }
+
+  private File[] findChildrenExcept(final File dir, final Pattern pattern, final File exception) {
+    final String exceptionName = (exception == null) ? null : exception.getName();
+    return FileUtil.listFiles(dir, new FilenameFilter() {
+      @Override
+      public boolean accept(File dir, String name) {
+        if (name.equals(exceptionName)) {
+          return false;
+        } else {
+          return pattern.matcher(name).matches();
+        }
+      }
+    });
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/io/RollingFileHandler.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/io/RollingFileHandler.java b/geode-core/src/main/java/org/apache/geode/internal/io/RollingFileHandler.java
new file mode 100644
index 0000000..288ca39
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/io/RollingFileHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.io;
+
+import org.apache.geode.i18n.LogWriterI18n;
+
+import java.io.File;
+
+/**
+ * Defines the constants and methods for rolling files (logs and stat archives).
+ */
+public interface RollingFileHandler {
+
+  int calcNextMainId(final File dir, final boolean toCreateNew);
+
+  int calcNextChildId(final File file, final int mainId);
+
+  File getParentFile(final File file);
+
+  String formatId(final int id);
+
+  void checkDiskSpace(final String type, final File newFile, final long spaceLimit, final File dir,
+      final LogWriterI18n logger);
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java b/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java
index a000d2c..4b333ed 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/logging/ManagerLogWriter.java
@@ -15,22 +15,24 @@
 package org.apache.geode.internal.logging;
 
 import org.apache.geode.distributed.internal.DistributionConfig;
-import org.apache.geode.distributed.internal.DistributionManager;
 import org.apache.geode.i18n.LogWriterI18n;
-import org.apache.geode.internal.FileUtil;
 import org.apache.geode.internal.OSProcess;
 import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.io.MainWithChildrenRollingFileHandler;
+import org.apache.geode.internal.io.RollingFileHandler;
 import org.apache.geode.internal.logging.log4j.AlertAppender;
 import org.apache.geode.internal.util.LogFileUtils;
 
-import java.io.*;
-import java.util.Arrays;
-import java.util.Comparator;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
 import java.util.Date;
-import java.util.regex.Pattern;
 
 /**
- * Implementation of {@link LogWriterI18n} for distributed system members. Its just like
+ * Implementation of {@link LogWriterI18n} for distributed system members. It's just like
  * {@link LocalLogWriter} except it has support for rolling and alerts.
  *
  * @since Geode 1.0
@@ -42,10 +44,27 @@ public class ManagerLogWriter extends LocalLogWriter {
 
   private final boolean fileSizeLimitInKB;
 
+  private final RollingFileHandler rollingFileHandler;
+
   private LogConfig cfg = null;
-  private DistributionManager dm = null;
 
-  // Constructors
+  private LocalLogWriter mainLogger = this;
+
+  private File logDir = null;
+  private int mainLogId = -1;
+  private int childId = 0;
+  private boolean useChildLogging = false;
+
+  /**
+   * Set to true when roll is in progress
+   */
+  private boolean rolling = false;
+  private boolean mainLog = true;
+
+  private File activeLogFile = null;
+
+  private boolean started = false;
+
   /**
    * Creates a writer that logs to <code>System.out</code>.
    * 
@@ -69,16 +88,7 @@ public class ManagerLogWriter extends LocalLogWriter {
   public ManagerLogWriter(int level, PrintStream out, String connectionName) {
     super(level, out, connectionName);
     this.fileSizeLimitInKB = Boolean.getBoolean(TEST_FILE_SIZE_LIMIT_IN_KB_PROPERTY);
-  }
-
-  private LocalLogWriter mainLogger = this;
-
-  /**
-   * Gets the logger that writes to the main log file. This logger may differ from the current
-   * logger when rolling logs are used.
-   */
-  public LogWriterI18n getMainLogger() {
-    return this.mainLogger;
+    this.rollingFileHandler = new MainWithChildrenRollingFileHandler();
   }
 
   /**
@@ -98,13 +108,12 @@ public class ManagerLogWriter extends LocalLogWriter {
         && cfg.getLogFileSizeLimit() != 0;
 
     if (useChildLogging()) {
-      childLogPattern = getLogPattern(this.cfg.getLogFile().getName());
-      logDir = getParentFile(this.cfg.getLogFile());
+      logDir = rollingFileHandler.getParentFile(this.cfg.getLogFile());
       // let archive id always follow main log id, not the vice versa
       // e.g. in getArchiveName(), it's using mainArchiveId = calcNextMainId(archiveDir);
       // This is the only place we assign mainLogId.
       // mainLogId is only referenced when useChildLogging==true
-      mainLogId = calcNextMainId(logDir, true);
+      mainLogId = rollingFileHandler.calcNextMainId(logDir, true);
     }
     if (started) {
       if (useChildLogging()) {
@@ -121,12 +130,6 @@ public class ManagerLogWriter extends LocalLogWriter {
     return this.activeLogFile;
   }
 
-  private Pattern childLogPattern = null;
-  private File logDir = null;
-  private int mainLogId = -1;
-  private int childId = 0;
-
-
   public File getLogDir() {
     return this.logDir;
   }
@@ -135,16 +138,6 @@ public class ManagerLogWriter extends LocalLogWriter {
     return this.mainLogId;
   }
 
-  public static String formatId(int id) {
-    StringBuffer result = new StringBuffer(10);
-    result.append('-');
-    if (id < 10) {
-      result.append('0');
-    }
-    result.append(id);
-    return result.toString();
-  }
-
   private File getNextChildLogFile() {
     String path = this.cfg.getLogFile().getPath();
     int extIdx = path.lastIndexOf('.');
@@ -153,7 +146,8 @@ public class ManagerLogWriter extends LocalLogWriter {
       ext = path.substring(extIdx);
       path = path.substring(0, extIdx);
     }
-    path = path + formatId(mainLogId) + formatId(this.childId) + ext;
+    path = path + rollingFileHandler.formatId(mainLogId) + rollingFileHandler.formatId(this.childId)
+        + ext;
     this.childId++;
     File result = new File(path);
     if (result.exists()) {
@@ -164,18 +158,10 @@ public class ManagerLogWriter extends LocalLogWriter {
     }
   }
 
-  private boolean useChildLogging = false;
-
   public boolean useChildLogging() {
     return this.useChildLogging;
   }
 
-  /**
-   * Set to true when roll is in progress
-   */
-  private boolean rolling = false;
-  private boolean mainLog = true;
-
   private long getLogFileSizeLimit() {
     if (rolling || mainLog) {
       return Long.MAX_VALUE;
@@ -198,8 +184,7 @@ public class ManagerLogWriter extends LocalLogWriter {
     return result * (1024 * 1024);
   }
 
-
-  private static String getMetaLogFileName(String baseLogFileName, int mainLogId) {
+  private String getMetaLogFileName(String baseLogFileName, int mainLogId) {
     String metaLogFile = null;
     int extIdx = baseLogFileName.lastIndexOf('.');
     String ext = "";
@@ -210,15 +195,13 @@ public class ManagerLogWriter extends LocalLogWriter {
     String fileName = new File(metaLogFile).getName();
     String parent = new File(metaLogFile).getParent();
 
-    metaLogFile = "meta-" + fileName + formatId(mainLogId) + ext;
+    metaLogFile = "meta-" + fileName + rollingFileHandler.formatId(mainLogId) + ext;
     if (parent != null) {
       metaLogFile = parent + File.separator + metaLogFile;
     }
     return metaLogFile;
   }
 
-  private File activeLogFile = null;
-
   private synchronized void switchLogs(File newLog, boolean newIsMain) {
     rolling = true;
     try {
@@ -258,7 +241,7 @@ public class ManagerLogWriter extends LocalLogWriter {
               // For windows to work we need to redirect everything
               // to a temporary file so we can get oldFile closed down
               // so we can rename it. We don't actually write to this tmp file
-              File tmpLogDir = getParentFile(this.cfg.getLogFile());
+              File tmpLogDir = rollingFileHandler.getParentFile(this.cfg.getLogFile());
               tmpFile = File.createTempFile("mlw", null, tmpLogDir);
               // close the old guy down before we do the rename
               PrintStream tmpps = OSProcess.redirectOutput(tmpFile,
@@ -326,25 +309,14 @@ public class ManagerLogWriter extends LocalLogWriter {
     }
   }
 
-  /**
-   * as a fix for bug #41474 we use "." if getParentFile returns null
-   */
-  private static File getParentFile(File f) {
-    File tmp = f.getAbsoluteFile().getParentFile();
-    if (tmp == null) {
-      tmp = new File(".");
-    }
-    return tmp;
-  }
-
-
   public static File getLogNameForOldMainLog(File log, boolean useOldFile) {
     /*
      * this is just searching for the existing logfile name we need to search for meta log file name
      * 
      */
-    File dir = getParentFile(log.getAbsoluteFile());
-    int previousMainId = calcNextMainId(dir, true);
+    RollingFileHandler rollingFileHandler = new MainWithChildrenRollingFileHandler();
+    File dir = rollingFileHandler.getParentFile(log.getAbsoluteFile());
+    int previousMainId = rollingFileHandler.calcNextMainId(dir, true);
     if (useOldFile) {
       if (previousMainId > 0) {
         previousMainId--;
@@ -354,225 +326,23 @@ public class ManagerLogWriter extends LocalLogWriter {
       previousMainId = 1;
     }
 
-    // comment out the following to fix bug 31789
-    // if (previousMainId > 1) {
-    // previousMainId--;
-    // }
     File result = null;
-    int childId = calcNextChildId(log, previousMainId > 0 ? previousMainId : 0);
+    int childId = rollingFileHandler.calcNextChildId(log, previousMainId > 0 ? previousMainId : 0);
     StringBuffer buf = new StringBuffer(log.getPath());
     int insertIdx = buf.lastIndexOf(".");
     if (insertIdx == -1) {
-      buf.append(formatId(previousMainId)).append(formatId(childId));
+      buf.append(rollingFileHandler.formatId(previousMainId))
+          .append(rollingFileHandler.formatId(childId));
     } else {
-      buf.insert(insertIdx, formatId(childId));
-      buf.insert(insertIdx, formatId(previousMainId));
+      buf.insert(insertIdx, rollingFileHandler.formatId(childId));
+      buf.insert(insertIdx, rollingFileHandler.formatId(previousMainId));
     }
     result = new File(buf.toString());
     return result;
   }
 
-
-  private static Pattern getLogPattern(String name) {
-    int extIdx = name.lastIndexOf('.');
-    String ext = "";
-    if (extIdx != -1) {
-      ext = "\\Q" + name.substring(extIdx) + "\\E";
-      name = name.substring(0, extIdx);
-    }
-    name = "\\Q" + name + "\\E" + "-\\d+-\\d+" + ext;
-    return Pattern.compile(name);
-  }
-
-  protected static final Pattern mainIdPattern = Pattern.compile(".+-\\d+-\\d+\\..+");
-  protected static final Pattern metaIdPattern = Pattern.compile("meta-.+-\\d+\\..+");
-
-  public static int calcNextMainId(File dir, boolean toCreateNew) {
-    int result = 0;
-    File[] childLogs = FileUtil.listFiles(dir, new FilenameFilter() {
-      public boolean accept(File d, String name) {
-        return mainIdPattern.matcher(name).matches();
-      }
-    });
-
-    /* Search child logs */
-    for (File childLog : childLogs) {
-      String name = childLog.getName();
-      int endIdIdx = name.lastIndexOf('-');
-      int startIdIdx = name.lastIndexOf('-', endIdIdx - 1);
-      String id = name.substring(startIdIdx + 1, endIdIdx);
-      try {
-        int mid = Integer.parseInt(id);
-        if (mid > result) {
-          result = mid;
-        }
-      } catch (NumberFormatException ignore) {
-      }
-    }
-
-    /* And search meta logs */
-    if (toCreateNew) {
-      File[] metaLogs = FileUtil.listFiles(dir, new FilenameFilter() {
-        public boolean accept(File d, String name) {
-          return metaIdPattern.matcher(name).matches();
-        }
-      });
-      for (File metaLog : metaLogs) {
-        String name = metaLog.getName();
-        int endIdIdx = name.lastIndexOf('.');
-        int startIdIdx = name.lastIndexOf('-', endIdIdx - 1);
-        String id = name.substring(startIdIdx + 1, endIdIdx);
-        try {
-          int mid = Integer.parseInt(id);
-          if (mid > result) {
-            result = mid;
-          }
-        } catch (NumberFormatException ignore) {
-        }
-      }
-      result++;
-    }
-
-    return result;
-  }
-
-  protected static final Pattern childIdPattern = Pattern.compile(".+-\\d+-\\d+\\..+");
-
-
-  public static int calcNextChildId(File log, int mainId) {
-    int result = 0;
-    File dir = getParentFile(log.getAbsoluteFile());
-    int endidx1 = log.getName().indexOf('-');
-    int endidx2 = log.getName().lastIndexOf('.');
-    String baseName = log.getName();
-    if (endidx1 != -1) {
-      baseName = log.getName().substring(0, endidx1);
-    } else {
-      baseName = log.getName().substring(0, endidx2);
-    }
-    File[] childLogs = FileUtil.listFiles(dir, new FilenameFilter() {
-      public boolean accept(File d, String name) {
-        return childIdPattern.matcher(name).matches();
-      }
-    });
-
-    /* Search child logs */
-
-    for (File childLog : childLogs) {
-      String name = childLog.getName();
-      // only compare the childlogid among the same set of log files.
-      if (!name.startsWith(baseName)) {
-        continue;
-      }
-      int endIdIdx = name.lastIndexOf('-');
-      int startIdIdx = name.lastIndexOf('-', endIdIdx - 1);
-      String id = name.substring(startIdIdx + 1, endIdIdx);
-
-      int startChild = name.lastIndexOf("-");
-      int endChild = name.lastIndexOf(".");
-      if (startChild > 0 && endChild > 0) {
-        String childId = name.substring(startChild + 1, endChild);
-
-        try {
-          int mainLogId = Integer.parseInt(id);
-          int childLogId = Integer.parseInt(childId);
-          if (mainLogId == mainId && childLogId > result) {
-            result = childLogId;
-          }
-        } catch (NumberFormatException ignore) {
-        }
-      }
-    }
-    result++;
-    return result;
-  }
-
-
-
-  // private static void debugLog(String msg, boolean stackDump) {
-  // try {
-  // FileOutputStream f = new FileOutputStream("debug.log", true);
-  // LogWriterI18n lw = new LocalLogWriter(ALL_LEVEL, new PrintStream(f));
-  // if (stackDump) {
-  // lw.info(msg, new RuntimeException("STACK"));
-  // } else {
-  // lw.info(msg);
-  // }
-  // f.close();
-  // } catch (IOException ignore) {
-  // }
-  // }
-
-  public static void removeOldLogs(LogConfig cfg, File logFile) {
-    LogWriterI18n log = new LocalLogWriter(INFO_LEVEL, System.err);
-    checkDiskSpace("log", null, ((long) cfg.getLogDiskSpaceLimit()) * (1024 * 1024),
-        getParentFile(logFile), getLogPattern(logFile.getName()), log);
-  }
-
-  public static void checkDiskSpace(String type, File newLog, long spaceLimit, File dir,
-      final Pattern logPattern, LogWriterI18n logger) {
-    if (spaceLimit == 0 || logPattern == null) {
-      return;
-    }
-    final String newLogName = (newLog == null) ? null : newLog.getName();
-    File[] childLogs = FileUtil.listFiles(dir, new FilenameFilter() {
-      public boolean accept(File d, String name) {
-        if (name.equals(newLogName)) {
-          return false;
-        } else {
-          boolean result = logPattern.matcher(name).matches();
-          return result;
-        }
-      }
-    });
-    if (childLogs == null) {
-      if (dir.isDirectory()) {
-        logger.warning(
-            LocalizedStrings.ManagerLogWriter_COULD_NOT_CHECK_DISK_SPACE_ON_0_BECAUSE_JAVAIOFILELISTFILES_RETURNED_NULL_THIS_COULD_BE_CAUSED_BY_A_LACK_OF_FILE_DESCRIPTORS,
-            dir);
-      }
-      return;
-    }
-    Arrays.sort(childLogs, new Comparator() {
-      public int compare(Object o1, Object o2) {
-        File f1 = (File) o1;
-        File f2 = (File) o2;
-        long diff = f1.lastModified() - f2.lastModified();
-        if (diff < 0) {
-          return -1;
-        } else if (diff > 0) {
-          return 1;
-        } else {
-          return 0;
-        }
-      }
-    });
-    long spaceUsed = 0;
-    for (File childLog : childLogs) {
-      spaceUsed += childLog.length();
-    }
-    int fIdx = 0;
-    while (spaceUsed >= spaceLimit && fIdx < childLogs.length) { // check array index to 37388
-      long childSize = childLogs[fIdx].length();
-      if (childLogs[fIdx].delete()) {
-        spaceUsed -= childSize;
-        logger.info(LocalizedStrings.ManagerLogWriter_DELETED_INACTIVE__0___1_,
-            new Object[] {type, childLogs[fIdx]});
-      } else {
-        logger.warning(LocalizedStrings.ManagerLogWriter_COULD_NOT_DELETE_INACTIVE__0___1_,
-            new Object[] {type, childLogs[fIdx]});
-      }
-      fIdx++;
-    }
-    if (spaceUsed > spaceLimit) {
-      logger.warning(
-          LocalizedStrings.ManagerLogWriter_COULD_NOT_FREE_SPACE_IN_0_DIRECTORY_THE_SPACE_USED_IS_1_WHICH_EXCEEDS_THE_CONFIGURED_LIMIT_OF_2,
-          new Object[] {type, Long.valueOf(spaceUsed), Long.valueOf(spaceLimit)});
-    }
-  }
-
   private void checkDiskSpace(File newLog) {
-    checkDiskSpace("log", newLog, getLogDiskSpaceLimit(), logDir, childLogPattern, mainLogger);
+    rollingFileHandler.checkDiskSpace("log", newLog, getLogDiskSpaceLimit(), logDir, mainLogger);
   }
 
   public void rollLog() {
@@ -596,8 +366,6 @@ public class ManagerLogWriter extends LocalLogWriter {
     }
   }
 
-  private boolean started = false;
-
   /**
    * Called when manager is done starting up. This is when a child log will be started if rolling is
    * configured.

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/statistics/HostStatSampler.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/statistics/HostStatSampler.java b/geode-core/src/main/java/org/apache/geode/internal/statistics/HostStatSampler.java
index 6d7b967..c3ed946 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/statistics/HostStatSampler.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/statistics/HostStatSampler.java
@@ -20,6 +20,7 @@ import org.apache.geode.Statistics;
 import org.apache.geode.SystemFailure;
 import org.apache.geode.distributed.internal.DistributionConfig;
 import org.apache.geode.internal.NanoTimer;
+import org.apache.geode.internal.io.MainWithChildrenRollingFileHandler;
 import org.apache.geode.internal.net.SocketCreator;
 import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.internal.logging.LogService;
@@ -89,12 +90,20 @@ public abstract class HostStatSampler
 
   private final CallbackSampler callbackSampler;
 
+  private final NanoTimer timer;
+
   protected HostStatSampler(CancelCriterion stopper, StatSamplerStats samplerStats) {
+    this(stopper, samplerStats, new NanoTimer());
+  }
+
+  protected HostStatSampler(CancelCriterion stopper, StatSamplerStats samplerStats,
+      NanoTimer timer) {
     this.stopper = stopper;
     this.statSamplerInitializedLatch = new StoppableCountDownLatch(this.stopper, 1);
     this.samplerStats = samplerStats;
     this.fileSizeLimitInKB = Boolean.getBoolean(TEST_FILE_SIZE_LIMIT_IN_KB_PROPERTY);
     this.callbackSampler = new CallbackSampler(stopper, samplerStats);
+    this.timer = timer;
   }
 
   public final StatSamplerStats getStatSamplerStats() {
@@ -171,8 +180,6 @@ public abstract class HostStatSampler
    */
   @Override
   public final void run() {
-    NanoTimer timer = new NanoTimer();
-
     final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS);
     if (isDebugEnabled_STATISTICS) {
       logger.trace(LogMarker.STATISTICS, "HostStatSampler started");
@@ -182,7 +189,8 @@ public abstract class HostStatSampler
       initSpecialStats();
 
       this.sampleCollector = new SampleCollector(this);
-      this.sampleCollector.initialize(this, NanoTimer.getTime());
+      this.sampleCollector.initialize(this, timer.getTime(),
+          new MainWithChildrenRollingFileHandler());
 
       this.statSamplerInitializedLatch.countDown();
       latchCountedDown = true;
@@ -197,7 +205,7 @@ public abstract class HostStatSampler
         }
         final long nanosBeforeSleep = timer.getLastResetTime();
         final long nanosToDelay = nanosLastTimeStamp + getNanoRate();
-        delay(timer, nanosToDelay);
+        delay(nanosToDelay);
         nanosLastTimeStamp = timer.getLastResetTime();
         if (!stopRequested() && isSamplingEnabled()) {
           final long nanosTimeStamp = timer.getLastResetTime();
@@ -371,11 +379,21 @@ public abstract class HostStatSampler
    * @since GemFire 7.0
    */
   public final boolean waitForInitialization(long ms) throws InterruptedException {
-    return this.statSamplerInitializedLatch.await(ms);
+    return awaitInitialization(ms, TimeUnit.MILLISECONDS);
+  }
+
+  /**
+   * Awaits the initialization of special statistics.
+   *
+   * @see #initSpecialStats
+   */
+  public final boolean awaitInitialization(final long timeout, final TimeUnit unit)
+      throws InterruptedException {
+    return this.statSamplerInitializedLatch.await(timeout, unit);
   }
 
   public final void changeArchive(File newFile) {
-    this.sampleCollector.changeArchive(newFile, NanoTimer.getTime());
+    this.sampleCollector.changeArchive(newFile, timer.getTime());
   }
 
   /**
@@ -476,10 +494,9 @@ public abstract class HostStatSampler
   }
 
   /**
-   * @param timer a NanoTimer used to compute the elapsed delay
    * @param nanosToDelay the timestamp to delay until it is the current time
    */
-  private void delay(NanoTimer timer, final long nanosToDelay) throws InterruptedException {
+  private void delay(final long nanosToDelay) throws InterruptedException {
     timer.reset();
     long now = timer.getLastResetTime();
     long remainingNanos = nanosToDelay - now;

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/statistics/SampleCollector.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/statistics/SampleCollector.java b/geode-core/src/main/java/org/apache/geode/internal/statistics/SampleCollector.java
index 610e848..9f1b343 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/statistics/SampleCollector.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/statistics/SampleCollector.java
@@ -24,6 +24,9 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import edu.umd.cs.findbugs.annotations.SuppressWarnings;
+
+import org.apache.geode.internal.io.RollingFileHandler;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.GemFireException;
@@ -136,15 +139,17 @@ public class SampleCollector {
    * 
    * @param config defines the configuration for the StatArchiveHandler
    * @param nanosTimeStamp the nanos time stamp to initialize stat archiver with
+   * @param rollingFileHandler provides file rolling behavior
    */
-  @edu.umd.cs.findbugs.annotations.SuppressWarnings(
-      value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD",
+  @SuppressWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD",
       justification = "There is never more than one SampleCollector instance.")
-  public void initialize(StatArchiveHandlerConfig config, long nanosTimeStamp) {
+  public void initialize(final StatArchiveHandlerConfig config, final long nanosTimeStamp,
+      final RollingFileHandler rollingFileHandler) {
     synchronized (SampleCollector.class) {
       instance = this;
       synchronized (this.sampleHandlers) {
-        StatArchiveHandler newStatArchiveHandler = new StatArchiveHandler(config, this);
+        StatArchiveHandler newStatArchiveHandler =
+            new StatArchiveHandler(config, this, rollingFileHandler);
         this.statArchiveHandler = newStatArchiveHandler;
         addSampleHandler(newStatArchiveHandler);
         newStatArchiveHandler.initialize(nanosTimeStamp);
@@ -366,7 +371,8 @@ public class SampleCollector {
     }
 
     // notify unmarked/new handlers but not marked/old handlers
-    notifyNewHandlersOfResources(handlers, this.resourceInstMap.values());
+    notifyNewHandlersOfResources(handlers, this.resourceTypeMap.values(),
+        this.resourceInstMap.values());
   }
 
   private ResourceType getResourceType(List<MarkableSampleHandler> handlers, Statistics statistics)
@@ -489,7 +495,7 @@ public class SampleCollector {
   }
 
   private void notifyNewHandlersOfResources(List<MarkableSampleHandler> handlers,
-      Collection<ResourceInstance> resources) {
+      Collection<ResourceType> types, Collection<ResourceInstance> resources) {
     final boolean isDebugEnabled_STATISTICS = logger.isTraceEnabled(LogMarker.STATISTICS);
     if (isDebugEnabled_STATISTICS) {
       logger.trace(LogMarker.STATISTICS,
@@ -509,6 +515,11 @@ public class SampleCollector {
           // allocatedResourceInstance...
           handler.allocatedResourceInstance(resourceInstance);
         }
+        for (ResourceType resourceType : types) {
+          if (!allocatedResourceTypes.contains(resourceType)) {
+            handler.allocatedResourceType(resourceType);
+          }
+        }
         handler.mark();
         count++;
       }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/statistics/SimpleStatSampler.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/statistics/SimpleStatSampler.java b/geode-core/src/main/java/org/apache/geode/internal/statistics/SimpleStatSampler.java
index 7eaa1e0..1707397 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/statistics/SimpleStatSampler.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/statistics/SimpleStatSampler.java
@@ -19,6 +19,7 @@ import java.io.File;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.CancelCriterion;
+import org.apache.geode.internal.NanoTimer;
 import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.logging.log4j.LocalizedMessage;
@@ -58,7 +59,11 @@ public class SimpleStatSampler extends HostStatSampler {
   private final StatisticsManager sm;
 
   public SimpleStatSampler(CancelCriterion stopper, StatisticsManager sm) {
-    super(stopper, new StatSamplerStats(sm, sm.getId()));
+    this(stopper, sm, new NanoTimer());
+  }
+
+  public SimpleStatSampler(CancelCriterion stopper, StatisticsManager sm, NanoTimer timer) {
+    super(stopper, new StatSamplerStats(sm, sm.getId()), timer);
     this.sm = sm;
     logger.info(LogMarker.STATISTICS, LocalizedMessage
         .create(LocalizedStrings.SimpleStatSampler_STATSSAMPLERATE_0, getSampleRate()));

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveHandler.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveHandler.java b/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveHandler.java
index 5520936..3eb9730 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveHandler.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveHandler.java
@@ -14,27 +14,25 @@
  */
 package org.apache.geode.internal.statistics;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.logging.log4j.Logger;
-
 import org.apache.geode.GemFireException;
 import org.apache.geode.GemFireIOException;
 import org.apache.geode.distributed.internal.InternalDistributedSystem;
 import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.io.RollingFileHandler;
 import org.apache.geode.internal.logging.InternalLogWriter;
 import org.apache.geode.internal.logging.LogService;
-import org.apache.geode.internal.logging.ManagerLogWriter;
 import org.apache.geode.internal.logging.log4j.LocalizedMessage;
 import org.apache.geode.internal.logging.log4j.LogMarker;
 import org.apache.geode.internal.logging.log4j.LogWriterAppender;
 import org.apache.geode.internal.logging.log4j.LogWriterAppenders;
 import org.apache.geode.internal.logging.log4j.LogWriterLogger;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.List;
 
 /**
  * Extracted from {@link HostStatSampler} and {@link GemFireStatSampler}.
@@ -56,6 +54,8 @@ public class StatArchiveHandler implements SampleHandler {
   /** The collector responsible for sample statistics and notifying handlers. */
   private final SampleCollector collector;
 
+  private final RollingFileHandler rollingFileHandler;
+
   /**
    * Indicates if archiving has been disabled by specifying empty string for the archive file name.
    * Other threads may call in to changeArchiveFile to manipulate this flag.
@@ -78,9 +78,11 @@ public class StatArchiveHandler implements SampleHandler {
    * Constructs a new instance. The {@link StatArchiveHandlerConfig} and {@link SampleCollector}
    * must not be null.
    */
-  public StatArchiveHandler(StatArchiveHandlerConfig config, SampleCollector sampleCollector) {
+  public StatArchiveHandler(StatArchiveHandlerConfig config, SampleCollector sampleCollector,
+      RollingFileHandler rollingFileHandler) {
     this.config = config;
     this.collector = sampleCollector;
+    this.rollingFileHandler = rollingFileHandler;
   }
 
   /**
@@ -465,13 +467,13 @@ public class StatArchiveHandler implements SampleHandler {
         if (!archiveDir.exists()) {
           archiveDir.mkdirs();
         }
-        mainArchiveId = ManagerLogWriter.calcNextMainId(archiveDir, false);
+        mainArchiveId = this.rollingFileHandler.calcNextMainId(archiveDir, false);
         mainArchiveIdCalculated = true;
       }
       if (mainArchiveId == 0) {
         mainArchiveId = 1;
       }
-      archiveId = ManagerLogWriter.calcNextChildId(archive, mainArchiveId);
+      archiveId = this.rollingFileHandler.calcNextChildId(archive, mainArchiveId);
       if (archiveId > 0) {
         archiveId--;
       }
@@ -482,11 +484,11 @@ public class StatArchiveHandler implements SampleHandler {
       StringBuffer buf = new StringBuffer(archive.getPath());
       int insertIdx = buf.lastIndexOf(".");
       if (insertIdx == -1) {
-        buf.append(ManagerLogWriter.formatId(mainArchiveId))
-            .append(ManagerLogWriter.formatId(archiveId));
+        buf.append(this.rollingFileHandler.formatId(mainArchiveId))
+            .append(this.rollingFileHandler.formatId(archiveId));
       } else {
-        buf.insert(insertIdx, ManagerLogWriter.formatId(archiveId));
-        buf.insert(insertIdx, ManagerLogWriter.formatId(mainArchiveId));
+        buf.insert(insertIdx, this.rollingFileHandler.formatId(archiveId));
+        buf.insert(insertIdx, this.rollingFileHandler.formatId(mainArchiveId));
       }
       result = new File(buf.toString());
     } while (result.exists());
@@ -499,8 +501,8 @@ public class StatArchiveHandler implements SampleHandler {
         markerName = markerName.substring(0, dotIdx);
       }
       StringBuffer buf = new StringBuffer(markerName);
-      buf.append(ManagerLogWriter.formatId(mainArchiveId)).append(ManagerLogWriter.formatId(0))
-          .append(".marker");
+      buf.append(this.rollingFileHandler.formatId(mainArchiveId))
+          .append(this.rollingFileHandler.formatId(0)).append(".marker");
       File marker = new File(buf.toString());
       if (marker.exists()) {
         if (!marker.delete()) {
@@ -520,8 +522,8 @@ public class StatArchiveHandler implements SampleHandler {
         markerName = markerName.substring(0, dotIdx);
       }
       StringBuffer buf = new StringBuffer(markerName);
-      buf.append(ManagerLogWriter.formatId(mainArchiveId)).append(ManagerLogWriter.formatId(0))
-          .append(".marker");
+      buf.append(this.rollingFileHandler.formatId(mainArchiveId))
+          .append(this.rollingFileHandler.formatId(0)).append(".marker");
       File marker = new File(buf.toString());
       if (!marker.exists()) {
         try {
@@ -555,7 +557,7 @@ public class StatArchiveHandler implements SampleHandler {
       if (!archiveDir.exists()) {
         archiveDir.mkdirs();
       }
-      mainArchiveId = ManagerLogWriter.calcNextMainId(archiveDir, false);
+      mainArchiveId = this.rollingFileHandler.calcNextMainId(archiveDir, false);
       mainArchiveId++;
       mainArchiveIdCalculated = true;
     }
@@ -572,8 +574,8 @@ public class StatArchiveHandler implements SampleHandler {
       markerName = markerName.substring(0, dotIdx);
     }
     StringBuffer buf = new StringBuffer(markerName);
-    buf.append(ManagerLogWriter.formatId(mainArchiveId)).append(ManagerLogWriter.formatId(0))
-        .append(".marker");
+    buf.append(this.rollingFileHandler.formatId(mainArchiveId))
+        .append(this.rollingFileHandler.formatId(0)).append(".marker");
     File marker = new File(buf.toString());
     if (!marker.exists()) {
       try {
@@ -596,9 +598,9 @@ public class StatArchiveHandler implements SampleHandler {
    * @return the modified archive file name to use; it is modified by applying the next main id if
    *         any files in the dir already have a main id in the file name
    */
-  private static File getRenameArchiveName(File archive) {
+  private File getRenameArchiveName(File archive) {
     File dir = archive.getAbsoluteFile().getParentFile();
-    int previousMainId = ManagerLogWriter.calcNextMainId(dir, false);
+    int previousMainId = this.rollingFileHandler.calcNextMainId(dir, false);
     if (previousMainId == 0) {
       previousMainId = 1;
     }
@@ -609,10 +611,11 @@ public class StatArchiveHandler implements SampleHandler {
       StringBuffer buf = new StringBuffer(archive.getPath());
       int insertIdx = buf.lastIndexOf(".");
       if (insertIdx == -1) {
-        buf.append(ManagerLogWriter.formatId(previousMainId)).append(ManagerLogWriter.formatId(1));
+        buf.append(this.rollingFileHandler.formatId(previousMainId))
+            .append(this.rollingFileHandler.formatId(1));
       } else {
-        buf.insert(insertIdx, ManagerLogWriter.formatId(1));
-        buf.insert(insertIdx, ManagerLogWriter.formatId(previousMainId));
+        buf.insert(insertIdx, this.rollingFileHandler.formatId(1));
+        buf.insert(insertIdx, this.rollingFileHandler.formatId(previousMainId));
       }
       result = new File(buf.toString());
     } while (result.exists());
@@ -621,52 +624,26 @@ public class StatArchiveHandler implements SampleHandler {
 
   /**
    * Remove old versions of the specified archive file name in order to stay under the specified
-   * disk space limit. Old versions of the archive file are those that match based on using
-   * {@link #getArchivePattern(String)} which ignores mainArchiveId and archiveId.
+   * disk space limit. Old versions of the archive file are those that match based on using a
+   * pattern which ignores mainArchiveId and archiveId.
    * 
    * @param archiveFile the archive file to remove old versions of
    * @param spaceLimit the disk space limit
    */
-  private static void removeOldArchives(File archiveFile, long spaceLimit) {
+  private void removeOldArchives(File archiveFile, long spaceLimit) {
     if (spaceLimit == 0 || archiveFile == null || archiveFile.getPath().equals("")) {
       return;
     }
     File archiveDir = archiveFile.getAbsoluteFile().getParentFile();
-    ManagerLogWriter.checkDiskSpace("archive", archiveFile, spaceLimit, archiveDir,
-        getArchivePattern(archiveFile.getName()), getOrCreateLogWriter());
+    this.rollingFileHandler.checkDiskSpace("archive", archiveFile, spaceLimit, archiveDir,
+        getOrCreateLogWriter());
   }
 
-  private static InternalLogWriter getOrCreateLogWriter() {
+  private InternalLogWriter getOrCreateLogWriter() {
     InternalLogWriter lw = InternalDistributedSystem.getStaticInternalLogWriter();
     if (lw == null) {
       lw = LogWriterLogger.create(logger);
     }
     return lw;
   }
-
-  /**
-   * Create a regex pattern which will match the specified archive file name even if it has a
-   * mainArchiveId and/or archiveId.
-   * 
-   * @param name archive file name to create a regex pattern for
-   * @return regex pattern to use in finding matching file names
-   */
-  private static Pattern getArchivePattern(String name) {
-    String ext = "";
-
-    int extIdx = name.lastIndexOf('.');
-    if (extIdx != -1) {
-      ext = "\\Q" + name.substring(extIdx) + "\\E";
-      name = name.substring(0, extIdx);
-    }
-
-    /* name may have -DD-DD on the end of it. Trim that part off. */
-    int dashIdx = name.indexOf('-');
-    if (dashIdx != -1) {
-      name = name.substring(0, dashIdx);
-    }
-
-    name = "\\Q" + name + "\\E" + "-\\d+-\\d+" + ext;
-    return Pattern.compile(name);
-  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveWriter.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveWriter.java b/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveWriter.java
index a4c15d1..4eabe3b 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveWriter.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/statistics/StatArchiveWriter.java
@@ -19,16 +19,28 @@ import org.apache.geode.InternalGemFireException;
 import org.apache.geode.StatisticDescriptor;
 import org.apache.geode.distributed.internal.DistributionConfig;
 import org.apache.geode.internal.NanoTimer;
-import org.apache.geode.internal.net.SocketCreator;
 import org.apache.geode.internal.i18n.LocalizedStrings;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.logging.log4j.LogMarker;
-
+import org.apache.geode.internal.net.SocketCreator;
 import org.apache.logging.log4j.Logger;
 
-import java.io.*;
+import java.io.BufferedOutputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
 import java.net.UnknownHostException;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TimeZone;
 import java.util.zip.GZIPOutputStream;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/statistics/StatMonitorHandler.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/statistics/StatMonitorHandler.java b/geode-core/src/main/java/org/apache/geode/internal/statistics/StatMonitorHandler.java
index a2b0ae4..0c9583e 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/statistics/StatMonitorHandler.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/statistics/StatMonitorHandler.java
@@ -32,9 +32,10 @@ public class StatMonitorHandler implements SampleHandler {
 
   private static final Logger logger = LogService.getLogger();
 
-  static final String ENABLE_MONITOR_THREAD =
+  protected static final String ENABLE_MONITOR_THREAD =
       DistributionConfig.GEMFIRE_PREFIX + "stats.enableMonitorThread";
-  static final boolean enableMonitorThread = Boolean.getBoolean(ENABLE_MONITOR_THREAD);
+
+  private final boolean enableMonitorThread;
 
   /** The registered monitors */
   private volatile List<StatisticsMonitor> monitors = Collections.<StatisticsMonitor>emptyList();
@@ -43,7 +44,9 @@ public class StatMonitorHandler implements SampleHandler {
   private volatile StatMonitorNotifier notifier;
 
   /** Constructs a new StatMonitorHandler instance */
-  public StatMonitorHandler() {}
+  public StatMonitorHandler() {
+    this.enableMonitorThread = Boolean.getBoolean(ENABLE_MONITOR_THREAD);
+  }
 
   /** Adds a monitor which will be notified of samples */
   public boolean addMonitor(StatisticsMonitor monitor) {
@@ -55,9 +58,8 @@ public class StatMonitorHandler implements SampleHandler {
         added = newMonitors.add(monitor);
         this.monitors = Collections.unmodifiableList(newMonitors);
       }
-      if (enableMonitorThread && !this.monitors.isEmpty() && this.notifier == null) {
-        this.notifier = new StatMonitorNotifier();
-        this.notifier.start();
+      if (!this.monitors.isEmpty()) {
+        startNotifier_IfEnabledAndNotRunning();
       }
       return added;
     }
@@ -73,9 +75,8 @@ public class StatMonitorHandler implements SampleHandler {
         removed = newMonitors.remove(monitor);
         this.monitors = Collections.unmodifiableList(newMonitors);
       }
-      if (enableMonitorThread && this.monitors.isEmpty() && this.notifier != null) {
-        this.notifier.stop();
-        this.notifier = null;
+      if (this.monitors.isEmpty()) {
+        stopNotifier_IfEnabledAndRunning();
       }
       return removed;
     }
@@ -86,16 +87,14 @@ public class StatMonitorHandler implements SampleHandler {
    */
   public void close() {
     synchronized (this) {
-      if (enableMonitorThread && this.notifier != null) {
-        this.notifier.stop();
-      }
+      stopNotifier_IfEnabledAndRunning();
     }
   }
 
   @Override
   public void sampled(long nanosTimeStamp, List<ResourceInstance> resourceInstances) {
     synchronized (this) {
-      if (enableMonitorThread) {
+      if (this.enableMonitorThread) {
         final StatMonitorNotifier thread = this.notifier;
         if (thread != null) {
           try {
@@ -150,6 +149,20 @@ public class StatMonitorHandler implements SampleHandler {
     }
   }
 
+  private void startNotifier_IfEnabledAndNotRunning() {
+    if (this.enableMonitorThread && this.notifier == null) {
+      this.notifier = new StatMonitorNotifier();
+      this.notifier.start();
+    }
+  }
+
+  private void stopNotifier_IfEnabledAndRunning() {
+    if (this.enableMonitorThread && this.notifier != null) {
+      this.notifier.stop();
+      this.notifier = null;
+    }
+  }
+
   /**
    * @since GemFire 7.0
    */

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/StoppableCountDownLatch.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/StoppableCountDownLatch.java b/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/StoppableCountDownLatch.java
index 740fd7f..4726e51 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/StoppableCountDownLatch.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/StoppableCountDownLatch.java
@@ -46,6 +46,7 @@ public class StoppableCountDownLatch {
   /**
    * @param count the number of times {@link #countDown} must be invoked before threads can pass
    *        through {@link #await()}
+   *
    * @throws IllegalArgumentException if {@code count} is negative
    */
   public StoppableCountDownLatch(CancelCriterion stopper, int count) {
@@ -68,14 +69,19 @@ public class StoppableCountDownLatch {
 
   /**
    * @param msTimeout how long to wait in milliseconds
+   *
    * @return true if it was unlatched
-   * @throws InterruptedException
    */
   public boolean await(long msTimeout) throws InterruptedException {
     stopper.checkCancelInProgress(null);
     return latch.await(msTimeout, TimeUnit.MILLISECONDS);
   }
 
+  public boolean await(final long timeout, final TimeUnit unit) throws InterruptedException {
+    stopper.checkCancelInProgress(null);
+    return latch.await(timeout, unit);
+  }
+
   public synchronized void countDown() {
     latch.countDown();
   }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/test/java/org/apache/geode/internal/statistics/DiskSpaceLimitIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/statistics/DiskSpaceLimitIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/statistics/DiskSpaceLimitIntegrationTest.java
new file mode 100644
index 0000000..2381a53
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/internal/statistics/DiskSpaceLimitIntegrationTest.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.statistics;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.apache.commons.io.FileUtils.moveFileToDirectory;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.geode.StatisticDescriptor;
+import org.apache.geode.Statistics;
+import org.apache.geode.StatisticsType;
+import org.apache.geode.internal.NanoTimer;
+import org.apache.geode.internal.io.MainWithChildrenRollingFileHandler;
+import org.apache.geode.internal.io.RollingFileHandler;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+@Category(IntegrationTest.class)
+public class DiskSpaceLimitIntegrationTest {
+
+  private static final long FILE_SIZE_LIMIT = 256;
+  private static final long DISK_SPACE_LIMIT = FILE_SIZE_LIMIT * 2;
+
+  private File dir;
+  private File dirOfDeletedFiles;
+
+  private String archiveFileName;
+
+  private LocalStatisticsFactory factory;
+  private StatisticDescriptor[] statisticDescriptors;
+  private StatisticsType statisticsType;
+  private Statistics statistics;
+
+  private SampleCollector sampleCollector;
+  private StatArchiveHandlerConfig config;
+
+  private NanoTimer timer = new NanoTimer();
+  private long nanosTimeStamp;
+
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+  @Rule
+  public TestName testName = new TestName();
+
+  @Before
+  public void setUp() throws Exception {
+    this.dir = this.temporaryFolder.getRoot();
+    this.dirOfDeletedFiles = this.temporaryFolder.newFolder("deleted");
+
+    this.archiveFileName =
+        new File(this.dir, this.testName.getMethodName() + ".gfs").getAbsolutePath();
+
+    this.factory = new LocalStatisticsFactory(null);
+    this.statisticDescriptors = new StatisticDescriptor[] {
+        this.factory.createIntCounter("stat1", "description of stat1", "units", true)};
+    this.statisticsType =
+        factory.createType("statisticsType1", "statisticsType1", this.statisticDescriptors);
+    this.statistics = factory.createAtomicStatistics(this.statisticsType, "statistics1", 1);
+
+    StatisticsSampler sampler = mock(StatisticsSampler.class);
+    when(sampler.getStatistics()).thenReturn(this.factory.getStatistics());
+
+    this.config = mock(StatArchiveHandlerConfig.class);
+    when(this.config.getArchiveFileName()).thenReturn(new File(this.archiveFileName));
+    when(this.config.getArchiveFileSizeLimit()).thenReturn(FILE_SIZE_LIMIT);
+    when(this.config.getSystemId()).thenReturn(1L);
+    when(this.config.getSystemStartTime()).thenReturn(System.currentTimeMillis());
+    when(this.config.getSystemDirectoryPath())
+        .thenReturn(this.temporaryFolder.getRoot().getAbsolutePath());
+    when(this.config.getProductDescription()).thenReturn(this.testName.getMethodName());
+
+    RollingFileHandler rollingFileHandler = new TestableRollingFileHandler();
+
+    this.sampleCollector = new SampleCollector(sampler);
+    this.sampleCollector.initialize(this.config, NanoTimer.getTime(), rollingFileHandler);
+
+    this.timer.reset();
+    this.nanosTimeStamp = this.timer.getLastResetTime() - getNanoRate();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    StatisticsTypeFactoryImpl.clear();
+  }
+
+  @Test
+  public void zeroKeepsAllFiles() throws Exception {
+    when(this.config.getArchiveDiskSpaceLimit()).thenReturn(0L);
+    sampleUntilFileExists(archiveFile(1));
+    sampleUntilFileExists(archiveFile(2));
+    assertThat(archiveFile(1)).exists();
+    assertThat(archiveFile(2)).exists();
+  }
+
+  @Test
+  public void sameKeepsOneFile() throws Exception {
+    when(this.config.getArchiveDiskSpaceLimit()).thenReturn(DISK_SPACE_LIMIT);
+    sampleUntilFileExists(archiveFile(1));
+    sampleUntilFileExists(archiveFile(2));
+    assertThat(archiveFile(1)).doesNotExist();
+    assertThat(archiveFile(2)).exists();
+    assertThat(everExisted(archiveFile(1))).isTrue();
+  }
+
+  private void sampleUntilFileExists(final File file)
+      throws InterruptedException, TimeoutException {
+    long minutes = 1;
+    long timeout = System.nanoTime() + MINUTES.toNanos(minutes);
+    int count = 0;
+    do {
+      sample(advanceNanosTimeStamp());
+      count++;
+      Thread.sleep(10);
+    } while (!everExisted(file) && System.nanoTime() < timeout);
+    if (!everExisted(file)) {
+      throw new TimeoutException("File " + file + " does not exist after " + count
+          + " samples within " + minutes + " " + MINUTES);
+    }
+    System.out.println("Sampled " + count + " times to create " + file);
+  }
+
+  private boolean everExisted(final File file) {
+    if (file.exists()) {
+      return true;
+    } else { // check dirOfDeletedFiles
+      String name = file.getName();
+      File deleted = new File(this.dirOfDeletedFiles, name);
+      return deleted.exists();
+    }
+  }
+
+  private void sample(final long time) {
+    getSampleCollector().sample(time);
+  }
+
+  private SampleCollector getSampleCollector() {
+    return this.sampleCollector;
+  }
+
+  private long advanceNanosTimeStamp() {
+    this.nanosTimeStamp += getNanoRate();
+    return this.nanosTimeStamp;
+  }
+
+  private long getNanoRate() {
+    return NanoTimer.millisToNanos(getSampleRate());
+  }
+
+  private long getSampleRate() {
+    return 1000; // 1 second
+  }
+
+  private File archiveFile(final int child) {
+    return new File(this.dir,
+        this.testName.getMethodName() + "-01-" + String.format("%02d", child) + ".gfs");
+  }
+
+  private File archiveFile() {
+    return new File(this.archiveFileName);
+  }
+
+  /**
+   * Override protected method to move file instead of deleting it.
+   */
+  private class TestableRollingFileHandler extends MainWithChildrenRollingFileHandler {
+    @Override
+    protected boolean delete(final File file) {
+      try {
+        moveFileToDirectory(file, dirOfDeletedFiles, false);
+        return true;
+      } catch (IOException e) {
+        throw new Error(e);
+      }
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/test/java/org/apache/geode/internal/statistics/FileSizeLimitIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/statistics/FileSizeLimitIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/statistics/FileSizeLimitIntegrationTest.java
new file mode 100644
index 0000000..1538862
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/internal/statistics/FileSizeLimitIntegrationTest.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.statistics;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.apache.geode.StatisticDescriptor;
+import org.apache.geode.Statistics;
+import org.apache.geode.StatisticsType;
+import org.apache.geode.internal.NanoTimer;
+import org.apache.geode.internal.io.MainWithChildrenRollingFileHandler;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.File;
+import java.util.concurrent.TimeoutException;
+
+@Category(IntegrationTest.class)
+public class FileSizeLimitIntegrationTest {
+
+  private static final long FILE_SIZE_LIMIT = 1;
+
+  private File dir;
+  private String archiveFileName;
+
+  private LocalStatisticsFactory factory;
+  private StatisticDescriptor[] statisticDescriptors;
+  private StatisticsType statisticsType;
+  private Statistics statistics;
+
+  private SampleCollector sampleCollector;
+
+  private NanoTimer timer = new NanoTimer();
+  private long nanosTimeStamp;
+
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+  @Rule
+  public TestName testName = new TestName();
+
+  @Before
+  public void setUp() throws Exception {
+    this.dir = this.temporaryFolder.getRoot();
+    this.archiveFileName =
+        new File(this.dir, this.testName.getMethodName() + ".gfs").getAbsolutePath();
+
+    this.factory = new LocalStatisticsFactory(null);
+    this.statisticDescriptors = new StatisticDescriptor[] {
+        this.factory.createIntCounter("stat1", "description of stat1", "units", true)};
+    this.statisticsType =
+        factory.createType("statisticsType1", "statisticsType1", this.statisticDescriptors);
+    this.statistics = factory.createAtomicStatistics(this.statisticsType, "statistics1", 1);
+
+    Answer<Statistics[]> statisticsAnswer = new Answer<Statistics[]>() {
+      public Statistics[] answer(InvocationOnMock invocation) throws Throwable {
+        return factory.getStatistics();
+      }
+    };
+
+    Answer<Integer> modCountAnswer = new Answer<Integer>() {
+      public Integer answer(InvocationOnMock invocation) throws Throwable {
+        return factory.getStatListModCount();
+      }
+    };
+
+    StatisticsSampler sampler = mock(StatisticsSampler.class);
+    when(sampler.getStatistics()).thenAnswer(statisticsAnswer);
+    when(sampler.getStatisticsModCount()).thenAnswer(modCountAnswer);
+
+    StatArchiveHandlerConfig config = mock(StatArchiveHandlerConfig.class);
+    when(config.getArchiveFileName()).thenReturn(new File(this.archiveFileName));
+    when(config.getArchiveFileSizeLimit()).thenReturn(FILE_SIZE_LIMIT);
+    when(config.getSystemId()).thenReturn(1L);
+    when(config.getSystemStartTime()).thenReturn(System.currentTimeMillis());
+    when(config.getSystemDirectoryPath())
+        .thenReturn(this.temporaryFolder.getRoot().getAbsolutePath());
+    when(config.getProductDescription()).thenReturn(this.testName.getMethodName());
+    when(config.getArchiveDiskSpaceLimit()).thenReturn(0L);
+
+    this.sampleCollector = new SampleCollector(sampler);
+    this.sampleCollector.initialize(config, this.timer.getTime(),
+        new MainWithChildrenRollingFileHandler());
+
+    this.timer.reset();
+    this.nanosTimeStamp = this.timer.getLastResetTime() - getNanoRate();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    StatisticsTypeFactoryImpl.clear();
+  }
+
+  @Test
+  public void rollsWhenLimitIsReached() throws Exception { // TODO: add test to assert size is
+                                                           // correct
+    sampleUntilFileExists(archiveFile(1));
+    sampleUntilFileExists(archiveFile(2));
+    assertThat(archiveFile(1)).exists();
+    assertThat(archiveFile(2)).exists();
+  }
+
+  private void sampleUntilFileExists(final File file)
+      throws InterruptedException, TimeoutException {
+    long timeout = System.nanoTime() + MINUTES.toNanos(1);
+    int count = 0;
+    do {
+      sample(advanceNanosTimeStamp());
+      count++;
+      Thread.sleep(10);
+    } while (!file.exists() && System.nanoTime() < timeout);
+    if (!file.exists()) {
+      throw new TimeoutException("File " + file + " does not exist after " + count
+          + " samples within " + 1 + " " + MINUTES);
+    }
+    System.out.println("Sampled " + count + " times to create " + file);
+  }
+
+  private void sample(final long time) {
+    getSampleCollector().sample(time);
+  }
+
+  private SampleCollector getSampleCollector() {
+    return this.sampleCollector;
+  }
+
+  private long advanceNanosTimeStamp() {
+    this.nanosTimeStamp += getNanoRate();
+    return this.nanosTimeStamp;
+  }
+
+  private long getNanoRate() {
+    return NanoTimer.millisToNanos(getSampleRate());
+  }
+
+  private long getSampleRate() {
+    return 1000; // 1 second
+  }
+
+  private File archiveFile(final int child) {
+    return new File(this.dir,
+        this.testName.getMethodName() + "-01-" + String.format("%02d", child) + ".gfs");
+  }
+
+  private File archiveFile() {
+    return new File(this.archiveFileName);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/test/java/org/apache/geode/internal/statistics/SampleCollectorTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/statistics/SampleCollectorTest.java b/geode-core/src/test/java/org/apache/geode/internal/statistics/SampleCollectorTest.java
index ab84ea4..718f744 100755
--- a/geode-core/src/test/java/org/apache/geode/internal/statistics/SampleCollectorTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/statistics/SampleCollectorTest.java
@@ -20,6 +20,7 @@ import static org.mockito.Mockito.*;
 import java.io.File;
 import java.util.List;
 
+import org.apache.geode.internal.io.MainWithChildrenRollingFileHandler;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -65,7 +66,8 @@ public class SampleCollectorTest {
 
     final StatisticsSampler sampler = new TestStatisticsSampler(manager);
     this.sampleCollector = new SampleCollector(sampler);
-    this.sampleCollector.initialize(mockStatArchiveHandlerConfig, NanoTimer.getTime());
+    this.sampleCollector.initialize(mockStatArchiveHandlerConfig, NanoTimer.getTime(),
+        new MainWithChildrenRollingFileHandler());
   }
 
   @After

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerTest.java b/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerTest.java
index 85e6f6d..782944a 100755
--- a/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerTest.java
@@ -14,20 +14,20 @@
  */
 package org.apache.geode.internal.statistics;
 
-import static org.junit.Assert.*;
-import static org.junit.Assume.*;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
 import org.apache.geode.internal.NanoTimer;
-import org.apache.geode.internal.statistics.StatMonitorHandler.StatMonitorNotifier;
 import org.apache.geode.internal.util.StopWatch;
 import org.apache.geode.test.junit.categories.UnitTest;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Unit tests for {@link StatMonitorHandler}.
@@ -140,56 +140,6 @@ public class StatMonitorHandlerTest {
     }
   }
 
-  @Test
-  public void testStatMonitorNotifierAliveButWaiting() throws Exception {
-    assumeTrue(StatMonitorHandler.enableMonitorThread);
-    StatMonitorHandler handler = new StatMonitorHandler();
-    TestStatisticsMonitor monitor = new TestStatisticsMonitor();
-    handler.addMonitor(monitor);
-
-    final StatMonitorNotifier notifier = handler.getStatMonitorNotifier();
-    assertTrue(notifier.isAlive());
-
-    waitUntilWaiting(notifier);
-
-    for (int i = 0; i < 20; i++) {
-      assert (notifier.isWaiting());
-      Thread.sleep(10);
-    }
-  }
-
-  @Test
-  public void testStatMonitorNotifierWakesUpForWork() throws Exception {
-    assumeTrue(StatMonitorHandler.enableMonitorThread);
-    StatMonitorHandler handler = new StatMonitorHandler();
-    TestStatisticsMonitor monitor = new TestStatisticsMonitor();
-    handler.addMonitor(monitor);
-
-    final StatMonitorNotifier notifier = handler.getStatMonitorNotifier();
-    assertTrue(notifier.isAlive());
-
-    waitUntilWaiting(notifier);
-
-    // if notification occurs then notifier woke up...
-    assertEquals(0, monitor.getNotificationCount());
-    handler.sampled(NanoTimer.getTime(), Collections.<ResourceInstance>emptyList());
-
-    waitForNotificationCount(monitor, 1, 2 * 1000, 10, false);
-    assertEquals(1, monitor.getNotificationCount());
-
-    // and goes back to waiting...
-    waitUntilWaiting(notifier);
-  }
-
-  private static void waitUntilWaiting(StatMonitorNotifier notifier) throws InterruptedException {
-    boolean done = false;
-    for (StopWatch time = new StopWatch(true); !done && time.elapsedTimeMillis() < 2000; done =
-        (notifier.isWaiting())) {
-      Thread.sleep(10);
-    }
-    assertTrue("waiting for notifier to be waiting", done);
-  }
-
   private static void waitForNotificationCount(final TestStatisticsMonitor monitor,
       final int expected, long ms, long interval, boolean throwOnTimeout)
       throws InterruptedException {

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/3bdd1049/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerWithEnabledMonitorThreadTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerWithEnabledMonitorThreadTest.java b/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerWithEnabledMonitorThreadTest.java
new file mode 100644
index 0000000..a08fa43
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/internal/statistics/StatMonitorHandlerWithEnabledMonitorThreadTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.statistics;
+
+import static org.apache.geode.internal.statistics.StatMonitorHandler.ENABLE_MONITOR_THREAD;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.geode.internal.NanoTimer;
+import org.apache.geode.internal.statistics.StatMonitorHandler.StatMonitorNotifier;
+import org.apache.geode.internal.util.StopWatch;
+import org.apache.geode.test.junit.categories.UnitTest;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.experimental.categories.Category;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Extracted tests from StatMonitorHandlerTest that require enableMonitorThread
+ */
+@Category(UnitTest.class)
+public class StatMonitorHandlerWithEnabledMonitorThreadTest {
+
+  @Rule
+  public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
+
+  @Before
+  public void before() throws Exception {
+    System.setProperty(ENABLE_MONITOR_THREAD, "true");
+  }
+
+  @Test
+  public void testStatMonitorNotifierAliveButWaiting() throws Exception {
+    StatMonitorHandler handler = new StatMonitorHandler();
+    TestStatisticsMonitor monitor = new TestStatisticsMonitor();
+    handler.addMonitor(monitor);
+
+    final StatMonitorNotifier notifier = handler.getStatMonitorNotifier();
+    assertTrue(notifier.isAlive());
+
+    waitUntilWaiting(notifier);
+
+    for (int i = 0; i < 20; i++) {
+      assertTrue(notifier.isWaiting());
+      Thread.sleep(10);
+    }
+  }
+
+  @Test
+  public void testStatMonitorNotifierWakesUpForWork() throws Exception {
+    StatMonitorHandler handler = new StatMonitorHandler();
+    TestStatisticsMonitor monitor = new TestStatisticsMonitor();
+    handler.addMonitor(monitor);
+
+    final StatMonitorNotifier notifier = handler.getStatMonitorNotifier();
+    assertTrue(notifier.isAlive());
+
+    waitUntilWaiting(notifier);
+
+    // if notification occurs then notifier woke up...
+    assertEquals(0, monitor.getNotificationCount());
+    handler.sampled(NanoTimer.getTime(), Collections.<ResourceInstance>emptyList());
+
+    waitForNotificationCount(monitor, 1, 2 * 1000, 10, false);
+    assertEquals(1, monitor.getNotificationCount());
+
+    // and goes back to waiting...
+    waitUntilWaiting(notifier);
+  }
+
+  private static void waitUntilWaiting(StatMonitorNotifier notifier) throws InterruptedException {
+    boolean done = false;
+    for (StopWatch time = new StopWatch(true); !done && time.elapsedTimeMillis() < 2000; done =
+        (notifier.isWaiting())) {
+      Thread.sleep(10);
+    }
+    assertTrue("waiting for notifier to be waiting", done);
+  }
+
+  private static void waitForNotificationCount(final TestStatisticsMonitor monitor,
+      final int expected, final long ms, final long interval, final boolean throwOnTimeout)
+      throws InterruptedException {
+    boolean done = false;
+    for (StopWatch time = new StopWatch(true); !done && time.elapsedTimeMillis() < ms; done =
+        (monitor.getNotificationCount() >= expected)) {
+      Thread.sleep(interval);
+    }
+    if (throwOnTimeout) {
+      assertTrue("waiting for notification count to be " + expected, done);
+    }
+  }
+
+  /**
+   * @since GemFire 7.0
+   */
+  private static class TestStatisticsMonitor extends StatisticsMonitor {
+    private volatile long timeStamp;
+    private volatile List<ResourceInstance> resourceInstances;
+    private volatile int notificationCount;
+
+    public TestStatisticsMonitor() {
+      super();
+    }
+
+    @Override
+    protected void monitor(long timeStamp, List<ResourceInstance> resourceInstances) {
+      this.timeStamp = timeStamp;
+      this.resourceInstances = resourceInstances;
+      this.notificationCount++;
+    }
+
+    long getTimeStamp() {
+      return this.timeStamp;
+    }
+
+    List<ResourceInstance> getResourceInstances() {
+      return this.resourceInstances;
+    }
+
+    int getNotificationCount() {
+      return this.notificationCount;
+    }
+  }
+}


Mime
View raw message