You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2015/08/21 22:29:30 UTC

[3/9] incubator-geode git commit: Dunit changes for JUnit 4 syntax. Refactoring to modernize API.

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/cache/CacheTestCase.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/cache/CacheTestCase.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/cache/CacheTestCase.java
new file mode 100755
index 0000000..8535d85
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/cache/CacheTestCase.java
@@ -0,0 +1,696 @@
+package com.gemstone.gemfire.test.dunit.cache;
+
+import static com.gemstone.gemfire.test.dunit.Assert.*;
+import static com.gemstone.gemfire.test.dunit.ExpectedExceptionString.*;
+import static com.gemstone.gemfire.test.dunit.Invoke.*;
+import static com.gemstone.gemfire.test.dunit.Wait.*;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+
+import com.gemstone.gemfire.SystemFailure;
+import com.gemstone.gemfire.cache.AttributesFactory;
+import com.gemstone.gemfire.cache.Cache;
+import com.gemstone.gemfire.cache.CacheException;
+import com.gemstone.gemfire.cache.CacheExistsException;
+import com.gemstone.gemfire.cache.CacheFactory;
+import com.gemstone.gemfire.cache.CacheTransactionManager;
+import com.gemstone.gemfire.cache.ExpirationAttributes;
+import com.gemstone.gemfire.cache.Region;
+import com.gemstone.gemfire.cache.RegionAttributes;
+import com.gemstone.gemfire.cache.RegionExistsException;
+import com.gemstone.gemfire.cache.TimeoutException;
+import com.gemstone.gemfire.cache.client.ClientCache;
+import com.gemstone.gemfire.cache.client.ClientCacheFactory;
+import com.gemstone.gemfire.cache.client.PoolManager;
+import com.gemstone.gemfire.cache30.CacheSerializableRunnable;
+import com.gemstone.gemfire.distributed.internal.DistributionMessageObserver;
+import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
+import com.gemstone.gemfire.internal.FileUtil;
+import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
+import com.gemstone.gemfire.internal.cache.HARegion;
+import com.gemstone.gemfire.internal.cache.InternalRegionArguments;
+import com.gemstone.gemfire.internal.cache.LocalRegion;
+import com.gemstone.gemfire.internal.cache.PartitionedRegion;
+import com.gemstone.gemfire.internal.cache.xmlcache.CacheCreation;
+import com.gemstone.gemfire.internal.cache.xmlcache.CacheXmlGenerator;
+import com.gemstone.gemfire.test.dunit.DistributedTestCase;
+import com.gemstone.gemfire.test.dunit.Host;
+import com.gemstone.gemfire.test.dunit.VM;
+import com.gemstone.gemfire.test.dunit.WaitCriterion;
+
+/**
+ * The abstract superclass of tests that require the creation of a
+ * {@link Cache}.
+ *
+ * @author David Whitlock
+ * @since 3.0
+ */
+@SuppressWarnings({ "deprecation", "serial", "rawtypes", "unchecked" })
+public abstract class CacheTestCase extends DistributedTestCase {
+
+  /** The Cache from which regions are obtained 
+   * 
+   * All references synchronized via <code>CacheTestCase.class</code>
+   * */
+  // static so it doesn't get serialized with SerializableRunnable inner classes
+  protected static Cache cache;
+  
+  ////////  Helper methods
+  /**
+   * Creates the <code>Cache</code> for this test
+   */
+  private void createCache() {
+    createCache(false);
+  }
+  
+  private void createCache(boolean client) {
+    createCache(client, null);
+  }
+  
+  private void createCache(boolean client, CacheFactory cf) {
+    synchronized(CacheTestCase.class) {
+      try {
+        System.setProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE", "true");
+        Cache c;
+        if (client) {
+          c = (Cache)new ClientCacheFactory(getSystem().getProperties()).create();
+        } else {
+          if(cf == null) {
+            c = CacheFactory.create(getSystem());
+          } else {
+            Properties props = getSystem().getProperties();
+            for(Map.Entry entry : props.entrySet()) {
+              cf.set((String) entry.getKey(), (String)entry.getValue());
+            }
+            c = cf.create();
+          }
+        }
+        cache = c;
+      } catch (CacheExistsException e) {
+        fail("the cache already exists", e);
+
+      } catch (RuntimeException ex) {
+        throw ex;
+
+      } catch (Exception ex) {
+        fail("Checked exception while initializing cache??", ex);
+      } finally {
+        System.clearProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE");
+      }
+    }
+  }
+
+  /**
+   * Creates the <code>Cache</code> for this test that is not connected
+   * to other members
+   */
+  public Cache createLonerCache() {
+    synchronized(CacheTestCase.class) {
+      try {
+        System.setProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE", "true");
+        Cache c = CacheFactory.create(getLonerSystem()); 
+        cache = c;
+      } catch (CacheExistsException e) {
+        fail("the cache already exists", e);
+
+      } catch (RuntimeException ex) {
+        throw ex;
+
+      } catch (Exception ex) {
+        fail("Checked exception while initializing cache??", ex);
+      } finally {
+        System.clearProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE");
+      }
+      return cache;
+    }
+  }
+  
+  /**
+   * Creates the <code>Cache</code> for this test that is not connected
+   * to other members.
+   * Added specifically to test scenario of defect #47181.
+   */
+  public Cache createLonerCacheWithEnforceUniqueHost() {
+    synchronized(CacheTestCase.class) {
+      try {
+        System.setProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE", "true");
+        Cache c = CacheFactory.create(getLonerSystemWithEnforceUniqueHost()); 
+        cache = c;
+      } catch (CacheExistsException e) {
+        fail("the cache already exists", e);
+
+      } catch (RuntimeException ex) {
+        throw ex;
+
+      } catch (Exception ex) {
+        fail("Checked exception while initializing cache??", ex);
+      } finally {
+        System.clearProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE");
+      }
+      return cache;
+    }
+  }
+
+  /**
+   * Creates the <code>Cache</code> for this test that has its own mcast group
+   */
+  public Cache createMcastCache() {
+    synchronized(CacheTestCase.class) {
+      try {
+        System.setProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE", "true");
+        Cache c = CacheFactory.create(getMcastSystem()); 
+        cache = c;
+      } catch (CacheExistsException e) {
+        fail("the cache already exists", e);
+
+      } catch (RuntimeException ex) {
+        throw ex;
+
+      } catch (Exception ex) {
+        fail("Checked exception while initializing cache??", ex);
+      } finally {
+        System.clearProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE");
+      }
+      return cache;
+    }
+  }
+
+  /**
+   * Creates the <code>Cache</code> for this test that has its own mcast group
+   */
+  public Cache createMcastCache(int jgroupsPort) {
+    synchronized(CacheTestCase.class) {
+      try {
+        System.setProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE", "true");
+        Cache c = CacheFactory.create(getMcastSystem(jgroupsPort)); 
+        cache = c;
+      } catch (CacheExistsException e) {
+        fail("the cache already exists", e);
+
+      } catch (RuntimeException ex) {
+        throw ex;
+
+      } catch (Exception ex) {
+        fail("Checked exception while initializing cache??", ex);
+      } finally {
+        System.clearProperty("gemfire.DISABLE_DISCONNECT_DS_ON_CACHE_CLOSE");
+      }
+      return cache;
+    }
+  }
+  /**
+   * Sets this test up with a CacheCreation as its cache.
+   * Any existing cache is closed. Whoever calls this must also call finishCacheXml
+   */
+  public static synchronized void beginCacheXml() {
+//    getLogWriter().info("before closeCache");
+    closeCache();
+//    getLogWriter().info("before TestCacheCreation");
+    cache = new TestCacheCreation();
+//    getLogWriter().info("after TestCacheCreation");
+  }
+  /**
+   * Finish what beginCacheXml started. It does this be generating a cache.xml
+   * file and then creating a real cache using that cache.xml.
+   */
+  public void finishCacheXml(String name) {
+    synchronized(CacheTestCase.class) {
+      File file = new File(name + "-cache.xml");
+      try {
+        PrintWriter pw = new PrintWriter(new FileWriter(file), true);
+        CacheXmlGenerator.generate(cache, pw);
+        pw.close();
+      } catch (IOException ex) {
+        fail("IOException during cache.xml generation to " + file, ex);
+      }
+      cache = null;
+      GemFireCacheImpl.testCacheXml = file;
+      try {
+        createCache();
+      } finally {
+        GemFireCacheImpl.testCacheXml = null;
+      }
+    }
+  }
+  
+  /**
+   * Finish what beginCacheXml started. It does this be generating a cache.xml
+   * file and then creating a real cache using that cache.xml.
+   */
+  public void finishCacheXml(String name, boolean useSchema, String xmlVersion) {
+    synchronized(CacheTestCase.class) {
+      File dir = new File("XML_" + xmlVersion);
+      dir.mkdirs();
+      File file = new File(dir, name + ".xml");
+      try {
+        PrintWriter pw = new PrintWriter(new FileWriter(file), true);
+        CacheXmlGenerator.generate(cache, pw, useSchema, xmlVersion);
+        pw.close();
+      } catch (IOException ex) {
+        fail("IOException during cache.xml generation to " + file, ex);
+      }
+      cache = null;
+      GemFireCacheImpl.testCacheXml = file;
+      try {
+        createCache();
+      } finally {
+        GemFireCacheImpl.testCacheXml = null;
+      }
+    }
+  }
+
+  /**
+   * Return a cache for obtaining regions, created lazily.
+   */
+  public final Cache getCache() {
+    return getCache(false);
+  }
+  
+  public final Cache getCache(CacheFactory cf) {
+    return getCache(false, cf);
+  }
+  
+  public final Cache getCache(boolean client) {
+    return getCache(client, null);
+  }
+
+  public final Cache getCache(boolean client, CacheFactory cf) {
+    synchronized (CacheTestCase.class) {
+      final GemFireCacheImpl gfCache = GemFireCacheImpl.getInstance();
+      if (gfCache != null && !gfCache.isClosed()
+          && gfCache.getCancelCriterion().cancelInProgress() != null) {
+        waitForCriterion(new WaitCriterion() {
+
+          public boolean done() {
+            return gfCache.isClosed();
+          }
+
+          public String description() {
+            return "waiting for cache to close";
+          }
+        }, 30 * 1000, 300, true);
+      }
+      if (cache == null || cache.isClosed()) {
+        cache = null;
+        createCache(client, cf);
+      }
+      if (client && cache != null) {
+        addExpectedExceptionString("java.net.ConnectException");
+      }
+      return cache;
+    }
+  }
+
+  /**
+   * creates a client cache from the factory if one does not already exist
+   * @since 6.5
+   * @param factory
+   * @return the client cache
+   */
+  public final ClientCache getClientCache(ClientCacheFactory factory) {
+    synchronized (CacheTestCase.class) {
+      final GemFireCacheImpl gfCache = GemFireCacheImpl.getInstance();
+      if (gfCache != null && !gfCache.isClosed()
+          && gfCache.getCancelCriterion().cancelInProgress() != null) {
+        waitForCriterion(new WaitCriterion() {
+
+          public boolean done() {
+            return gfCache.isClosed();
+          }
+
+          public String description() {
+            return "waiting for cache to close";
+          }
+        }, 30 * 1000, 300, true);
+      }
+      if (cache == null || cache.isClosed()) {
+        cache = null;
+        disconnectFromDS();
+        cache = (Cache)factory.create();
+      }
+      if (cache != null) {
+        addExpectedExceptionString("java.net.ConnectException");
+      }
+      return (ClientCache)cache;
+    }
+  }
+
+  /**
+   * same as {@link #getCache()} but with casting
+   */
+  public final GemFireCacheImpl getGemfireCache() {
+    return (GemFireCacheImpl)getCache();
+  }
+  public static synchronized final boolean hasCache() {
+      return cache != null;
+  }
+
+  /**
+   * Return current cache without creating one.
+   */
+  public static synchronized final Cache basicGetCache() {
+      return cache;
+  }
+
+  public static synchronized void disconnectFromDS() {
+    closeCache();
+    DistributedTestCase.disconnectFromDS();
+  }
+  
+  /** Close the cache */
+  public static synchronized void closeCache() {
+    //Workaround for that fact that some classes are now extending
+    //CacheTestCase but not using it properly.
+    if(cache == null) {
+      cache = GemFireCacheImpl.getInstance();
+    }
+    try {
+    if (cache != null) {
+      try {
+        if (!cache.isClosed()) {
+          if (cache instanceof GemFireCacheImpl) {
+            CacheTransactionManager txMgr = ((GemFireCacheImpl)cache).getTxManager();
+            if (txMgr != null) {
+              if (txMgr.exists()) {
+                try {
+                  // make sure we cleanup this threads txid stored in a thread local
+                  txMgr.rollback();
+                }catch(Exception ignore) {
+                  
+                }
+              }
+            }
+          }
+          try {
+            cache.close();
+          }
+          catch (VirtualMachineError e) {
+            SystemFailure.initiateFailure(e);
+            throw e;
+          }
+          catch (Throwable t) {
+          }
+          finally {
+          }
+        }
+        // @todo darrel: destroy DiskStore files
+      }
+      finally {
+        cache = null;
+      }
+    } // cache != null
+    } finally {
+      //Make sure all pools are closed, even if we never
+      //created a cache
+      PoolManager.close(false);
+    }
+  }
+
+  /** Closed the cache in all VMs. */
+  protected void closeAllCache() {
+    closeCache();
+    invokeInEveryVM(CacheTestCase.class, "closeCache");
+  }
+
+  @Override
+  public void tearDown2() throws Exception {
+    // locally destroy all root regions and close the cache
+    remoteTearDown();
+    // Now invoke it in every VM
+    for (int h = 0; h < Host.getHostCount(); h++) {
+      Host host = Host.getHost(h);
+      for (int v = 0; v < host.getVMCount(); v++) {
+        VM vm = host.getVM(v);
+        vm.invoke(CacheTestCase.class, "remoteTearDown");
+      }
+    }
+    super.tearDown2(); 
+  }
+
+  /**
+   * Local destroy all root regions and close the cache.  
+   */
+  protected synchronized static void remoteTearDown() {
+    try {
+      DistributionMessageObserver.setInstance(null);
+      if (cache != null && !cache.isClosed()) {
+        //try to destroy the root regions first so that
+        //we clean up any persistent files.
+        for (Iterator itr = cache.rootRegions().iterator(); itr.hasNext();) {
+          Region root = (Region)itr.next();
+//          String name = root.getName();
+          //for colocated regions you can't locally destroy a partitioned
+          //region.
+    if(root.isDestroyed() || root instanceof HARegion || root instanceof PartitionedRegion) {
+            continue;
+          }
+          try {
+            root.localDestroyRegion("teardown");
+          }
+          catch (VirtualMachineError e) {
+            SystemFailure.initiateFailure(e);
+            throw e;
+          }
+          catch (Throwable t) {
+            getLogWriter().error(t);
+          }
+        }
+      }
+    }
+    finally {
+      try {
+        closeCache();
+      }
+      catch (VirtualMachineError e) {
+        SystemFailure.initiateFailure(e);
+        throw e;
+      }
+      catch (Throwable t) {
+        getLogWriter().error("Error in closing the cache ", t);
+        
+      }
+    }
+
+    try {
+      cleanDiskDirs();
+    } catch(IOException e) {
+      getLogWriter().error("Error cleaning disk dirs", e);
+    }
+  }
+  
+  /**
+   * Returns a region with the given name and attributes
+   */
+  public final Region createRegion(String name,
+                                      RegionAttributes attrs)
+    throws CacheException {
+    return createRegion(name, "root", attrs);
+  }
+  
+  /**
+   * Provide any internal region arguments, typically required when 
+   * internal use (aka meta-data) regions are needed.
+   * @return internal arguements, which may be null.  If null, then default 
+   * InternalRegionArguments are used to construct the Region
+   */
+  public InternalRegionArguments getInternalRegionArguments()
+  {
+    return null;
+  }
+
+  final public Region createRegion(String name, String rootName,
+                                      RegionAttributes attrs)
+    throws CacheException {
+    Region root = getRootRegion(rootName);
+    if (root == null) {
+      // don't put listeners on root region
+      RegionAttributes rootAttrs = attrs;
+      AttributesFactory fac = new AttributesFactory(attrs);
+      ExpirationAttributes expiration = ExpirationAttributes.DEFAULT;
+
+      // fac.setCacheListener(null);
+      fac.setCacheLoader(null);
+      fac.setCacheWriter(null);
+      fac.setPoolName(null);
+      fac.setPartitionAttributes(null);
+      fac.setRegionTimeToLive(expiration);
+      fac.setEntryTimeToLive(expiration);
+      fac.setRegionIdleTimeout(expiration);
+      fac.setEntryIdleTimeout(expiration);
+      rootAttrs = fac.create();
+      root = createRootRegion(rootName, rootAttrs);
+    }
+
+    InternalRegionArguments internalArgs = getInternalRegionArguments();
+    if (internalArgs == null) {
+      return root.createSubregion(name, attrs);
+    } else {
+      try {
+        LocalRegion lr = (LocalRegion) root;
+        return lr.createSubregion(name, attrs, internalArgs);
+      } catch (IOException ioe) {
+        AssertionError assErr = new AssertionError("unexpected exception");
+        assErr.initCause(ioe);
+        throw assErr;
+      } catch (ClassNotFoundException cnfe) {
+        AssertionError assErr = new AssertionError("unexpected exception");
+        assErr.initCause(cnfe);
+        throw assErr;
+      } 
+    }
+  }
+  
+  public final Region getRootRegion() {
+    return getRootRegion("root");
+  }
+  
+  public final Region getRootRegion(String rootName) {
+    return getCache().getRegion(rootName);
+  }
+
+  protected final Region createRootRegion(RegionAttributes attrs)
+  throws RegionExistsException, TimeoutException {
+    return createRootRegion("root", attrs);
+  }
+
+  public final Region createRootRegion(String rootName, RegionAttributes attrs)
+  throws RegionExistsException, TimeoutException {
+    return getCache().createRegion(rootName, attrs);
+  }
+
+  /**
+   * send an unordered message requiring an ack to all connected members 
+   * in order to flush the unordered communication channel
+   */
+  public void sendUnorderedMessageToAll() {
+    //if (getCache() instanceof distcache.gemfire.GemFireCacheImpl) {
+      try {
+        com.gemstone.gemfire.distributed.internal.HighPriorityAckedMessage msg = new com.gemstone.gemfire.distributed.internal.HighPriorityAckedMessage();
+        msg.send(InternalDistributedSystem.getConnectedInstance().getDM().getNormalDistributionManagerIds(), false);
+      }
+      catch (Exception e) {
+        throw new RuntimeException("Unable to send unordered message due to exception", e);
+      }
+    //}
+  }
+
+  /**
+   * send an unordered message requiring an ack to all connected admin members 
+   * in order to flush the unordered communication channel
+   */
+//  public void sendUnorderedMessageToAdminMembers() {
+//    //if (getCache() instanceof distcache.gemfire.GemFireCacheImpl) {
+//      try {
+//        com.gemstone.gemfire.distributed.internal.HighPriorityAckedMessage msg = new com.gemstone.gemfire.distributed.internal.HighPriorityAckedMessage();
+//        msg.send(DistributedSystemHelper.getAdminMembers(), false);
+//      }
+//      catch (Exception e) {
+//        throw new RuntimeException("Unable to send unordered message due to exception", e);
+//      }
+//    //}
+//  }
+
+  /**
+   * send an ordered message requiring an ack to all connected members 
+   * in order to flush the ordered communication channel
+   */
+  public void sendSerialMessageToAll() {
+    if (getCache() instanceof GemFireCacheImpl) {
+      try {
+        com.gemstone.gemfire.distributed.internal.SerialAckedMessage msg = new com.gemstone.gemfire.distributed.internal.SerialAckedMessage();
+        msg.send(InternalDistributedSystem.getConnectedInstance().getDM().getNormalDistributionManagerIds(), false);
+      }
+      catch (Exception e) {
+        throw new RuntimeException("Unable to send serial message due to exception", e);
+      }
+    }
+  }
+
+  /**
+   * @deprecated Use DistributedTestCase.addExpectedException
+   */
+  @Deprecated
+  protected CacheSerializableRunnable addExceptionTag1(final String expectedException) {
+    CacheSerializableRunnable addExceptionTag = new CacheSerializableRunnable(
+    "addExceptionTag") {
+      public void run2()
+      {
+        getCache().getLogger().info(
+            "<ExpectedException action=add>" + expectedException
+            + "</ExpectedException>");
+      }
+    };
+    
+    return addExceptionTag;
+  }
+
+  /**
+   * @deprecated Use DistributedTestCase.addExpectedException
+   */
+  @Deprecated
+  protected CacheSerializableRunnable removeExceptionTag1(final String expectedException) {
+    CacheSerializableRunnable removeExceptionTag = new CacheSerializableRunnable(
+    "removeExceptionTag") {
+      public void run2() throws CacheException {
+        getCache().getLogger().info(
+            "<ExpectedException action=remove>" + expectedException
+            + "</ExpectedException>");
+      }
+    };
+    return removeExceptionTag;
+  }
+
+  /**
+   * Used to generate a cache.xml. Basically just a CacheCreation
+   * with a few more methods implemented.
+   */
+  static class TestCacheCreation extends CacheCreation {
+    private boolean closed = false;
+    @Override
+    public void close() {
+      this.closed = true;
+    }
+    @Override
+    public boolean isClosed() {
+      return this.closed;
+    }
+  }
+  
+  public static File getDiskDir() {
+    int vmNum = VM.getCurrentVMNum();
+    File dir = new File("diskDir", "disk" + String.valueOf(vmNum)).getAbsoluteFile();
+    dir.mkdirs();
+    return dir;
+  }
+  
+  /**
+   * Return a set of disk directories
+   * for persistence tests. These directories
+   * will be automatically cleaned up 
+   * on test case closure.
+   */
+  public static File[] getDiskDirs() {
+    return new File[] {getDiskDir()};
+  }
+  
+  public static void cleanDiskDirs() throws IOException {
+    FileUtil.delete(getDiskDir());
+    File[] defaultStoreFiles = new File(".").listFiles(new FilenameFilter() {
+      
+      public boolean accept(File dir, String name) {
+        return name.startsWith("BACKUPDiskStore-" + System.getProperty("vmid"));
+      }
+    });
+    
+    for(File file: defaultStoreFiles) {
+      FileUtil.delete(file);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/ChildVM.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/ChildVM.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/ChildVM.java
new file mode 100644
index 0000000..bbd540d
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/ChildVM.java
@@ -0,0 +1,62 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.test.dunit.standalone;
+
+import hydra.HydraRuntimeException;
+import hydra.Log;
+
+import java.rmi.Naming;
+
+import org.apache.logging.log4j.Logger;
+
+import com.gemstone.gemfire.internal.OSProcess;
+import com.gemstone.gemfire.internal.logging.LogService;
+
+import com.gemstone.gemfire.test.dunit.standalone.DUnitLauncher.MasterRemote;
+
+/**
+ * @author dsmith
+ *
+ */
+public class ChildVM {
+  
+  static {
+    createHydraLogWriter();
+  }
+  
+  private final static Logger logger = LogService.getLogger();
+  
+  public static void main(String[] args) throws Throwable {
+    try {
+      int namingPort = Integer.getInteger(DUnitLauncher.RMI_PORT_PARAM).intValue();
+      int vmNum = Integer.getInteger(DUnitLauncher.VM_NUM_PARAM).intValue();
+      int pid = OSProcess.getId();
+      logger.info("VM" + vmNum + " is launching" + (pid > 0? " with PID " + pid : ""));
+      MasterRemote holder = (MasterRemote) Naming.lookup("//localhost:" + namingPort + "/" + DUnitLauncher.MASTER_PARAM);
+      DUnitLauncher.init(holder);
+      DUnitLauncher.locatorPort = holder.getLocatorPort();
+      Naming.rebind("//localhost:" + namingPort + "/vm" + vmNum, new RemoteDUnitVM());
+      holder.signalVMReady();
+      //This loop is here so this VM will die even if the master is mean killed.
+      while(true) {
+        holder.ping();
+        Thread.sleep(1000);
+      }
+    } catch (Throwable t) {
+      t.printStackTrace();
+      System.exit(1);
+    }
+  }
+
+  private static void createHydraLogWriter() {
+    try {
+      Log.createLogWriter("dunit-childvm", "fine");
+    } catch (HydraRuntimeException ignore) {
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/DUnitLauncher.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/DUnitLauncher.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/DUnitLauncher.java
new file mode 100644
index 0000000..5d27004
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/DUnitLauncher.java
@@ -0,0 +1,416 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.test.dunit.standalone;
+
+import hydra.Log;
+import hydra.MethExecutorResult;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.net.URISyntaxException;
+import java.nio.channels.FileChannel;
+import java.nio.charset.Charset;
+import java.rmi.AccessException;
+import java.rmi.AlreadyBoundException;
+import java.rmi.NotBoundException;
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.rmi.server.UnicastRemoteObject;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.Assert;
+
+import batterytest.greplogs.ExpectedStrings;
+import batterytest.greplogs.LogConsumer;
+
+import com.gemstone.gemfire.distributed.Locator;
+import com.gemstone.gemfire.internal.AvailablePortHelper;
+import com.gemstone.gemfire.internal.logging.LogService;
+
+import com.gemstone.gemfire.test.dunit.BounceResult;
+import com.gemstone.gemfire.test.dunit.DUnitEnv;
+import com.gemstone.gemfire.test.dunit.Host;
+import com.gemstone.gemfire.test.dunit.RemoteDUnitVMIF;
+import com.gemstone.gemfire.test.dunit.SerializableCallable;
+import com.gemstone.gemfire.test.dunit.VM;
+
+/**
+ * A class to build a fake test configuration and launch some DUnit VMS.
+ * 
+ * For use within eclipse. This class completely skips hydra and just starts
+ * some vms directly, creating a fake test configuration
+ * 
+ * Also, it's a good idea to set your working directory, because the test code
+ * a lot of files that it leaves around.
+ * 
+ * @author dsmith
+ *
+ */
+public class DUnitLauncher {
+
+  static int locatorPort;
+
+  private static final int NUM_VMS = 4;
+  private static final int DEBUGGING_VM_NUM = -1;
+  private static final int LOCATOR_VM_NUM = -2;
+
+  static final long STARTUP_TIMEOUT = 30 * 1000;
+  private static final String SUSPECT_FILENAME = "dunit_suspect.log";
+  private static File DUNIT_SUSPECT_FILE;
+
+  public static final String DUNIT_DIR = "dunit";
+  public static final String LOG_LEVEL = System.getProperty("logLevel", "config");
+  public static final String WORKSPACE_DIR_PARAM = "WORKSPACE_DIR";
+  public static final boolean LOCATOR_LOG_TO_DISK = Boolean.getBoolean("locatorLogToDisk");
+
+  static final String MASTER_PARAM = "DUNIT_MASTER";
+  static final String RMI_PORT_PARAM = "gemfire.DUnitLauncher.RMI_PORT";
+  static final String VM_NUM_PARAM = "gemfire.DUnitLauncher.VM_NUM";
+
+  private static final String LAUNCHED_PROPERTY = "gemfire.DUnitLauncher.LAUNCHED";
+
+  private DUnitLauncher() {
+  }
+  
+  private static boolean isHydra() {
+    try {
+      //TODO - this is hacky way to test for a hydra environment - see
+      //if there is registered test configuration object.
+      Class<?> clazz = Class.forName("hydra.TestConfig");
+      Method getInstance = clazz.getMethod("getInstance", new Class[0]);
+      getInstance.invoke(null, null);
+      return true;
+    } catch (Exception e) {
+      return false;
+    }
+  }
+  /**
+   * Launch DUnit. If the unit test was launched through
+   * the hydra framework, leave the test alone.
+   */
+  public static void launchIfNeeded() {
+    if(System.getProperties().contains(VM_NUM_PARAM)) {
+      //we're a dunit child vm, do nothing.
+      return;
+    }
+
+    if(!isHydra() &&!isLaunched()) {
+      try {
+        launch();
+      } catch (Exception e) {
+        throw new RuntimeException("Unable to launch dunit VMS", e);
+      }
+    }
+  }
+  
+  /**
+   * Test it see if the eclise dunit environment is launched.
+   */
+  public static boolean isLaunched() {
+    return Boolean.getBoolean(LAUNCHED_PROPERTY);
+  }
+  
+  public static String getLocatorString() {
+    return "localhost[" + locatorPort + "]";
+  }
+
+  
+  private static void launch() throws URISyntaxException, AlreadyBoundException, IOException, InterruptedException, NotBoundException  {
+//  initialize the log writer that hydra uses
+    Log.createLogWriter( "dunit-master", LOG_LEVEL );
+
+    DUNIT_SUSPECT_FILE = new File(SUSPECT_FILENAME);
+    DUNIT_SUSPECT_FILE.delete();
+    DUNIT_SUSPECT_FILE.deleteOnExit();
+    
+    locatorPort = AvailablePortHelper.getRandomAvailableTCPPort();
+     
+    //create an RMI registry and add an object to share our tests config
+    int namingPort = AvailablePortHelper.getRandomAvailableTCPPort();
+    Registry registry = LocateRegistry.createRegistry(namingPort);
+
+    final ProcessManager processManager = new ProcessManager(namingPort, registry);
+    Master master = new Master(registry, processManager);
+    registry.bind(MASTER_PARAM, master);
+
+    Runtime.getRuntime().addShutdownHook(new Thread() {
+      public void run() {
+        processManager.killVMs();
+      }
+    });
+    
+    //Create a VM for the locator
+    processManager.launchVM(LOCATOR_VM_NUM);
+    
+    //Launch an initial set of VMs
+    for(int i=0; i < NUM_VMS; i++) {
+      processManager.launchVM(i);
+    }
+    
+    //wait for the VMS to start up
+    if(!processManager.waitForVMs(STARTUP_TIMEOUT)) {
+      throw new RuntimeException("VMs did not start up with 30 seconds");
+    }
+    
+    //populate the Host class with our stubs. The tests use this host class
+    DUnitHost host = new DUnitHost(InetAddress.getLocalHost().getCanonicalHostName(), processManager);
+    host.init(registry, NUM_VMS);
+
+    init(master);
+    
+    startLocator(registry);
+  }
+  
+  public static Properties getDistributedSystemProperties() {
+    Properties p = new Properties();
+    p.setProperty("locators", getLocatorString());
+    p.setProperty("mcast-port", "0");
+    p.setProperty("enable-cluster-configuration", "false");
+    p.setProperty("use-cluster-configuration", "false");
+    p.setProperty("log-level", LOG_LEVEL);
+    return p;
+  }
+
+  /**
+   * Add an appender to Log4j which sends all INFO+ messages to a separate file
+   * which will be used later to scan for suspect strings.  The pattern of the
+   * messages conforms to the original log format so that hydra will be able
+   * to parse them.
+   */
+  private static void addSuspectFileAppender(final String workspaceDir) {
+    final String suspectFilename = new File(workspaceDir, SUSPECT_FILENAME).getAbsolutePath();
+
+    final LoggerContext appenderContext = ((org.apache.logging.log4j.core.Logger)
+        LogManager.getLogger(LogService.BASE_LOGGER_NAME)).getContext();
+
+    final PatternLayout layout = PatternLayout.createLayout(
+        "[%level{lowerCase=true} %date{yyyy/MM/dd HH:mm:ss.SSS z} <%thread> tid=%tid] %message%n%throwable%n", null, null,
+        Charset.defaultCharset(), true, false, "", "");
+    
+    final FileAppender fileAppender = FileAppender.createAppender(suspectFilename, "true", "false",
+        DUnitLauncher.class.getName(), "true", "false", "false", "0", layout, null, null, null, appenderContext.getConfiguration());
+    fileAppender.start();
+
+    LoggerConfig loggerConfig = appenderContext.getConfiguration().getLoggerConfig(LogService.BASE_LOGGER_NAME);
+    loggerConfig.addAppender(fileAppender, Level.INFO, null);
+  }
+  
+  private static void startLocator(Registry registry) throws IOException, NotBoundException {
+    RemoteDUnitVMIF remote = (RemoteDUnitVMIF) registry.lookup("vm" + LOCATOR_VM_NUM);
+    final File locatorLogFile =
+        LOCATOR_LOG_TO_DISK ? new File("locator-" + locatorPort + ".log") : new File(""); 
+    MethExecutorResult result = remote.executeMethodOnObject(new SerializableCallable() {
+      public Object call() throws IOException {
+        Properties p = getDistributedSystemProperties();
+        // I never want this locator to end up starting a jmx manager
+        // since it is part of the unit test framework
+        p.setProperty("jmx-manager", "false");
+        //Disable the shared configuration on this locator.
+        //Shared configuration tests create their own locator
+        p.setProperty("enable-cluster-configuration", "false");
+        Locator.startLocatorAndDS(locatorPort, locatorLogFile, p);
+        return null;
+      }
+    }, "call");
+    if(result.getException() != null) {
+      RuntimeException ex = new RuntimeException("Failed to start locator", result.getException());
+      ex.printStackTrace();
+      throw ex;
+    }
+  }
+
+  public static void init(MasterRemote master) {
+    DUnitEnv.set(new StandAloneDUnitEnv(master));
+    //fake out tests that are using a bunch of hydra stuff
+    String workspaceDir = System.getProperty(DUnitLauncher.WORKSPACE_DIR_PARAM) ;
+    workspaceDir = workspaceDir == null ? new File(".").getAbsolutePath() : workspaceDir;
+    
+    addSuspectFileAppender(workspaceDir);
+    
+    //Free off heap memory when disconnecting from the distributed system
+    System.setProperty("gemfire.free-off-heap-memory", "true");
+    
+    //indicate that this CM is controlled by the eclipse dunit.
+    System.setProperty(LAUNCHED_PROPERTY, "true");
+  }
+  
+  public static void closeAndCheckForSuspects() {
+    if (isLaunched()) {
+      final boolean skipLogMsgs = ExpectedStrings.skipLogMsgs("dunit");
+      final List<?> expectedStrings = ExpectedStrings.create("dunit");
+      final LogConsumer logConsumer = new LogConsumer(skipLogMsgs, expectedStrings, "log4j", 5);
+
+      final StringBuilder suspectStringBuilder = new StringBuilder();
+
+      BufferedReader buffReader = null;
+      FileChannel fileChannel = null;
+      try {
+        fileChannel = new FileOutputStream(DUNIT_SUSPECT_FILE, true).getChannel();
+        buffReader = new BufferedReader(new FileReader(DUNIT_SUSPECT_FILE));
+      } catch (FileNotFoundException e) {
+        System.err.println("Could not find the suspect string output file: " + e);
+        return;
+      }
+      try {
+        String line;
+        try {
+          while ((line = buffReader.readLine()) != null) {
+            final StringBuilder builder = logConsumer.consume(line);
+            if (builder != null) {
+              suspectStringBuilder.append(builder);
+            }
+          }
+        } catch (IOException e) {
+          System.err.println("Could not read the suspect string output file: " + e);
+        }
+        
+        try {
+          fileChannel.truncate(0);
+        } catch (IOException e) {
+          System.err.println("Could not truncate the suspect string output file: " + e);
+        }
+        
+      } finally {
+        try {
+          buffReader.close();
+          fileChannel.close();
+        } catch (IOException e) {
+          System.err.println("Could not close the suspect string output file: " + e);
+        }
+      }
+
+      if (suspectStringBuilder.length() != 0) {
+        System.err.println("Suspicious strings were written to the log during this run.\n"
+            + "Fix the strings or use DistributedTestCase.addExpectedException to ignore.\n"
+            + suspectStringBuilder);
+        
+        Assert.fail("Suspicious strings were written to the log during this run.\n"
+            + "Fix the strings or use DistributedTestCase.addExpectedException to ignore.\n"
+            + suspectStringBuilder);
+      }
+    }
+  }
+
+  public interface MasterRemote extends Remote {
+    public int getLocatorPort() throws RemoteException;
+    public void signalVMReady() throws RemoteException;
+    public void ping() throws RemoteException;
+    public BounceResult bounce(int pid) throws RemoteException;
+  }
+  
+  public static class Master extends UnicastRemoteObject implements MasterRemote {
+    private static final long serialVersionUID = 1178600200232603119L;
+    
+    private final Registry registry;
+    private final ProcessManager processManager;
+
+
+    public Master(Registry registry, ProcessManager processManager) throws RemoteException {
+      this.processManager = processManager;
+      this.registry = registry;
+    }
+
+    public int getLocatorPort()  throws RemoteException{
+      return locatorPort;
+    }
+
+    public synchronized void signalVMReady() {
+      processManager.signalVMReady();
+    }
+    
+    public void ping() {
+      //do nothing
+    }
+
+    @Override
+    public BounceResult bounce(int pid) {
+      processManager.bounce(pid);
+      
+      try {
+        if(!processManager.waitForVMs(STARTUP_TIMEOUT)) {
+          throw new RuntimeException("VMs did not start up with 30 seconds");
+        }
+        RemoteDUnitVMIF remote = (RemoteDUnitVMIF) registry.lookup("vm" + pid);
+        return new BounceResult(pid, remote);
+      } catch (RemoteException | NotBoundException e) {
+        throw new RuntimeException("could not lookup name", e);
+      } catch (InterruptedException e) {
+        throw new RuntimeException("Failed waiting for VM", e);
+      }
+    }
+  }
+  
+  private static class DUnitHost extends Host {
+    private static final long serialVersionUID = -8034165624503666383L;
+    
+    private transient final VM debuggingVM;
+
+    private transient ProcessManager processManager;
+    
+    public DUnitHost(String hostName, ProcessManager processManager) throws RemoteException {
+      super(hostName);
+      this.debuggingVM = new VM(this, -1, new RemoteDUnitVM());
+      this.processManager = processManager;
+    }
+    
+    public void init(Registry registry, int numVMs) throws AccessException, RemoteException, NotBoundException, InterruptedException {
+      for(int i = 0; i < numVMs; i++) {
+        RemoteDUnitVMIF remote = processManager.getStub(i);
+        addVM(i, remote);
+      }
+      
+      addLocator(LOCATOR_VM_NUM, processManager.getStub(LOCATOR_VM_NUM));
+      
+      addHost(this);
+    }
+
+    @Override
+    public VM getVM(int n) {
+      
+      if(n == DEBUGGING_VM_NUM) {
+        //for ease of debugging, pass -1 to get the local VM
+        return debuggingVM;
+      }
+
+      int oldVMCount = getVMCount();
+      if(n >= oldVMCount) {
+        //If we don't have a VM with that number, dynamically create it.
+        try {
+          for(int i = oldVMCount; i <= n; i++) {
+            processManager.launchVM(i);
+          }
+          processManager.waitForVMs(STARTUP_TIMEOUT);
+
+          for(int i = oldVMCount; i <= n; i++) {
+            addVM(i, processManager.getStub(i));
+          }
+
+        } catch (IOException | InterruptedException | NotBoundException e) {
+          throw new RuntimeException("Could not dynamically launch vm + " + n, e);
+        }
+      }
+      
+      return super.getVM(n);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/ProcessManager.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/ProcessManager.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/ProcessManager.java
new file mode 100644
index 0000000..d625892
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/ProcessManager.java
@@ -0,0 +1,228 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.test.dunit.standalone;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.lang.management.ManagementFactory;
+import java.lang.management.RuntimeMXBean;
+import java.rmi.AccessException;
+import java.rmi.NotBoundException;
+import java.rmi.RemoteException;
+import java.rmi.registry.Registry;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.io.FileUtils;
+
+import com.gemstone.gemfire.internal.FileUtil;
+import com.gemstone.gemfire.internal.logging.LogService;
+
+import com.gemstone.gemfire.test.dunit.RemoteDUnitVMIF;
+
+/**
+ * @author dsmith
+ *
+ */
+public class ProcessManager {
+  
+  private int namingPort;
+  private Map<Integer, ProcessHolder> processes = new HashMap<Integer, ProcessHolder>();
+  private File log4jConfig;
+  private int pendingVMs;
+  private Registry registry;
+
+  public ProcessManager(int namingPort, Registry registry) {
+    this.namingPort = namingPort;
+    this.registry = registry;
+  }
+  
+  public void launchVMs() throws IOException, NotBoundException {
+    log4jConfig = LogService.findLog4jConfigInCurrentDir();
+  }
+
+  public synchronized void launchVM(int vmNum) throws IOException {
+    if(processes.containsKey(vmNum)) {
+      throw new IllegalStateException("VM " + vmNum + " is already running.");
+    }
+    
+    String[] cmd = buildJavaCommand(vmNum, namingPort);
+    System.out.println("Executing " + Arrays.asList(cmd));
+    File workingDir = getVMDir(vmNum);
+    try {
+      FileUtil.delete(workingDir);
+    } catch(IOException e) {
+      //This delete is occasionally failing on some platforms, maybe due to a lingering
+      //process. Allow the process to be launched anyway.
+      System.err.println("Unable to delete " + workingDir + ". Currently contains " 
+                          + Arrays.asList(workingDir.list()));
+    }
+    workingDir.mkdirs();
+    if (log4jConfig != null) {
+      FileUtils.copyFileToDirectory(log4jConfig, workingDir);
+    }
+    
+    //TODO - delete directory contents, preferably with commons io FileUtils
+    Process process = Runtime.getRuntime().exec(cmd, null, workingDir);
+    pendingVMs++;
+    ProcessHolder holder = new ProcessHolder(process);
+    processes.put(vmNum, holder);
+    linkStreams(vmNum, holder, process.getErrorStream(), System.err);
+    linkStreams(vmNum, holder, process.getInputStream(), System.out);
+  }
+
+  public static File getVMDir(int vmNum) {
+    return new File(DUnitLauncher.DUNIT_DIR, "vm" + vmNum);
+  }
+  
+  public synchronized void killVMs() {
+    for(ProcessHolder process : processes.values()) {
+      if(process != null) {
+        //TODO - stop it gracefully? Why bother
+        process.kill();
+      }
+    }
+  }
+  
+  public synchronized void bounce(int vmNum) {
+    if(!processes.containsKey(vmNum)) {
+      throw new IllegalStateException("No such process " + vmNum);
+    }
+    try {
+      ProcessHolder holder = processes.remove(vmNum);
+      holder.kill();
+      holder.getProcess().waitFor();
+      launchVM(vmNum);
+    } catch (InterruptedException | IOException e) {
+      throw new RuntimeException("Unable to restart VM " + vmNum, e);
+    }
+  }
+   
+  private void linkStreams(final int vmNum, final ProcessHolder holder, final InputStream in, final PrintStream out) {
+    Thread ioTransport = new Thread() {
+      public void run() {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        String vmName = (vmNum==-2)? "[locator]" : "[vm_"+vmNum+"]";
+        try {
+          String line = reader.readLine();
+          while(line != null) {
+            out.print(vmName);
+            out.println(line);
+            line = reader.readLine();
+          }
+        } catch(Exception e) {
+          if(!holder.isKilled()) {
+            out.println("Error transporting IO from child process");
+            e.printStackTrace(out);
+          }
+        }
+      }
+    };
+
+    ioTransport.setDaemon(true);
+    ioTransport.start();
+  }
+
+  private String[] buildJavaCommand(int vmNum, int namingPort) {
+    String cmd = System.getProperty( "java.home" ) + File.separator + "bin" + File.separator + "java";
+    String classPath = System.getProperty("java.class.path");
+    //String tmpDir = System.getProperty("java.io.tmpdir");
+    String agent = getAgentString();
+    return new String[] {
+      cmd, "-classpath", classPath,
+      "-D" + DUnitLauncher.RMI_PORT_PARAM + "=" + namingPort,
+      "-D" + DUnitLauncher.VM_NUM_PARAM + "=" + vmNum,
+      "-D" + DUnitLauncher.WORKSPACE_DIR_PARAM + "=" + new File(".").getAbsolutePath(),
+      "-DlogLevel=" + DUnitLauncher.LOG_LEVEL,
+      "-Djava.library.path=" + System.getProperty("java.library.path"),
+      "-Xrunjdwp:transport=dt_socket,server=y,suspend=n",
+      "-XX:+HeapDumpOnOutOfMemoryError",
+      "-Xmx512m",
+      "-XX:MaxPermSize=256M",
+      "-Dgemfire.DEFAULT_MAX_OPLOG_SIZE=10",
+      "-Dgemfire.disallowMcastDefaults=true",
+      "-XX:MaxPermSize=256M",
+      "-ea",
+      agent,
+      ChildVM.class.getName()
+    };
+  }
+  
+  /**
+   * Get the java agent passed to this process and pass it to the child VMs.
+   * This was added to support jacoco code coverage reports
+   */
+  private String getAgentString() {
+    RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean();
+    if (runtimeBean != null) {
+      for(String arg: runtimeBean.getInputArguments()) {
+        if(arg.contains("-javaagent:")) {
+          //HACK for gradle bug  GRADLE-2859. Jacoco is passing a relative path
+          //That won't work when we pass this to dunit VMs in a different 
+          //directory
+          arg = arg.replace("-javaagent:..", "-javaagent:" + System.getProperty("user.dir") + File.separator + "..");
+          arg = arg.replace("destfile=..", "destfile=" + System.getProperty("user.dir") + File.separator + "..");
+          return arg;
+        }
+      }
+    }
+    
+    return "-DdummyArg=true";
+  }
+
+  synchronized void signalVMReady() {
+    pendingVMs--;
+    this.notifyAll();
+  }
+  
+  public synchronized boolean waitForVMs(long timeout) throws InterruptedException {
+    long end = System.currentTimeMillis() + timeout;
+    while(pendingVMs > 0) {
+      long remaining = end - System.currentTimeMillis();
+      if(remaining <= 0) {
+        return false;
+      }
+      this.wait(remaining);
+    }
+    
+    return true;
+  }
+  
+  private static class ProcessHolder {
+    private final Process process;
+    private volatile boolean killed = false;
+    
+    public ProcessHolder(Process process) {
+      this.process = process;
+    }
+
+    public void kill() {
+      this.killed = true;
+      process.destroy();
+      
+    }
+
+    public Process getProcess() {
+      return process;
+    }
+
+    public boolean isKilled() {
+      return killed;
+    }
+  }
+
+  public RemoteDUnitVMIF getStub(int i) throws AccessException, RemoteException, NotBoundException, InterruptedException {
+    waitForVMs(DUnitLauncher.STARTUP_TIMEOUT);
+    return (RemoteDUnitVMIF) registry.lookup("vm" + i);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/RemoteDUnitVM.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/RemoteDUnitVM.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/RemoteDUnitVM.java
new file mode 100644
index 0000000..031b706
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/RemoteDUnitVM.java
@@ -0,0 +1,136 @@
+/*=========================================================================
+ * Copyright (c) 2002-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.test.dunit.standalone;
+
+import hydra.MethExecutor;
+import hydra.MethExecutorResult;
+
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+import org.apache.logging.log4j.Logger;
+
+import com.gemstone.gemfire.internal.logging.LogService;
+
+import com.gemstone.gemfire.test.dunit.RemoteDUnitVMIF;
+
+/**
+ * @author dsmith
+ *
+ */
+public class RemoteDUnitVM extends UnicastRemoteObject implements RemoteDUnitVMIF {
+  
+  private static final Logger logger = LogService.getLogger();
+  
+  public RemoteDUnitVM() throws RemoteException {
+    super();
+  }
+
+  /** 
+   * Called remotely by the master controller to cause the client to execute 
+   * the instance method on the object.  Does this synchronously (does not spawn
+   * a thread).  This method is used by the unit test framework, dunit.
+   *
+   * @param obj the object to execute the method on
+   * @param methodName the name of the method to execute
+   * @return the result of method execution
+   */ 
+   public MethExecutorResult executeMethodOnObject( Object obj, String methodName ) {
+     String name = obj.getClass().getName() + "." + methodName + 
+       " on object: " + obj;
+     logger.info("Received method: " + name);
+     long start = System.currentTimeMillis();
+     MethExecutorResult result = MethExecutor.executeObject( obj, methodName );
+     long delta = System.currentTimeMillis() - start;
+     logger.info( "Got result: " + result.toString().trim()  + " from " +
+               name + " (took " + delta + " ms)");
+     return result;
+   }
+
+   /**
+    * Executes a given instance method on a given object with the given
+    * arguments. 
+    */
+   public MethExecutorResult executeMethodOnObject(Object obj,
+                                                   String methodName,
+                                                   Object[] args) {
+     String name = obj.getClass().getName() + "." + methodName + 
+              (args != null ? " with " + args.length + " args": "") +
+       " on object: " + obj;
+     logger.info("Received method: " + name);
+     long start = System.currentTimeMillis();
+     MethExecutorResult result = 
+       MethExecutor.executeObject(obj, methodName, args);
+     long delta = System.currentTimeMillis() - start;
+     logger.info( "Got result: " + result.toString() + " from " + name + 
+               " (took " + delta + " ms)");
+     return result;
+   }
+
+  /** 
+   * Called remotely by the master controller to cause the client to execute 
+   * the method on the class.  Does this synchronously (does not spawn a thread).
+   * This method is used by the unit test framework, dunit.
+   *
+   * @param className the name of the class execute
+   * @param methodName the name of the method to execute
+   * @return the result of method execution
+   */ 
+   public MethExecutorResult executeMethodOnClass( String className, String methodName ) {
+     String name = className + "." + methodName;
+     logger.info("Received method: " +  name);
+     long start = System.currentTimeMillis();
+     MethExecutorResult result = MethExecutor.execute( className, methodName );
+     long delta = System.currentTimeMillis() - start;
+     logger.info( "Got result: " + result.toString() + " from " + name + 
+               " (took " + delta + " ms)");
+     
+     return result;
+   }
+
+   /**
+    * Executes a given static method in a given class with the given
+    * arguments. 
+    */
+   public MethExecutorResult executeMethodOnClass(String className,
+                                                  String methodName,
+                                                  Object[] args) {
+     String name = className + "." + methodName + 
+       (args != null ? " with " + args.length + " args": "");
+     logger.info("Received method: " + name);
+     long start = System.currentTimeMillis();
+     MethExecutorResult result = 
+       MethExecutor.execute(className, methodName, args);
+     long delta = System.currentTimeMillis() - start;
+     logger.info( "Got result: " + result.toString() + " from " + name +
+               " (took " + delta + " ms)");
+     return result;
+   }
+
+  public void executeTask(int tsid, int type, int index) throws RemoteException {
+    throw new UnsupportedOperationException();
+    
+  }
+  
+  public void runShutdownHook() throws RemoteException {
+    
+  }
+
+  public void notifyDynamicActionComplete(int actionId) throws RemoteException {
+    throw new UnsupportedOperationException();
+    
+  }
+
+  public void shutDownVM(boolean disconnect, boolean runShutdownHook)
+      throws RemoteException {
+  }
+
+  public void disconnectVM()
+  throws RemoteException {
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/StandAloneDUnitEnv.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/StandAloneDUnitEnv.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/StandAloneDUnitEnv.java
new file mode 100644
index 0000000..8746a70
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/standalone/StandAloneDUnitEnv.java
@@ -0,0 +1,66 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.test.dunit.standalone;
+
+import java.io.File;
+import java.rmi.RemoteException;
+import java.util.Properties;
+
+import com.gemstone.gemfire.test.dunit.BounceResult;
+import com.gemstone.gemfire.test.dunit.DUnitEnv;
+import com.gemstone.gemfire.test.dunit.standalone.DUnitLauncher.MasterRemote;
+
+public class StandAloneDUnitEnv extends DUnitEnv {
+
+  private MasterRemote master;
+
+  public StandAloneDUnitEnv(MasterRemote master) {
+    this.master = master;
+  }
+
+  @Override
+  public String getLocatorString() {
+    return DUnitLauncher.getLocatorString();
+  }
+
+  @Override
+  public String getLocatorAddress() {
+    return "localhost";
+  }
+  
+  @Override
+  public int getLocatorPort() {
+    return DUnitLauncher.locatorPort;
+  }
+
+  @Override
+  public Properties getDistributedSystemProperties() {
+    return DUnitLauncher.getDistributedSystemProperties();
+  }
+
+  @Override
+  public int getPid() {
+    return Integer.getInteger(DUnitLauncher.VM_NUM_PARAM, -1).intValue();
+  }
+
+  @Override
+  public int getVMID() {
+    return getPid();
+  }
+
+  @Override
+  public BounceResult bounce(int pid) throws RemoteException {
+    return master.bounce(pid);
+  }
+
+  @Override
+  public File getWorkingDirectory(int pid) {
+    return ProcessManager.getVMDir(pid);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/BasicDUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/BasicDUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/BasicDUnitTest.java
new file mode 100644
index 0000000..d0b1710
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/BasicDUnitTest.java
@@ -0,0 +1,124 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.test.dunit.tests;
+
+import static com.gemstone.gemfire.test.dunit.Assert.*;
+import static org.hamcrest.Matchers.*;
+
+import java.util.Properties;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.gemstone.gemfire.test.dunit.AsyncInvocation;
+import com.gemstone.gemfire.test.dunit.DistributedTestCase;
+import com.gemstone.gemfire.test.dunit.Host;
+import com.gemstone.gemfire.test.dunit.RMIException;
+import com.gemstone.gemfire.test.dunit.VM;
+
+/**
+ * This class tests the basic functionality of the distributed unit
+ * test framework.
+ */
+public class BasicDUnitTest extends DistributedTestCase {
+  private static final long serialVersionUID = 1L;
+
+  private static final String REMOTE_THROW_EXCEPTION_MESSAGE = "Throwing remoteThrowException";
+  
+  private static Properties bindings = new Properties();
+
+  /**
+   * Tests how the DUnit framework handles an error
+   */
+  @Test(expected = RMIException.class)
+  public void testDontCatchRemoteException() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    vm.invoke(getClass(), "remoteThrowException");
+  }
+
+  @Test
+  public void testRemoteInvocationWithException() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    try {
+      vm.invoke(getClass(), "remoteThrowException");
+      fail("Should have thrown a BasicTestException");
+
+    } catch (RMIException expected) {
+      Throwable cause = expected.getCause();
+      assertThat(cause, is(instanceOf(BasicTestException.class)));
+      assertThat(cause.getMessage(), is("Throwing remoteThrowException"));
+    }
+  }
+  
+  @Test
+  public void testRemoteInvokeAsync() throws InterruptedException {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    String name = this.getUniqueName();
+    String value = "Hello";
+
+    AsyncInvocation ai = vm.invokeAsync(this.getClass(), "remoteBind", new Object[] { name, value });
+    ai.join();
+    if (ai.exceptionOccurred()) {
+      fail("remoteBind failed", ai.getException());
+    }
+
+    ai = vm.invokeAsync(this.getClass(), "remoteValidateBind", new Object[] {name, value });
+    ai.join();
+    if (ai.exceptionOccurred()) {
+      fail("remoteValidateBind failed", ai.getException());
+    }
+  }
+
+  @Test
+  public void testRemoteInvokeAsyncWithException() throws InterruptedException {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+
+    AsyncInvocation ai = vm.invokeAsync(this.getClass(), "remoteThrowException");
+    ai.join();
+    assertTrue(ai.exceptionOccurred());
+    Throwable ex = ai.getException();
+    assertTrue(ex instanceof BasicTestException);
+  }
+
+  @Test
+  @Ignore("not implemented")
+  public void testRemoteInvocationBoolean() {
+  }
+
+  /**
+   * Accessed via reflection.  DO NOT REMOVE
+   */
+  protected static void remoteThrowException() {
+    throw new BasicTestException(REMOTE_THROW_EXCEPTION_MESSAGE);
+  }
+  
+  protected static void remoteBind(String name, String s) {
+    new BasicDUnitTest().getSystem(); // forces connection
+    bindings.setProperty(name, s);
+  }
+
+  protected static void remoteValidateBind(String name, String expected) {
+    assertEquals(expected, bindings.getProperty(name));
+  }
+
+  protected static class BasicTestException extends RuntimeException {
+    private static final long serialVersionUID = 1L;
+
+    public BasicTestException() {
+      super();
+    }
+    
+    public BasicTestException(String message) {
+      super(message);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/DUnitFrameworkTestSuite.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/DUnitFrameworkTestSuite.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/DUnitFrameworkTestSuite.java
new file mode 100755
index 0000000..82664b2
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/DUnitFrameworkTestSuite.java
@@ -0,0 +1,16 @@
+package com.gemstone.gemfire.test.dunit.tests;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+  BasicDUnitTest.class,
+  VMDUnitTest.class,
+})
+/**
+ * Suite of tests for the test.dunit DUnit Test framework.
+ */
+public class DUnitFrameworkTestSuite {
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/TestFailure.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/TestFailure.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/TestFailure.java
new file mode 100644
index 0000000..d6d89e3
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/TestFailure.java
@@ -0,0 +1,37 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.test.dunit.tests;
+
+import static org.junit.Assert.*;
+
+import com.gemstone.gemfire.test.dunit.DistributedTestCase;
+
+/**
+ * The tests in this class always fail.  It is used when developing
+ * DUnit to give us an idea of how test failure are logged, etc.
+ *
+ * @author David Whitlock
+ *
+ * @since 3.0
+ */
+public class TestFailure extends DistributedTestCase {
+  private static final long serialVersionUID = 1L;
+
+  public void testFailure() {
+    assertTrue("Test Failure", false);
+  }
+
+  public void testError() {
+    String s = "Test Error";
+    throw new Error(s);
+  }
+
+  public void testHang() throws InterruptedException {
+    Thread.sleep(100000 * 1000);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/VMDUnitTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/VMDUnitTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/VMDUnitTest.java
new file mode 100644
index 0000000..12faffe
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/test/dunit/tests/VMDUnitTest.java
@@ -0,0 +1,245 @@
+/*=========================================================================
+ * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
+ * This product is protected by U.S. and international copyright
+ * and intellectual property laws. Pivotal products are covered by
+ * one or more patents listed at http://www.pivotal.io/patents.
+ *=========================================================================
+ */
+package com.gemstone.gemfire.test.dunit.tests;
+
+import static org.hamcrest.Matchers.*;
+import static org.junit.Assert.*;
+
+import java.io.Serializable;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Test;
+
+import com.gemstone.gemfire.test.dunit.AsyncInvocation;
+import com.gemstone.gemfire.test.dunit.DistributedTestCase;
+import com.gemstone.gemfire.test.dunit.Host;
+import com.gemstone.gemfire.test.dunit.RMIException;
+import com.gemstone.gemfire.test.dunit.VM;
+
+/**
+ * This class tests the functionality of the {@link VM} class.
+ */
+public class VMDUnitTest extends DistributedTestCase {
+  private static final long serialVersionUID = 1L;
+  
+  private static final boolean BOOLEAN_VALUE = true;
+  private static final byte BYTE_VALUE = (byte) 40;
+  private static final long LONG_VALUE = 42L;
+  private static final String STRING_VALUE = "BLAH BLAH BLAH";
+
+  private static final AtomicInteger COUNTER = new AtomicInteger();
+  
+  @Test
+  public void testInvokeNonExistentMethod() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    try {
+      vm.invoke(VMDUnitTest.class, "nonExistentMethod");
+      fail("Should have thrown an RMIException");
+
+    } catch (RMIException ex) {
+      String s = "Excepted a NoSuchMethodException, got a " + ex.getCause();;
+      assertTrue(s, ex.getCause() instanceof NoSuchMethodException);
+    }
+  }
+
+  @Test
+  public void testInvokeStaticBoolean() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    assertEquals(BOOLEAN_VALUE, vm.invokeBoolean(VMDUnitTest.class, "remoteBooleanMethod")); 
+  }
+
+  @Test
+  public void testInvokeStaticBooleanNotBoolean() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    try {
+      vm.invokeBoolean(VMDUnitTest.class, "remoteByteMethod");
+      fail("Should have thrown an IllegalArgumentException");
+
+    } catch (IllegalArgumentException ex) {
+      String s = "Method \"remoteByteMethod\" in class \"" + getClass().getName() + "\" returned a \"" + Byte.class.getName() + "\" expected a boolean";
+      assertThat(ex.getMessage(), equalTo(s));
+    }
+  }
+
+  @Test
+  public void testInvokeStaticLong() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    assertEquals(LONG_VALUE, vm.invokeLong(VMDUnitTest.class, "remoteLongMethod")); 
+  }
+
+  @Test
+  public void testInvokeStaticLongNotLong() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    try {
+      vm.invokeLong(VMDUnitTest.class, "remoteByteMethod");
+      fail("Should have thrown an IllegalArgumentException");
+
+    } catch (IllegalArgumentException ex) {
+      String s = "Method \"remoteByteMethod\" in class \"" + getClass().getName() + "\" returned a \"" + Byte.class.getName() + "\" expected a long";
+      assertThat(ex.getMessage(), equalTo(s));
+    }
+  }
+
+  @Test
+  public void testInvokeInstanceLong() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    assertEquals(LONG_VALUE, vm.invokeLong(new ClassWithLong(), "getLong"));
+  }
+
+  @Test
+  public void testInvokeInstanceLongNotLong() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    try {
+      vm.invokeLong(new ClassWithByte(), "getByte");
+      fail("Should have thrown an IllegalArgumentException");
+
+    } catch (IllegalArgumentException ex) {
+      String s = "Method \"getByte\" in class \"" + ClassWithByte.class.getName() + "\" returned a \"" + Byte.class.getName() + "\" expected a long";
+      assertThat(ex.getMessage(), equalTo(s));
+    }
+  }
+
+  @Test
+  public void testInvokeInstance() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    assertEquals(STRING_VALUE, vm.invoke(new ClassWithString(), "getString"));
+  }
+
+  @Test
+  public void testInvokeRunnable() {
+    Host host = Host.getHost(0);
+    VM vm = host.getVM(0);
+    try {
+      vm.invoke(new InvokeRunnable());
+      fail("Should have thrown a BasicTestException");
+
+    } catch (RMIException ex) {
+      assertTrue(ex.getCause() instanceof BasicDUnitTest.BasicTestException);
+    }
+  }
+  
+  @Test
+  public void testReturnValue() throws Exception {
+    final Host host = Host.getHost(0);
+    final VM vm = host.getVM(0);
+    // Assert class static invocation works
+    AsyncInvocation a1 = vm.invokeAsync(getClass(), "getAndIncStaticCount");
+    a1.join();
+    assertEquals(new Integer(0), a1.getReturnValue());
+    // Assert class static invocation with args works
+    a1 = vm.invokeAsync(getClass(), "incrementStaticCount", new Object[] {new Integer(2)});
+    a1.join();
+    assertEquals(new Integer(3), a1.getReturnValue());
+    // Assert that previous values are not returned when invoking method w/ no return val
+    a1 = vm.invokeAsync(getClass(), "incStaticCount");
+    a1.join();
+    assertNull(a1.getReturnValue());
+    // Assert that previous null returns are over-written 
+    a1 = vm.invokeAsync(getClass(), "getAndIncStaticCount");
+    a1.join();
+    assertEquals(new Integer(4), a1.getReturnValue());
+
+    // Assert object method invocation works with zero arg method
+    final VMTestObject o = new VMTestObject(0);
+    a1 = vm.invokeAsync(o, "incrementAndGet", new Object[] {});
+    a1.join();
+    assertEquals(new Integer(1), a1.getReturnValue());
+    // Assert object method invocation works with no return
+    a1 = vm.invokeAsync(o, "set", new Object[] {new Integer(3)});
+    a1.join();
+    assertNull(a1.getReturnValue());
+  }
+
+  protected static Integer getAndIncStaticCount() {
+    return new Integer(COUNTER.getAndIncrement());
+  }
+  
+  protected static Integer incrementStaticCount(Integer inc) {
+    return new Integer(COUNTER.addAndGet(inc.intValue()));
+  }
+  
+  protected static void incStaticCount() {
+    COUNTER.incrementAndGet();
+  }
+  
+  /**
+   * Accessed via reflection.  DO NOT REMOVE
+   */
+  protected static byte remoteByteMethod() {
+    return BYTE_VALUE;
+  }
+
+  /**
+   * Accessed via reflection.  DO NOT REMOVE
+   */
+  protected static boolean remoteBooleanMethod() {
+    return BOOLEAN_VALUE;
+  }
+
+  /**
+   * Accessed via reflection.  DO NOT REMOVE
+   */
+  protected static long remoteLongMethod() {
+    return LONG_VALUE;
+  }
+
+  protected static class InvokeRunnable implements Serializable, Runnable {
+    private static final long serialVersionUID = 1L;
+    @Override
+    public void run() {
+      throw new BasicDUnitTest.BasicTestException();
+    }
+  }
+
+  protected static class ClassWithString implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public String getString() {
+      return STRING_VALUE;
+    }
+  }
+
+  protected static class VMTestObject implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private final AtomicInteger val;
+    public VMTestObject(int init) {
+      this.val = new AtomicInteger(init);
+    }
+    public Integer get() {
+      return new Integer(this.val.get());
+    }
+    public Integer incrementAndGet() {
+      return new Integer(this.val.incrementAndGet());
+    }
+    public void set(Integer newVal) {
+      this.val.set(newVal.intValue());
+    }
+  }
+  
+  protected static class ClassWithLong implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public long getLong() {
+      return LONG_VALUE;
+    }
+  }
+
+  protected static class ClassWithByte implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public byte getByte() {
+      return BYTE_VALUE;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/dunit/DistributedTestCase.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/dunit/DistributedTestCase.java b/gemfire-core/src/test/java/dunit/DistributedTestCase.java
index 8aa8b6d..d26d3e1 100755
--- a/gemfire-core/src/test/java/dunit/DistributedTestCase.java
+++ b/gemfire-core/src/test/java/dunit/DistributedTestCase.java
@@ -144,7 +144,7 @@ public abstract class DistributedTestCase extends TestCase implements java.io.Se
     vm.invoke(new SerializableRunnable("Attach Debugger") {
       public void run() {
         com.gemstone.gemfire.internal.util.DebuggerSupport.
-        waitForJavaDebugger(getSystem().getLogWriter().convertToLogWriterI18n(), msg);
+        waitForJavaDebugger(msg);
       } 
     });
   }
@@ -526,7 +526,7 @@ public abstract class DistributedTestCase extends TestCase implements java.io.Se
    * NOTE: if you use this method be sure that you clean up the VM before the end of your
    * test with disconnectFromDS() or disconnectAllFromDS().
    */
-  public boolean crashDistributedSystem(VM vm) {
+  public static boolean crashDistributedSystem(VM vm) {
     return (Boolean)vm.invoke(new SerializableCallable("crash distributed system") {
       public Object call() throws Exception {
         DistributedSystem msys = InternalDistributedSystem.getAnyInstance();
@@ -544,7 +544,7 @@ public abstract class DistributedTestCase extends TestCase implements java.io.Se
    * NOTE: if you use this method be sure that you clean up the VM before the end of your
    * test with disconnectFromDS() or disconnectAllFromDS().
    */
-  public void crashDistributedSystem(final DistributedSystem msys) {
+  public static void crashDistributedSystem(final DistributedSystem msys) {
     MembershipManagerHelper.inhibitForcedDisconnectLogging(true);
     MembershipManagerHelper.playDead(msys);
     JChannel c = MembershipManagerHelper.getJChannel(msys);

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/dunit/VM.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/dunit/VM.java b/gemfire-core/src/test/java/dunit/VM.java
index 323dbfb..537c53b 100644
--- a/gemfire-core/src/test/java/dunit/VM.java
+++ b/gemfire-core/src/test/java/dunit/VM.java
@@ -14,8 +14,6 @@ import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.rmi.RemoteException;
 import java.util.concurrent.Callable;
-//import java.util.Iterator;
-//import java.util.Vector;
 
 /**
  * This class represents a Java Virtual Machine that runs on a host.

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-core/src/test/java/dunit/standalone/ProcessManager.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/dunit/standalone/ProcessManager.java b/gemfire-core/src/test/java/dunit/standalone/ProcessManager.java
index 5b38816..965b1ab 100644
--- a/gemfire-core/src/test/java/dunit/standalone/ProcessManager.java
+++ b/gemfire-core/src/test/java/dunit/standalone/ProcessManager.java
@@ -157,7 +157,7 @@ public class ProcessManager {
       "-XX:MaxPermSize=256M",
       "-ea",
       agent,
-      "dunit.standalone.ChildVM"
+      ChildVM.class.getName()
     };
   }
   

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/49b2913a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/categories/MembershipTest.java
----------------------------------------------------------------------
diff --git a/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/categories/MembershipTest.java b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/categories/MembershipTest.java
new file mode 100755
index 0000000..458eaad
--- /dev/null
+++ b/gemfire-junit/src/test/java/com/gemstone/gemfire/test/junit/categories/MembershipTest.java
@@ -0,0 +1,9 @@
+package com.gemstone.gemfire.test.junit.categories;
+
+/**
+ * JUnit Test Category that specifies a test involving membership in a distributed system.
+ * 
+ * @author Kirk Lund
+ */
+public class MembershipTest {
+}