You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by js...@apache.org on 2017/04/16 16:18:09 UTC

[1/8] geode git commit: GEODE-2686: Remove JarClassLoader

Repository: geode
Updated Branches:
  refs/heads/develop f272762f8 -> 6f7f94399


http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java
index c3d7f5e..b111e45 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java
@@ -24,7 +24,7 @@ import org.apache.geode.cache.Cache;
 import org.apache.geode.distributed.internal.ClusterConfigurationService;
 import org.apache.geode.distributed.internal.InternalLocator;
 import org.apache.geode.internal.ClassPathLoader;
-import org.apache.geode.internal.JarClassLoader;
+import org.apache.geode.internal.DeployedJar;
 import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.internal.lang.StringUtils;
@@ -36,6 +36,8 @@ import org.apache.geode.test.dunit.rules.Server;
 
 import java.io.File;
 import java.io.Serializable;
+import java.net.URL;
+import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -137,7 +139,9 @@ public class ClusterConfig implements Serializable {
         .collect(Collectors.toSet());
     Set<String> actualJarNames = toSetIgnoringHiddenFiles(
         serverVM.getWorkingDir().list((dir, filename) -> filename.contains(".jar")));
-    assertThat(actualJarNames).isEqualTo(expectedJarNames);
+
+    // We will end up with extra jars on disk if they are deployed and then undeployed
+    assertThat(expectedJarNames).isSubsetOf(actualJarNames);
 
     // verify config exists in memory
     serverVM.invoke(() -> {
@@ -154,22 +158,21 @@ public class ClusterConfig implements Serializable {
       }
 
       for (String jar : this.getJarNames()) {
-        JarClassLoader jarClassLoader = findJarClassLoader(jar);
-        assertThat(jarClassLoader).isNotNull();
-        assertThat(Class.forName(nameOfClassContainedInJar(jar), true, jarClassLoader)).isNotNull();
+        DeployedJar deployedJar = ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jar);
+        assertThat(deployedJar).isNotNull();
+        assertThat(Class.forName(nameOfClassContainedInJar(jar), true,
+            new URLClassLoader(new URL[] {deployedJar.getFileURL()}))).isNotNull();
       }
-    });
-  }
 
-  private static JarClassLoader findJarClassLoader(final String jarName) {
-    Collection<ClassLoader> classLoaders = ClassPathLoader.getLatest().getClassLoaders();
-    for (ClassLoader classLoader : classLoaders) {
-      if (classLoader instanceof JarClassLoader
-          && ((JarClassLoader) classLoader).getJarName().equals(jarName)) {
-        return (JarClassLoader) classLoader;
+      // If we have extra jars on disk left over from undeploy, make sure they aren't used
+      Set<String> undeployedJarNames = new HashSet<>(actualJarNames);
+      undeployedJarNames.removeAll(expectedJarNames);
+      for (String jar : undeployedJarNames) {
+        DeployedJar undeployedJar =
+            ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jar);
+        assertThat(undeployedJar).isNull();
       }
-    }
-    return null;
+    });
   }
 
 
@@ -183,7 +186,7 @@ public class ClusterConfig implements Serializable {
   }
 
   private static String getServerJarName(String jarName) {
-    return JarDeployer.JAR_PREFIX + jarName + "#1";
+    return jarName.replace(".jar", "") + ".v1.jar";
   }
 
 

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java
index 0a4785d..19a1662 100644
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/GfshShellConnectionRule.java
@@ -14,6 +14,7 @@
  */
 package org.apache.geode.test.dunit.rules;
 
+import static org.apache.geode.test.dunit.IgnoredException.addIgnoredException;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import org.apache.geode.internal.lang.StringUtils;
@@ -23,6 +24,7 @@ import org.apache.geode.management.internal.cli.HeadlessGfsh;
 import org.apache.geode.management.internal.cli.i18n.CliStrings;
 import org.apache.geode.management.internal.cli.result.CommandResult;
 import org.apache.geode.management.internal.cli.util.CommandStringBuilder;
+import org.apache.geode.test.dunit.IgnoredException;
 import org.apache.geode.test.junit.rules.DescribedExternalResource;
 import org.json.JSONArray;
 import org.junit.rules.TemporaryFolder;
@@ -58,6 +60,7 @@ public class GfshShellConnectionRule extends DescribedExternalResource {
   private PortType portType = PortType.jmxManger;
   private HeadlessGfsh gfsh = null;
   private boolean connected = false;
+  private IgnoredException ignoredException;
   private TemporaryFolder temporaryFolder = new TemporaryFolder();
 
   public GfshShellConnectionRule() {
@@ -78,6 +81,9 @@ public class GfshShellConnectionRule extends DescribedExternalResource {
   protected void before(Description description) throws Throwable {
     this.gfsh = new HeadlessGfsh(getClass().getName(), 30,
         temporaryFolder.newFolder("gfsh_files").getAbsolutePath());
+    ignoredException =
+        addIgnoredException("java.rmi.NoSuchObjectException: no such object in table");
+
     // do not auto connect if no port initialized
     if (portSupplier == null) {
       return;
@@ -168,6 +174,10 @@ public class GfshShellConnectionRule extends DescribedExternalResource {
   @Override
   protected void after(Description description) throws Throwable {
     close();
+
+    if (ignoredException != null) {
+      ignoredException.remove();
+    }
   }
 
   public void disconnect() throws Exception {

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/CommandOverHttpDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/CommandOverHttpDUnitTest.java b/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/CommandOverHttpDUnitTest.java
index 5f4cd74..2a6a03b 100644
--- a/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/CommandOverHttpDUnitTest.java
+++ b/geode-web/src/test/java/org/apache/geode/management/internal/cli/commands/CommandOverHttpDUnitTest.java
@@ -33,7 +33,7 @@ import org.apache.geode.test.junit.runner.SuiteRunner;
     ListAndDescribeDiskStoreCommandsDUnitTest.class, ListIndexCommandDUnitTest.class,
     MemberCommandsDUnitTest.class, MiscellaneousCommandsDUnitTest.class,
     QueueCommandsDUnitTest.class, ShellCommandsDUnitTest.class, ShowDeadlockDUnitTest.class,
-    ShowMetricsDUnitTest.class, ShowStackTraceDUnitTest.class, UserCommandsDUnitTest.class})
+    ShowMetricsDUnitTest.class, ShowStackTraceDUnitTest.class})
 public class CommandOverHttpDUnitTest {
   @ClassRule
   public static ProvideSystemProperty provideSystemProperty =


[6/8] geode git commit: GEODE-2705: Jars undeployed from cluster configuration will not be loaded from disk on member restart

Posted by js...@apache.org.
GEODE-2705: Jars undeployed from cluster configuration will not be loaded from disk on member restart


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

Branch: refs/heads/develop
Commit: ee11b0a43b66a42eb10a16b92993436b2298c042
Parents: 6fd2d12
Author: Jared Stewart <js...@pivotal.io>
Authored: Thu Apr 6 10:22:26 2017 -0700
Committer: Jared Stewart <js...@pivotal.io>
Committed: Sun Apr 16 09:10:01 2017 -0700

----------------------------------------------------------------------
 .../org/apache/geode/internal/DeployedJar.java  |   6 +
 .../org/apache/geode/internal/JarDeployer.java  | 148 +++++++++++--------
 .../cache/ClusterConfigurationLoader.java       |  30 +++-
 .../geode/internal/cache/GemFireCacheImpl.java  |   7 +-
 .../ClassPathLoaderIntegrationTest.java         |  10 +-
 .../geode/internal/ClassPathLoaderTest.java     |  58 --------
 .../internal/JarDeployerIntegrationTest.java    |  63 +++++++-
 .../geode/management/DeployJarTestSuite.java    |   4 +-
 .../internal/configuration/ClusterConfig.java   |  23 ++-
 .../ClusterConfigDeployJarDUnitTest.java        |  76 ++++++++++
 10 files changed, 278 insertions(+), 147 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java b/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
index f4f4028..8adec1f 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
@@ -85,6 +85,9 @@ public class DeployedJar {
     this(versionedJarFile, jarName, Files.readAllBytes(versionedJarFile.toPath()));
   }
 
+  /**
+   * Writes the given jarBytes to versionedJarFile
+   */
   public DeployedJar(File versionedJarFile, final String jarName, byte[] jarBytes)
       throws IOException {
     Assert.assertTrue(jarBytes != null, "jarBytes cannot be null");
@@ -377,6 +380,9 @@ public class DeployedJar {
     return new byte[0];
   }
 
+  /**
+   * @return the unversioned name of this jar file, e.g. myJar.jar
+   */
   public String getJarName() {
     return this.jarName;
   }

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java b/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
index ad5c435..669802c 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
@@ -18,10 +18,6 @@ import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toSet;
 
 import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang.ArrayUtils;
-import org.apache.geode.GemFireException;
-import org.apache.geode.GemFireIOException;
-import org.apache.geode.SystemFailure;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.logging.log4j.Logger;
 
@@ -34,13 +30,9 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Serializable;
 import java.net.URL;
-import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -83,11 +75,23 @@ public class JarDeployer implements Serializable {
     return this.deployDirectory;
   }
 
+  /**
+   * Writes the jarBytes for the given jarName to the next version of that jar file (if the bytes do
+   * not match the latest deployed version)
+   * 
+   * @return the DeployedJar that was written from jarBytes, or null if those bytes matched the
+   *         latest deployed version
+   */
   public DeployedJar deployWithoutRegistering(final String jarName, final byte[] jarBytes)
       throws IOException {
     lock.lock();
 
     try {
+      boolean shouldDeployNewVersion = shouldDeployNewVersion(jarName, jarBytes);
+      if (!shouldDeployNewVersion) {
+        return null;
+      }
+
       verifyWritableDeployDirectory();
 
       File newVersionedJarFile = getNextVersionedJarFile(jarName);
@@ -129,6 +133,7 @@ public class JarDeployer implements Serializable {
     lock.unlock();
   }
 
+
   protected File getNextVersionedJarFile(String unversionedJarName) {
     File[] oldVersions = findSortedOldVersionsOfJar(unversionedJarName);
 
@@ -246,21 +251,17 @@ public class JarDeployer implements Serializable {
     }
   }
 
-  protected Set<String> findDistinctDeployedJars() {
+  protected Set<String> findDistinctDeployedJarsOnDisk() {
     // Find all deployed JAR files
-    final File[] oldFiles = this.deployDirectory.listFiles(new FilenameFilter() {
-      @Override
-      public boolean accept(final File file, final String name) {
-        return versionedPattern.matcher(name).matches();
-      }
-    });
+    final File[] oldFiles =
+        this.deployDirectory.listFiles((file, name) -> versionedPattern.matcher(name).matches());
 
     // Now add just the original JAR name to the set
-    final Set<String> jarNames = new HashSet<String>();
+    final Set<String> jarNames = new HashSet<>();
     for (File oldFile : oldFiles) {
       Matcher matcher = versionedPattern.matcher(oldFile.getName());
       matcher.find();
-      jarNames.add(matcher.group(1));
+      jarNames.add(matcher.group(1) + ".jar");
     }
     return jarNames;
   }
@@ -368,60 +369,39 @@ public class JarDeployer implements Serializable {
     String newJarName = unversionedJarNameWithoutExtension + ".v" + jarVersion + ".jar";
 
     File newJar = new File(this.deployDirectory, newJarName);
-    logger.debug("Renaming deployed jar from " + oldJar.getCanonicalPath() + " to "
-        + newJar.getCanonicalPath());
+    logger.debug("Renaming deployed jar from {} to {}", oldJar.getCanonicalPath(),
+        newJar.getCanonicalPath());
 
     FileUtils.moveFile(oldJar, newJar);
-    FileUtils.deleteQuietly(oldJar);
   }
 
   /**
-   * Re-deploy all previously deployed JAR files.
+   * Re-deploy all previously deployed JAR files on disk.
    */
-  public void loadPreviouslyDeployedJars() {
+  public void loadPreviouslyDeployedJarsFromDisk() {
+    logger.info("Loading previously deployed jars");
     lock.lock();
     try {
       verifyWritableDeployDirectory();
       renameJarsWithOldNamingConvention();
 
-      final Set<String> jarNames = findDistinctDeployedJars();
+      final Set<String> jarNames = findDistinctDeployedJarsOnDisk();
       if (jarNames.isEmpty()) {
         return;
       }
 
-      Map<String, DeployedJar> latestVersionOfEachJar = new LinkedHashMap<>();
+      List<DeployedJar> latestVersionOfEachJar = new ArrayList<>();
 
       for (String jarName : jarNames) {
-        final File[] jarFiles = findSortedOldVersionsOfJar(jarName);
-
-        Optional<File> latestValidDeployedJarOptional =
-            Arrays.stream(jarFiles).filter(Objects::nonNull).filter(jarFile -> {
-              try {
-                return DeployedJar.isValidJarContent(FileUtils.readFileToByteArray(jarFile));
-              } catch (IOException e) {
-                return false;
-              }
-            }).findFirst();
-
-        if (!latestValidDeployedJarOptional.isPresent()) {
-          // No valid version of this jar
-          continue;
-        }
+        DeployedJar deployedJar = findLatestValidDeployedJarFromDisk(jarName);
 
-        File latestValidDeployedJar = latestValidDeployedJarOptional.get();
-        latestVersionOfEachJar.put(jarName, new DeployedJar(latestValidDeployedJar, jarName));
-
-        // Remove any old left-behind versions of this JAR file
-        for (File jarFile : jarFiles) {
-          if (!latestValidDeployedJar.equals(jarFile)) {
-            FileUtils.deleteQuietly(jarFile);
-          }
+        if (deployedJar != null) {
+          latestVersionOfEachJar.add(deployedJar);
+          deleteOtherVersionsOfJar(deployedJar);
         }
       }
 
-      registerNewVersions(latestVersionOfEachJar.values().stream().collect(toList()));
-      // ClassPathLoader.getLatest().deploy(latestVersionOfEachJar.keySet().toArray(),
-      // latestVersionOfEachJar.values().toArray())
+      registerNewVersions(latestVersionOfEachJar);
     } catch (Exception e) {
       throw new RuntimeException(e);
     } finally {
@@ -429,6 +409,43 @@ public class JarDeployer implements Serializable {
     }
   }
 
+  /**
+   * Deletes all versions of this jar on disk other than the given version
+   */
+  public void deleteOtherVersionsOfJar(DeployedJar deployedJar) {
+    logger.info("Deleting all versions of " + deployedJar.getJarName() + " other than "
+        + deployedJar.getFileName());
+    final File[] jarFiles = findSortedOldVersionsOfJar(deployedJar.getJarName());
+
+    Stream.of(jarFiles).filter(jarFile -> !jarFile.equals(deployedJar.getFile()))
+        .forEach(jarFile -> {
+          logger.info("Deleting old version of jar: " + jarFile.getAbsolutePath());
+          FileUtils.deleteQuietly(jarFile);
+        });
+  }
+
+  public DeployedJar findLatestValidDeployedJarFromDisk(String unversionedJarName)
+      throws IOException {
+    final File[] jarFiles = findSortedOldVersionsOfJar(unversionedJarName);
+
+    Optional<File> latestValidDeployedJarOptional =
+        Arrays.stream(jarFiles).filter(Objects::nonNull).filter(jarFile -> {
+          try {
+            return DeployedJar.isValidJarContent(FileUtils.readFileToByteArray(jarFile));
+          } catch (IOException e) {
+            return false;
+          }
+        }).findFirst();
+
+    if (!latestValidDeployedJarOptional.isPresent()) {
+      // No valid version of this jar
+      return null;
+    }
+
+    File latestValidDeployedJar = latestValidDeployedJarOptional.get();
+
+    return new DeployedJar(latestValidDeployedJar, unversionedJarName);
+  }
 
   public URL[] getDeployedJarURLs() {
     return this.deployedJars.values().stream().map(DeployedJar::getFileURL).toArray(URL[]::new);
@@ -441,6 +458,7 @@ public class JarDeployer implements Serializable {
     try {
       for (DeployedJar deployedJar : deployedJars) {
         if (deployedJar != null) {
+          logger.info("Registering new version of jar: {}", deployedJar.toString());
           DeployedJar oldJar = this.deployedJars.put(deployedJar.getJarName(), deployedJar);
           if (oldJar != null) {
             oldJar.cleanUp();
@@ -488,13 +506,7 @@ public class JarDeployer implements Serializable {
         String jarName = jarNames[i];
         byte[] newJarBytes = jarBytes[i];
 
-        boolean shouldDeployNewVersion = shouldDeployNewVersion(jarName, newJarBytes);
-
-        if (shouldDeployNewVersion) {
-          deployedJars[i] = deployWithoutRegistering(jarName, newJarBytes);
-        } else {
-          deployedJars[i] = null;
-        }
+        deployedJars[i] = deployWithoutRegistering(jarName, newJarBytes);
       }
 
       return registerNewVersions(Arrays.asList(deployedJars));
@@ -511,7 +523,7 @@ public class JarDeployer implements Serializable {
     }
 
     if (oldDeployedJar.hasSameContentAs(newJarBytes)) {
-      logger.warn("Jar is identical to the latest deployed version: ",
+      logger.warn("Jar is identical to the latest deployed version: {}",
           oldDeployedJar.getFileCanonicalPath());
 
       return false;
@@ -520,6 +532,11 @@ public class JarDeployer implements Serializable {
     return true;
   }
 
+  /**
+   * Returns the latest registered {@link DeployedJar} for the given JarName
+   * 
+   * @param jarName - the unversioned jar name, e.g. myJar.jar
+   */
   public DeployedJar findDeployedJar(String jarName) {
     return this.deployedJars.get(jarName);
   }
@@ -565,9 +582,24 @@ public class JarDeployer implements Serializable {
 
       deployedJar.cleanUp();
 
+      deleteAllVersionsOfJar(jarName);
       return deployedJar.getFileCanonicalPath();
     } finally {
       lock.unlock();
     }
   }
+
+  public void deleteAllVersionsOfJar(String unversionedJarName) {
+    lock.lock();
+    try {
+      File[] jarFiles = findSortedOldVersionsOfJar(unversionedJarName);
+      for (File jarFile : jarFiles) {
+        logger.info("Deleting: {}", jarFile.getAbsolutePath());
+        FileUtils.deleteQuietly(jarFile);
+      }
+    } finally {
+      lock.unlock();
+    }
+
+  }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java b/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java
index 2b627b2..55e3542 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java
@@ -14,6 +14,9 @@
  */
 package org.apache.geode.internal.cache;
 
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -27,7 +30,9 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
 import java.util.Set;
+import java.util.stream.Stream;
 
+import org.apache.commons.lang.ArrayUtils;
 import org.apache.geode.internal.ClassPathLoader;
 import org.apache.logging.log4j.Logger;
 
@@ -61,21 +66,36 @@ public class ClusterConfigurationLoader {
    */
   public static void deployJarsReceivedFromClusterConfiguration(Cache cache,
       ConfigurationResponse response) throws IOException, ClassNotFoundException {
+    logger.info("Requesting cluster configuration");
     if (response == null) {
       return;
     }
 
     String[] jarFileNames = response.getJarNames();
     byte[][] jarBytes = response.getJars();
+    logger.info("Got response with jars: {}", Stream.of(jarFileNames).collect(joining(",")));
 
     if (jarFileNames != null && jarBytes != null) {
-      List<DeployedJar> deployedJars =
-          ClassPathLoader.getLatest().getJarDeployer().deploy(jarFileNames, jarBytes);
+      JarDeployer jarDeployer = ClassPathLoader.getLatest().getJarDeployer();
+      jarDeployer.suspendAll();
+      try {
+        List<String> extraJarsOnServer =
+            jarDeployer.findDeployedJars().stream().map(DeployedJar::getJarName)
+                .filter(jarName -> !ArrayUtils.contains(jarFileNames, jarName)).collect(toList());
+
+        for (String extraJar : extraJarsOnServer) {
+          logger.info("Removing jar not present in cluster configuration: {}", extraJar);
+          jarDeployer.deleteAllVersionsOfJar(extraJar);
+        }
 
-      deployedJars.stream().filter(Objects::nonNull)
-          .forEach((jar) -> logger.info("Deployed " + (jar.getFile().getAbsolutePath())));
+        List<DeployedJar> deployedJars = jarDeployer.deploy(jarFileNames, jarBytes);
+
+        deployedJars.stream().filter(Objects::nonNull)
+            .forEach((jar) -> logger.info("Deployed: {}", jar.getFile().getAbsolutePath()));
+      } finally {
+        jarDeployer.resumeAll();
+      }
     }
-    // TODO: Jared - Does this need to actually undeploy extra jars like the javadoc says?
   }
 
   /***

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
index fb311e7..069b608 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
@@ -108,7 +108,6 @@ import org.apache.geode.distributed.internal.membership.InternalDistributedMembe
 import org.apache.geode.i18n.LogWriterI18n;
 import org.apache.geode.internal.Assert;
 import org.apache.geode.internal.ClassPathLoader;
-import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.SystemTimer;
 import org.apache.geode.internal.cache.control.InternalResourceManager;
 import org.apache.geode.internal.cache.control.InternalResourceManager.ResourceType;
@@ -1238,8 +1237,10 @@ public class GemFireCacheImpl
     initializeServices();
 
     try {
-      // Deploy all the jars from the deploy working dir.
-      ClassPathLoader.getLatest().getJarDeployer().loadPreviouslyDeployedJars();
+      if (configurationResponse == null) {
+        // Deploy all the jars from the deploy working dir.
+        ClassPathLoader.getLatest().getJarDeployer().loadPreviouslyDeployedJarsFromDisk();
+      }
       ClusterConfigurationLoader.applyClusterXmlConfiguration(this, configurationResponse,
           system.getConfig());
       initializeDeclarativeCache();

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
index d30feb6..14108c7 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
@@ -91,7 +91,7 @@ public class ClassPathLoaderIntegrationTest {
     fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
     fos.close();
 
-    System.setProperty("user.dir", temporaryFolder.getRoot().getAbsolutePath());
+    // System.setProperty("user.dir", temporaryFolder.getRoot().getAbsolutePath());
     ClassPathLoader.setLatestToDefault(temporaryFolder.getRoot());
   }
 
@@ -226,9 +226,7 @@ public class ClassPathLoaderIntegrationTest {
     outStream.write(jarBytes);
     outStream.close();
 
-    Properties properties = new Properties();
-    properties.setProperty("user.dir", temporaryFolder.getRoot().getAbsolutePath());
-    ServerStarterRule serverStarterRule = new ServerStarterRule();
+    ServerStarterRule serverStarterRule = new ServerStarterRule(temporaryFolder.getRoot());
     serverStarterRule.startServer();
 
     GemFireCacheImpl gemFireCache = GemFireCacheImpl.getInstance();
@@ -248,9 +246,7 @@ public class ClassPathLoaderIntegrationTest {
     File jarVersion1 = createVersionOfJar("Version1", "MyFunction", "MyJar.jar");
     File jarVersion2 = createVersionOfJar("Version2", "MyFunction", "MyJar.jar");
 
-    Properties properties = new Properties();
-    properties.setProperty("user.dir", temporaryFolder.getRoot().getAbsolutePath());
-    ServerStarterRule serverStarterRule = new ServerStarterRule();
+    ServerStarterRule serverStarterRule = new ServerStarterRule(temporaryFolder.getRoot());
     serverStarterRule.startServer();
 
     GemFireCacheImpl gemFireCache = GemFireCacheImpl.getInstance();

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java
index 0d26caf..0e37075 100755
--- a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java
@@ -494,39 +494,6 @@ public class ClassPathLoaderTest {
     }
   }
 
-  private static void exploreClassLoader(ClassLoader cl, int indent) {
-    String prefix = "";
-    for (int i = 0; i < indent; i++) {
-      prefix += "\t";
-    }
-    System.out.println(prefix + "ClassLoader toString() = " + cl);
-
-    Class<?> clazz = cl.getClass();
-    System.out.println(prefix + "ClassLoader getClass().getName() = " + clazz.getName());
-    exploreClassLoaderSuperClass(prefix, clazz);
-
-    try {
-      URL[] urls = ((URLClassLoader) cl).getURLs();
-      StringBuilder sb = new StringBuilder(prefix).append("ClassLoader getURLs = [");
-      for (int i = 0; i < urls.length; i++) {
-        if (i > 0) {
-          sb.append(", ");
-        }
-        sb.append(urls[i].toString());
-      }
-      sb.append("]");
-      System.out.println(sb.toString());
-    } catch (Exception e) {
-      System.out.println(prefix + "ClassLoader is not a URLClassLoader");
-    }
-
-    ClassLoader parent = cl.getParent();
-    if (parent != null) {
-      System.out.println(prefix + "ClassLoader has parent...");
-      exploreClassLoader(parent, ++indent);
-    }
-  }
-
   private static void exploreClassLoaderSuperClass(String prefix, Class<?> clazz) {
     Class<?> superClazz = clazz.getSuperclass();
     if (superClazz != null) {
@@ -646,31 +613,6 @@ public class ClassPathLoaderTest {
     }
   }
 
-  static class OneClassClassLoader extends ClassLoader {
-
-    private final GeneratingClassLoader genClassLoader = new GeneratingClassLoader();
-    private String className;
-
-    public OneClassClassLoader(final String className) {
-      super(null); // no parent!!
-      this.className = className;
-    }
-
-    @Override
-    public Class<?> findClass(String name) throws ClassNotFoundException {
-      if (!name.equals(className)) {
-        throw new ClassNotFoundException();
-      } else {
-        return this.genClassLoader.findClass(name);
-      }
-    }
-
-    @Override
-    public boolean equals(final Object other) {
-      return (other instanceof OneClassClassLoader);
-    }
-  }
-
   @SuppressWarnings("serial")
   static class BrokenError extends Error {
   }

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
index 9e42c20..e9af0e7 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
@@ -17,7 +17,6 @@
 package org.apache.geode.internal;
 
 
-import static org.apache.geode.internal.Assert.fail;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
@@ -31,7 +30,6 @@ import org.junit.rules.TemporaryFolder;
 import java.io.File;
 import java.io.IOException;
 import java.util.Set;
-import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -87,7 +85,7 @@ public class JarDeployerIntegrationTest {
     assertThat(sortedOldJars).hasSize(2);
     assertThat(sortedOldJars[0].getName()).contains(".v2.");
     assertThat(sortedOldJars[1].getName()).contains(".v1.");
-    assertThat(jarDeployer.findDistinctDeployedJars()).hasSize(1);
+    assertThat(jarDeployer.findDistinctDeployedJarsOnDisk()).hasSize(1);
   }
 
   @Test
@@ -208,4 +206,63 @@ public class JarDeployerIntegrationTest {
   }
 
 
+  @Test
+  public void testDeleteAllVersionsOfJar() throws Exception {
+    File deployDir = jarDeployer.getDeployDirectory();
+
+    File jarAVersion1 = new File(deployDir, "myJarA.v1.jar");
+    this.classBuilder.writeJarFromName("ClassA", jarAVersion1);
+
+    File jarAVersion2 = new File(deployDir, "myJarA.v2.jar");
+    this.classBuilder.writeJarFromName("ClassA", jarAVersion2);
+
+    File jarBVersion2 = new File(deployDir, "myJarB.v2.jar");
+    this.classBuilder.writeJarFromName("ClassB", jarBVersion2);
+
+    File jarBVersion3 = new File(deployDir, "myJarB.v3.jar");
+    this.classBuilder.writeJarFromName("ClassB", jarBVersion3);
+
+    jarDeployer.deleteAllVersionsOfJar("myJarA.jar");
+
+    assertThat(jarAVersion1).doesNotExist();
+    assertThat(jarAVersion2).doesNotExist();
+    assertThat(jarBVersion2).exists();
+    assertThat(jarBVersion3).exists();
+  }
+
+
+  @Test
+  public void testDeleteOtherVersionsOfJar() throws Exception {
+    File deployDir = jarDeployer.getDeployDirectory();
+
+    File jarAVersion1 = new File(deployDir, "myJarA.v1.jar");
+    this.classBuilder.writeJarFromName("ClassA", jarAVersion1);
+
+    File jarAVersion2 = new File(deployDir, "myJarA.v2.jar");
+    this.classBuilder.writeJarFromName("ClassA", jarAVersion2);
+
+    File jarBVersion2 = new File(deployDir, "myJarB.v2.jar");
+    this.classBuilder.writeJarFromName("ClassB", jarBVersion2);
+
+    File jarBVersion3 = new File(deployDir, "myJarB.v3.jar");
+    this.classBuilder.writeJarFromName("ClassB", jarBVersion3);
+
+    DeployedJar deployedJarBVersion3 = new DeployedJar(jarBVersion3, "myJarB.jar");
+    jarDeployer.deleteOtherVersionsOfJar(deployedJarBVersion3);
+
+    assertThat(jarAVersion1).exists();
+    assertThat(jarAVersion2).exists();
+    assertThat(jarBVersion2).doesNotExist();
+    assertThat(jarBVersion3).exists();
+
+    DeployedJar deployedJarAVersion1 = new DeployedJar(jarAVersion1, "myJarA.jar");
+    jarDeployer.deleteOtherVersionsOfJar(deployedJarAVersion1);
+
+    assertThat(jarAVersion1).exists();
+    assertThat(jarAVersion2).doesNotExist();
+    assertThat(jarBVersion2).doesNotExist();
+    assertThat(jarBVersion3).exists();
+  }
+
+
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java b/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
index 3149432..ee46cbf 100644
--- a/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
+++ b/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
@@ -20,12 +20,14 @@ import org.apache.geode.internal.DeployedJarJUnitTest;
 import org.apache.geode.internal.JarDeployerIntegrationTest;
 import org.apache.geode.management.internal.cli.commands.DeployCommandRedeployDUnitTest;
 import org.apache.geode.management.internal.cli.commands.DeployCommandsDUnitTest;
+import org.apache.geode.management.internal.configuration.ClusterConfigDeployJarDUnitTest;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses({DeployedJarJUnitTest.class, DeployCommandsDUnitTest.class,
     JarDeployerIntegrationTest.class, ClassPathLoaderIntegrationTest.class,
-    ClassPathLoaderTest.class, DeployCommandRedeployDUnitTest.class})
+    ClassPathLoaderTest.class, DeployCommandRedeployDUnitTest.class,
+    ClusterConfigDeployJarDUnitTest.class})
 public class DeployJarTestSuite {
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java
index b111e45..fc920c4 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfig.java
@@ -17,6 +17,7 @@
 package org.apache.geode.management.internal.configuration;
 
 
+import static java.util.stream.Collectors.toSet;
 import static org.apache.geode.distributed.ConfigurationProperties.LOG_FILE_SIZE_LIMIT;
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -47,6 +48,7 @@ import java.util.List;
 import java.util.Properties;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 public class ClusterConfig implements Serializable {
   private List<ConfigGroup> groups;
@@ -89,7 +91,7 @@ public class ClusterConfig implements Serializable {
 
   public void verifyLocator(MemberVM<Locator> locatorVM) {
     Set<String> expectedGroupConfigs =
-        this.getGroups().stream().map(ConfigGroup::getName).collect(Collectors.toSet());
+        this.getGroups().stream().map(ConfigGroup::getName).collect(toSet());
 
     // verify info exists in memeory
     locatorVM.invoke(() -> {
@@ -135,10 +137,12 @@ public class ClusterConfig implements Serializable {
 
   public void verifyServer(MemberVM<Server> serverVM) throws ClassNotFoundException {
     // verify files exist in filesystem
-    Set<String> expectedJarNames = this.getJarNames().stream().map(ClusterConfig::getServerJarName)
-        .collect(Collectors.toSet());
-    Set<String> actualJarNames = toSetIgnoringHiddenFiles(
-        serverVM.getWorkingDir().list((dir, filename) -> filename.contains(".jar")));
+    Set<String> expectedJarNames = this.getJarNames().stream().collect(toSet());
+
+    String[] actualJarFiles =
+        serverVM.getWorkingDir().list((dir, filename) -> filename.contains(".jar"));
+    Set<String> actualJarNames = Stream.of(actualJarFiles)
+        .map(jar -> jar.replaceAll("\\.v\\d+\\.jar", ".jar")).collect(toSet());
 
     // We will end up with extra jars on disk if they are deployed and then undeployed
     assertThat(expectedJarNames).isSubsetOf(actualJarNames);
@@ -168,6 +172,7 @@ public class ClusterConfig implements Serializable {
       Set<String> undeployedJarNames = new HashSet<>(actualJarNames);
       undeployedJarNames.removeAll(expectedJarNames);
       for (String jar : undeployedJarNames) {
+        System.out.println("Verifying undeployed jar: " + jar);
         DeployedJar undeployedJar =
             ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jar);
         assertThat(undeployedJar).isNull();
@@ -181,15 +186,9 @@ public class ClusterConfig implements Serializable {
     if (array == null) {
       return new HashSet<>();
     }
-    return Arrays.stream(array).filter((String name) -> !name.startsWith("."))
-        .collect(Collectors.toSet());
-  }
-
-  private static String getServerJarName(String jarName) {
-    return jarName.replace(".jar", "") + ".v1.jar";
+    return Arrays.stream(array).filter((String name) -> !name.startsWith(".")).collect(toSet());
   }
 
-
   private static String nameOfClassContainedInJar(String jarName) {
     switch (jarName) {
       case "cluster.jar":

http://git-wip-us.apache.org/repos/asf/geode/blob/ee11b0a4/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java
index 7cc84d6..980f81f 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java
@@ -17,6 +17,7 @@ package org.apache.geode.management.internal.configuration;
 
 import static org.apache.geode.distributed.ConfigurationProperties.GROUPS;
 import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS;
+import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
@@ -143,6 +144,7 @@ public class ClusterConfigDeployJarDUnitTest extends ClusterConfigBaseTest {
     serverProps.setProperty(GROUPS, "group2");
     MemberVM server2 = lsRule.startServerVM(2, serverProps, locator.getPort());
     serverProps.setProperty(GROUPS, "group1,group2");
+    serverProps.setProperty(LOG_LEVEL, "info");
     MemberVM server3 = lsRule.startServerVM(3, serverProps, locator.getPort());
 
     ConfigGroup cluster = new ConfigGroup("cluster");
@@ -178,6 +180,80 @@ public class ClusterConfigDeployJarDUnitTest extends ClusterConfigBaseTest {
     // test undeploy cluster
     gfshConnector.executeAndVerifyCommand("undeploy --jar=cluster.jar");
 
+
+    cluster = cluster.removeJar("cluster.jar");
+    server3Config.verify(locator);
+    server1Config.verify(server1);
+    server2Config.verify(server2);
+    server3Config.verify(server3);
+
+    gfshConnector.executeAndVerifyCommand("undeploy --jar=group1.jar --group=group1");
+
+    group1 = group1.removeJar("group1.jar");
+    /*
+     * TODO: This is the current (weird) behavior If you started server4 with group1,group2 after
+     * this undeploy command, it would have group1.jar (brought from
+     * cluster_config/group2/group1.jar on locator) whereas server3 (also in group1,group2) does not
+     * have this jar.
+     */
+    ClusterConfig weirdServer3Config =
+        new ClusterConfig(cluster, group1, new ConfigGroup(group2).removeJar("group1.jar"));
+
+    server3Config.verify(locator);
+    server1Config.verify(server1);
+    server2Config.verify(server2);
+    weirdServer3Config.verify(server3);
+  }
+
+  @Test
+  public void testUndeployWithServerBounce() throws Exception {
+    // set up the locator/servers
+    MemberVM locator = lsRule.startLocatorVM(0, locatorProps);
+    serverProps.setProperty(GROUPS, "group1");
+    MemberVM server1 = lsRule.startServerVM(1, serverProps, locator.getPort());
+    serverProps.setProperty(GROUPS, "group2");
+    MemberVM server2 = lsRule.startServerVM(2, serverProps, locator.getPort());
+    serverProps.setProperty(GROUPS, "group1,group2");
+    serverProps.setProperty(LOG_LEVEL, "info");
+    MemberVM server3 = lsRule.startServerVM(3, serverProps, locator.getPort());
+
+    ConfigGroup cluster = new ConfigGroup("cluster");
+    ConfigGroup group1 = new ConfigGroup("group1");
+    ConfigGroup group2 = new ConfigGroup("group2");
+    ClusterConfig expectedClusterConfig = new ClusterConfig(cluster);
+    ClusterConfig server1Config = new ClusterConfig(cluster, group1);
+    ClusterConfig server2Config = new ClusterConfig(cluster, group2);
+    ClusterConfig server3Config = new ClusterConfig(cluster, group1, group2);
+
+    gfshConnector.connect(locator);
+    assertThat(gfshConnector.isConnected()).isTrue();
+
+    gfshConnector.executeAndVerifyCommand("deploy --jar=" + clusterJar);
+
+    // deploy cluster.jar to the cluster
+    cluster.addJar("cluster.jar");
+    expectedClusterConfig.verify(locator);
+    expectedClusterConfig.verify(server1);
+    expectedClusterConfig.verify(server2);
+    expectedClusterConfig.verify(server3);
+
+    // deploy group1.jar to both group1 and group2
+    gfshConnector.executeAndVerifyCommand("deploy --jar=" + group1Jar + " --group=group1,group2");
+
+    group1.addJar("group1.jar");
+    group2.addJar("group1.jar");
+    server3Config.verify(locator);
+    server1Config.verify(server1);
+    server2Config.verify(server2);
+    server3Config.verify(server3);
+
+    server3.getVM().bounce();
+
+    // test undeploy cluster
+    gfshConnector.executeAndVerifyCommand("undeploy --jar=cluster.jar");
+    server3 = lsRule.startServerVM(3, serverProps, locator.getPort());
+
+
     cluster = cluster.removeJar("cluster.jar");
     server3Config.verify(locator);
     server1Config.verify(server1);


[2/8] geode git commit: GEODE-2686: Remove JarClassLoader

Posted by js...@apache.org.
http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/internal/JarDeployerDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerDUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerDUnitTest.java
deleted file mode 100644
index a365899..0000000
--- a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerDUnitTest.java
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * 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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-
-import org.apache.geode.cache.execute.Execution;
-import org.apache.geode.cache.execute.FunctionService;
-import org.apache.geode.cache.execute.ResultCollector;
-import org.apache.geode.distributed.ConfigurationProperties;
-import org.apache.geode.distributed.DistributedSystem;
-import org.apache.geode.distributed.internal.InternalDistributedSystem;
-import org.apache.geode.test.dunit.Assert;
-import org.apache.geode.test.dunit.Host;
-import org.apache.geode.test.dunit.SerializableRunnable;
-import org.apache.geode.test.dunit.VM;
-import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase;
-import org.apache.geode.test.junit.categories.DistributedTest;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileLock;
-import java.nio.file.Files;
-import java.util.List;
-import java.util.Properties;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.regex.Pattern;
-
-/**
- * Unit tests for the JarDeployer class
- * 
- * @since GemFire 7.0
- */
-@Category(DistributedTest.class)
-@SuppressWarnings("serial")
-public class JarDeployerDUnitTest extends JUnit4CacheTestCase {
-
-  static FileLock savedFileLock = null;
-  private final ClassBuilder classBuilder = new ClassBuilder();
-
-  @Override
-  public final void preTearDownCacheTestCase() throws Exception {
-    JarDeployer jarDeployer = new JarDeployer();
-    for (JarClassLoader jarClassLoader : jarDeployer.findJarClassLoaders()) {
-      if (jarClassLoader.getJarName().startsWith("JarDeployerDUnit")) {
-        jarDeployer.undeploy(jarClassLoader.getJarName());
-      }
-    }
-    for (String functionName : FunctionService.getRegisteredFunctions().keySet()) {
-      if (functionName.startsWith("JarDeployerDUnit")) {
-        FunctionService.unregisterFunction(functionName);
-      }
-    }
-    disconnectAllFromDS();
-    deleteSavedJarFiles();
-  }
-
-
-
-  @Test
-  public void testDeployExclusiveLock() throws IOException, ClassNotFoundException {
-    final JarDeployer jarDeployer = new JarDeployer();
-    final File currentDir = new File(".").getAbsoluteFile();
-    final VM vm = Host.getHost(0).getVM(0);
-
-    // Deploy the Class JAR file
-    final File jarFile1 = jarDeployer.getNextVersionJarFile("JarDeployerDUnit.jar");
-    byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDELA");
-    jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitDELA");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    assertNotNull(ClassPathLoader.getLatest().getResource("JarDeployerDUnitDELA.class"));
-
-    // Attempt to acquire an exclusive lock in the other VM
-    vm.invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        FileOutputStream outStream = null;
-        FileLock fileLock = null;
-
-        try {
-          outStream = new FileOutputStream(jarFile1, true);
-          fileLock = outStream.getChannel().tryLock(0, 1, false);
-          if (fileLock != null) {
-            fail("Should not have been able to obtain exclusive lock on file:"
-                + jarFile1.getAbsolutePath());
-          }
-        } catch (FileNotFoundException fnfex) {
-          Assert.fail("JAR file not found where expected", fnfex);
-        } catch (IOException ioex) {
-          Assert.fail("IOException when trying to obtain exclusive lock", ioex);
-        } finally {
-          if (outStream != null) {
-            try {
-              outStream.close();
-            } catch (IOException ioex) {
-              fail("Could not close lock file output stream");
-            }
-          }
-          if (fileLock != null) {
-            try {
-              fileLock.channel().close();
-            } catch (IOException ioex) {
-              fail("Could not close lock file channel");
-            }
-          }
-        }
-      }
-    });
-  }
-
-  @Test
-  public void testDeploySharedLock() throws IOException, ClassNotFoundException {
-    final JarDeployer jarDeployer = new JarDeployer();
-    final File currentDir = new File(".").getAbsoluteFile();
-    final VM vm = Host.getHost(0).getVM(0);
-
-    // Deploy the JAR file
-    final File jarFile1 = jarDeployer.getNextVersionJarFile("JarDeployerDUnit.jar");
-    byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDSLA");
-    jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitDSLA");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    // Acquire a shared lock in the other VM
-    vm.invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        if (!jarFile1.exists()) {
-          fail("JAR file not found where expected: " + jarFile1.getName());
-        }
-        try {
-          JarDeployerDUnitTest.savedFileLock = acquireSharedLock(jarFile1);
-        } catch (IOException ioex) {
-          fail("Unable to acquire the shared file lock");
-        }
-      }
-    });
-
-    // Now update the JAR file and make sure the first one isn't deleted
-    jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDSLB");
-    jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitDSLB");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitC");
-      fail("Class should not be found on Classpath: JarDeployerDUniDSLA");
-    } catch (ClassNotFoundException expected) { // expected
-    }
-
-    if (!jarFile1.exists()) {
-      fail("JAR file should not have been deleted: " + jarFile1.getName());
-    }
-
-    vm.invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        try {
-          releaseLock(JarDeployerDUnitTest.savedFileLock, jarFile1);
-        } catch (IOException ioex) {
-          fail("Unable to release the shared file lock");
-        }
-      }
-    });
-  }
-
-  @Test
-  public void testUndeploySharedLock() throws IOException, ClassNotFoundException {
-    final JarDeployer jarDeployer = new JarDeployer();
-    final File currentDir = new File(".").getAbsoluteFile();
-    final VM vm = Host.getHost(0).getVM(0);
-
-    // Deploy the JAR file
-    final File jarFile1 = jarDeployer.getNextVersionJarFile("JarDeployerDUnit.jar");
-    byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitUSL");
-    jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitUSL");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    // Acquire a shared lock in the other VM
-    vm.invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        if (!jarFile1.exists()) {
-          fail("JAR file not found where expected: " + jarFile1.getName());
-        }
-        try {
-          JarDeployerDUnitTest.savedFileLock = acquireSharedLock(jarFile1);
-        } catch (IOException ioex) {
-          fail("Unable to acquire the shared file lock");
-        }
-      }
-    });
-
-    // Now undeploy the JAR file and make sure the first one isn't deleted
-    jarDeployer.undeploy("JarDeployerDUnit.jar");
-
-    if (!jarFile1.exists()) {
-      fail("JAR file should not have been deleted: " + jarFile1.getName());
-    }
-
-    vm.invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        try {
-          releaseLock(JarDeployerDUnitTest.savedFileLock, jarFile1);
-        } catch (IOException ioex) {
-          fail("Unable to release the shared file lock");
-        }
-      }
-    });
-  }
-
-  @Test
-  public void testDeployUpdateByAnotherVM() throws IOException, ClassNotFoundException {
-    final JarDeployer jarDeployer = new JarDeployer();
-    final File currentDir = new File(".").getAbsoluteFile();
-    final VM vm = Host.getHost(0).getVM(0);
-
-    final File jarFile1 = jarDeployer.getNextVersionJarFile("JarDeployerDUnit.jar");
-    byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDUBAVMA");
-    jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitDUBAVMA");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    final File jarFile2 = jarDeployer.getNextVersionJarFile(jarFile1.getName());
-    final byte[] vmJarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDUBAVMB");
-    vm.invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        if (!jarFile1.exists()) {
-          fail("JAR file not found where expected: " + jarFile1.getName());
-        }
-
-        // The other VM writes out a newer version of the JAR file.
-        try {
-          writeJarBytesToFile(jarFile2, vmJarBytes);
-        } catch (IOException ioex) {
-          fail("Could not write JAR File");
-        }
-      }
-    });
-
-    // This VM is told to deploy the same JAR file.
-    jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {vmJarBytes});
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitDUBAVMB");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitDUBAVMA");
-      fail("Class should not be found on Classpath: JarDeployerDUnitDUBAVMA");
-    } catch (ClassNotFoundException expected) { // expected
-    }
-
-    if (!jarFile2.exists()) {
-      fail("JAR file should not have been deleted: " + jarFile2.getName());
-    }
-
-    // Make sure the second deploy didn't create a 3rd version of the JAR file.
-    final File jarFile3 = jarDeployer.getNextVersionJarFile(jarFile2.getName());
-    if (jarFile3.exists()) {
-      fail("JAR file should not have been created: " + jarFile3.getName());
-    }
-  }
-
-  @Test
-  public void testLoadPreviouslyDeployedJars() throws IOException {
-    final File parentJarFile = new File(JarDeployer.JAR_PREFIX + "JarDeployerDUnitAParent.jar#1");
-    final File usesJarFile = new File(JarDeployer.JAR_PREFIX + "JarDeployerDUnitUses.jar#1");
-    final File functionJarFile =
-        new File(JarDeployer.JAR_PREFIX + "JarDeployerDUnitFunction.jar#1");
-
-    // Write out a JAR files.
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("package jddunit.parent;");
-    stringBuffer.append("public class JarDeployerDUnitParent {");
-    stringBuffer.append("public String getValueParent() {");
-    stringBuffer.append("return \"PARENT\";}}");
-
-    byte[] jarBytes = this.classBuilder.createJarFromClassContent(
-        "jddunit/parent/JarDeployerDUnitParent", stringBuffer.toString());
-    FileOutputStream outStream = new FileOutputStream(parentJarFile);
-    outStream.write(jarBytes);
-    outStream.close();
-
-    stringBuffer = new StringBuffer();
-    stringBuffer.append("package jddunit.uses;");
-    stringBuffer.append("public class JarDeployerDUnitUses {");
-    stringBuffer.append("public String getValueUses() {");
-    stringBuffer.append("return \"USES\";}}");
-
-    jarBytes = this.classBuilder.createJarFromClassContent("jddunit/uses/JarDeployerDUnitUses",
-        stringBuffer.toString());
-    outStream = new FileOutputStream(usesJarFile);
-    outStream.write(jarBytes);
-    outStream.close();
-
-    stringBuffer = new StringBuffer();
-    stringBuffer.append("package jddunit.function;");
-    stringBuffer.append("import jddunit.parent.JarDeployerDUnitParent;");
-    stringBuffer.append("import jddunit.uses.JarDeployerDUnitUses;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer.append(
-        "public class JarDeployerDUnitFunction  extends JarDeployerDUnitParent implements Function {");
-    stringBuffer.append("private JarDeployerDUnitUses uses = new JarDeployerDUnitUses();");
-    stringBuffer.append("public boolean hasResult() {return true;}");
-    stringBuffer.append(
-        "public void execute(FunctionContext context) {context.getResultSender().lastResult(getValueParent() + \":\" + uses.getValueUses());}");
-    stringBuffer.append("public String getId() {return \"JarDeployerDUnitFunction\";}");
-    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
-    stringBuffer.append("public boolean isHA() {return false;}}");
-
-    ClassBuilder functionClassBuilder = new ClassBuilder();
-    functionClassBuilder.addToClassPath(parentJarFile.getAbsolutePath());
-    functionClassBuilder.addToClassPath(usesJarFile.getAbsolutePath());
-    jarBytes = functionClassBuilder.createJarFromClassContent(
-        "jddunit/function/JarDeployerDUnitFunction", stringBuffer.toString());
-    outStream = new FileOutputStream(functionJarFile);
-    outStream.write(jarBytes);
-    outStream.close();
-
-    // Start the distributed system and check to see if the function executes correctly
-    DistributedSystem distributedSystem = getSystem();
-    getCache();
-
-    Execution execution =
-        FunctionService.onMember(distributedSystem, distributedSystem.getDistributedMember());
-    ResultCollector resultCollector = execution.execute("JarDeployerDUnitFunction");
-    @SuppressWarnings("unchecked")
-    List<String> result = (List<String>) resultCollector.getResult();
-    assertEquals("PARENT:USES", result.get(0));
-  }
-
-  @Test
-  public void testDeployToAlternateDirectory() throws IOException, ClassNotFoundException {
-    final File alternateDir = new File("JarDeployerDUnit");
-    alternateDir.mkdir();
-
-    // Add the alternate directory to the distributed system, get it back out, and then create
-    // a JarDeployer object with it.
-    Properties properties = new Properties();
-    properties.put(ConfigurationProperties.DEPLOY_WORKING_DIR, alternateDir.getAbsolutePath());
-    InternalDistributedSystem distributedSystem = getSystem(properties);
-    final JarDeployer jarDeployer =
-        new JarDeployer(distributedSystem.getConfig().getDeployWorkingDir());
-
-    File jarFile = jarDeployer.getNextVersionJarFile("JarDeployerDUnit.jar");
-    byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDTAC");
-    jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
-
-    try {
-      ClassPathLoader.getLatest().forName("JarDeployerDUnitDTAC");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    if (!jarFile.exists()) {
-      fail("JAR file not found where expected: " + jarFile.getName());
-    }
-
-    if (!doesFileMatchBytes(jarFile, jarBytes)) {
-      fail("Contents of JAR file do not match those provided: " + jarFile.getName());
-    }
-  }
-
-
-
-  @Test
-  public void testSuspendAndResume() throws IOException, ClassNotFoundException {
-    AtomicReference<Boolean> okayToResume = new AtomicReference<>(false);
-
-    final JarDeployer jarDeployer = new JarDeployer();
-    byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitSAR");
-    final JarDeployer suspendingJarDeployer = new JarDeployer();
-    final CountDownLatch latch = new CountDownLatch(1);
-
-    Thread thread = new Thread() {
-      @Override
-      public void run() {
-        try {
-          suspendingJarDeployer.suspendAll();
-          latch.countDown();
-          Thread.sleep(3000);
-        } catch (InterruptedException iex) {
-          // It doesn't matter, just fail the test
-        }
-        okayToResume.set(true);
-        suspendingJarDeployer.resumeAll();
-      }
-    };
-    thread.start();
-
-    try {
-      latch.await();
-    } catch (InterruptedException iex) {
-      // It doesn't matter, just fail the test
-    }
-    jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
-    if (!okayToResume.get()) {
-      fail("JarDeployer did not suspend as expected");
-    }
-  }
-
-
-  @Test
-  public void testZeroLengthFile() throws IOException, ClassNotFoundException {
-    final JarDeployer jarDeployer = new JarDeployer();
-
-    try {
-      jarDeployer.deploy(new String[] {"JarDeployerDUnitZLF.jar"}, new byte[][] {new byte[0]});
-      fail("Zero length files are not deployable");
-    } catch (IllegalArgumentException expected) {
-      // Expected
-    }
-
-    try {
-      jarDeployer.deploy(new String[] {"JarDeployerDUnitZLF1.jar", "JarDeployerDUnitZLF2.jar"},
-          new byte[][] {this.classBuilder.createJarFromName("JarDeployerDUnitZLF1"), new byte[0]});
-      fail("Zero length files are not deployable");
-    } catch (IllegalArgumentException expected) {
-      // Expected
-    }
-  }
-
-  @Test
-  public void testInvalidJarFile() throws IOException, ClassNotFoundException {
-    final JarDeployer jarDeployer = new JarDeployer();
-
-    try {
-      jarDeployer.deploy(new String[] {"JarDeployerDUnitIJF.jar"},
-          new byte[][] {"INVALID JAR CONTENT".getBytes()});
-      fail("Non-JAR files are not deployable");
-    } catch (IllegalArgumentException expected) {
-      // Expected
-    }
-
-    try {
-      jarDeployer.deploy(new String[] {"JarDeployerDUnitIJF1.jar", "JarDeployerDUnitIJF2.jar"},
-          new byte[][] {this.classBuilder.createJarFromName("JarDeployerDUnitIJF1"),
-              "INVALID JAR CONTENT".getBytes()});
-      fail("Non-JAR files are not deployable");
-    } catch (IllegalArgumentException expected) {
-      // Expected
-    }
-
-    final VM vm = Host.getHost(0).getVM(1);
-    vm.invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        File invalidFile = new File(JarDeployer.JAR_PREFIX + "JarDeployerDUnitIJF.jar#3");
-        try {
-          RandomAccessFile randomAccessFile = new RandomAccessFile(invalidFile, "rw");
-          randomAccessFile.write("GARBAGE".getBytes(), 0, 7);
-          randomAccessFile.close();
-        } catch (IOException ioex) {
-          Assert.fail("Error trying to create garbage file for test", ioex);
-        }
-
-        getSystem();
-        getCache();
-
-        if (invalidFile.exists()) {
-          fail("Invalid JAR file should have been deleted at startup");
-        }
-      }
-    });
-  }
-
-  FileLock acquireSharedLock(final File file) throws IOException {
-    return new FileInputStream(file).getChannel().lock(0, 1, true);
-  }
-
-  void releaseLock(final FileLock fileLock, final File lockFile) throws IOException {
-    if (lockFile == null) {
-      return;
-    }
-
-    try {
-      if (fileLock != null) {
-        fileLock.release();
-        fileLock.channel().close();
-      }
-    } finally {
-      if (!lockFile.delete()) {
-        lockFile.deleteOnExit();
-      }
-    }
-  }
-
-  protected boolean doesFileMatchBytes(final File file, final byte[] bytes) throws IOException {
-    // If the don't have the same number of bytes then nothing to do
-    if (file.length() != bytes.length) {
-      return false;
-    }
-
-    // Open the file then loop comparing each byte
-    int index = 0;
-    try (InputStream inStream = new FileInputStream(file)) {
-      for (; index < bytes.length; index++) {
-        if (((byte) inStream.read()) != bytes[index])
-          break;
-      }
-    }
-
-    // If we didn't get to the end then something was different
-    return index >= bytes.length;
-  }
-
-  private void deleteSavedJarFiles() throws IOException {
-    Pattern pattern = Pattern.compile("^" + JarDeployer.JAR_PREFIX + "JarDeployerDUnit.*#\\d++$");
-    File[] files = new File(".").listFiles((dir1, name) -> pattern.matcher(name).matches());
-    if (files != null) {
-      for (File file : files) {
-        Files.delete(file.toPath());
-      }
-    }
-  }
-
-  void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
-    final OutputStream outStream = new FileOutputStream(jarFile);
-    outStream.write(jarBytes);
-    outStream.close();
-  }
-}

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
index 71daecc..9e42c20 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
@@ -11,9 +11,12 @@
  * 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;
 
+
 import static org.apache.geode.internal.Assert.fail;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -22,162 +25,187 @@ import org.apache.geode.test.junit.categories.IntegrationTest;
 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 org.junit.rules.TemporaryFolder;
 
 import java.io.File;
 import java.io.IOException;
-import java.nio.file.Files;
+import java.util.Set;
+import java.util.concurrent.BrokenBarrierException;
 import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @Category(IntegrationTest.class)
 public class JarDeployerIntegrationTest {
-
   private ClassBuilder classBuilder;
 
   @Rule
   public TemporaryFolder temporaryFolder = new TemporaryFolder();
 
-  @Rule
-  public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
+  JarDeployer jarDeployer;
 
   @Before
   public void setup() {
-    System.setProperty("user.dir", temporaryFolder.getRoot().getAbsolutePath());
     classBuilder = new ClassBuilder();
-    ClassPathLoader.setLatestToDefault();
+    jarDeployer = new JarDeployer(temporaryFolder.getRoot());
   }
 
-  @Test
-  public void testDeployFileAndChange() throws Exception {
-    final JarDeployer jarDeployer = new JarDeployer();
+  private byte[] createJarWithClass(String className) throws IOException {
+    String stringBuilder = "package integration.parent;" + "public class " + className + " {}";
 
-    // First deploy of the JAR file
-    byte[] jarBytes = this.classBuilder.createJarFromName("ClassA");
-    JarClassLoader jarClassLoader =
-        jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes})[0];
-    File deployedJar = new File(jarClassLoader.getFileCanonicalPath());
+    return this.classBuilder.createJarFromClassContent("integration/parent/" + className,
+        stringBuilder);
+  }
+
+  @Test
+  public void testFileVersioning() throws Exception {
+    String jarName = "JarDeployerIntegrationTest.jar";
 
-    assertThat(deployedJar).exists();
-    assertThat(deployedJar.getName()).contains("#1");
-    assertThat(deployedJar.getName()).doesNotContain("#2");
+    byte[] firstJarBytes = createJarWithClass("ClassA");
 
-    assertThat(ClassPathLoader.getLatest().forName("ClassA")).isNotNull();
+    // First deploy of the JAR file
+    DeployedJar firstDeployedJar = jarDeployer.deployWithoutRegistering(jarName, firstJarBytes);
 
-    assertThat(doesFileMatchBytes(deployedJar, jarBytes));
+    assertThat(firstDeployedJar.getFile()).exists().hasBinaryContent(firstJarBytes);
+    assertThat(firstDeployedJar.getFile().getName()).contains(".v1.").doesNotContain(".v2.");
 
     // Now deploy an updated JAR file and make sure that the next version of the JAR file
-    // was created and the first one was deleted.
-    jarBytes = this.classBuilder.createJarFromName("ClassB");
-    JarClassLoader newJarClassLoader =
-        jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes})[0];
-    File nextDeployedJar = new File(newJarClassLoader.getFileCanonicalPath());
-
-    assertThat(nextDeployedJar.exists());
-    assertThat(nextDeployedJar.getName()).contains("#2");
-    assertThat(doesFileMatchBytes(nextDeployedJar, jarBytes));
+    // was created
+    byte[] secondJarBytes = createJarWithClass("ClassB");
 
-    assertThat(ClassPathLoader.getLatest().forName("ClassB")).isNotNull();
+    DeployedJar secondDeployedJar = jarDeployer.deployWithoutRegistering(jarName, secondJarBytes);
+    File secondDeployedJarFile = new File(secondDeployedJar.getFileCanonicalPath());
 
-    assertThatThrownBy(() -> ClassPathLoader.getLatest().forName("ClassA"))
-        .isExactlyInstanceOf(ClassNotFoundException.class);
+    assertThat(secondDeployedJarFile).exists().hasBinaryContent(secondJarBytes);
+    assertThat(secondDeployedJarFile.getName()).contains(".v2.").doesNotContain(".v1.");
 
-    assertThat(jarDeployer.findSortedOldVersionsOfJar("JarDeployerDUnit.jar")).hasSize(1);
+    File[] sortedOldJars = jarDeployer.findSortedOldVersionsOfJar(jarName);
+    assertThat(sortedOldJars).hasSize(2);
+    assertThat(sortedOldJars[0].getName()).contains(".v2.");
+    assertThat(sortedOldJars[1].getName()).contains(".v1.");
     assertThat(jarDeployer.findDistinctDeployedJars()).hasSize(1);
   }
 
   @Test
-  public void testDeployNoUpdateWhenNoChange() throws Exception {
-    final JarDeployer jarDeployer = new JarDeployer();
-
-    // First deploy of the JAR file
-    byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDNUWNC");
-    JarClassLoader jarClassLoader =
-        jarDeployer.deploy(new String[] {"JarDeployerDUnit2.jar"}, new byte[][] {jarBytes})[0];
-    File deployedJar = new File(jarClassLoader.getFileCanonicalPath());
-
-    assertThat(deployedJar).exists();
-    assertThat(deployedJar.getName()).contains("#1");
-    JarClassLoader newJarClassLoader =
-        jarDeployer.deploy(new String[] {"JarDeployerDUnit2.jar"}, new byte[][] {jarBytes})[0];
-    assertThat(newJarClassLoader).isNull();
-  }
-
-  @Test
-  public void testDeployToInvalidDirectory() throws IOException, ClassNotFoundException {
+  public void testDeployToInvalidDirectory() throws Exception {
     final File alternateDir = new File(temporaryFolder.getRoot(), "JarDeployerDUnit");
     alternateDir.delete();
 
     final JarDeployer jarDeployer = new JarDeployer(alternateDir);
+
     final CyclicBarrier barrier = new CyclicBarrier(2);
     final byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDTID");
 
     // Test to verify that deployment fails if the directory doesn't exist.
     assertThatThrownBy(() -> {
-      jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
+      jarDeployer.deployWithoutRegistering("JarDeployerIntegrationTest.jar", jarBytes);
     }).isInstanceOf(IOException.class);
 
     // Test to verify that deployment succeeds if the directory doesn't
     // initially exist, but is then created while the JarDeployer is looping
     // looking for a valid directory.
-    Thread thread = new Thread() {
-      @Override
-      public void run() {
-        try {
-          barrier.await();
-        } catch (Exception e) {
-          fail(e);
-        }
-
-        try {
-          jarDeployer.deploy(new String[] {"JarDeployerDUnit.jar"}, new byte[][] {jarBytes});
-        } catch (Exception e) {
-          fail(e);
-        }
-      }
-    };
-    thread.start();
-
-    try {
+    Future<Boolean> done = Executors.newSingleThreadExecutor().submit(() -> {
       barrier.await();
-      Thread.sleep(500);
-      alternateDir.mkdir();
-      thread.join();
-    } catch (Exception e) {
-      fail(e);
-    }
+      jarDeployer.deployWithoutRegistering("JarDeployerIntegrationTest.jar", jarBytes);
+      return true;
+    });
+
+    barrier.await();
+    Thread.sleep(500);
+    alternateDir.mkdir();
+    assertThat(done.get(2, TimeUnit.MINUTES)).isTrue();
   }
 
   @Test
   public void testVersionNumberCreation() throws Exception {
-    JarDeployer jarDeployer = new JarDeployer();
-
-    File versionedName = jarDeployer.getNextVersionJarFile("myJar.jar");
-    assertThat(versionedName.getName()).isEqualTo(JarDeployer.JAR_PREFIX + "myJar.jar" + "#1");
+    File versionedName = jarDeployer.getNextVersionedJarFile("myJar.jar");
+    assertThat(versionedName.getName()).isEqualTo("myJar.v1.jar");
 
     byte[] jarBytes = this.classBuilder.createJarFromName("ClassA");
-    JarClassLoader jarClassLoader =
-        jarDeployer.deploy(new String[] {"myJar.jar"}, new byte[][] {jarBytes})[0];
-    File deployedJar = new File(jarClassLoader.getFileCanonicalPath());
+    File deployedJarFile = jarDeployer.deployWithoutRegistering("myJar.jar", jarBytes).getFile();
 
-    assertThat(deployedJar.getName()).isEqualTo(JarDeployer.JAR_PREFIX + "myJar.jar" + "#1");
-    assertThat(jarDeployer.getNextVersionJarFile(deployedJar.getName()).getName())
-        .isEqualTo(JarDeployer.JAR_PREFIX + "myJar.jar" + "#2");
+    assertThat(deployedJarFile.getName()).isEqualTo("myJar.v1.jar");
 
+    File secondDeployedJarFile =
+        jarDeployer.deployWithoutRegistering("myJar.jar", jarBytes).getFile();
+
+    assertThat(secondDeployedJarFile.getName()).isEqualTo("myJar.v2.jar");
   }
 
   @Test
-  public void testVersionNumberMatcher() throws Exception {
-    JarDeployer jarDeployer = new JarDeployer();
-    int version = jarDeployer.extractVersionFromFilename(
-        temporaryFolder.newFile(JarDeployer.JAR_PREFIX + "MyJar.jar" + "#1"));
+  public void testVersionNumberMatcher() throws IOException {
+    int version =
+        jarDeployer.extractVersionFromFilename(temporaryFolder.newFile("MyJar.v1.jar").getName());
 
     assertThat(version).isEqualTo(1);
   }
 
-  private boolean doesFileMatchBytes(final File file, final byte[] bytes) throws IOException {
-    return bytes == Files.readAllBytes(file.toPath());
+  @Test
+  public void testRenamingOfOldJarFiles() throws Exception {
+    File deployDir = jarDeployer.getDeployDirectory();
+
+    File jarAVersion1 = new File(deployDir, "vf.gf#myJarA.jar#1");
+    this.classBuilder.writeJarFromName("ClassA", jarAVersion1);
+
+    File jarAVersion2 = new File(deployDir, "vf.gf#myJarA.jar#2");
+    this.classBuilder.writeJarFromName("ClassA", jarAVersion2);
+
+    File jarBVersion2 = new File(deployDir, "vf.gf#myJarB.jar#2");
+    this.classBuilder.writeJarFromName("ClassB", jarBVersion2);
+
+    File jarBVersion3 = new File(deployDir, "vf.gf#myJarB.jar#3");
+    this.classBuilder.writeJarFromName("ClassB", jarBVersion3);
+
+    Set<File> deployedJarsBeforeRename = Stream
+        .of(jarAVersion1, jarAVersion2, jarBVersion2, jarBVersion3).collect(Collectors.toSet());
+
+    jarDeployer.renameJarsWithOldNamingConvention();
+
+    deployedJarsBeforeRename.forEach(oldJar -> assertThat(oldJar).doesNotExist());
+
+    File renamedJarAVersion1 = new File(deployDir, "myJarA.v1.jar");
+    File renamedJarAVersion2 = new File(deployDir, "myJarA.v2.jar");
+    File renamedJarBVersion2 = new File(deployDir, "myJarB.v2.jar");
+    File renamedJarBVersion3 = new File(deployDir, "myJarB.v3.jar");
+    Set<File> expectedJarsAfterRename = Stream
+        .of(renamedJarAVersion1, renamedJarAVersion2, renamedJarBVersion2, renamedJarBVersion3)
+        .collect(Collectors.toSet());
+
+    Set<File> actualJarsInDeployDir =
+        Stream.of(jarDeployer.getDeployDirectory().listFiles()).collect(Collectors.toSet());
+
+    assertThat(actualJarsInDeployDir).isEqualTo(expectedJarsAfterRename);
   }
 
+  @Test
+  public void testOldJarNameMatcher() throws Exception {
+    File deployDir = jarDeployer.getDeployDirectory();
+
+    File jarAVersion1 = new File(deployDir, "vf.gf#myJarA.jar#1");
+    this.classBuilder.writeJarFromName("ClassA", jarAVersion1);
+
+    File jarAVersion2 = new File(deployDir, "vf.gf#myJarA.jar#2");
+    this.classBuilder.writeJarFromName("ClassA", jarAVersion2);
+
+    File jarBVersion2 = new File(deployDir, "vf.gf#myJarB.jar#2");
+    this.classBuilder.writeJarFromName("ClassB", jarBVersion2);
+
+    File jarBVersion3 = new File(deployDir, "vf.gf#myJarB.jar#3");
+    this.classBuilder.writeJarFromName("ClassB", jarBVersion3);
+
+    Set<File> jarsWithOldNamingConvention = Stream
+        .of(jarAVersion1, jarAVersion2, jarBVersion2, jarBVersion3).collect(Collectors.toSet());
+
+    jarsWithOldNamingConvention.forEach(
+        jarFile -> assertThat(jarDeployer.isOldNamingConvention(jarFile.getName())).isTrue());
+
+    Set<File> foundJarsWithOldNamingConvention = jarDeployer.findJarsWithOldNamingConvention();
+    assertThat(foundJarsWithOldNamingConvention).isEqualTo(jarsWithOldNamingConvention);
+  }
+
+
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java
index 0cc003e..dcbbeb0 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java
@@ -20,6 +20,22 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.geode.internal.ClassPathLoader;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.filefilter.DirectoryFileFilter;
 import org.apache.commons.io.filefilter.RegexFileFilter;
@@ -38,7 +54,7 @@ import org.apache.geode.cache.persistence.PersistentID;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.distributed.DistributedSystem;
 import org.apache.geode.internal.ClassBuilder;
-import org.apache.geode.internal.JarClassLoader;
+import org.apache.geode.internal.DeployedJar;
 import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.cache.persistence.BackupManager;
 import org.apache.geode.internal.util.IOUtils;
@@ -1078,15 +1094,16 @@ public class IncrementalBackupDUnitTest extends JUnit4CacheTestCase {
     /*
      * Deploy a "dummy"�jar to the VM.
      */
-    vm0.invoke(new SerializableCallable() {
+    File deployedJarFile = (File) vm0.invoke(new SerializableCallable() {
       @Override
       public Object call() throws Exception {
-        JarDeployer deployer = new JarDeployer();
-        deployer.deploy(new String[] {jarName}, new byte[][] {classBytes});
-        return null;
+        DeployedJar deployedJar =
+            ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, classBytes);
+        return deployedJar.getFile();
       }
     });
 
+    assert (deployedJarFile.exists());
     /*
      * Perform backup. Make sure it is successful.
      */
@@ -1149,10 +1166,10 @@ public class IncrementalBackupDUnitTest extends JUnit4CacheTestCase {
     vm0.invoke(new SerializableCallable() {
       @Override
       public Object call() throws Exception {
-        JarDeployer deployer = new JarDeployer();
-        for (JarClassLoader jarClassLoader : deployer.findJarClassLoaders()) {
+        for (DeployedJar jarClassLoader : ClassPathLoader.getLatest().getJarDeployer()
+            .findDeployedJars()) {
           if (jarClassLoader.getJarName().startsWith(jarName)) {
-            deployer.undeploy(jarClassLoader.getJarName());
+            ClassPathLoader.getLatest().getJarDeployer().undeploy(jarClassLoader.getJarName());
           }
         }
         return null;

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java b/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
new file mode 100644
index 0000000..3149432
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
@@ -0,0 +1,31 @@
+/*
+ * 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.management;
+
+import org.apache.geode.internal.ClassPathLoaderIntegrationTest;
+import org.apache.geode.internal.ClassPathLoaderTest;
+import org.apache.geode.internal.DeployedJarJUnitTest;
+import org.apache.geode.internal.JarDeployerIntegrationTest;
+import org.apache.geode.management.internal.cli.commands.DeployCommandRedeployDUnitTest;
+import org.apache.geode.management.internal.cli.commands.DeployCommandsDUnitTest;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({DeployedJarJUnitTest.class, DeployCommandsDUnitTest.class,
+    JarDeployerIntegrationTest.class, ClassPathLoaderIntegrationTest.class,
+    ClassPathLoaderTest.class, DeployCommandRedeployDUnitTest.class})
+public class DeployJarTestSuite {
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java
new file mode 100644
index 0000000..8280f5d
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.management.internal.cli.commands;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.geode.cache.execute.Execution;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.distributed.DistributedSystem;
+import org.apache.geode.internal.ClassBuilder;
+import org.apache.geode.internal.ClassPathLoader;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
+import org.apache.geode.test.dunit.rules.Locator;
+import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.dunit.rules.Server;
+import org.apache.geode.test.junit.categories.DistributedTest;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.List;
+
+@Category(DistributedTest.class)
+public class DeployCommandRedeployDUnitTest implements Serializable {
+  private static final String VERSION1 = "Version1";
+  private static final String VERSION2 = "Version2";
+
+  private static final String jarNameA = "DeployCommandRedeployDUnitTestA.jar";
+  private static final String functionA = "DeployCommandRedeployDUnitFunctionA";
+  private File jarAVersion1;
+  private File jarAVersion2;
+
+  private static final String jarNameB = "DeployCommandRedeployDUnitTestB.jar";
+  private static final String functionB = "DeployCommandRedeployDUnitFunctionB";
+  private static final String packageB = "jddunit.function";
+  private static final String fullyQualifiedFunctionB = packageB + "." + functionB;
+  private File jarBVersion1;
+  private File jarBVersion2;
+
+  private MemberVM locator;
+  private MemberVM server;
+
+  @Rule
+  public LocatorServerStartupRule lsRule = new LocatorServerStartupRule();
+
+  @Rule
+  public transient GfshShellConnectionRule gfshConnector = new GfshShellConnectionRule();
+
+  @Before
+  public void setup() throws Exception {
+    jarAVersion1 = createJarWithFunctionA(VERSION1);
+    jarAVersion2 = createJarWithFunctionA(VERSION2);
+
+    jarBVersion1 = createJarWithFunctionB(VERSION1);
+    jarBVersion2 = createJarWithFunctionB(VERSION2);
+
+    locator = lsRule.startLocatorVM(0);
+    server = lsRule.startServerVM(1, locator.getPort());
+
+    gfshConnector.connectAndVerify(locator);
+  }
+
+  @Test
+  public void redeployJarsWithNewVersionsOfFunctions() throws Exception {
+    gfshConnector.executeAndVerifyCommand("deploy --jar=" + jarAVersion1.getCanonicalPath());
+    server.invoke(() -> assertThatCanLoad(jarNameA, functionA));
+    server.invoke(() -> assertThatFunctionHasVersion(functionA, VERSION1));
+
+
+    gfshConnector.executeAndVerifyCommand("deploy --jar=" + jarBVersion1.getCanonicalPath());
+    server.invoke(() -> assertThatCanLoad(jarNameA, functionA));
+    server.invoke(() -> assertThatCanLoad(jarNameB, fullyQualifiedFunctionB));
+    server.invoke(() -> assertThatFunctionHasVersion(functionA, VERSION1));
+    server.invoke(() -> assertThatFunctionHasVersion(functionB, VERSION1));
+
+    gfshConnector.executeAndVerifyCommand("deploy --jar=" + jarBVersion2.getCanonicalPath());
+    server.invoke(() -> assertThatCanLoad(jarNameA, functionA));
+    server.invoke(() -> assertThatCanLoad(jarNameB, fullyQualifiedFunctionB));
+    server.invoke(() -> assertThatFunctionHasVersion(functionA, VERSION1));
+    server.invoke(() -> assertThatFunctionHasVersion(functionB, VERSION2));
+
+    gfshConnector.executeAndVerifyCommand("deploy --jar=" + jarAVersion2.getCanonicalPath());
+    server.invoke(() -> assertThatCanLoad(jarNameA, functionA));
+    server.invoke(() -> assertThatCanLoad(jarNameB, fullyQualifiedFunctionB));
+    server.invoke(() -> assertThatFunctionHasVersion(functionA, VERSION2));
+    server.invoke(() -> assertThatFunctionHasVersion(functionB, VERSION2));
+  }
+
+  // Note that jar A is a Declarable Function, while jar B is only a Function.
+  // Also, the function for jar A resides in the default package, whereas jar B specifies a package.
+  // This ensures that this test has identical coverage to some tests that it replaced.
+  private File createJarWithFunctionA(String version) throws Exception {
+    String classContents =
+        "import java.util.Properties;" + "import org.apache.geode.cache.Declarable;"
+            + "import org.apache.geode.cache.execute.Function;"
+            + "import org.apache.geode.cache.execute.FunctionContext;" + "public class " + functionA
+            + " implements Function, Declarable {" + "public String getId() {return \"" + functionA
+            + "\";}" + "public void init(Properties props) {}"
+            + "public void execute(FunctionContext context) {context.getResultSender().lastResult(\""
+            + version + "\");}" + "public boolean hasResult() {return true;}"
+            + "public boolean optimizeForWrite() {return false;}"
+            + "public boolean isHA() {return false;}}";
+
+    File jar = new File(lsRule.getTempFolder().newFolder(jarNameA + version), this.jarNameA);
+    ClassBuilder functionClassBuilder = new ClassBuilder();
+    functionClassBuilder.writeJarFromContent(functionA, classContents, jar);
+
+    return jar;
+  }
+
+  private File createJarWithFunctionB(String version) throws IOException {
+    String classContents =
+        "package " + packageB + ";" + "import org.apache.geode.cache.execute.Function;"
+            + "import org.apache.geode.cache.execute.FunctionContext;" + "public class " + functionB
+            + " implements Function {" + "public boolean hasResult() {return true;}"
+            + "public void execute(FunctionContext context) {context.getResultSender().lastResult(\""
+            + version + "\");}" + "public String getId() {return \"" + functionB + "\";}"
+            + "public boolean optimizeForWrite() {return false;}"
+            + "public boolean isHA() {return false;}}";
+
+    File jar = new File(lsRule.getTempFolder().newFolder(jarNameB + version), this.jarNameB);
+    ClassBuilder functionClassBuilder = new ClassBuilder();
+    functionClassBuilder.writeJarFromContent("jddunit/function/" + functionB, classContents, jar);
+
+    return jar;
+  }
+
+  private void assertThatFunctionHasVersion(String functionId, String version) {
+    GemFireCacheImpl gemFireCache = GemFireCacheImpl.getInstance();
+    DistributedSystem distributedSystem = gemFireCache.getDistributedSystem();
+    Execution execution =
+        FunctionService.onMember(distributedSystem, distributedSystem.getDistributedMember());
+    List<String> result = (List<String>) execution.execute(functionId).getResult();
+    assertThat(result.get(0)).isEqualTo(version);
+  }
+
+  private void assertThatCanLoad(String jarName, String className) throws ClassNotFoundException {
+    assertThat(ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jarName)).isNotNull();
+    assertThat(ClassPathLoader.getLatest().forName(className)).isNotNull();
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java
index 7b0823b..6df2572 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java
@@ -14,53 +14,28 @@
  */
 package org.apache.geode.management.internal.cli.commands;
 
-import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_CLUSTER_CONFIGURATION;
 import static org.apache.geode.distributed.ConfigurationProperties.GROUPS;
-import static org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_PORT;
-import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER;
-import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_BIND_ADDRESS;
-import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_PORT;
-import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_START;
-import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL;
-import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
-import static org.apache.geode.distributed.ConfigurationProperties.NAME;
-import static org.apache.geode.test.dunit.Assert.assertEquals;
-import static org.apache.geode.test.dunit.Assert.assertFalse;
-import static org.apache.geode.test.dunit.Assert.assertTrue;
-import static org.apache.geode.test.dunit.Assert.fail;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertNotNull;
 
-import org.apache.geode.distributed.Locator;
-import org.apache.geode.distributed.internal.DistributionManager;
-import org.apache.geode.distributed.internal.InternalLocator;
-import org.apache.geode.distributed.internal.ClusterConfigurationService;
-import org.apache.geode.internal.AvailablePort;
-import org.apache.geode.internal.AvailablePortHelper;
 import org.apache.geode.internal.ClassBuilder;
-import org.apache.geode.internal.JarDeployer;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.i18n.CliStrings;
-import org.apache.geode.management.internal.cli.remote.CommandExecutionContext;
-import org.apache.geode.management.internal.cli.remote.CommandProcessor;
+import org.apache.geode.internal.ClassPathLoader;
 import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.test.dunit.Assert;
-import org.apache.geode.test.dunit.Host;
-import org.apache.geode.test.dunit.SerializableRunnable;
-import org.apache.geode.test.dunit.VM;
-import org.apache.geode.test.dunit.Wait;
-import org.apache.geode.test.dunit.WaitCriterion;
-import org.apache.geode.test.dunit.rules.ServerStarterRule;
+import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
+import org.apache.geode.test.dunit.rules.Locator;
+import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
+import org.apache.geode.test.dunit.rules.MemberVM;
+import org.apache.geode.test.dunit.rules.Server;
 import org.apache.geode.test.junit.categories.DistributedTest;
+import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
 import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
+import java.io.Serializable;
 import java.util.Properties;
-import java.util.regex.Pattern;
 
 /**
  * Unit tests for the DeployCommands class
@@ -69,453 +44,238 @@ import java.util.regex.Pattern;
  */
 @SuppressWarnings("serial")
 @Category(DistributedTest.class)
-public class DeployCommandsDUnitTest extends CliCommandTestBase {
-
-  private final Pattern pattern =
-      Pattern.compile("^" + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit.*#\\d++$");
-  private File newDeployableJarFile;
-  private transient ClassBuilder classBuilder;
-  private transient CommandProcessor commandProcessor;
-
-  @Override
-  public final void postSetUpCliCommandTestBase() throws Exception {
-    this.newDeployableJarFile = new File(this.temporaryFolder.getRoot().getCanonicalPath()
-        + File.separator + "DeployCommandsDUnit1.jar");
-    this.classBuilder = new ClassBuilder();
-    this.commandProcessor = new CommandProcessor();
-    assertFalse(this.commandProcessor.isStopped());
-
-    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
-      public void run() {
-        deleteSavedJarFiles();
-      }
-    });
-    deleteSavedJarFiles();
+public class DeployCommandsDUnitTest implements Serializable {
+  private static final String GROUP1 = "Group1";
+  private static final String GROUP2 = "Group2";
+
+  private final String class1 = "DeployCommandsDUnitA";
+  private final String class2 = "DeployCommandsDUnitB";
+  private final String class3 = "DeployCommandsDUnitC";
+  private final String class4 = "DeployCommandsDUnitD";
+
+  private final String jarName1 = "DeployCommandsDUnit1.jar";
+  private final String jarName2 = "DeployCommandsDUnit2.jar";
+  private final String jarName3 = "DeployCommandsDUnit3.jar";
+  private final String jarName4 = "DeployCommandsDUnit4.jar";
+
+  private File jar1;
+  private File jar2;
+  private File jar3;
+  private File jar4;
+  private File subdirWithJars3and4;
+
+  private MemberVM locator;
+  private MemberVM server1;
+  private MemberVM server2;
+
+  @Rule
+  public LocatorServerStartupRule lsRule = new LocatorServerStartupRule();
+
+  @Rule
+  public transient GfshShellConnectionRule gfshConnector = new GfshShellConnectionRule();
+
+  @Before
+  public void setup() throws Exception {
+    ClassBuilder classBuilder = new ClassBuilder();
+    File jarsDir = lsRule.getTempFolder().newFolder();
+    jar1 = new File(jarsDir, jarName1);
+    jar2 = new File(jarsDir, jarName2);
+
+    subdirWithJars3and4 = new File(jarsDir, "subdir");
+    subdirWithJars3and4.mkdirs();
+    jar3 = new File(subdirWithJars3and4, jarName3);
+    jar4 = new File(subdirWithJars3and4, jarName4);
+
+    classBuilder.writeJarFromName(class1, jar1);
+    classBuilder.writeJarFromName(class2, jar2);
+    classBuilder.writeJarFromName(class3, jar3);
+    classBuilder.writeJarFromName(class4, jar4);
+
+    locator = lsRule.startLocatorVM(0);
+
+    Properties props = new Properties();
+    props.setProperty(GROUPS, GROUP1);
+    server1 = lsRule.startServerVM(1, props, locator.getPort());
+
+    props.setProperty(GROUPS, GROUP2);
+    server2 = lsRule.startServerVM(2, props, locator.getPort());
+
+    gfshConnector.connectAndVerify(locator);
   }
 
-  @SuppressWarnings("serial")
-  @Override
-  protected final void preTearDownCliCommandTestBase() throws Exception {
-    Host.getHost(0).getVM(1).invoke(new SerializableRunnable() {
-      public void run() {
-        DistributionManager.isDedicatedAdminVM = false;
-      }
-    });
+  @Test
+  public void deployJarToOneGroup() throws Exception {
+    // Deploy a jar to a single group
+    CommandResult cmdResult =
+        gfshConnector.executeAndVerifyCommand("deploy --jar=" + jar2 + " --group=" + GROUP1);
+    String resultString = commandResultToString(cmdResult);
 
-    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
-      public void run() {
-        deleteSavedJarFiles();
-      }
-    });
-    deleteSavedJarFiles();
+    assertThat(resultString).contains(server1.getName());
+    assertThat(resultString).doesNotContain(server2.getName());
+    assertThat(resultString).contains(jarName2);
+
+    server1.invoke(() -> assertThatCanLoad(jarName2, class2));
+    server2.invoke(() -> assertThatCannotLoad(jarName2, class2));
   }
 
   @Test
-  public void testDeploy() throws Exception {
-    final Properties props = new Properties();
-    final Host host = Host.getHost(0);
-    final VM vm = host.getVM(0);
-    final String vmName = "VM" + vm.getPid();
-
-    // Create the cache in this VM
-    props.setProperty(NAME, "Controller");
-    props.setProperty(GROUPS, "Group1");
-    getSystem(props);
-    getCache();
-
-    // Create the cache in the other VM
-    vm.invoke(new SerializableRunnable() {
-      public void run() {
-        props.setProperty(NAME, vmName);
-        props.setProperty(GROUPS, "Group2");
-        getSystem(props);
-        getCache();
-      }
+  public void deployMultipleJarsToOneGroup() throws Exception {
+    // Deploy of multiple JARs to a single group
+    CommandResult cmdResult = gfshConnector.executeAndVerifyCommand(
+        "deploy --group=" + GROUP1 + " --dir=" + subdirWithJars3and4.getCanonicalPath());
+    String resultString = commandResultToString(cmdResult);
+
+    assertThat(resultString).describedAs(resultString).contains(server1.getName());
+    assertThat(resultString).doesNotContain(server2.getName());
+    assertThat(resultString).contains(jarName3);
+    assertThat(resultString).contains(jarName4);
+
+    server1.invoke(() -> {
+      assertThatCanLoad(jarName3, class3);
+      assertThatCanLoad(jarName4, class4);
+    });
+    server2.invoke(() -> {
+      assertThatCannotLoad(jarName3, class3);
+      assertThatCannotLoad(jarName4, class4);
     });
 
-    DeployCommands deployCommands = new DeployCommands();
-
-    // Single JAR all members
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit1.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitA")});
-    Result result = deployCommands.deploy(null, "DeployCommandsDUnit1.jar", null);
-
-    assertEquals(true, result.hasNextLine());
-
-    String resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(1, countMatchesInString(resultString, "Controller"));
-    assertEquals(1, countMatchesInString(resultString, vmName));
-    assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit1.jar"));
-
-    // Single JAR with group
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit2.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitB")});
-    result = deployCommands.deploy(new String[] {"Group2"}, "DeployCommandsDUnit2.jar", null);
-
-    assertEquals(true, result.hasNextLine());
-
-    resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(false, resultString.contains("Controller"));
-    assertEquals(1, countMatchesInString(resultString, vmName));
-    assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit2.jar"));
-
-    // Multiple JARs to all members
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit3.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitC"),
-        "DeployCommandsDUnit4.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitD")});
-    result = deployCommands.deploy(null, null, "AnyDirectory");
-
-    assertEquals(true, result.hasNextLine());
-
-    resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(2, countMatchesInString(resultString, "Controller"));
-    assertEquals(2, countMatchesInString(resultString, vmName));
-    assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit3.jar"));
-    assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit4.jar"));
-
-    // Multiple JARs to a group
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit5.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitE"),
-        "DeployCommandsDUnit6.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitF")});
-    result = deployCommands.deploy(new String[] {"Group1"}, null, "AnyDirectory");
-
-    assertEquals(true, result.hasNextLine());
-
-    resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(2, countMatchesInString(resultString, "Controller"));
-    assertEquals(false, resultString.contains(vmName));
-    assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit5.jar"));
-    assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit6.jar"));
-  }
 
-  @Test
-  public void testUndeploy() throws Exception {
-    final Properties props = new Properties();
-    final Host host = Host.getHost(0);
-    final VM vm = host.getVM(0);
-    final String vmName = "VM" + vm.getPid();
-
-    // Create the cache in this VM
-    props.setProperty(NAME, "Controller");
-    props.setProperty(GROUPS, "Group1");
-    getSystem(props);
-    getCache();
-
-    // Create the cache in the other VM
-    vm.invoke(new SerializableRunnable() {
-      public void run() {
-        props.setProperty(NAME, vmName);
-        props.setProperty(GROUPS, "Group2");
-        getSystem(props);
-        getCache();
-      }
+    // Undeploy of multiple jars by specifying group
+    gfshConnector.executeAndVerifyCommand("undeploy --group=" + GROUP1);
+    server1.invoke(() -> {
+      assertThatCannotLoad(jarName3, class3);
+      assertThatCannotLoad(jarName4, class4);
+    });
+    server2.invoke(() -> {
+      assertThatCannotLoad(jarName3, class3);
+      assertThatCannotLoad(jarName4, class4);
     });
-
-    DeployCommands deployCommands = new DeployCommands();
-
-    // Deploy a couple of JAR files which can be undeployed
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit1.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitA")});
-    deployCommands.deploy(new String[] {"Group1"}, "DeployCommandsDUnit1.jar", null);
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit2.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitB")});
-    deployCommands.deploy(new String[] {"Group2"}, "DeployCommandsDUnit2.jar", null);
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit3.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitC")});
-    deployCommands.deploy(null, "DeployCommandsDUnit3.jar", null);
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit4.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitD")});
-    deployCommands.deploy(null, "DeployCommandsDUnit4.jar", null);
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit5.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitE")});
-    deployCommands.deploy(null, "DeployCommandsDUnit5.jar", null);
-
-    // Undeploy for 1 group
-    Result result = deployCommands.undeploy(new String[] {"Group1"}, "DeployCommandsDUnit1.jar");
-    assertEquals(true, result.hasNextLine());
-    String resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(1, countMatchesInString(resultString, "Controller"));
-    assertEquals(false, resultString.contains(vmName));
-    assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit1.jar"));
-
-    // Multiple Undeploy for all members
-    result = deployCommands.undeploy(null, "DeployCommandsDUnit2.jar, DeployCommandsDUnit3.jar");
-    assertEquals(true, result.hasNextLine());
-    resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(2, countMatchesInString(resultString, "Controller"));
-    assertEquals(2, countMatchesInString(resultString, vmName));
-    assertEquals(3, countMatchesInString(resultString, "DeployCommandsDUnit2.jar"));
-    assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit3.jar"));
-
-    // Undeploy all (no JAR specified)
-    result = deployCommands.undeploy(null, null);
-    assertEquals(true, result.hasNextLine());
-    resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(2, countMatchesInString(resultString, "Controller"));
-    assertEquals(2, countMatchesInString(resultString, vmName));
-    assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit4.jar"));
-    assertEquals(4, countMatchesInString(resultString, "DeployCommandsDUnit5.jar"));
   }
 
   @Test
-  public void testListDeployed() throws Exception {
-    final Properties props = new Properties();
-    final Host host = Host.getHost(0);
-    final VM vm = host.getVM(0);
-    final String vmName = "VM" + vm.getPid();
-
-    // Create the cache in this VM
-    props.setProperty(NAME, "Controller");
-    props.setProperty(GROUPS, "Group1");
-    getSystem(props);
-    getCache();
-
-    // Create the cache in the other VM
-    vm.invoke(new SerializableRunnable() {
-      public void run() {
-        props.setProperty(NAME, vmName);
-        props.setProperty(GROUPS, "Group2");
-        getSystem(props);
-        getCache();
-      }
-    });
-
-    DeployCommands deployCommands = new DeployCommands();
-
-    // Deploy a couple of JAR files which can be listed
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit1.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitA")});
-    deployCommands.deploy(new String[] {"Group1"}, "DeployCommandsDUnit1.jar", null);
-    CommandExecutionContext.setBytesFromShell(new byte[][] {"DeployCommandsDUnit2.jar".getBytes(),
-        this.classBuilder.createJarFromName("DeployCommandsDUnitB")});
-    deployCommands.deploy(new String[] {"Group2"}, "DeployCommandsDUnit2.jar", null);
-
-    // List for all members
-    Result result = deployCommands.listDeployed(null);
-    assertEquals(true, result.hasNextLine());
-    String resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(1, countMatchesInString(resultString, "Controller"));
-    assertEquals(1, countMatchesInString(resultString, vmName));
-    assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit1.jar"));
-    assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit2.jar"));
-
-    // List for members in Group1
-    result = deployCommands.listDeployed("Group1");
-    assertEquals(true, result.hasNextLine());
-    resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(1, countMatchesInString(resultString, "Controller"));
-    assertEquals(false, resultString.contains(vmName));
-    assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit1.jar"));
-    assertEquals(false, resultString.contains("DeployCommandsDUnit2.jar"));
-
-    // List for members in Group2
-    result = deployCommands.listDeployed("Group2");
-    assertEquals(true, result.hasNextLine());
-    resultString = result.nextLine();
-    assertEquals(false, resultString.contains("ERROR"));
-    assertEquals(false, resultString.contains("Controller"));
-    assertEquals(1, countMatchesInString(resultString, vmName));
-    assertEquals(false, resultString.contains("DeployCommandsDUnit1.jar"));
-    assertEquals(2, countMatchesInString(resultString, "DeployCommandsDUnit2.jar"));
+  public void deployJarToAllServers() throws Exception {
+    // Deploy a jar to all servers
+    CommandResult cmdResult = gfshConnector.executeAndVerifyCommand("deploy --jar=" + jar1);
+
+    String resultString = commandResultToString(cmdResult);
+    assertThat(resultString).contains(server1.getName());
+    assertThat(resultString).contains(server2.getName());
+    assertThat(resultString).contains(jarName1);
+
+    server1.invoke(() -> assertThatCanLoad(jarName1, class1));
+    server2.invoke(() -> assertThatCanLoad(jarName1, class1));
+
+    // Undeploy of jar by specifying group
+    gfshConnector.executeAndVerifyCommand("undeploy --group=" + GROUP1);
+    server1.invoke(() -> assertThatCannotLoad(jarName1, class1));
+    server2.invoke(() -> assertThatCanLoad(jarName1, class1));
   }
 
-  /**
-   * Does an end-to-end test using the complete CLI framework while ensuring that the shared
-   * configuration is updated.
-   */
   @Test
-  public void testEndToEnd() throws Exception {
-    final String groupName = getName();
-    final int[] ports = AvailablePortHelper.getRandomAvailableTCPPorts(2);
-    jmxPort = ports[0];
-    httpPort = ports[1];
-    try {
-      jmxHost = InetAddress.getLocalHost().getHostName();
-    } catch (UnknownHostException ignore) {
-      jmxHost = "localhost";
-    }
+  public void deployMultipleJarsToAllServers() throws Exception {
+    gfshConnector.executeAndVerifyCommand("deploy --dir=" + subdirWithJars3and4.getCanonicalPath());
 
-    // Start the Locator and wait for shared configuration to be available
-    final int locatorPort = AvailablePort.getRandomAvailablePort(AvailablePort.SOCKET);
-    final String locatorLogPath = this.temporaryFolder.getRoot().getCanonicalPath() + File.separator
-        + "locator-" + locatorPort + ".log";
-
-    final Properties locatorProps = new Properties();
-    locatorProps.setProperty(NAME, "Locator");
-    locatorProps.setProperty(MCAST_PORT, "0");
-    locatorProps.setProperty(LOG_LEVEL, "fine");
-    locatorProps.setProperty(ENABLE_CLUSTER_CONFIGURATION, "true");
-    locatorProps.setProperty(JMX_MANAGER, "true");
-    locatorProps.setProperty(JMX_MANAGER_START, "true");
-    locatorProps.setProperty(JMX_MANAGER_BIND_ADDRESS, String.valueOf(jmxHost));
-    locatorProps.setProperty(JMX_MANAGER_PORT, String.valueOf(jmxPort));
-    locatorProps.setProperty(HTTP_SERVICE_PORT, String.valueOf(httpPort));
-
-    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        final File locatorLogFile = new File(locatorLogPath);
-        try {
-          final InternalLocator locator = (InternalLocator) Locator.startLocatorAndDS(locatorPort,
-              locatorLogFile, null, locatorProps);
-
-          WaitCriterion wc = new WaitCriterion() {
-            @Override
-            public boolean done() {
-              return locator.isSharedConfigurationRunning();
-            }
-
-            @Override
-            public String description() {
-              return "Waiting for shared configuration to be started";
-            }
-          };
-          Wait.waitForCriterion(wc, 5000, 500, true);
-
-        } catch (IOException e) {
-          fail("Unable to create a locator with a shared configuration", e);
-        }
-      }
+    server1.invoke(() -> {
+      assertThatCanLoad(jarName3, class3);
+      assertThatCanLoad(jarName4, class4);
+    });
+    server2.invoke(() -> {
+      assertThatCanLoad(jarName3, class3);
+      assertThatCanLoad(jarName4, class4);
     });
 
-    connect(jmxHost, jmxPort, httpPort, getDefaultShell());
+    gfshConnector.executeAndVerifyCommand("undeploy");
 
-    Host.getHost(0).getVM(1).invoke(() -> {
-      Properties properties = new Properties();
-      properties.setProperty("name", "Manager");
-      properties.setProperty("groups", groupName);
-      ServerStarterRule serverStarterRule = new ServerStarterRule();
-      serverStarterRule.withProperties(properties).withConnectionToLocator(locatorPort)
-          .startServer();
+    server1.invoke(() -> {
+      assertThatCannotLoad(jarName3, class3);
+      assertThatCannotLoad(jarName4, class4);
+    });
+    server2.invoke(() -> {
+      assertThatCannotLoad(jarName3, class3);
+      assertThatCannotLoad(jarName4, class4);
     });
+  }
 
-    // Create a JAR file
-    this.classBuilder.writeJarFromName("DeployCommandsDUnitA", this.newDeployableJarFile);
+  @Test
+  public void undeployOfMultipleJars() throws Exception {
+    gfshConnector.executeAndVerifyCommand("deploy --dir=" + subdirWithJars3and4.getCanonicalPath());
 
-    // Deploy the JAR
-    CommandResult cmdResult =
-        executeCommand("deploy --jar=" + this.newDeployableJarFile.getCanonicalPath());
-    assertEquals(Result.Status.OK, cmdResult.getStatus());
-
-    String stringResult = commandResultToString(cmdResult);
-    assertEquals(3, countLinesInString(stringResult, false));
-    assertTrue(stringContainsLine(stringResult, "Member.*JAR.*JAR Location"));
-    assertTrue(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*"
-        + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1"));
-
-    // Undeploy the JAR
-    cmdResult = executeCommand("undeploy --jar=DeployCommandsDUnit1.jar");
-    assertEquals(Result.Status.OK, cmdResult.getStatus());
-
-    stringResult = commandResultToString(cmdResult);
-    assertEquals(3, countLinesInString(stringResult, false));
-    assertThat(stringContainsLine(stringResult, "Member.*JAR.*Un-Deployed From JAR Location"))
-        .describedAs(stringResult).isTrue();
-    assertThat(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*"
-        + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1")).describedAs(stringResult)
-            .isTrue();;
-
-
-    // Deploy the JAR to a group
-    cmdResult = executeCommand(
-        "deploy --jar=" + this.newDeployableJarFile.getCanonicalPath() + " --group=" + groupName);
-    assertThat(cmdResult.getStatus()).describedAs(cmdResult.toString()).isEqualTo(Result.Status.OK);
-
-    stringResult = commandResultToString(cmdResult);
-    assertEquals(3, countLinesInString(stringResult, false));
-    assertThat(stringContainsLine(stringResult, "Member.*JAR.*JAR Location"))
-        .describedAs(stringResult).isTrue();
-
-    assertThat(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*"
-        + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1")).describedAs(stringResult)
-            .isTrue();
-
-    // Make sure the deployed jar in the shared config
-    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        ClusterConfigurationService sharedConfig =
-            ((InternalLocator) Locator.getLocator()).getSharedConfiguration();
-        try {
-          assertTrue(sharedConfig.getConfiguration(groupName).getJarNames()
-              .contains("DeployCommandsDUnit1.jar"));
-        } catch (Exception e) {
-          Assert.fail("Error occurred in cluster configuration service", e);
-        }
-      }
+    server1.invoke(() -> {
+      assertThatCanLoad(jarName3, class3);
+      assertThatCanLoad(jarName4, class4);
+    });
+    server2.invoke(() -> {
+      assertThatCanLoad(jarName3, class3);
+      assertThatCanLoad(jarName4, class4);
     });
 
-    // List deployed for group
-    cmdResult = executeCommand("list deployed --group=" + groupName);
-    assertEquals(Result.Status.OK, cmdResult.getStatus());
-
-    stringResult = commandResultToString(cmdResult);
-    assertEquals(3, countLinesInString(stringResult, false));
-    assertTrue(stringContainsLine(stringResult, "Member.*JAR.*JAR Location"));
-    assertTrue(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*"
-        + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1"));
-
-    // Undeploy for group
-    cmdResult = executeCommand("undeploy --group=" + groupName);
-    assertEquals(Result.Status.OK, cmdResult.getStatus());
-
-    stringResult = commandResultToString(cmdResult);
-    assertEquals(3, countLinesInString(stringResult, false));
-    assertTrue(stringContainsLine(stringResult, "Member.*JAR.*Un-Deployed From JAR Location"));
-    assertTrue(stringContainsLine(stringResult, "Manager.*DeployCommandsDUnit1.jar.*"
-        + JarDeployer.JAR_PREFIX + "DeployCommandsDUnit1.jar#1"));
-
-    // Make sure the deployed jar was removed from the shared config
-    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
-      @Override
-      public void run() {
-        ClusterConfigurationService sharedConfig =
-            ((InternalLocator) Locator.getLocator()).getSharedConfiguration();
-        try {
-          assertFalse(sharedConfig.getConfiguration(groupName).getJarNames()
-              .contains("DeployCommandsDUnit1.jar"));
-        } catch (Exception e) {
-          Assert.fail("Error occurred in cluster configuration service", e);
-        }
-      }
+    gfshConnector
+        .executeAndVerifyCommand("undeploy --jars=" + jar3.getName() + "," + jar4.getName());
+    server1.invoke(() -> {
+      assertThatCannotLoad(jarName3, class3);
+      assertThatCannotLoad(jarName4, class4);
     });
+    server2.invoke(() -> {
+      assertThatCannotLoad(jarName3, class3);
+      assertThatCannotLoad(jarName4, class4);
+    });
+  }
 
-    // List deployed with nothing deployed
-    cmdResult = executeCommand("list deployed");
-    assertEquals(Result.Status.OK, cmdResult.getStatus());
-    assertTrue(
-        commandResultToString(cmdResult).contains(CliStrings.LIST_DEPLOYED__NO_JARS_FOUND_MESSAGE));
+  private void assertThatCanLoad(String jarName, String className) throws ClassNotFoundException {
+    assertThat(ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jarName)).isNotNull();
+    assertThat(ClassPathLoader.getLatest().forName(className)).isNotNull();
   }
 
-  private void deleteSavedJarFiles() {
-    this.newDeployableJarFile.delete();
+  private void assertThatCannotLoad(String jarName, String className) {
+    assertThat(ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jarName)).isNull();
+    assertThatThrownBy(() -> ClassPathLoader.getLatest().forName(className))
+        .isExactlyInstanceOf(ClassNotFoundException.class);
+  }
 
-    File dirFile = new File(".");
 
-    // Find all deployed JAR files
-    File[] oldJarFiles = dirFile.listFiles(new FilenameFilter() {
-      @Override
-      public boolean accept(final File file, final String name) {
-        return DeployCommandsDUnitTest.this.pattern.matcher(name).matches();
-      }
-    });
+  @Test
+  public void testListDeployed() throws Exception {
+    // Deploy a couple of JAR files which can be listed
+    gfshConnector.executeAndVerifyCommand(
+        "deploy jar --group=" + GROUP1 + " --jar=" + jar1.getCanonicalPath());
+    gfshConnector.executeAndVerifyCommand(
+        "deploy jar --group=" + GROUP2 + " --jar=" + jar2.getCanonicalPath());
+
+    // List for all members
+    CommandResult commandResult = gfshConnector.executeAndVerifyCommand("list deployed");
+    String resultString = commandResultToString(commandResult);
+    assertThat(resultString).contains(server1.getName());
+    assertThat(resultString).contains(server2.getName());
+    assertThat(resultString).contains(jarName1);
+    assertThat(resultString).contains(jarName2);
+
+    // List for members in Group1
+    commandResult = gfshConnector.executeAndVerifyCommand("list deployed --group=" + GROUP1);
+    resultString = commandResultToString(commandResult);
+    assertThat(resultString).contains(server1.getName());
+    assertThat(resultString).doesNotContain(server2.getName());
+
+    assertThat(resultString).contains(jarName1);
+    assertThat(resultString).doesNotContain(jarName2);
+
+    // List for members in Group2
+    commandResult = gfshConnector.executeAndVerifyCommand("list deployed --group=" + GROUP2);
+    resultString = commandResultToString(commandResult);
+    assertThat(resultString).doesNotContain(server1.getName());
+    assertThat(resultString).contains(server2.getName());
+
+    assertThat(resultString).doesNotContain(jarName1);
+    assertThat(resultString).contains(jarName2);
+  }
 
-    // Now delete them
-    if (oldJarFiles != null) {
-      for (File oldJarFile : oldJarFiles) {
-        oldJarFile.delete();
-      }
+  protected static String commandResultToString(final CommandResult commandResult) {
+    assertNotNull(commandResult);
+    commandResult.resetToFirstLine();
+    StringBuilder buffer = new StringBuilder(commandResult.getHeader());
+    while (commandResult.hasNextLine()) {
+      buffer.append(commandResult.nextLine());
     }
+    buffer.append(commandResult.getFooter());
+    return buffer.toString();
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/UserCommandsDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/UserCommandsDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/UserCommandsDUnitTest.java
deleted file mode 100644
index 24f5cdb..0000000
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/UserCommandsDUnitTest.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.management.internal.cli.commands;
-
-import static org.apache.geode.test.dunit.Assert.assertEquals;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.geode.distributed.ConfigurationProperties;
-import org.apache.geode.internal.ClassBuilder;
-import org.apache.geode.internal.ClassPathLoader;
-import org.apache.geode.management.cli.Result;
-import org.apache.geode.management.internal.cli.CommandManager;
-import org.apache.geode.management.internal.cli.result.CommandResult;
-import org.apache.geode.test.dunit.Host;
-import org.apache.geode.test.dunit.SerializableRunnable;
-import org.apache.geode.test.junit.categories.DistributedTest;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Properties;
-
-/**
- * Unit tests for configuring user commands.
- *
- * @since GemFire 8.0
- */
-@Category(DistributedTest.class)
-public class UserCommandsDUnitTest extends CliCommandTestBase {
-
-  private static final long serialVersionUID = 1L;
-  final File jarDirectory = new File((new File(
-      ClassPathLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath()))
-          .getParent(),
-      "ext");
-  final File jarFile = new File(this.jarDirectory, "UserCommandsDUnit.jar");
-  boolean deleteJarDirectory = false;
-
-  @Override
-  public final void postSetUpCliCommandTestBase() throws Exception {
-    createUserCommandJarFile();
-  }
-
-  @Override
-  public final void postTearDownCacheTestCase() throws Exception {
-    if (this.deleteJarDirectory) {
-      FileUtils.deleteDirectory(this.jarDirectory);
-    } else {
-      FileUtils.forceDelete(this.jarFile);
-    }
-
-    System.clearProperty(CommandManager.USER_CMD_PACKAGES_PROPERTY);
-    ClassPathLoader.setLatestToDefault();
-    CommandManager.clearInstance();
-
-    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
-      private static final long serialVersionUID = 1L;
-
-      @Override
-      public void run() {
-        System.clearProperty(CommandManager.USER_CMD_PACKAGES_PROPERTY);
-        ClassPathLoader.setLatestToDefault();
-        CommandManager.clearInstance();
-      }
-    });
-  }
-
-  public void createUserCommandJarFile() throws IOException {
-    this.deleteJarDirectory = this.jarDirectory.mkdir();
-
-    StringBuffer stringBuffer = new StringBuffer();
-
-    stringBuffer.append("package junit.ucdunit;");
-    stringBuffer.append("import org.springframework.shell.core.CommandMarker;");
-    stringBuffer
-        .append("import org.springframework.shell.core.annotation.CliAvailabilityIndicator;");
-    stringBuffer.append("import org.springframework.shell.core.annotation.CliCommand;");
-    stringBuffer.append("import org.springframework.shell.core.annotation.CliOption;");
-    stringBuffer.append("import org.apache.geode.management.cli.Result;");
-    stringBuffer.append("import org.apache.geode.management.internal.cli.CliUtil;");
-    stringBuffer.append("import org.apache.geode.management.internal.cli.result.ResultBuilder;");
-    stringBuffer.append("import org.apache.geode.management.internal.cli.shell.Gfsh;");
-
-    stringBuffer.append(
-        "public final class UCDunitClass implements CommandMarker { public UCDunitClass() {}");
-    stringBuffer.append("@CliCommand(value = { \"ucdunitcmd\" }, help = \"ucdunitcmd help\")");
-    stringBuffer.append(
-        "public final Result ucdunitcmd(@CliOption(key = { \"name\" }, help = \"ucdunitcmd name help\") String name) {");
-    stringBuffer.append("return ResultBuilder.createInfoResult(\"ucdunitcmd \" + name); }");
-    stringBuffer.append("@CliAvailabilityIndicator({ \"ucdunitcmd\" })");
-    stringBuffer.append("public final boolean isAvailable() { return true; } }");
-
-    ClassBuilder classBuilder = new ClassBuilder();
-    final byte[] jarBytes = classBuilder.createJarFromClassContent("junit/ucdunit/UCDunitClass",
-        stringBuffer.toString());
-
-    final FileOutputStream outStream = new FileOutputStream(this.jarFile);
-    outStream.write(jarBytes);
-    outStream.close();
-  }
-
-  @Test
-  public void testCommandLineProperty() {
-    System.setProperty(CommandManager.USER_CMD_PACKAGES_PROPERTY, "junit.ucdunit");
-
-    ClassPathLoader.setLatestToDefault();
-    CommandManager.clearInstance();
-
-    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
-      private static final long serialVersionUID = 1L;
-
-      @Override
-      public void run() {
-        System.setProperty(CommandManager.USER_CMD_PACKAGES_PROPERTY, "junit.ucdunit");
-        ClassPathLoader.setLatestToDefault();
-        CommandManager.clearInstance();
-      }
-    });
-
-    setUpJmxManagerOnVm0ThenConnect(null);
-
-    CommandResult cmdResult = executeCommand("ucdunitcmd");
-    assertEquals(Result.Status.OK, cmdResult.getStatus());
-  }
-
-  @Test
-  public void testGemFireProperty() {
-    System.setProperty(CommandManager.USER_CMD_PACKAGES_PROPERTY, "junit.ucdunit");
-
-    ClassPathLoader.setLatestToDefault();
-    CommandManager.clearInstance();
-
-    Host.getHost(0).getVM(0).invoke(new SerializableRunnable() {
-      private static final long serialVersionUID = 1L;
-
-      @Override
-      public void run() {
-        ClassPathLoader.setLatestToDefault();
-        CommandManager.clearInstance();
-      }
-    });
-
-    Properties properties = new Properties();
-    properties.setProperty(ConfigurationProperties.USER_COMMAND_PACKAGES, "junit.ucdunit");
-    setUpJmxManagerOnVm0ThenConnect(properties);
-
-    CommandResult cmdResult = executeCommand("ucdunitcmd");
-    assertEquals(Result.Status.OK, cmdResult.getStatus());
-  }
-}


[3/8] geode git commit: GEODE-2686: Remove JarClassLoader

Posted by js...@apache.org.
http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java
index 6baddaf..0d26caf 100755
--- a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderTest.java
@@ -14,7 +14,9 @@
  */
 package org.apache.geode.internal;
 
-import static org.junit.Assert.*;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.io.BufferedInputStream;
 import java.io.File;
@@ -62,7 +64,22 @@ public class ClassPathLoaderTest {
   public void testLatestExists() throws Exception {
     System.out.println("\nStarting ClassPathLoaderTest#testLatestExists");
 
-    assertNotNull(ClassPathLoader.getLatest());
+    assertThat(ClassPathLoader.getLatest()).isNotNull();
+  }
+
+  @Test
+  public void testZeroLengthFile() throws IOException, ClassNotFoundException {
+    assertThatThrownBy(() -> {
+      ClassPathLoader.getLatest().getJarDeployer().deploy(new String[] {"JarDeployerDUnitZLF.jar"},
+          new byte[][] {new byte[0]});
+    }).isInstanceOf(IllegalArgumentException.class);
+
+    assertThatThrownBy(() -> {
+      ClassPathLoader.getLatest().getJarDeployer().deploy(
+          new String[] {"JarDeployerDUnitZLF1.jar", "JarDeployerDUnitZLF2.jar"},
+          new byte[][] {new ClassBuilder().createJarFromName("JarDeployerDUnitZLF1"), new byte[0]});
+
+    }).isInstanceOf(IllegalArgumentException.class);
   }
 
   /**
@@ -73,13 +90,10 @@ public class ClassPathLoaderTest {
   public void testForNameThrowsClassNotFoundException() throws Exception {
     System.out.println("\nStarting ClassPathLoaderTest#testForNameThrowsClassNotFoundException");
 
-    try {
-      String classToLoad = "com.nowhere.DoesNotExist";
-      ClassPathLoader.getLatest().forName(classToLoad);
-      fail();
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
+    String classToLoad = "com.nowhere.DoesNotExist";
+
+    assertThatThrownBy(() -> ClassPathLoader.getLatest().forName(classToLoad))
+        .isInstanceOf(ClassNotFoundException.class);
   }
 
   /**
@@ -92,7 +106,7 @@ public class ClassPathLoaderTest {
 
     String classToLoad = "org.apache.geode.internal.classpathloaderjunittest.DoesExist";
     Class<?> clazz = ClassPathLoader.getLatest().forName(classToLoad);
-    assertNotNull(clazz);
+    assertThat(clazz).isNotNull();
   }
 
   /**
@@ -105,10 +119,10 @@ public class ClassPathLoaderTest {
 
     String resourceToGet = "org/apache/geode/internal/classpathloaderjunittest/DoesExist.class";
     URL url = ClassPathLoader.getLatest().getResource(resourceToGet);
-    assertNotNull(url);
+    assertThat(url).isNotNull();
 
     InputStream is = url != null ? url.openStream() : null;
-    assertNotNull(is);
+    assertThat(is).isNotNull();
 
     int totalBytesRead = 0;
     byte[] input = new byte[256];
@@ -122,7 +136,7 @@ public class ClassPathLoaderTest {
 
     // if the following fails then maybe javac changed and DoesExist.class
     // contains other than 374 bytes of data... consider updating this test
-    assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
+    assertThat(totalBytesRead).isEqualTo(GENERATED_CLASS_BYTES_COUNT);
   }
 
   /**
@@ -135,12 +149,12 @@ public class ClassPathLoaderTest {
 
     String resourceToGet = "org/apache/geode/internal/classpathloaderjunittest/DoesExist.class";
     Enumeration<URL> urls = ClassPathLoader.getLatest().getResources(resourceToGet);
-    assertNotNull(urls);
-    assertTrue(urls.hasMoreElements());
+    assertThat(urls).isNotNull();
+    assertThat(urls.hasMoreElements()).isTrue();
 
     URL url = urls.nextElement();
     InputStream is = url != null ? url.openStream() : null;
-    assertNotNull(is);
+    assertThat(is).isNotNull();
 
     int totalBytesRead = 0;
     byte[] input = new byte[256];
@@ -154,7 +168,7 @@ public class ClassPathLoaderTest {
 
     // if the following fails then maybe javac changed and DoesExist.class
     // contains other than 374 bytes of data... consider updating this test
-    assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
+    assertThat(totalBytesRead).isEqualTo(GENERATED_CLASS_BYTES_COUNT);
   }
 
   /**
@@ -167,7 +181,7 @@ public class ClassPathLoaderTest {
 
     String resourceToGet = "org/apache/geode/internal/classpathloaderjunittest/DoesExist.class";
     InputStream is = ClassPathLoader.getLatest().getResourceAsStream(resourceToGet);
-    assertNotNull(is);
+    assertThat(is).isNotNull();
 
     int totalBytesRead = 0;
     byte[] input = new byte[256];
@@ -181,7 +195,7 @@ public class ClassPathLoaderTest {
 
     // if the following fails then maybe javac changed and DoesExist.class
     // contains other than 374 bytes of data... consider updating this test
-    assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
+    assertThat(totalBytesRead).isEqualTo(GENERATED_CLASS_BYTES_COUNT);
   }
 
   /**
@@ -197,44 +211,22 @@ public class ClassPathLoaderTest {
     String classToLoad = "com.nowhere.TestGeneratingClassLoader";
 
     Class<?> clazz = gcl.loadClass(classToLoad);
-    assertNotNull(clazz);
-    assertEquals(classToLoad, clazz.getName());
+    assertThat(clazz).isNotNull();
+    assertThat(clazz.getName()).isEqualTo(classToLoad);
 
     Object obj = clazz.newInstance();
-    assertEquals(clazz.getName(), obj.getClass().getName());
+    assertThat(obj.getClass().getName()).isEqualTo(clazz.getName());
 
-    try {
+    assertThatThrownBy(() -> {
       Class.forName(classToLoad);
-      fail("Should have thrown ClassNotFoundException");
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
+    }).isInstanceOf(ClassNotFoundException.class);
 
     Class<?> clazzForName = Class.forName(classToLoad, true, gcl);
-    assertNotNull(clazzForName);
-    assertEquals(clazz, clazzForName);
+    assertThat(clazzForName).isNotNull();
+    assertThat(clazzForName).isEqualTo(clazz);
 
     Object objForName = clazzForName.newInstance();
-    assertEquals(classToLoad, objForName.getClass().getName());
-  }
-
-  /**
-   * Verifies that custom loader is used to load class.
-   */
-  @Test
-  public void testForNameWithCustomLoader() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testForNameWithCustomLoader");
-
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl = dcl.addOrReplace(new GeneratingClassLoader());
-
-    String classToLoad = "com.nowhere.TestForNameWithCustomLoader";
-    Class<?> clazz = dcl.forName(classToLoad);
-    assertNotNull(clazz);
-    assertEquals(classToLoad, clazz.getName());
-
-    Object obj = clazz.newInstance();
-    assertEquals(classToLoad, obj.getClass().getName());
+    assertThat(objForName.getClass().getName()).isEqualTo(classToLoad);
   }
 
   /**
@@ -251,7 +243,7 @@ public class ClassPathLoaderTest {
     String classToLoad = "[Ljava.lang.String;";
     Class<?> clazz = null;
     clazz = dcl.forName(classToLoad);
-    assertEquals(classToLoad, clazz.getName());
+    assertThat(clazz.getName()).isEqualTo(classToLoad);
   }
 
   /**
@@ -265,33 +257,27 @@ public class ClassPathLoaderTest {
     final ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
     final String classToLoad = "com.nowhere.TestForNameWithTCCL";
 
-    try {
+    assertThatThrownBy(() -> {
       dcl.forName(classToLoad);
-      fail("Should have thrown ClassNotFoundException");
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
+
+    }).isInstanceOf(ClassNotFoundException.class);
 
     ClassLoader cl = Thread.currentThread().getContextClassLoader();
     try {
       // ensure that TCCL is only CL that can find this class
       Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
       Class<?> clazz = dcl.forName(classToLoad);
-      assertNotNull(clazz);
+      assertThat(clazz).isNotNull();
       Object instance = clazz.newInstance();
-      assertNotNull(instance);
-      assertEquals(classToLoad, instance.getClass().getName());
+      assertThat(instance).isNotNull();
+      assertThat(instance.getClass().getName()).isEqualTo(classToLoad);
     } finally {
       Thread.currentThread().setContextClassLoader(cl);
     }
 
-    try {
+    assertThatThrownBy(() -> {
       dcl.forName(classToLoad);
-      fail("Should have thrown ClassNotFoundException");
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
-
+    }).isInstanceOf(ClassNotFoundException.class);
   }
 
   /**
@@ -305,20 +291,17 @@ public class ClassPathLoaderTest {
     ClassLoader cl = new NullClassLoader();
     String classToLoad = "java.lang.String";
 
-    try {
+    assertThatThrownBy(() -> {
       Class.forName(classToLoad, true, cl);
-      fail();
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
+    }).isInstanceOf(ClassNotFoundException.class);
 
     String resourceToGet = "java/lang/String.class";
 
     URL url = cl.getResource(resourceToGet);
-    assertNull(url);
+    assertThat(url).isNull();
 
     InputStream is = cl.getResourceAsStream(resourceToGet);
-    assertNull(is);
+    assertThat(is).isNull();
   }
 
   /**
@@ -333,15 +316,15 @@ public class ClassPathLoaderTest {
     String classToLoad = "java.lang.String";
 
     Class<?> clazz = Class.forName(classToLoad, true, cl);
-    assertNotNull(clazz);
+    assertThat(clazz).isNotNull();
 
     String resourceToGet = "java/lang/String.class";
 
     URL url = cl.getResource(resourceToGet);
-    assertNotNull(url);
+    assertThat(url).isNotNull();
 
     InputStream is = cl.getResourceAsStream(resourceToGet);
-    assertNotNull(is);
+    assertThat(is).isNotNull();
   }
 
   /**
@@ -355,28 +338,19 @@ public class ClassPathLoaderTest {
     ClassLoader cl = new BrokenClassLoader();
 
     String classToLoad = "java.lang.String";
-    try {
+
+    assertThatThrownBy(() -> {
       Class.forName(classToLoad, true, cl);
-      fail();
-    } catch (ClassNotFoundException e) {
-      throw e;
-    } catch (BrokenError expected) {
-      // Expected
-    }
+    }).isInstanceOf(BrokenError.class);
 
     String resourceToGet = "java/lang/String.class";
-    try {
+    assertThatThrownBy(() -> {
       cl.getResource(resourceToGet);
-      fail();
-    } catch (BrokenError expected) {
-      // Expected
-    }
-    try {
+    }).isInstanceOf(BrokenError.class);
+
+    assertThatThrownBy(() -> {
       cl.getResourceAsStream(resourceToGet);
-      fail();
-    } catch (BrokenError expected) {
-      // Expected
-    }
+    }).isInstanceOf(BrokenError.class);
   }
 
   /**
@@ -390,7 +364,6 @@ public class ClassPathLoaderTest {
     System.out.println("\nStarting ClassPathLoaderTest#testBrokenTCCLThrowsErrors");
 
     ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl.addOrReplace(new NullClassLoader());
 
     ClassLoader cl = Thread.currentThread().getContextClassLoader();
     try {
@@ -398,29 +371,18 @@ public class ClassPathLoaderTest {
       Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
 
       String classToLoad = "java.lang.String";
-      try {
+      assertThatThrownBy(() -> {
         dcl.forName(classToLoad);
-        fail();
-      } catch (ClassNotFoundException e) {
-        throw e;
-      } catch (BrokenError expected) {
-        // Expected
-      }
+      }).isInstanceOf(BrokenError.class);
 
       String resourceToGet = "java/lang/String.class";
-      try {
+      assertThatThrownBy(() -> {
         dcl.getResource(resourceToGet);
-        fail();
-      } catch (BrokenError expected) {
-        // Expected
-      }
+      }).isInstanceOf(BrokenError.class);
 
-      try {
+      assertThatThrownBy(() -> {
         dcl.getResourceAsStream(resourceToGet);
-        fail();
-      } catch (BrokenError expected) {
-        // Expected
-      }
+      }).isInstanceOf(BrokenError.class);
     } finally {
       Thread.currentThread().setContextClassLoader(cl);
     }
@@ -436,7 +398,6 @@ public class ClassPathLoaderTest {
 
     // create DCL such that parent cannot find anything
     ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);
-    dcl.addOrReplace(new NullClassLoader());
 
     ClassLoader cl = Thread.currentThread().getContextClassLoader();
     try {
@@ -445,52 +406,13 @@ public class ClassPathLoaderTest {
 
       String classToLoad = "java.lang.String";
       Class<?> clazz = dcl.forName(classToLoad);
-      assertNotNull(clazz);
+      assertThat(clazz).isNotNull();
 
       String resourceToGet = "java/lang/String.class";
       URL url = dcl.getResource(resourceToGet);
-      assertNotNull(url);
+      assertThat(url).isNotNull();
       InputStream is = dcl.getResourceAsStream(resourceToGet);
-      assertNotNull(is);
-    } finally {
-      Thread.currentThread().setContextClassLoader(cl);
-    }
-  }
-
-  /**
-   * Verifies that the 3rd custom loader will find the class. Parent cannot find it and TCCL is
-   * broken. This verifies that all custom loaders are checked and that the custom loaders are all
-   * checked before TCCL.
-   */
-  @Test
-  public void testForNameWithMultipleCustomLoaders() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testForNameWithMultipleCustomLoaders");
-
-    // create DCL such that the 3rd loader should find the class
-    // first custom loader becomes parent which won't find anything
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    final GeneratingClassLoader generatingClassLoader = new GeneratingClassLoader();
-    dcl = dcl.addOrReplace(generatingClassLoader);
-    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
-    dcl = dcl.addOrReplace(new NullClassLoader());
-
-    String classToLoad = "com.nowhere.TestForNameWithMultipleCustomLoaders";
-
-    ClassLoader cl = Thread.currentThread().getContextClassLoader();
-    try {
-      // set TCCL to throw errors which makes sure we find before checking TCCL
-      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
-
-      Class<?> clazz = dcl.forName(classToLoad);
-      assertNotNull(clazz);
-      assertEquals(classToLoad, clazz.getName());
-      assertTrue("Class not loaded by a GeneratingClassLoader.",
-          clazz.getClassLoader() instanceof GeneratingClassLoader);
-      assertEquals("Class not loaded by generatingClassLoader.", generatingClassLoader,
-          clazz.getClassLoader());
-
-      Object obj = clazz.newInstance();
-      assertEquals(classToLoad, obj.getClass().getName());
+      assertThat(is).isNotNull();
     } finally {
       Thread.currentThread().setContextClassLoader(cl);
     }
@@ -508,28 +430,24 @@ public class ClassPathLoaderTest {
 
     String classToLoad = "com.nowhere.TestExcludeTCCL";
 
-    try {
+    assertThatThrownBy(() -> {
       dcl.forName(classToLoad);
-      fail("Should have thrown ClassNotFoundException");
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
+
+    }).isInstanceOf(ClassNotFoundException.class);
 
     ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
     try {
-      // ensure that TCCL is only CL that can find this class
       Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
-      dcl.forName(classToLoad);
-      fail("Should have thrown ClassNotFoundException");
-    } catch (ClassNotFoundException expected) {
-      // Expected
+
+      assertThatThrownBy(() -> {
+        dcl.forName(classToLoad);
+      }).isInstanceOf(ClassNotFoundException.class);
     } finally {
       Thread.currentThread().setContextClassLoader(cl);
     }
-
   }
 
-
   /**
    * Verifies that <tt>getResource</tt> will skip TCCL if <tt>excludeThreadContextClassLoader</tt>
    * has been set to true.
@@ -541,13 +459,13 @@ public class ClassPathLoaderTest {
     ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);
 
     String resourceToGet = "com/nowhere/testGetResourceExcludeTCCL.rsc";
-    assertNull(dcl.getResource(resourceToGet));
+    assertThat(dcl.getResource(resourceToGet)).isNull();
 
     ClassLoader cl = Thread.currentThread().getContextClassLoader();
     try {
       // ensure that TCCL is only CL that can find this resource
       Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
-      assertNull(dcl.getResource(resourceToGet));
+      assertThat(dcl.getResource(resourceToGet)).isNull();
     } finally {
       Thread.currentThread().setContextClassLoader(cl);
     }
@@ -564,161 +482,18 @@ public class ClassPathLoaderTest {
     ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);
 
     String resourceToGet = "com/nowhere/testGetResourceAsStreamExcludeTCCL.rsc";
-    assertNull(dcl.getResourceAsStream(resourceToGet));
+    assertThat(dcl.getResourceAsStream(resourceToGet)).isNull();
 
     ClassLoader cl = Thread.currentThread().getContextClassLoader();
     try {
       // ensure that TCCL is only CL that can find this resource
       Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
-      assertNull(dcl.getResourceAsStream(resourceToGet));
-    } finally {
-      Thread.currentThread().setContextClassLoader(cl);
-    }
-  }
-
-  @Test
-  public void testAddFindsLatestClassLoader() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testAddFindsLatestClassLoader");
-
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl = dcl.addOrReplace(new GeneratingClassLoader());
-
-    String classToLoad = "com.nowhere.TestAddFindsLatestClassLoader";
-    Class<?> clazz = dcl.forName(classToLoad);
-    assertNotNull(clazz);
-
-    dcl = dcl.addOrReplace(new BrokenClassLoader());
-
-    try {
-      dcl.forName(classToLoad);
-      fail();
-    } catch (BrokenError expected) {
-      // Expected
-    }
-  }
-
-  /**
-   * Verifies removing a ClassLoader.
-   */
-  @Test
-  public void testRemoveClassLoader() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testRemoveClassLoader");
-
-    GeneratingClassLoader genClassLoader = new GeneratingClassLoader();
-    ClassPathLoader cpl = ClassPathLoader.createWithDefaults(false);
-    cpl = cpl.addOrReplace(genClassLoader);
-
-    String classToLoad = "com.nowhere.TestRemoveClassLoader";
-    Class<?> clazz = cpl.forName(classToLoad);
-    assertNotNull(clazz);
-
-    cpl = cpl.remove(genClassLoader);
-
-    try {
-      clazz = cpl.forName(classToLoad);
-      fail();
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
-  }
-
-  /**
-   * Verifies that a ClassLoader will be replaced when added more than once.
-   */
-  @Test
-  public void testClassLoaderReplace() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testClassLoaderReplace");
-
-    String class1ToLoad = "ClassA";
-    String class2ToLoad = "ClassB";
-
-    ClassPathLoader cpl = ClassPathLoader.createWithDefaults(false);
-    cpl = cpl.addOrReplace(new OneClassClassLoader(class1ToLoad));
-
-    try {
-      @SuppressWarnings("unused")
-      Class<?> clazz = cpl.forName(class1ToLoad);
-    } catch (ClassNotFoundException unexpected) {
-      fail();
-    }
-
-    try {
-      @SuppressWarnings("unused")
-      Class<?> clazz = cpl.forName(class2ToLoad);
-      fail();
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
-
-    cpl = cpl.addOrReplace(new OneClassClassLoader(class2ToLoad));
-    try {
-      @SuppressWarnings("unused")
-      Class<?> clazz = cpl.forName(class2ToLoad);
-    } catch (ClassNotFoundException unexpected) {
-      fail();
-    }
-
-    try {
-      @SuppressWarnings("unused")
-      Class<?> clazz = cpl.forName(class1ToLoad);
-      fail();
-    } catch (ClassNotFoundException expected) {
-      // Expected
-    }
-  }
-
-  @Test
-  public void testAsClassLoaderLoadClassWithMultipleCustomLoaders() throws Exception {
-    System.out.println(
-        "\nStarting ClassPathLoaderTest#testAsClassLoaderLoadClassWithMultipleCustomLoaders");
-
-    // create DCL such that the 3rd loader should find the class
-    // first custom loader becomes parent which won't find anything
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    final GeneratingClassLoader generatingClassLoader = new GeneratingClassLoader();
-    dcl = dcl.addOrReplace(generatingClassLoader);
-    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
-    dcl = dcl.addOrReplace(new NullClassLoader());
-
-    final String classToLoad = "com.nowhere.TestForNameWithMultipleCustomLoaders";
-
-    ClassLoader cl = Thread.currentThread().getContextClassLoader();
-    try {
-      // set TCCL to throw errors which makes sure we find before checking TCCL
-      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
-
-      final ClassLoader classLoader = dcl.asClassLoader();
-      final Class<?> clazz = classLoader.loadClass(classToLoad);
-      assertNotNull(clazz);
-      assertEquals(classToLoad, clazz.getName());
-      assertTrue(clazz.getClassLoader() instanceof GeneratingClassLoader);
-      assertEquals(generatingClassLoader, clazz.getClassLoader());
-
-      final Object obj = clazz.newInstance();
-      assertEquals(classToLoad, obj.getClass().getName());
-
-      final Class<?> clazz2 = dcl.forName(classToLoad);
-      assertSame("Should load same class as calling classLoader.", clazz, clazz2);
-
-      final Class<?> clazz3 = Class.forName(classToLoad, true, classLoader);
-      assertSame("Should load same class as calling classLoader.", clazz, clazz3);
-
+      assertThat(dcl.getResourceAsStream(resourceToGet)).isNull();
     } finally {
       Thread.currentThread().setContextClassLoader(cl);
     }
   }
 
-  private static void exploreClassLoaders() {
-    System.out.println("Thread.currentThread().getContextClassLoader()...");
-    exploreClassLoader(Thread.currentThread().getContextClassLoader(), 1);
-
-    System.out.println("class.getClassLoader()...");
-    exploreClassLoader(ClassPathLoaderTest.class.getClassLoader(), 1);
-
-    System.out.println("ClassLoader.getSystemClassLoader()...");
-    exploreClassLoader(ClassLoader.getSystemClassLoader(), 1);
-  }
-
   private static void exploreClassLoader(ClassLoader cl, int indent) {
     String prefix = "";
     for (int i = 0; i < indent; i++) {
@@ -734,8 +509,9 @@ public class ClassPathLoaderTest {
       URL[] urls = ((URLClassLoader) cl).getURLs();
       StringBuilder sb = new StringBuilder(prefix).append("ClassLoader getURLs = [");
       for (int i = 0; i < urls.length; i++) {
-        if (i > 0)
+        if (i > 0) {
           sb.append(", ");
+        }
         sb.append(urls[i].toString());
       }
       sb.append("]");
@@ -816,7 +592,7 @@ public class ClassPathLoaderTest {
     /**
      * Currently unused but potentially useful for some future test. This causes this loader to only
      * generate a class that the parent could not find.
-     *
+     * 
      * @param parent the parent class loader to check with first
      */
     @SuppressWarnings("unused")

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java
new file mode 100644
index 0000000..7216463
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java
@@ -0,0 +1,538 @@
+/*
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionContext;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.cache.execute.ResultSender;
+import org.apache.geode.internal.cache.execute.FunctionContextImpl;
+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.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Random;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * TODO: Need to fix this testDeclarableFunctionsWithParms and testClassOnClasspath on Windows:
+ */
+@Category(IntegrationTest.class)
+public class DeployedJarJUnitTest {
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  @Rule
+  public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
+
+  private final ClassBuilder classBuilder = new ClassBuilder();
+
+  @Before
+  public void setup() throws Exception {
+    File workingDir = temporaryFolder.newFolder();
+
+    ClassPathLoader.setLatestToDefault(workingDir);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    for (String functionName : FunctionService.getRegisteredFunctions().keySet()) {
+      FunctionService.unregisterFunction(functionName);
+    }
+
+    ClassPathLoader.setLatestToDefault();
+  }
+
+  @Test
+  public void testIsValidJarContent() throws IOException {
+    assertTrue(
+        DeployedJar.isValidJarContent(this.classBuilder.createJarFromName("JarClassLoaderJUnitA")));
+  }
+
+  @Test
+  public void testIsInvalidJarContent() {
+    assertFalse(DeployedJar.isValidJarContent("INVALID JAR CONTENT".getBytes()));
+  }
+
+  @Test
+  public void testClassOnClasspath() throws Exception {
+    // Deploy the first JAR file and make sure the class is on the Classpath
+    byte[] jarBytes =
+        this.classBuilder.createJarFromClassContent("com/jcljunit/JarClassLoaderJUnitA",
+            "package com.jcljunit; public class JarClassLoaderJUnitA {}");
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
+
+    try {
+      ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitA");
+    } catch (ClassNotFoundException cnfex) {
+      fail("JAR file not correctly added to Classpath");
+    }
+
+    // Update the JAR file and make sure the first class is no longer on the Classpath
+    // and the second one is.
+    jarBytes = this.classBuilder.createJarFromClassContent("com/jcljunit/JarClassLoaderJUnitB",
+        "package com.jcljunit; public class JarClassLoaderJUnitB {}");
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
+
+    try {
+      ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitB");
+    } catch (ClassNotFoundException cnfex) {
+      fail("JAR file not correctly added to Classpath");
+    }
+
+    try {
+      ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitA");
+      fail("Class should not be found on Classpath");
+    } catch (ClassNotFoundException expected) { // expected
+    }
+
+  }
+
+  @Test
+  public void testFailingCompilation() throws Exception {
+    StringBuffer stringBuffer = new StringBuffer();
+    stringBuffer.append("import org.apache.geode.cache.Declarable;");
+    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
+    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
+    stringBuffer.append("public class JarClassLoaderJUnitFunction implements Function {}");
+    String functionString = stringBuffer.toString();
+
+    try {
+      this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
+      fail("This code should have failed to compile and thrown an exception");
+    } catch (Exception ex) {
+      // All good
+    }
+  }
+
+  @Test
+  public void testFunctions() throws IOException, ClassNotFoundException {
+    // Test creating a JAR file with a function
+    StringBuffer stringBuffer = new StringBuffer();
+    stringBuffer.append("import java.util.Properties;");
+    stringBuffer.append("import org.apache.geode.cache.Declarable;");
+    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
+    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
+    stringBuffer.append("public class JarClassLoaderJUnitFunction implements Function {");
+    stringBuffer.append("public void init(Properties props) {}");
+    stringBuffer.append("public boolean hasResult() {return true;}");
+    stringBuffer.append(
+        "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"GOODv1\");}");
+    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}");
+    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
+    stringBuffer.append("public boolean isHA() {return false;}}");
+    String functionString = stringBuffer.toString();
+
+    byte[] jarBytes =
+        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
+
+    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
+    assertNotNull(function);
+    TestResultSender resultSender = new TestResultSender();
+    FunctionContext functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
+    function.execute(functionContext);
+    assertEquals("GOODv1", (String) resultSender.getResults());
+
+    // Test updating the function with a new JAR file
+    functionString = functionString.replace("v1", "v2");
+    jarBytes =
+        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
+
+    function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
+    assertNotNull(function);
+    resultSender = new TestResultSender();
+    functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
+    function.execute(functionContext);
+    assertEquals("GOODv2", (String) resultSender.getResults());
+
+    // Test returning null for the Id
+    String functionNullIdString =
+        functionString.replace("return \"JarClassLoaderJUnitFunction\"", "return null");
+    jarBytes = this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction",
+        functionNullIdString);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
+
+    assertNull(FunctionService.getFunction("JarClassLoaderJUnitFunction"));
+
+    // Test removing the JAR
+    ClassPathLoader.getLatest().getJarDeployer().undeploy("JarClassLoaderJUnit.jar");
+    assertNull(FunctionService.getFunction("JarClassLoaderJUnitFunction"));
+  }
+
+  /**
+   * Ensure that abstract functions aren't added to the Function Service.
+   */
+  @Test
+  public void testAbstractFunction() throws IOException, ClassNotFoundException {
+    // Add an abstract Function to the Classpath
+    StringBuffer stringBuffer = new StringBuffer();
+    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
+    stringBuffer.append("public abstract class JarClassLoaderJUnitFunction implements Function {");
+    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}}");
+    String functionString = stringBuffer.toString();
+
+    byte[] jarBytes =
+        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitFunction.jar",
+        jarBytes);
+
+    try {
+      ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunction");
+    } catch (ClassNotFoundException cnfex) {
+      fail("JAR file not correctly added to Classpath");
+    }
+
+    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
+    assertNull(function);
+  }
+
+  @Test
+  public void testDeclarableFunctionsWithNoCacheXml() throws Exception {
+
+    final String jarName = "JarClassLoaderJUnitNoXml.jar";
+
+    // Add a Declarable Function without parameters for the class to the Classpath
+    StringBuffer stringBuffer = new StringBuffer();
+    stringBuffer.append("import java.util.Properties;");
+    stringBuffer.append("import org.apache.geode.cache.Declarable;");
+    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
+    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
+    stringBuffer
+        .append("public class JarClassLoaderJUnitFunctionNoXml implements Function, Declarable {");
+    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunctionNoXml\";}");
+    stringBuffer.append("public void init(Properties props) {}");
+    stringBuffer.append(
+        "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"NOPARMSv1\");}");
+    stringBuffer.append("public boolean hasResult() {return true;}");
+    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
+    stringBuffer.append("public boolean isHA() {return false;}}");
+    String functionString = stringBuffer.toString();
+
+    byte[] jarBytes = this.classBuilder
+        .createJarFromClassContent("JarClassLoaderJUnitFunctionNoXml", functionString);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarBytes);
+
+    try {
+      ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunctionNoXml");
+    } catch (ClassNotFoundException cnfex) {
+      fail("JAR file not correctly added to Classpath");
+    }
+
+    // Check to see if the function without parameters executes correctly
+    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunctionNoXml");
+    assertNotNull(function);
+    TestResultSender resultSender = new TestResultSender();
+    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
+    assertEquals("NOPARMSv1", (String) resultSender.getResults());
+  }
+
+  @Test
+  public void testDependencyBetweenJars() throws IOException, ClassNotFoundException {
+    final File parentJarFile = temporaryFolder.newFile("JarClassLoaderJUnitParent.jar");
+    final File usesJarFile = temporaryFolder.newFile("JarClassLoaderJUnitUses.jar");
+
+    JarDeployer jarDeployer = ClassPathLoader.getLatest().getJarDeployer();
+
+    // Write out a JAR files.
+    StringBuffer stringBuffer = new StringBuffer();
+    stringBuffer.append("package jcljunit.parent;");
+    stringBuffer.append("public class JarClassLoaderJUnitParent {");
+    stringBuffer.append("public String getValueParent() {");
+    stringBuffer.append("return \"PARENT\";}}");
+
+    byte[] jarBytes = this.classBuilder.createJarFromClassContent(
+        "jcljunit/parent/JarClassLoaderJUnitParent", stringBuffer.toString());
+    writeJarBytesToFile(parentJarFile, jarBytes);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitParent.jar", jarBytes);
+
+    stringBuffer = new StringBuffer();
+    stringBuffer.append("package jcljunit.uses;");
+    stringBuffer.append("public class JarClassLoaderJUnitUses {");
+    stringBuffer.append("public String getValueUses() {");
+    stringBuffer.append("return \"USES\";}}");
+
+    jarBytes = this.classBuilder.createJarFromClassContent("jcljunit/uses/JarClassLoaderJUnitUses",
+        stringBuffer.toString());
+    writeJarBytesToFile(usesJarFile, jarBytes);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUses.jar", jarBytes);
+
+    stringBuffer = new StringBuffer();
+    stringBuffer.append("package jcljunit.function;");
+    stringBuffer.append("import jcljunit.parent.JarClassLoaderJUnitParent;");
+    stringBuffer.append("import jcljunit.uses.JarClassLoaderJUnitUses;");
+    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
+    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
+    stringBuffer.append(
+        "public class JarClassLoaderJUnitFunction  extends JarClassLoaderJUnitParent implements Function {");
+    stringBuffer.append("private JarClassLoaderJUnitUses uses = new JarClassLoaderJUnitUses();");
+    stringBuffer.append("public boolean hasResult() {return true;}");
+    stringBuffer.append(
+        "public void execute(FunctionContext context) {context.getResultSender().lastResult(getValueParent() + \":\" + uses.getValueUses());}");
+    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}");
+    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
+    stringBuffer.append("public boolean isHA() {return false;}}");
+
+    ClassBuilder functionClassBuilder = new ClassBuilder();
+    functionClassBuilder.addToClassPath(parentJarFile.getAbsolutePath());
+    functionClassBuilder.addToClassPath(usesJarFile.getAbsolutePath());
+    jarBytes = functionClassBuilder.createJarFromClassContent(
+        "jcljunit/function/JarClassLoaderJUnitFunction", stringBuffer.toString());
+
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitFunction.jar",
+        jarBytes);
+
+
+    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
+    assertNotNull(function);
+    TestResultSender resultSender = new TestResultSender();
+    FunctionContext functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
+    function.execute(functionContext);
+    assertEquals("PARENT:USES", (String) resultSender.getResults());
+  }
+
+  @Test
+  public void testFindResource() throws IOException, ClassNotFoundException {
+    final String fileName = "file.txt";
+    final String fileContent = "FILE CONTENT";
+
+    byte[] jarBytes = this.classBuilder.createJarFromFileContent(fileName, fileContent);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitResource.jar",
+        jarBytes);
+
+    InputStream inputStream = ClassPathLoader.getLatest().getResourceAsStream(fileName);
+    assertNotNull(inputStream);
+
+    final byte[] fileBytes = new byte[fileContent.length()];
+    inputStream.read(fileBytes);
+    inputStream.close();
+    assertTrue(fileContent.equals(new String(fileBytes)));
+  }
+
+  @Test
+  public void testUpdateClassInJar() throws IOException, ClassNotFoundException {
+    // First use of the JAR file
+    byte[] jarBytes = this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitTestClass",
+        "public class JarClassLoaderJUnitTestClass { public Integer getValue5() { return new Integer(5); } }");
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUpdate.jar", jarBytes);
+
+    try {
+      Class<?> clazz = ClassPathLoader.getLatest().forName("JarClassLoaderJUnitTestClass");
+      Object object = clazz.newInstance();
+      Method getValue5Method = clazz.getMethod("getValue5", new Class[] {});
+      Integer value = (Integer) getValue5Method.invoke(object, new Object[] {});
+      assertEquals(value.intValue(), 5);
+
+    } catch (InvocationTargetException itex) {
+      fail("JAR file not correctly added to Classpath" + itex);
+    } catch (NoSuchMethodException nsmex) {
+      fail("JAR file not correctly added to Classpath" + nsmex);
+    } catch (InstantiationException iex) {
+      fail("JAR file not correctly added to Classpath" + iex);
+    } catch (IllegalAccessException iaex) {
+      fail("JAR file not correctly added to Classpath" + iaex);
+    } catch (ClassNotFoundException cnfex) {
+      fail("JAR file not correctly added to Classpath" + cnfex);
+    }
+
+    // Now create an updated JAR file and make sure that the method from the new
+    // class is available.
+    jarBytes = this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitTestClass",
+        "public class JarClassLoaderJUnitTestClass { public Integer getValue10() { return new Integer(10); } }");
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUpdate.jar", jarBytes);
+
+
+    try {
+      Class<?> clazz = ClassPathLoader.getLatest().forName("JarClassLoaderJUnitTestClass");
+      Object object = clazz.newInstance();
+      Method getValue10Method = clazz.getMethod("getValue10", new Class[] {});
+      Integer value = (Integer) getValue10Method.invoke(object, new Object[] {});
+      assertEquals(value.intValue(), 10);
+
+    } catch (InvocationTargetException itex) {
+      fail("JAR file not correctly added to Classpath" + itex);
+    } catch (NoSuchMethodException nsmex) {
+      fail("JAR file not correctly added to Classpath" + nsmex);
+    } catch (InstantiationException iex) {
+      fail("JAR file not correctly added to Classpath" + iex);
+    } catch (IllegalAccessException iaex) {
+      fail("JAR file not correctly added to Classpath" + iaex);
+    } catch (ClassNotFoundException cnfex) {
+      fail("JAR file not correctly added to Classpath" + cnfex);
+    }
+  }
+
+  @Test
+  public void testMultiThread() throws IOException, ClassNotFoundException {
+    // Add two JARs to the classpath
+    byte[] jarBytes = this.classBuilder.createJarFromName("JarClassLoaderJUnitA");
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitA.jar", jarBytes);
+
+    jarBytes = this.classBuilder.createJarFromClassContent("com/jcljunit/JarClassLoaderJUnitB",
+        "package com.jcljunit; public class JarClassLoaderJUnitB {}");
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitB.jar", jarBytes);
+
+    String[] classNames = new String[] {"JarClassLoaderJUnitA", "com.jcljunit.JarClassLoaderJUnitB",
+        "NON-EXISTENT CLASS"};
+
+    // Spawn some threads which try to instantiate these classes
+    final int threadCount = 10;
+    final int numLoops = 1000;
+    final CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount + 1);
+    for (int i = 0; i < threadCount; i++) {
+      new ForNameExerciser(cyclicBarrier, numLoops, classNames).start();
+    }
+
+    // Wait for all of the threads to be ready
+    try {
+      cyclicBarrier.await();
+    } catch (InterruptedException iex) {
+      fail("Interrupted while waiting for barrier");
+    } catch (BrokenBarrierException bbex) {
+      fail("Broken barrier while waiting");
+    }
+
+    // Loop while each thread tries N times to instantiate a non-existent class
+    for (int i = 0; i < numLoops; i++) {
+      try {
+        cyclicBarrier.await(5, TimeUnit.SECONDS);
+      } catch (InterruptedException iex) {
+        fail("Interrupted while waiting for barrier");
+      } catch (TimeoutException tex) {
+        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+        long[] threadIds = threadMXBean.findDeadlockedThreads();
+
+        if (threadIds != null) {
+          StringBuffer deadLockTrace = new StringBuffer();
+          for (long threadId : threadIds) {
+            ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, 100);
+            deadLockTrace.append(threadInfo.getThreadName()).append("\n");
+            for (StackTraceElement stackTraceElem : threadInfo.getStackTrace()) {
+              deadLockTrace.append("\t").append(stackTraceElem).append("\n");
+            }
+          }
+
+          fail("Deadlock with trace:\n" + deadLockTrace.toString());
+        }
+
+        fail("Timeout while waiting for barrier - no deadlock detected");
+      } catch (BrokenBarrierException bbex) {
+        fail("Broken barrier while waiting");
+      }
+    }
+  }
+
+
+  private void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
+    final OutputStream outStream = new FileOutputStream(jarFile);
+    outStream.write(jarBytes);
+    outStream.close();
+  }
+
+  private static class TestResultSender implements ResultSender<Object> {
+    private Object result;
+
+    public TestResultSender() {}
+
+    protected Object getResults() {
+      return this.result;
+    }
+
+    @Override
+    public void lastResult(final Object lastResult) {
+      this.result = lastResult;
+    }
+
+    @Override
+    public void sendResult(final Object oneResult) {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void sendException(final Throwable t) {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  static final Random random = new Random();
+
+  private class ForNameExerciser extends Thread {
+    private final CyclicBarrier cyclicBarrier;
+    private final int numLoops;
+    private final String[] classNames;
+
+    ForNameExerciser(final CyclicBarrier cyclicBarrier, final int numLoops,
+        final String[] classNames) {
+      this.cyclicBarrier = cyclicBarrier;
+      this.numLoops = numLoops;
+      this.classNames = classNames;
+    }
+
+    @Override
+    public void run() {
+      try {
+        this.cyclicBarrier.await();
+      } catch (InterruptedException iex) {
+        fail("Interrupted while waiting for latch");
+      } catch (BrokenBarrierException bbex) {
+        fail("Broken barrier while waiting");
+      }
+      for (int i = 0; i < this.numLoops; i++) {
+        try {
+          // Random select a name from the list of class names and try to load it
+          String className = this.classNames[random.nextInt(this.classNames.length)];
+          ClassPathLoader.getLatest().forName(className);
+        } catch (ClassNotFoundException expected) { // expected
+        }
+        try {
+          this.cyclicBarrier.await();
+        } catch (InterruptedException iex) {
+          fail("Interrupted while waiting for barrrier");
+        } catch (BrokenBarrierException bbex) {
+          fail("Broken barrier while waiting");
+        }
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/internal/JarClassLoaderJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/JarClassLoaderJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/JarClassLoaderJUnitTest.java
deleted file mode 100644
index adc1d2e..0000000
--- a/geode-core/src/test/java/org/apache/geode/internal/JarClassLoaderJUnitTest.java
+++ /dev/null
@@ -1,851 +0,0 @@
-/*
- * 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;
-
-import static org.apache.geode.distributed.ConfigurationProperties.*;
-import static org.junit.Assert.*;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.lang.management.ManagementFactory;
-import java.lang.management.ThreadInfo;
-import java.lang.management.ThreadMXBean;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Properties;
-import java.util.Random;
-import java.util.concurrent.BrokenBarrierException;
-import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.regex.Pattern;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-
-import org.apache.geode.cache.CacheFactory;
-import org.apache.geode.cache.execute.Function;
-import org.apache.geode.cache.execute.FunctionContext;
-import org.apache.geode.cache.execute.FunctionService;
-import org.apache.geode.cache.execute.ResultSender;
-import org.apache.geode.internal.cache.InternalCache;
-import org.apache.geode.internal.cache.execute.FunctionContextImpl;
-import org.apache.geode.test.junit.categories.IntegrationTest;
-
-/**
- * TODO: Need to fix this testDeclarableFunctionsWithParms and testClassOnClasspath on Windows:
- */
-@Category(IntegrationTest.class)
-public class JarClassLoaderJUnitTest {
-
-  private static final String JAR_PREFIX = "vf.gf#";
-
-  private final ClassBuilder classBuilder = new ClassBuilder();
-  final Pattern pattern = Pattern.compile("^" + JAR_PREFIX + "JarClassLoaderJUnit.*#\\d++$");
-
-  private InternalCache cache;
-
-  @After
-  public void tearDown() throws Exception {
-    for (ClassLoader classLoader : ClassPathLoader.getLatest().getClassLoaders()) {
-      if (classLoader instanceof JarClassLoader) {
-        JarClassLoader jarClassLoader = (JarClassLoader) classLoader;
-        if (jarClassLoader.getJarName().startsWith("JarClassLoaderJUnit")) {
-          ClassPathLoader.getLatest().removeAndSetLatest(jarClassLoader);
-        }
-      }
-    }
-    for (String functionName : FunctionService.getRegisteredFunctions().keySet()) {
-      if (functionName.startsWith("JarClassLoaderJUnit")) {
-        FunctionService.unregisterFunction(functionName);
-      }
-    }
-
-    if (this.cache != null) {
-      this.cache.close();
-    }
-
-    deleteSavedJarFiles();
-  }
-
-  @Test
-  public void testValidJarContent() throws IOException {
-    assertTrue(JarClassLoader
-        .isValidJarContent(this.classBuilder.createJarFromName("JarClassLoaderJUnitA")));
-  }
-
-  @Test
-  public void testInvalidJarContent() {
-    assertFalse(JarClassLoader.isValidJarContent("INVALID JAR CONTENT".getBytes()));
-  }
-
-  @Test
-  public void testClassOnClasspath() throws IOException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#1");
-    final File jarFile2 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#2");
-    ClassPathLoader classPathLoader = ClassPathLoader.createWithDefaults(false);
-
-    // Deploy the first JAR file and make sure the class is on the Classpath
-    byte[] jarBytes =
-        this.classBuilder.createJarFromClassContent("com/jcljunit/JarClassLoaderJUnitA",
-            "package com.jcljunit; public class JarClassLoaderJUnitA {}");
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader = new JarClassLoader(jarFile1, "JarClassLoaderJUnit.jar", jarBytes);
-    classPathLoader = classPathLoader.addOrReplace(classLoader);
-
-    try {
-      classPathLoader.forName("com.jcljunit.JarClassLoaderJUnitA");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    // Update the JAR file and make sure the first class is no longer on the Classpath
-    // and the second one is.
-    jarBytes = this.classBuilder.createJarFromClassContent("com/jcljunit/JarClassLoaderJUnitB",
-        "package com.jcljunit; public class JarClassLoaderJUnitB {}");
-    writeJarBytesToFile(jarFile2, jarBytes);
-    classLoader = new JarClassLoader(jarFile2, "JarClassLoaderJUnit.jar", jarBytes);
-    classPathLoader = classPathLoader.addOrReplace(classLoader);
-
-    try {
-      classPathLoader.forName("com.jcljunit.JarClassLoaderJUnitB");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    try {
-      classPathLoader.forName("com.jcljunit.JarClassLoaderJUnitA");
-      fail("Class should not be found on Classpath");
-    } catch (ClassNotFoundException expected) { // expected
-    }
-
-    classPathLoader.remove(classLoader);
-  }
-
-  @Test
-  public void testFailingCompilation() throws Exception {
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import org.apache.geode.cache.Declarable;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer.append("public class JarClassLoaderJUnitFunction implements Function {}");
-    String functionString = stringBuffer.toString();
-
-    try {
-      this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-      fail("This code should have failed to compile and thrown an exception");
-    } catch (Exception ex) {
-      // All good
-    }
-  }
-
-  @Test
-  public void testFunctions() throws IOException, ClassNotFoundException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#1");
-    final File jarFile2 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#2");
-    ClassPathLoader classPathLoader = ClassPathLoader.createWithDefaults(false);
-
-    // Test creating a JAR file with a function
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import java.util.Properties;");
-    stringBuffer.append("import org.apache.geode.cache.Declarable;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer.append("public class JarClassLoaderJUnitFunction implements Function {");
-    stringBuffer.append("public void init(Properties props) {}");
-    stringBuffer.append("public boolean hasResult() {return true;}");
-    stringBuffer.append(
-        "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"GOODv1\");}");
-    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}");
-    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
-    stringBuffer.append("public boolean isHA() {return false;}}");
-    String functionString = stringBuffer.toString();
-
-    byte[] jarBytes =
-        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader = new JarClassLoader(jarFile1, "JarClassLoaderJUnit.jar", jarBytes);
-    classPathLoader = classPathLoader.addOrReplace(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNotNull(function);
-    TestResultSender resultSender = new TestResultSender();
-    FunctionContext functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
-    function.execute(functionContext);
-    assertEquals("GOODv1", (String) resultSender.getResults());
-
-    // Test updating the function with a new JAR file
-    functionString = functionString.replace("v1", "v2");
-    jarBytes =
-        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-    writeJarBytesToFile(jarFile2, jarBytes);
-    classLoader = new JarClassLoader(jarFile2, "JarClassLoaderJUnit.jar", jarBytes);
-    classPathLoader = classPathLoader.addOrReplace(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNotNull(function);
-    resultSender = new TestResultSender();
-    functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
-    function.execute(functionContext);
-    assertEquals("GOODv2", (String) resultSender.getResults());
-
-    // Test returning null for the Id
-    String functionNullIdString =
-        functionString.replace("return \"JarClassLoaderJUnitFunction\"", "return null");
-    jarBytes = this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction",
-        functionNullIdString);
-    writeJarBytesToFile(jarFile1, jarBytes);
-    classLoader = new JarClassLoader(jarFile1, "JarClassLoaderJUnit.jar", jarBytes);
-    classPathLoader = classPathLoader.addOrReplace(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-    assertNull(FunctionService.getFunction("JarClassLoaderJUnitFunction"));
-
-    // Test removing the JAR
-    classPathLoader = classPathLoader.remove(classLoader);
-    assertNull(FunctionService.getFunction("JarClassLoaderJUnitFunction"));
-  }
-
-  /**
-   * Ensure that abstract functions aren't added to the Function Service.
-   */
-  @Test
-  public void testAbstractFunction() throws IOException, ClassNotFoundException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#1");
-
-    Properties properties = new Properties();
-    properties.setProperty(MCAST_PORT, "0");
-    CacheFactory cacheFactory = new CacheFactory(properties);
-    this.cache = (InternalCache) cacheFactory.create();
-
-    // Add an abstract Function to the Classpath
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("public abstract class JarClassLoaderJUnitFunction implements Function {");
-    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}}");
-    String functionString = stringBuffer.toString();
-
-    byte[] jarBytes =
-        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader =
-        new JarClassLoader(jarFile1, "JarClassLoaderJUnitFunction.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    try {
-      ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunction");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNull(function);
-  }
-
-  @Test
-  public void testDeclarableFunctionsWithNoCacheXml() throws IOException, ClassNotFoundException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnitNoXml.jar#1");
-
-    // Add a Declarable Function without parameters for the class to the Classpath
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import java.util.Properties;");
-    stringBuffer.append("import org.apache.geode.cache.Declarable;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer
-        .append("public class JarClassLoaderJUnitFunctionNoXml implements Function, Declarable {");
-    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunctionNoXml\";}");
-    stringBuffer.append("public void init(Properties props) {}");
-    stringBuffer.append(
-        "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"NOPARMSv1\");}");
-    stringBuffer.append("public boolean hasResult() {return true;}");
-    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
-    stringBuffer.append("public boolean isHA() {return false;}}");
-    String functionString = stringBuffer.toString();
-
-    byte[] jarBytes = this.classBuilder
-        .createJarFromClassContent("JarClassLoaderJUnitFunctionNoXml", functionString);
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader =
-        new JarClassLoader(jarFile1, "JarClassLoaderJUnitFunctionNoXml.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    try {
-      ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunctionNoXml");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    // Check to see if the function without parameters executes correctly
-    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunctionNoXml");
-    assertNotNull(function);
-    TestResultSender resultSender = new TestResultSender();
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("NOPARMSv1", (String) resultSender.getResults());
-  }
-
-  @Test
-  public void testDeclarableFunctionsWithoutParms() throws IOException, ClassNotFoundException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#1");
-    final File jarFile2 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#2");
-
-    Properties properties = new Properties();
-    properties.setProperty(MCAST_PORT, "0");
-    CacheFactory cacheFactory = new CacheFactory(properties);
-    this.cache = (InternalCache) cacheFactory.create();
-
-    // Add a Declarable Function without parameters for the class to the Classpath
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import java.util.Properties;");
-    stringBuffer.append("import org.apache.geode.cache.Declarable;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer
-        .append("public class JarClassLoaderJUnitFunction implements Function, Declarable {");
-    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}");
-    stringBuffer.append("public void init(Properties props) {}");
-    stringBuffer.append(
-        "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"NOPARMSv1\");}");
-    stringBuffer.append("public boolean hasResult() {return true;}");
-    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
-    stringBuffer.append("public boolean isHA() {return false;}}");
-    String functionString = stringBuffer.toString();
-
-    byte[] jarBytes =
-        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader =
-        new JarClassLoader(jarFile1, "JarClassLoaderJUnitFunction.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    try {
-      ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunction");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    // Create a cache.xml file and configure the cache with it
-    stringBuffer = new StringBuffer();
-    stringBuffer.append("<?xml version=\"1.0\"?>");
-    stringBuffer.append("<!DOCTYPE cache PUBLIC");
-    stringBuffer.append("  \"-//GemStone Systems, Inc.//GemFire Declarative Caching 7.0//EN\"");
-    stringBuffer.append("  \"http://www.gemstone.com/dtd/cache7_0.dtd\">");
-    stringBuffer.append("<cache>");
-    stringBuffer.append("  <function-service>");
-    stringBuffer.append("    <function>");
-    stringBuffer.append("      <class-name>JarClassLoaderJUnitFunction</class-name>");
-    stringBuffer.append("    </function>");
-    stringBuffer.append(" </function-service>");
-    stringBuffer.append("</cache>");
-    String cacheXmlString = stringBuffer.toString();
-    this.cache.loadCacheXml(new ByteArrayInputStream(cacheXmlString.getBytes()));
-
-    // Check to see if the function without parameters executes correctly
-    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNotNull(function);
-    TestResultSender resultSender = new TestResultSender();
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("NOPARMSv1", (String) resultSender.getResults());
-
-    // Update the second function (change the value returned from execute) by deploying a JAR file
-    functionString = functionString.replace("v1", "v2");
-    jarBytes =
-        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-    writeJarBytesToFile(jarFile2, jarBytes);
-
-    classLoader = new JarClassLoader(jarFile2, "JarClassLoaderJUnitFunction.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    // Check to see if the updated function without parameters executes correctly
-    function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNotNull(function);
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("NOPARMSv2", (String) resultSender.getResults());
-  }
-
-  @Test
-  public void testDeclarableFunctionsWithParms() throws IOException, ClassNotFoundException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#1");
-    final File jarFile2 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#2");
-
-    Properties properties = new Properties();
-    properties.setProperty(MCAST_PORT, "0");
-    CacheFactory cacheFactory = new CacheFactory(properties);
-    this.cache = (InternalCache) cacheFactory.create();
-
-    // Add a Declarable Function with parameters to the class to the Classpath
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import java.util.Properties;");
-    stringBuffer.append("import org.apache.geode.cache.Declarable;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer
-        .append("public class JarClassLoaderJUnitFunction implements Function, Declarable {");
-    stringBuffer.append("private Properties properties;");
-    stringBuffer.append(
-        "public String getId() {if(this.properties==null) {return \"JarClassLoaderJUnitFunction\";} else {return (String) this.properties.get(\"id\");}}");
-    stringBuffer.append("public void init(Properties props) {properties = props;}");
-    stringBuffer.append(
-        "public void execute(FunctionContext context) {context.getResultSender().lastResult(properties.get(\"returnValue\") + \"v1\");}");
-    stringBuffer.append("public boolean hasResult() {return true;}");
-    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
-    stringBuffer.append("public boolean isHA() {return false;}}");
-    String functionString = stringBuffer.toString();
-
-    byte[] jarBytes =
-        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader =
-        new JarClassLoader(jarFile1, "JarClassLoaderJUnitFunction.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    try {
-      ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunction");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    // Create a cache.xml file and configure the cache with it
-    stringBuffer = new StringBuffer();
-    stringBuffer.append("<?xml version=\"1.0\"?>");
-    stringBuffer.append("<!DOCTYPE cache PUBLIC");
-    stringBuffer.append("  \"-//GemStone Systems, Inc.//GemFire Declarative Caching 7.0//EN\"");
-    stringBuffer.append("  \"http://www.gemstone.com/dtd/cache7_0.dtd\">");
-    stringBuffer.append("<cache>");
-    stringBuffer.append("  <function-service>");
-    stringBuffer.append("    <function>");
-    stringBuffer.append("      <class-name>JarClassLoaderJUnitFunction</class-name>");
-    stringBuffer.append(
-        "      <parameter name=\"id\"><string>JarClassLoaderJUnitFunctionA</string></parameter>");
-    stringBuffer.append("      <parameter name=\"returnValue\"><string>DOG</string></parameter>");
-    stringBuffer.append("    </function>");
-    stringBuffer.append("    <function>");
-    stringBuffer.append("      <class-name>JarClassLoaderJUnitFunction</class-name>");
-    stringBuffer.append(
-        "      <parameter name=\"id\"><string>JarClassLoaderJUnitFunctionB</string></parameter>");
-    stringBuffer.append("      <parameter name=\"returnValue\"><string>CAT</string></parameter>");
-    stringBuffer.append("    </function>");
-    stringBuffer.append(" </function-service>");
-    stringBuffer.append("</cache>");
-    String cacheXmlString = stringBuffer.toString();
-    this.cache.loadCacheXml(new ByteArrayInputStream(cacheXmlString.getBytes()));
-
-    // Check to see if the functions with parameters execute correctly
-    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunctionA");
-    assertNotNull(function);
-    TestResultSender resultSender = new TestResultSender();
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("DOGv1", (String) resultSender.getResults());
-
-    function = FunctionService.getFunction("JarClassLoaderJUnitFunctionB");
-    assertNotNull(function);
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("CATv1", (String) resultSender.getResults());
-
-    // Update the first function (change the value returned from execute)
-    functionString = functionString.replace("v1", "v2");
-    jarBytes =
-        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-    writeJarBytesToFile(jarFile2, jarBytes);
-    classLoader = new JarClassLoader(jarFile2, "JarClassLoaderJUnitFunction.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    // Check to see if the updated functions with parameters execute correctly
-    function = FunctionService.getFunction("JarClassLoaderJUnitFunctionA");
-    assertNotNull(function);
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("DOGv2", (String) resultSender.getResults());
-
-    function = FunctionService.getFunction("JarClassLoaderJUnitFunctionB");
-    assertNotNull(function);
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("CATv2", (String) resultSender.getResults());
-
-    // Update cache xml to add a new function and replace an existing one
-    cacheXmlString =
-        cacheXmlString.replace("JarClassLoaderJUnitFunctionA", "JarClassLoaderJUnitFunctionC")
-            .replace("CAT", "BIRD");
-    this.cache.loadCacheXml(new ByteArrayInputStream(cacheXmlString.getBytes()));
-
-    // Update the first function (change the value returned from execute)
-    functionString = functionString.replace("v2", "v3");
-    jarBytes =
-        this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-    writeJarBytesToFile(jarFile1, jarBytes);
-    classLoader = new JarClassLoader(jarFile1, "JarClassLoaderJUnitFunction.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    // Check to see if the updated functions with parameters execute correctly
-    function = FunctionService.getFunction("JarClassLoaderJUnitFunctionA");
-    assertNotNull(function);
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("DOGv3", (String) resultSender.getResults());
-
-    function = FunctionService.getFunction("JarClassLoaderJUnitFunctionC");
-    assertNotNull(function);
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("DOGv3", (String) resultSender.getResults());
-
-    function = FunctionService.getFunction("JarClassLoaderJUnitFunctionB");
-    assertNotNull(function);
-    function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("BIRDv3", (String) resultSender.getResults());
-  }
-
-  @Test
-  public void testDependencyBetweenJars() throws IOException, ClassNotFoundException {
-    final File parentJarFile = new File(JAR_PREFIX + "JarClassLoaderJUnitParent.jar#1");
-    final File usesJarFile = new File(JAR_PREFIX + "JarClassLoaderJUnitUses.jar#1");
-    final File functionJarFile = new File(JAR_PREFIX + "JarClassLoaderJUnitFunction.jar#1");
-
-    // Write out a JAR files.
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("package jcljunit.parent;");
-    stringBuffer.append("public class JarClassLoaderJUnitParent {");
-    stringBuffer.append("public String getValueParent() {");
-    stringBuffer.append("return \"PARENT\";}}");
-
-    byte[] jarBytes = this.classBuilder.createJarFromClassContent(
-        "jcljunit/parent/JarClassLoaderJUnitParent", stringBuffer.toString());
-    writeJarBytesToFile(parentJarFile, jarBytes);
-    JarClassLoader parentClassLoader =
-        new JarClassLoader(parentJarFile, "JarClassLoaderJUnitParent.jar", jarBytes);
-
-    stringBuffer = new StringBuffer();
-    stringBuffer.append("package jcljunit.uses;");
-    stringBuffer.append("public class JarClassLoaderJUnitUses {");
-    stringBuffer.append("public String getValueUses() {");
-    stringBuffer.append("return \"USES\";}}");
-
-    jarBytes = this.classBuilder.createJarFromClassContent("jcljunit/uses/JarClassLoaderJUnitUses",
-        stringBuffer.toString());
-    writeJarBytesToFile(usesJarFile, jarBytes);
-    JarClassLoader usesClassLoader =
-        new JarClassLoader(usesJarFile, "JarClassLoaderJUnitUses.jar", jarBytes);
-
-    stringBuffer = new StringBuffer();
-    stringBuffer.append("package jcljunit.function;");
-    stringBuffer.append("import jcljunit.parent.JarClassLoaderJUnitParent;");
-    stringBuffer.append("import jcljunit.uses.JarClassLoaderJUnitUses;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer.append(
-        "public class JarClassLoaderJUnitFunction  extends JarClassLoaderJUnitParent implements Function {");
-    stringBuffer.append("private JarClassLoaderJUnitUses uses = new JarClassLoaderJUnitUses();");
-    stringBuffer.append("public boolean hasResult() {return true;}");
-    stringBuffer.append(
-        "public void execute(FunctionContext context) {context.getResultSender().lastResult(getValueParent() + \":\" + uses.getValueUses());}");
-    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}");
-    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
-    stringBuffer.append("public boolean isHA() {return false;}}");
-
-    ClassBuilder functionClassBuilder = new ClassBuilder();
-    functionClassBuilder.addToClassPath(parentJarFile.getAbsolutePath());
-    functionClassBuilder.addToClassPath(usesJarFile.getAbsolutePath());
-    jarBytes = functionClassBuilder.createJarFromClassContent(
-        "jcljunit/function/JarClassLoaderJUnitFunction", stringBuffer.toString());
-    writeJarBytesToFile(functionJarFile, jarBytes);
-    JarClassLoader functionClassLoader =
-        new JarClassLoader(functionJarFile, "JarClassLoaderJUnitFunction.jar", jarBytes);
-
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(functionClassLoader);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(parentClassLoader);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(usesClassLoader);
-
-    functionClassLoader.loadClassesAndRegisterFunctions();
-
-    Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNotNull(function);
-    TestResultSender resultSender = new TestResultSender();
-    FunctionContext functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
-    function.execute(functionContext);
-    assertEquals("PARENT:USES", (String) resultSender.getResults());
-  }
-
-  @Test
-  public void testFindResource() throws IOException, ClassNotFoundException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnitResource.jar#1");
-    ClassPathLoader classPathLoader = ClassPathLoader.createWithDefaults(false);
-    final String fileName = "file.txt";
-    final String fileContent = "FILE CONTENT";
-
-    byte[] jarBytes = this.classBuilder.createJarFromFileContent(fileName, fileContent);
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader =
-        new JarClassLoader(jarFile1, "JarClassLoaderJUnitResource.jar", jarBytes);
-    classPathLoader = classPathLoader.addOrReplace(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    InputStream inputStream = classLoader.getResourceAsStream(fileName);
-    assertNotNull(inputStream);
-
-    final byte[] fileBytes = new byte[fileContent.length()];
-    inputStream.read(fileBytes);
-    inputStream.close();
-    assertTrue(fileContent.equals(new String(fileBytes)));
-  }
-
-  @Test
-  public void testUpdateClassInJar() throws IOException, ClassNotFoundException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#1");
-    final File jarFile2 = new File(JAR_PREFIX + "JarClassLoaderJUnit.jar#2");
-    ClassPathLoader classPathLoader = ClassPathLoader.createWithDefaults(false);
-
-    // First use of the JAR file
-    byte[] jarBytes = this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitTestClass",
-        "public class JarClassLoaderJUnitTestClass { public Integer getValue5() { return new Integer(5); } }");
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader = new JarClassLoader(jarFile1, "JarClassLoaderJUnit.jar", jarBytes);
-    classPathLoader = classPathLoader.addOrReplace(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    try {
-      Class<?> clazz = classPathLoader.forName("JarClassLoaderJUnitTestClass");
-      Object object = clazz.newInstance();
-      Method getValue5Method = clazz.getMethod("getValue5", new Class[] {});
-      Integer value = (Integer) getValue5Method.invoke(object, new Object[] {});
-      assertEquals(value.intValue(), 5);
-
-    } catch (InvocationTargetException itex) {
-      fail("JAR file not correctly added to Classpath" + itex);
-    } catch (NoSuchMethodException nsmex) {
-      fail("JAR file not correctly added to Classpath" + nsmex);
-    } catch (InstantiationException iex) {
-      fail("JAR file not correctly added to Classpath" + iex);
-    } catch (IllegalAccessException iaex) {
-      fail("JAR file not correctly added to Classpath" + iaex);
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath" + cnfex);
-    }
-
-    // Now create an updated JAR file and make sure that the method from the new
-    // class is available.
-    jarBytes = this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitTestClass",
-        "public class JarClassLoaderJUnitTestClass { public Integer getValue10() { return new Integer(10); } }");
-    writeJarBytesToFile(jarFile2, jarBytes);
-    classLoader = new JarClassLoader(jarFile2, "JarClassLoaderJUnit.jar", jarBytes);
-    classPathLoader = classPathLoader.addOrReplace(classLoader);
-    classLoader.loadClassesAndRegisterFunctions();
-
-    try {
-      Class<?> clazz = classPathLoader.forName("JarClassLoaderJUnitTestClass");
-      Object object = clazz.newInstance();
-      Method getValue10Method = clazz.getMethod("getValue10", new Class[] {});
-      Integer value = (Integer) getValue10Method.invoke(object, new Object[] {});
-      assertEquals(value.intValue(), 10);
-
-    } catch (InvocationTargetException itex) {
-      fail("JAR file not correctly added to Classpath" + itex);
-    } catch (NoSuchMethodException nsmex) {
-      fail("JAR file not correctly added to Classpath" + nsmex);
-    } catch (InstantiationException iex) {
-      fail("JAR file not correctly added to Classpath" + iex);
-    } catch (IllegalAccessException iaex) {
-      fail("JAR file not correctly added to Classpath" + iaex);
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath" + cnfex);
-    }
-  }
-
-  @Test
-  public void testMultiThread() throws IOException {
-    final File jarFile1 = new File(JAR_PREFIX + "JarClassLoaderJUnitA.jar#1");
-    final File jarFile2 = new File(JAR_PREFIX + "JarClassLoaderJUnitB.jar#1");
-
-    // Add two JARs to the classpath
-    byte[] jarBytes = this.classBuilder.createJarFromName("JarClassLoaderJUnitA");
-    writeJarBytesToFile(jarFile1, jarBytes);
-    JarClassLoader classLoader = new JarClassLoader(jarFile1, "JarClassLoaderJUnitA.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-
-    jarBytes = this.classBuilder.createJarFromClassContent("com/jcljunit/JarClassLoaderJUnitB",
-        "package com.jcljunit; public class JarClassLoaderJUnitB {}");
-    writeJarBytesToFile(jarFile2, jarBytes);
-    classLoader = new JarClassLoader(jarFile2, "JarClassLoaderJUnitB.jar", jarBytes);
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(classLoader);
-
-    String[] classNames = new String[] {"JarClassLoaderJUnitA", "com.jcljunit.JarClassLoaderJUnitB",
-        "NON-EXISTENT CLASS"};
-
-    // Spawn some threads which try to instantiate these classes
-    final int threadCount = 10;
-    final int numLoops = 1000;
-    final CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount + 1);
-    for (int i = 0; i < threadCount; i++) {
-      new ForNameExerciser(cyclicBarrier, numLoops, classNames).start();
-    }
-
-    // Wait for all of the threads to be ready
-    try {
-      cyclicBarrier.await();
-    } catch (InterruptedException iex) {
-      fail("Interrupted while waiting for barrier");
-    } catch (BrokenBarrierException bbex) {
-      fail("Broken barrier while waiting");
-    }
-
-    // Loop while each thread tries N times to instantiate a non-existent class
-    for (int i = 0; i < numLoops; i++) {
-      try {
-        cyclicBarrier.await(5, TimeUnit.SECONDS);
-      } catch (InterruptedException iex) {
-        fail("Interrupted while waiting for barrier");
-      } catch (TimeoutException tex) {
-        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
-        long[] threadIds = threadMXBean.findDeadlockedThreads();
-
-        if (threadIds != null) {
-          StringBuffer deadLockTrace = new StringBuffer();
-          for (long threadId : threadIds) {
-            ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, 100);
-            deadLockTrace.append(threadInfo.getThreadName()).append("\n");
-            for (StackTraceElement stackTraceElem : threadInfo.getStackTrace()) {
-              deadLockTrace.append("\t").append(stackTraceElem).append("\n");
-            }
-          }
-
-          fail("Deadlock with trace:\n" + deadLockTrace.toString());
-        }
-
-        fail("Timeout while waiting for barrier - no deadlock detected");
-      } catch (BrokenBarrierException bbex) {
-        fail("Broken barrier while waiting");
-      }
-    }
-  }
-
-  private void deleteSavedJarFiles() {
-    File dirFile = new File(".");
-
-    // Find all created JAR files
-    File[] oldJarFiles = dirFile.listFiles(new FilenameFilter() {
-      @Override
-      public boolean accept(final File file, final String name) {
-        return JarClassLoaderJUnitTest.this.pattern.matcher(name).matches();
-      }
-    });
-
-    // Now delete them
-    if (oldJarFiles != null) {
-      for (File oldJarFile : oldJarFiles) {
-        if (!oldJarFile.delete()) {
-          RandomAccessFile randomAccessFile = null;
-          try {
-            randomAccessFile = new RandomAccessFile(oldJarFile, "rw");
-            randomAccessFile.setLength(0);
-          } catch (IOException ioex) {
-            fail("IOException when trying to deal with a stubborn JAR file");
-          } finally {
-            try {
-              if (randomAccessFile != null) {
-                randomAccessFile.close();
-              }
-            } catch (IOException ioex) {
-              fail("IOException when trying to deal with a stubborn JAR file");
-            }
-          }
-          oldJarFile.deleteOnExit();
-        }
-      }
-    }
-  }
-
-  private void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
-    final OutputStream outStream = new FileOutputStream(jarFile);
-    outStream.write(jarBytes);
-    outStream.close();
-  }
-
-  private static class TestResultSender implements ResultSender<Object> {
-    private Object result;
-
-    public TestResultSender() {}
-
-    protected Object getResults() {
-      return this.result;
-    }
-
-    @Override
-    public void lastResult(final Object lastResult) {
-      this.result = lastResult;
-    }
-
-    @Override
-    public void sendResult(final Object oneResult) {
-      throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void sendException(final Throwable t) {
-      throw new UnsupportedOperationException();
-    }
-  }
-
-  static final Random random = new Random();
-
-  private class ForNameExerciser extends Thread {
-    private final CyclicBarrier cyclicBarrier;
-    private final int numLoops;
-    private final String[] classNames;
-
-    ForNameExerciser(final CyclicBarrier cyclicBarrier, final int numLoops,
-        final String[] classNames) {
-      this.cyclicBarrier = cyclicBarrier;
-      this.numLoops = numLoops;
-      this.classNames = classNames;
-    }
-
-    @Override
-    public void run() {
-      try {
-        this.cyclicBarrier.await();
-      } catch (InterruptedException iex) {
-        fail("Interrupted while waiting for latch");
-      } catch (BrokenBarrierException bbex) {
-        fail("Broken barrier while waiting");
-      }
-      for (int i = 0; i < this.numLoops; i++) {
-        try {
-          // Random select a name from the list of class names and try to load it
-          String className = this.classNames[random.nextInt(this.classNames.length)];
-          ClassPathLoader.getLatest().forName(className);
-        } catch (ClassNotFoundException expected) { // expected
-        }
-        try {
-          this.cyclicBarrier.await();
-        } catch (InterruptedException iex) {
-          fail("Interrupted while waiting for barrrier");
-        } catch (BrokenBarrierException bbex) {
-          fail("Broken barrier while waiting");
-        }
-      }
-    }
-  }
-}


[5/8] geode git commit: GEODE-2686: Remove JarClassLoader

Posted by js...@apache.org.
GEODE-2686: Remove JarClassLoader

 - Remove JarClassLoader
 - Replace ClassPathLoader's collection of JarClassLoaders with a single URLClassLoader
 - Change naming scheme for deployed jars from 'vf.gf#myJar.jar#1' to 'myJar.v1.jar'


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

Branch: refs/heads/develop
Commit: 6fd2d123a6c4dbb0c7af0bbf135314bda0c6d5e9
Parents: f272762
Author: Jared Stewart <js...@pivotal.io>
Authored: Thu Jan 19 12:00:04 2017 -0800
Committer: Jared Stewart <js...@pivotal.io>
Committed: Sun Apr 16 09:10:00 2017 -0700

----------------------------------------------------------------------
 .../apache/geode/internal/ClassPathLoader.java  | 515 ++---------
 .../org/apache/geode/internal/DeployedJar.java  | 442 ++++++++++
 .../geode/internal/InternalDataSerializer.java  |   2 +-
 .../apache/geode/internal/JarClassLoader.java   | 721 ----------------
 .../org/apache/geode/internal/JarDeployer.java  | 699 +++++++--------
 .../cache/ClusterConfigurationLoader.java       |  40 +-
 .../geode/internal/cache/GemFireCacheImpl.java  |   4 +-
 .../cache/persistence/BackupManager.java        |  10 +-
 .../internal/cli/functions/DeployFunction.java  |  11 +-
 .../cli/functions/ListDeployedFunction.java     |  10 +-
 .../cli/functions/UndeployFunction.java         |  15 +-
 .../ClassPathLoaderIntegrationTest.java         | 454 +++++-----
 .../geode/internal/ClassPathLoaderTest.java     | 402 ++-------
 .../geode/internal/DeployedJarJUnitTest.java    | 538 ++++++++++++
 .../geode/internal/JarClassLoaderJUnitTest.java | 851 -------------------
 .../geode/internal/JarDeployerDUnitTest.java    | 574 -------------
 .../internal/JarDeployerIntegrationTest.java    | 214 +++--
 .../cache/IncrementalBackupDUnitTest.java       |  33 +-
 .../geode/management/DeployJarTestSuite.java    |  31 +
 .../DeployCommandRedeployDUnitTest.java         | 159 ++++
 .../cli/commands/DeployCommandsDUnitTest.java   | 662 +++++----------
 .../cli/commands/UserCommandsDUnitTest.java     | 164 ----
 .../internal/configuration/ClusterConfig.java   |  35 +-
 .../dunit/rules/GfshShellConnectionRule.java    |  10 +
 .../cli/commands/CommandOverHttpDUnitTest.java  |   2 +-
 25 files changed, 2323 insertions(+), 4275 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/internal/ClassPathLoader.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/ClassPathLoader.java b/geode-core/src/main/java/org/apache/geode/internal/ClassPathLoader.java
index 9ab7c30..41cce05 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/ClassPathLoader.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/ClassPathLoader.java
@@ -14,6 +14,9 @@
  */
 package org.apache.geode.internal;
 
+import static java.util.stream.Collectors.joining;
+
+import org.apache.commons.io.FileUtils;
 import org.apache.geode.distributed.internal.DistributionConfig;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.internal.util.CollectionUtils;
@@ -27,6 +30,7 @@ import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -62,271 +66,54 @@ public final class ClassPathLoader {
    * 
    * See also http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html
    */
-
-  public static final String ENABLE_TRACE_PROPERTY =
-      DistributionConfig.GEMFIRE_PREFIX + "ClassPathLoader.enableTrace";
-  public static final String ENABLE_TRACE_DEFAULT_VALUE = "false";
-  private final boolean ENABLE_TRACE = false;
-
   private static final Logger logger = LogService.getLogger();
 
   public static final String EXCLUDE_TCCL_PROPERTY =
       DistributionConfig.GEMFIRE_PREFIX + "excludeThreadContextClassLoader";
   public static final boolean EXCLUDE_TCCL_DEFAULT_VALUE = false;
-  private boolean excludeTCCL;
-
-  // This calculates the location of the extlib directory relative to the
-  // location of the gemfire jar file. If for some reason the ClassPathLoader
-  // class is found in a directory instead of a JAR file (as when testing),
-  // then it will be relative to the location of the root of the package and
-  // class.
-  public static final String EXT_LIB_DIR_PARENT_PROPERTY =
-      DistributionConfig.GEMFIRE_PREFIX + "ClassPathLoader.EXT_LIB_DIR";
-  public static final String EXT_LIB_DIR_PARENT_DEFAULT =
-      ClassPathLoader.class.getProtectionDomain().getCodeSource().getLocation().getPath();
-
-  static final File defineEXT_LIB_DIR() {
-    return new File(
-        (new File(System.getProperty(EXT_LIB_DIR_PARENT_PROPERTY, EXT_LIB_DIR_PARENT_DEFAULT)))
-            .getParent(),
-        "ext");
-  }
-
-  // This token is placed into the list of class loaders to determine where
-  // to insert the TCCL when in forName(...), getResource(...), etc.
-  private static final ClassLoader TCCL_PLACEHOLDER = new ClassLoader() { // This is never used for
-                                                                          // class loading
-  };
-
-  private static final AtomicReference<ClassPathLoader> latest =
-      new AtomicReference<ClassPathLoader>();
-
-  private final List<ClassLoader> classLoaders;
-
-  private static final Set<ClassLoader> defaultLoaders;
-  static {
-    defaultLoaders = new HashSet<ClassLoader>();
-    try {
-      ClassLoader classLoader = ClassPathLoader.class.getClassLoader();
-      if (classLoader != null) {
-        defaultLoaders.add(classLoader);
-      }
-    } catch (SecurityException sex) {
-      // Nothing to do, just don't add it
-    }
 
-    try {
-      ClassLoader classLoader = ClassLoader.getSystemClassLoader();
-      if (classLoader != null) {
-        defaultLoaders.add(classLoader);
-      }
-    } catch (SecurityException sex) {
-      // Nothing to do, just don't add it
-    }
+  private static volatile ClassPathLoader latest;
 
-    setLatestToDefault();
-  }
+  private volatile URLClassLoader classLoaderForDeployedJars;
+  private final JarDeployer jarDeployer;
 
-  /**
-   * Starting at the files or directories identified by 'files', search for valid JAR files and
-   * return a list of their URLs. Sub-directories will also be searched.
-   * 
-   * @param files Files or directories to search for valid JAR content.
-   * 
-   * @return A list of URLs for all JAR files found.
-   */
-  private static List<URL> getJarURLsFromFiles(final File... files) {
-    final List<URL> urls = new ArrayList<URL>();
-
-    Assert.assertTrue(files != null, "file list cannot be null");
-
-    for (File file : files) {
-      if (file.exists()) {
-        if (file.isDirectory()) {
-          urls.addAll(getJarURLsFromFiles(file.listFiles()));
-        } else {
-          if (!JarClassLoader.hasValidJarContent(file)) {
-            logger.warn("Invalid JAR content when attempting to create ClassLoader for file: {}",
-                file.getAbsolutePath());
-            continue;
-          }
+  private boolean excludeTCCL;
 
-          try {
-            urls.add(file.toURI().toURL());
-          } catch (MalformedURLException muex) {
-            logger.warn(
-                "Encountered invalid URL when attempting to create ClassLoader for file: {}:{}",
-                file.getAbsolutePath(), muex.getMessage());
-            continue;
-          }
-        }
-      }
-    }
+  public void rebuildClassLoaderForDeployedJars() {
+    ClassLoader parent = ClassPathLoader.class.getClassLoader();
 
-    return urls;
+    this.classLoaderForDeployedJars = new URLClassLoader(jarDeployer.getDeployedJarURLs(), parent);
   }
 
-  private ClassPathLoader(final List<ClassLoader> classLoaders, final boolean excludeTCCL) {
-
-    Assert.assertTrue(classLoaders != null, "custom loaders must not be null");
-    for (ClassLoader classLoader : classLoaders) {
-      Assert.assertTrue(classLoader != null, "null classloaders not allowed");
-    }
-
-    this.classLoaders = new ArrayList<ClassLoader>(classLoaders);
+  public ClassPathLoader(boolean excludeTCCL) {
     this.excludeTCCL = excludeTCCL;
+    this.jarDeployer = new JarDeployer();
+    rebuildClassLoaderForDeployedJars();
   }
 
-  /**
-   * Get a copy of the collection of ClassLoaders currently in use.
-   * 
-   * @return Collection of ClassLoaders currently in use.
-   */
-  public Collection<ClassLoader> getClassLoaders() {
-    List<ClassLoader> classLoadersCopy = new ArrayList<ClassLoader>(this.classLoaders);
-
-    for (int i = 0; i < classLoadersCopy.size(); i++) {
-      if (classLoadersCopy.get(i).equals(TCCL_PLACEHOLDER)) {
-        if (excludeTCCL) {
-          classLoadersCopy.remove(i);
-        } else {
-          classLoadersCopy.set(i, Thread.currentThread().getContextClassLoader());
-        }
-        break;
-      }
-    }
-
-    return classLoadersCopy;
-  }
-
-  // This is exposed for testing.
-  static ClassPathLoader createWithDefaults(final boolean excludeTCCL) {
-    List<ClassLoader> classLoaders = new LinkedList<ClassLoader>();
-
-    classLoaders.add(TCCL_PLACEHOLDER);
-
-    for (final ClassLoader classLoader : defaultLoaders) {
-      classLoaders.add(classLoader);
-    }
-
-    // Add user JAR files from the EXT_LIB_DIR directory using a single ClassLoader
-    try {
-      File EXT_LIB_DIR = defineEXT_LIB_DIR();
-      if (EXT_LIB_DIR.exists()) {
-        if (!EXT_LIB_DIR.isDirectory() || !EXT_LIB_DIR.canRead()) {
-          logger.warn("Cannot read from directory when attempting to load JAR files: {}",
-              EXT_LIB_DIR.getAbsolutePath());
-        } else {
-          List<URL> extLibJarURLs = getJarURLsFromFiles(EXT_LIB_DIR);
-          ClassLoader classLoader =
-              new URLClassLoader(extLibJarURLs.toArray(new URL[extLibJarURLs.size()]));
-          classLoaders.add(classLoader);
-        }
-      }
-    } catch (SecurityException sex) {
-      // Nothing to do, just don't add it
-    }
-
-    return new ClassPathLoader(classLoaders, excludeTCCL);
+  public ClassPathLoader(boolean excludeTCCL, File workingDir) {
+    this.excludeTCCL = excludeTCCL;
+    this.jarDeployer = new JarDeployer(workingDir);
+    rebuildClassLoaderForDeployedJars();
   }
 
   public static ClassPathLoader setLatestToDefault() {
-    return setLatestToDefault(Boolean.getBoolean(EXCLUDE_TCCL_PROPERTY));
+    latest = new ClassPathLoader(Boolean.getBoolean(EXCLUDE_TCCL_PROPERTY));
+    return latest;
   }
 
-  public static ClassPathLoader setLatestToDefault(final boolean excludeTCCL) {
-    ClassPathLoader classPathLoader = createWithDefaults(excludeTCCL);
-
-    // Clean up JarClassLoaders that attached to the previous ClassPathLoader
-    ClassPathLoader oldClassPathLoader = latest.getAndSet(classPathLoader);
-    if (oldClassPathLoader != null) {
-      for (ClassLoader classLoader : oldClassPathLoader.classLoaders) {
-        if (classLoader instanceof JarClassLoader) {
-          ((JarClassLoader) classLoader).cleanUp();
-        }
-      }
-    }
-
-    return classPathLoader;
+  public static ClassPathLoader setLatestToDefault(File workingDir) {
+    latest = new ClassPathLoader(Boolean.getBoolean(EXCLUDE_TCCL_PROPERTY), workingDir);
+    return latest;
   }
 
-  // This is exposed for testing.
-  ClassPathLoader addOrReplace(final ClassLoader classLoader) {
-    final boolean isDebugEnabled = logger.isTraceEnabled();
-    if (isDebugEnabled) {
-      logger.trace("adding classLoader: {}", classLoader);
-    }
-
-    List<ClassLoader> classLoadersCopy = new ArrayList<ClassLoader>(this.classLoaders);
-    classLoadersCopy.add(0, classLoader);
-
-    // Ensure there is only one instance of this class loader in the list
-    ClassLoader removingClassLoader = null;
-    int index = classLoadersCopy.lastIndexOf(classLoader);
-    if (index != 0) {
-      removingClassLoader = classLoadersCopy.get(index);
-      if (isDebugEnabled) {
-        logger.trace("removing previous classLoader: {}", removingClassLoader);
-      }
-      classLoadersCopy.remove(index);
-    }
-
-    if (removingClassLoader != null && removingClassLoader instanceof JarClassLoader) {
-      ((JarClassLoader) removingClassLoader).cleanUp();
-    }
-
-    return new ClassPathLoader(classLoadersCopy, this.excludeTCCL);
-  }
-
-  /**
-   * Add or replace the provided {@link ClassLoader} to the list held by this ClassPathLoader. Then
-   * use the resulting list to create a new ClassPathLoader and set it as the latest.
-   * 
-   * @param classLoader {@link ClassLoader} to add
-   */
-  public ClassPathLoader addOrReplaceAndSetLatest(final ClassLoader classLoader) {
-    ClassPathLoader classPathLoader = addOrReplace(classLoader);
-    latest.set(classPathLoader);
-    return classPathLoader;
+  public JarDeployer getJarDeployer() {
+    return this.jarDeployer;
   }
 
   // This is exposed for testing.
-  ClassPathLoader remove(final ClassLoader classLoader) {
-    final boolean isDebugEnabled = logger.isTraceEnabled();
-    if (isDebugEnabled) {
-      logger.trace("removing classLoader: {}", classLoader);
-    }
-
-    List<ClassLoader> classLoadersCopy = new ArrayList<ClassLoader>();
-    classLoadersCopy.addAll(this.classLoaders);
-
-    if (!classLoadersCopy.contains(classLoader)) {
-      if (isDebugEnabled) {
-        logger.trace("cannot remove classLoader since it doesn't exist: {}", classLoader);
-      }
-      return this;
-    }
-
-    classLoadersCopy.remove(classLoader);
-
-    if (classLoader instanceof JarClassLoader) {
-      ((JarClassLoader) classLoader).cleanUp();
-    }
-
-    return new ClassPathLoader(classLoadersCopy, this.excludeTCCL);
-  }
-
-  /**
-   * Remove the provided {@link ClassLoader} from the list held by this ClassPathLoader. Then use
-   * the resulting list to create a new ClassPathLoader and set it as the latest. Silently ignores
-   * requests to remove non-existent ClassLoaders.
-   * 
-   * @param classLoader {@link ClassLoader} to remove
-   */
-  public ClassPathLoader removeAndSetLatest(final ClassLoader classLoader) {
-    ClassPathLoader classPathLoader = remove(classLoader);
-    latest.set(classPathLoader);
-    return classPathLoader;
+  static ClassPathLoader createWithDefaults(final boolean excludeTCCL) {
+    return new ClassPathLoader(excludeTCCL);
   }
 
   public URL getResource(final String name) {
@@ -334,52 +121,26 @@ public final class ClassPathLoader {
     if (isDebugEnabled) {
       logger.trace("getResource({})", name);
     }
-    URL url = null;
-    ClassLoader tccl = null;
-    if (!excludeTCCL) {
-      tccl = Thread.currentThread().getContextClassLoader();
-    }
 
-    for (ClassLoader classLoader : this.classLoaders) {
-      if (classLoader == TCCL_PLACEHOLDER) {
-        try {
-          if (tccl != null) {
-            if (isDebugEnabled) {
-              logger.trace("getResource trying TCCL: {}", tccl);
-            }
-            url = tccl.getResource(name);
-            if (url != null) {
-              if (isDebugEnabled) {
-                logger.trace("getResource found by TCCL");
-              }
-              return url;
-            }
-          } else {
-            if (isDebugEnabled) {
-              logger.trace("getResource skipping TCCL because it's null");
-            }
-          }
-        } catch (SecurityException sex) {
-          // Continue to next ClassLoader
-        }
-      } else if (excludeTCCL || !classLoader.equals(tccl)) {
-        if (isDebugEnabled) {
-          logger.trace("getResource trying classLoader: {}", classLoader);
-        }
-        url = classLoader.getResource(name);
+    for (ClassLoader classLoader : getClassLoaders()) {
+      if (isDebugEnabled) {
+        logger.trace("getResource trying: {}", classLoader);
+      }
+      try {
+        URL url = classLoader.getResource(name);
+
         if (url != null) {
           if (isDebugEnabled) {
-            logger.trace("getResource found by classLoader: {}", classLoader);
+            logger.trace("getResource found by: {}", classLoader);
           }
           return url;
         }
+      } catch (SecurityException e) {
+        // try next classLoader
       }
     }
 
-    if (isDebugEnabled) {
-      logger.trace("getResource returning null");
-    }
-    return url;
+    return null;
   }
 
   public Class<?> forName(final String name) throws ClassNotFoundException {
@@ -387,53 +148,25 @@ public final class ClassPathLoader {
     if (isDebugEnabled) {
       logger.trace("forName({})", name);
     }
-    Class<?> clazz = null;
-    ClassLoader tccl = null;
-    if (!excludeTCCL) {
-      tccl = Thread.currentThread().getContextClassLoader();
-    }
 
-    for (ClassLoader classLoader : this.classLoaders) {
+    for (ClassLoader classLoader : this.getClassLoaders()) {
+      if (isDebugEnabled) {
+        logger.trace("forName trying: {}", classLoader);
+      }
       try {
-        if (classLoader == TCCL_PLACEHOLDER) {
-          if (tccl != null) {
-            if (isDebugEnabled) {
-              logger.trace("forName trying TCCL: {}", tccl);
-            }
-            clazz = Class.forName(name, true, tccl);
-            if (clazz != null) {
-              if (isDebugEnabled) {
-                logger.trace("forName found by TCCL");
-              }
-              return clazz;
-            } else {
-              if (isDebugEnabled) {
-                logger.trace("forName skipping TCCL because it's null");
-              }
-            }
-          }
-        } else if (excludeTCCL || !classLoader.equals(tccl)) {
+        Class<?> clazz = Class.forName(name, true, classLoader);
+
+        if (clazz != null) {
           if (isDebugEnabled) {
-            logger.trace("forName trying classLoader: {}", classLoader);
-          }
-          clazz = Class.forName(name, true, classLoader);
-          if (clazz != null) {
-            if (isDebugEnabled) {
-              logger.trace("forName found by classLoader: {}", classLoader);
-            }
-            return clazz;
+            logger.trace("forName found by: {}", classLoader);
           }
+          return clazz;
         }
-      } catch (SecurityException sex) {
-        // Continue to next ClassLoader
-      } catch (ClassNotFoundException cnfex) {
-        // Continue to next ClassLoader
+      } catch (SecurityException | ClassNotFoundException e) {
+        // try next classLoader
       }
     }
 
-    if (isDebugEnabled) {
-      logger.trace("forName throwing ClassNotFoundException");
-    }
     throw new ClassNotFoundException(name);
   }
 
@@ -442,20 +175,10 @@ public final class ClassPathLoader {
    */
   public Class<?> getProxyClass(final Class<?>[] classObjs) {
     IllegalArgumentException ex = null;
-    ClassLoader tccl = null;
-    if (!excludeTCCL) {
-      tccl = Thread.currentThread().getContextClassLoader();
-    }
 
-    for (ClassLoader classLoader : this.classLoaders) {
+    for (ClassLoader classLoader : this.getClassLoaders()) {
       try {
-        if (classLoader == TCCL_PLACEHOLDER) {
-          if (tccl != null) {
-            return Proxy.getProxyClass(tccl, classObjs);
-          }
-        } else if (excludeTCCL || !classLoader.equals(tccl)) {
-          return Proxy.getProxyClass(classLoader, classObjs);
-        }
+        return Proxy.getProxyClass(classLoader, classObjs);
       } catch (SecurityException sex) {
         // Continue to next classloader
       } catch (IllegalArgumentException iaex) {
@@ -464,7 +187,6 @@ public final class ClassPathLoader {
       }
     }
 
-    assert ex != null;
     if (ex != null) {
       throw ex;
     }
@@ -475,19 +197,9 @@ public final class ClassPathLoader {
   public String toString() {
     final StringBuilder sb = new StringBuilder(getClass().getName());
     sb.append("@").append(System.identityHashCode(this)).append("{");
-    sb.append("isLatest=").append(getLatest() == this);
     sb.append(", excludeTCCL=").append(this.excludeTCCL);
     sb.append(", classLoaders=[");
-    for (int i = 0; i < this.classLoaders.size(); i++) {
-      if (i > 0) {
-        sb.append(", ");
-      }
-      sb.append(this.classLoaders.get(i).toString());
-    }
-    sb.append("]");
-    if (!this.excludeTCCL) {
-      sb.append(", TCCL=").append(Thread.currentThread().getContextClassLoader());
-    }
+    sb.append(this.getClassLoaders().stream().map(ClassLoader::toString).collect(joining(", ")));
     sb.append("]}");
     return sb.toString();
   }
@@ -514,13 +226,12 @@ public final class ClassPathLoader {
 
   /**
    * Returns an input stream for reading the specified resource.
-   * 
+   *
    * <p>
    * The search order is described in the documentation for {@link #getResource(String)}.
    * </p>
    * 
    * @param name The resource name
-   * 
    * @return An input stream for reading the resource, or <tt>null</tt> if the resource could not be
    *         found
    */
@@ -553,122 +264,51 @@ public final class ClassPathLoader {
     return getResourceAsStream(name);
   }
 
+
   /**
    * Finds all the resources with the given name. This method will first search the class loader of
    * the context class for the resource before searching all other {@link ClassLoader}s.
    * 
    * @param contextClass The class whose class loader will first be searched
-   * 
    * @param name The resource name
-   *
    * @return An enumeration of {@link java.net.URL <tt>URL</tt>} objects for the resource. If no
    *         resources could be found, the enumeration will be empty. Resources that the class
    *         loader doesn't have access to will not be in the enumeration.
-   *
    * @throws IOException If I/O errors occur
-   * 
    * @see ClassLoader#getResources(String)
    */
   public Enumeration<URL> getResources(final Class<?> contextClass, final String name)
       throws IOException {
-    final boolean isDebugEnabled = logger.isTraceEnabled();
+    final LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
 
-    if (isDebugEnabled) {
-      logger.trace(new StringBuilder("getResources(").append(name).append(")"));
+    if (contextClass != null) {
+      CollectionUtils.addAll(urls, contextClass.getClassLoader().getResources(name));
     }
 
-    final LinkedHashSet<URL> urls = new LinkedHashSet<URL>();
-
-    try {
-      if (contextClass != null) {
-        CollectionUtils.addAll(urls, contextClass.getClassLoader().getResources(name));
+    for (ClassLoader classLoader : getClassLoaders()) {
+      Enumeration<URL> resources = classLoader.getResources(name);
+      if (resources != null && resources.hasMoreElements()) {
+        CollectionUtils.addAll(urls, resources);
       }
-    } catch (IOException ignore) {
-      // ignore and search others
     }
 
-    Enumeration<URL> resources = null;
-    ClassLoader tccl = null;
-    if (!excludeTCCL) {
-      tccl = Thread.currentThread().getContextClassLoader();
-    }
+    return Collections.enumeration(urls);
+  }
 
-    IOException ioException = null;
-    for (ClassLoader classLoader : this.classLoaders) {
-      ioException = null; // reset to null for next ClassLoader
-      if (classLoader == TCCL_PLACEHOLDER) {
-        try {
-          if (tccl != null) {
-            if (isDebugEnabled) {
-              logger.trace("getResources trying TCCL: {}", tccl);
-            }
-            resources = tccl.getResources(name);
-            if (resources != null && resources.hasMoreElements()) {
-              if (isDebugEnabled) {
-                logger.trace("getResources found by TCCL");
-              }
-              CollectionUtils.addAll(urls, resources);
-            }
-          } else {
-            if (isDebugEnabled) {
-              logger.trace("getResources skipping TCCL because it's null");
-            }
-          }
-        } catch (SecurityException ignore) {
-          // Continue to next ClassLoader
-        } catch (IOException ignore) {
-          ioException = ignore;
-          // Continue to next ClassLoader
-        }
-      } else if (excludeTCCL || !classLoader.equals(tccl)) {
-        try {
-          if (isDebugEnabled) {
-            logger.trace("getResources trying classLoader: {}", classLoader);
-          }
-          resources = classLoader.getResources(name);
-          if (resources != null && resources.hasMoreElements()) {
-            if (logger.isTraceEnabled()) {
-              logger.trace(
-                  new StringBuilder("getResources found by classLoader: ").append(classLoader));
-            }
-            CollectionUtils.addAll(urls, resources);
-          }
-        } catch (IOException ignore) {
-          ioException = ignore;
-          // Continue to next ClassLoader
-        }
-      }
-    }
+  public Enumeration<URL> getResources(final String name) throws IOException {
+    return getResources(null, name);
+  }
 
-    if (ioException != null) {
-      if (isDebugEnabled) {
-        logger.trace("getResources throwing IOException");
-      }
-      throw ioException;
-    }
+  private List<ClassLoader> getClassLoaders() {
+    ArrayList<ClassLoader> classLoaders = new ArrayList<>();
 
-    if (isDebugEnabled) {
-      logger.trace("getResources returning empty enumeration");
+    if (!excludeTCCL) {
+      classLoaders.add(Thread.currentThread().getContextClassLoader());
     }
 
-    return Collections.enumeration(urls);
-  }
+    classLoaders.add(classLoaderForDeployedJars);
 
-  /**
-   * Finds all the resources with the given name.
-   * 
-   * @param name The resource name
-   *
-   * @return An enumeration of {@link java.net.URL <tt>URL</tt>} objects for the resource. If no
-   *         resources could be found, the enumeration will be empty. Resources that the class
-   *         loader doesn't have access to will not be in the enumeration.
-   *
-   * @throws IOException If I/O errors occur
-   * 
-   * @see ClassLoader#getResources(String)
-   */
-  public Enumeration<URL> getResources(String name) throws IOException {
-    return getResources(null, name);
+    return classLoaders;
   }
 
   /**
@@ -707,7 +347,14 @@ public final class ClassPathLoader {
   }
 
   public static ClassPathLoader getLatest() {
-    return latest.get();
+    if (latest == null) {
+      synchronized (ClassPathLoader.class) {
+        if (latest == null)
+          setLatestToDefault();
+      }
+    }
+
+    return latest;
   }
 
   /**
@@ -717,7 +364,7 @@ public final class ClassPathLoader {
    * @since GemFire 8.1
    */
   public static final ClassLoader getLatestAsClassLoader() {
-    return latest.get().asClassLoader();
+    return latest.asClassLoader();
   }
 
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java b/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
new file mode 100644
index 0000000..f4f4028
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
@@ -0,0 +1,442 @@
+/*
+ * 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;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.nio.channels.FileLock;
+import java.nio.file.Files;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+import org.apache.geode.cache.Cache;
+import org.apache.logging.log4j.Logger;
+
+import org.apache.geode.cache.CacheClosedException;
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.Declarable;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.pdx.internal.TypeRegistry;
+import sun.nio.ch.ChannelInputStream;
+
+/**
+ * ClassLoader for a single JAR file.
+ * 
+ * @since GemFire 7.0
+ */
+public class DeployedJar {
+  private final static Logger logger = LogService.getLogger();
+  private final static MessageDigest messageDigest = getMessageDigest();
+
+  private final String jarName;
+  private final File file;
+  private final byte[] md5hash;
+  private final Collection<Function> registeredFunctions = new ArrayList<Function>();
+
+  private static MessageDigest getMessageDigest() {
+    try {
+      return MessageDigest.getInstance("MD5");
+    } catch (NoSuchAlgorithmException nsaex) {
+      // Failure just means we can't do a simple compare for content equality
+    }
+    return null;
+  }
+
+  public File getFile() {
+    return this.file;
+  }
+
+  public int getVersion() {
+    return JarDeployer.extractVersionFromFilename(file.getName());
+  }
+
+  public DeployedJar(File versionedJarFile, String jarName) throws IOException {
+    this(versionedJarFile, jarName, Files.readAllBytes(versionedJarFile.toPath()));
+  }
+
+  public DeployedJar(File versionedJarFile, final String jarName, byte[] jarBytes)
+      throws IOException {
+    Assert.assertTrue(jarBytes != null, "jarBytes cannot be null");
+    Assert.assertTrue(jarName != null, "jarName cannot be null");
+    Assert.assertTrue(versionedJarFile != null, "versionedJarFile cannot be null");
+
+    this.file = versionedJarFile;
+    this.jarName = jarName;
+
+    final byte[] fileContent = getJarContent();
+    if (!Arrays.equals(fileContent, jarBytes)) {
+      throw new FileNotFoundException("JAR file: " + versionedJarFile.getAbsolutePath()
+          + ", was modified prior to obtaining a lock: " + jarName);
+    }
+
+    if (!isValidJarContent(getJarContent())) {
+      throw new IllegalArgumentException(
+          "File does not contain valid JAR content: " + versionedJarFile.getAbsolutePath());
+    }
+
+    if (messageDigest != null) {
+      this.md5hash = messageDigest.digest(jarBytes);
+    } else {
+      this.md5hash = null;
+    }
+  }
+
+  /**
+   * Peek into the JAR data and make sure that it is valid JAR content.
+   * 
+   * @param inputStream InputStream containing data to be validated.
+   * @return True if the data has JAR content, false otherwise
+   */
+  private static boolean hasValidJarContent(final InputStream inputStream) {
+    JarInputStream jarInputStream = null;
+    boolean valid = false;
+
+    try {
+      jarInputStream = new JarInputStream(inputStream);
+      valid = (jarInputStream.getNextJarEntry() != null);
+    } catch (IOException ignore) {
+      // Ignore this exception and just return false
+    } finally {
+      try {
+        jarInputStream.close();
+      } catch (IOException ioex) {
+        // Ignore this exception and just return result
+      }
+    }
+
+    return valid;
+  }
+
+  /**
+   * Peek into the JAR data and make sure that it is valid JAR content.
+   * 
+   * @param jarBytes Bytes of data to be validated.
+   * @return True if the data has JAR content, false otherwise
+   */
+  public static boolean isValidJarContent(final byte[] jarBytes) {
+    return hasValidJarContent(new ByteArrayInputStream(jarBytes));
+  }
+
+  /**
+   * Peek into the JAR data and make sure that it is valid JAR content.
+   * 
+   * @param jarFile File whose contents should be validated.
+   * @return True if the data has JAR content, false otherwise
+   */
+  public static boolean hasValidJarContent(final File jarFile) {
+    try {
+      return hasValidJarContent(new FileInputStream(jarFile));
+    } catch (IOException ioex) {
+      return false;
+    }
+  }
+
+  /**
+   * Scan the JAR file and attempt to load all classes and register any function classes found.
+   */
+  // This method will process the contents of the JAR file as stored in this.jarByteContent
+  // instead of reading from the original JAR file. This is done because we can't open up
+  // the original file and then close it without releasing the shared lock that was obtained
+  // in the constructor. Once this method is finished, all classes will have been loaded and
+  // there will no longer be a need to hang on to the original contents so they will be
+  // discarded.
+  public synchronized void loadClassesAndRegisterFunctions() throws ClassNotFoundException {
+    final boolean isDebugEnabled = logger.isDebugEnabled();
+    if (isDebugEnabled) {
+      logger.debug("Registering functions with DeployedJar: {}", this);
+    }
+
+    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.getJarContent());
+
+    JarInputStream jarInputStream = null;
+    try {
+      jarInputStream = new JarInputStream(byteArrayInputStream);
+      JarEntry jarEntry = jarInputStream.getNextJarEntry();
+
+      while (jarEntry != null) {
+        if (jarEntry.getName().endsWith(".class")) {
+          if (isDebugEnabled) {
+            logger.debug("Attempting to load class: {}, from JAR file: {}", jarEntry.getName(),
+                this.file.getAbsolutePath());
+          }
+
+          final String className = jarEntry.getName().replaceAll("/", "\\.").substring(0,
+              (jarEntry.getName().length() - 6));
+          try {
+            Class<?> clazz = ClassPathLoader.getLatest().forName(className);
+            Collection<Function> registerableFunctions = getRegisterableFunctionsFromClass(clazz);
+            for (Function function : registerableFunctions) {
+              FunctionService.registerFunction(function);
+              if (isDebugEnabled) {
+                logger.debug("Registering function class: {}, from JAR file: {}", className,
+                    this.file.getAbsolutePath());
+              }
+              this.registeredFunctions.add(function);
+            }
+          } catch (ClassNotFoundException cnfex) {
+            logger.error("Unable to load all classes from JAR file: {}",
+                this.file.getAbsolutePath(), cnfex);
+            throw cnfex;
+          } catch (NoClassDefFoundError ncdfex) {
+            logger.error("Unable to load all classes from JAR file: {}",
+                this.file.getAbsolutePath(), ncdfex);
+            throw ncdfex;
+          }
+        }
+        jarEntry = jarInputStream.getNextJarEntry();
+      }
+    } catch (IOException ioex) {
+      logger.error("Exception when trying to read class from ByteArrayInputStream", ioex);
+    } finally {
+      if (jarInputStream != null) {
+        try {
+          jarInputStream.close();
+        } catch (IOException ioex) {
+          logger.error("Exception attempting to close JAR input stream", ioex);
+        }
+      }
+    }
+  }
+
+  synchronized void cleanUp() {
+    for (Function function : this.registeredFunctions) {
+      FunctionService.unregisterFunction(function.getId());
+    }
+    this.registeredFunctions.clear();
+
+    try {
+      TypeRegistry typeRegistry =
+          ((GemFireCacheImpl) CacheFactory.getAnyInstance()).getPdxRegistry();
+      if (typeRegistry != null) {
+        typeRegistry.flushCache();
+      }
+    } catch (CacheClosedException ccex) {
+      // That's okay, it just means there was nothing to flush to begin with
+    }
+  }
+
+  /**
+   * Uses MD5 hashes to determine if the original byte content of this DeployedJar is the same as
+   * that past in.
+   * 
+   * @param compareToBytes Bytes to compare the original content to
+   * @return True of the MD5 hash is the same o
+   */
+  public boolean hasSameContentAs(final byte[] compareToBytes) {
+    // If the MD5 hash can't be calculated then silently return no match
+    if (messageDigest == null || this.md5hash == null) {
+      return Arrays.equals(compareToBytes, getJarContent());
+    }
+
+    byte[] compareToMd5 = messageDigest.digest(compareToBytes);
+    if (logger.isDebugEnabled()) {
+      logger.debug("For JAR file: {}, Comparing MD5 hash {} to {}", this.file.getAbsolutePath(),
+          new String(this.md5hash), new String(compareToMd5));
+    }
+    return Arrays.equals(this.md5hash, compareToMd5);
+  }
+
+  /**
+   * Check to see if the class implements the Function interface. If so, it will be registered with
+   * FunctionService. Also, if the functions's class was originally declared in a cache.xml file
+   * then any properties specified at that time will be reused when re-registering the function.
+   * 
+   * @param clazz Class to check for implementation of the Function class
+   * @return A collection of Objects that implement the Function interface.
+   */
+  private Collection<Function> getRegisterableFunctionsFromClass(Class<?> clazz) {
+    final List<Function> registerableFunctions = new ArrayList<Function>();
+
+    try {
+      if (Function.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
+        boolean registerUninitializedFunction = true;
+        if (Declarable.class.isAssignableFrom(clazz)) {
+          try {
+            final List<Properties> propertiesList =
+                ((GemFireCacheImpl) CacheFactory.getAnyInstance())
+                    .getDeclarableProperties(clazz.getName());
+
+            if (!propertiesList.isEmpty()) {
+              registerUninitializedFunction = false;
+              // It's possible that the same function was declared multiple times in cache.xml
+              // with different properties. So, register the function using each set of
+              // properties.
+              for (Properties properties : propertiesList) {
+                @SuppressWarnings("unchecked")
+                Function function = newFunction((Class<Function>) clazz, true);
+                if (function != null) {
+                  ((Declarable) function).init(properties);
+                  if (function.getId() != null) {
+                    registerableFunctions.add(function);
+                  }
+                }
+              }
+            }
+          } catch (CacheClosedException ccex) {
+            // That's okay, it just means there were no properties to init the function with
+          }
+        }
+
+        if (registerUninitializedFunction) {
+          @SuppressWarnings("unchecked")
+          Function function = newFunction((Class<Function>) clazz, false);
+          if (function != null && function.getId() != null) {
+            registerableFunctions.add(function);
+          }
+        }
+      }
+    } catch (Exception ex) {
+      logger.error("Attempting to register function from JAR file: " + this.file.getAbsolutePath(),
+          ex);
+    }
+
+    return registerableFunctions;
+  }
+
+  private Function newFunction(final Class<Function> clazz, final boolean errorOnNoSuchMethod) {
+    try {
+      final Constructor<Function> constructor = clazz.getConstructor();
+      return constructor.newInstance();
+    } catch (NoSuchMethodException nsmex) {
+      if (errorOnNoSuchMethod) {
+        logger.error("Zero-arg constructor is required, but not found for class: {}",
+            clazz.getName(), nsmex);
+      } else {
+        if (logger.isDebugEnabled()) {
+          logger.debug(
+              "Not registering function because it doesn't have a zero-arg constructor: {}",
+              clazz.getName());
+        }
+      }
+    } catch (SecurityException sex) {
+      logger.error("Zero-arg constructor of function not accessible for class: {}", clazz.getName(),
+          sex);
+    } catch (IllegalAccessException iae) {
+      logger.error("Zero-arg constructor of function not accessible for class: {}", clazz.getName(),
+          iae);
+    } catch (InvocationTargetException ite) {
+      logger.error("Error when attempting constructor for function for class: {}", clazz.getName(),
+          ite);
+    } catch (InstantiationException ie) {
+      logger.error("Unable to instantiate function for class: {}", clazz.getName(), ie);
+    } catch (ExceptionInInitializerError eiiex) {
+      logger.error("Error during function initialization for class: {}", clazz.getName(), eiiex);
+    }
+    return null;
+  }
+
+  private byte[] getJarContent() {
+    InputStream channelInputStream = null;
+    try {
+      channelInputStream = new FileInputStream(this.file);
+
+      final ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
+      final byte[] bytes = new byte[4096];
+
+      int bytesRead;
+      while (((bytesRead = channelInputStream.read(bytes)) != -1)) {
+        byteOutStream.write(bytes, 0, bytesRead);
+      }
+      channelInputStream.close();
+      return byteOutStream.toByteArray();
+    } catch (IOException e) {
+      logger.error("Error when attempting to read jar contents: ", e);
+    }
+
+    return new byte[0];
+  }
+
+  public String getJarName() {
+    return this.jarName;
+  }
+
+  public String getFileName() {
+    return this.file.getName();
+  }
+
+  public String getFileCanonicalPath() throws IOException {
+    return this.file.getCanonicalPath();
+  }
+
+  public URL getFileURL() {
+    try {
+      return this.file.toURL();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  @Override
+  public int hashCode() {
+    final int prime = 31;
+    int result = 1;
+    result = prime * result + ((this.jarName == null) ? 0 : this.jarName.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (obj == null) {
+      return false;
+    }
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+    DeployedJar other = (DeployedJar) obj;
+    if (this.jarName == null) {
+      if (other.jarName != null) {
+        return false;
+      }
+    } else if (!this.jarName.equals(other.jarName)) {
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder(getClass().getName());
+    sb.append("@").append(System.identityHashCode(this)).append("{");
+    sb.append("jarName=").append(this.jarName);
+    sb.append(",file=").append(this.file.getAbsolutePath());
+    sb.append(",md5hash=").append(Arrays.toString(this.md5hash));
+    sb.append(",version=").append(this.getVersion());
+    sb.append("}");
+    return sb.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/internal/InternalDataSerializer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/InternalDataSerializer.java b/geode-core/src/main/java/org/apache/geode/internal/InternalDataSerializer.java
index f4f4069..51212c6 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/InternalDataSerializer.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/InternalDataSerializer.java
@@ -4033,7 +4033,7 @@ public abstract class InternalDataSerializer extends DataSerializer implements D
 
   public static void flushClassCache() {
     if (classCache != null) {
-      // Not locking classCache during clear as doing so causes a deadlock in the JarClassLoader
+      // Not locking classCache during clear as doing so causes a deadlock in the DeployedJar
       classCache.clear();
     }
   }

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/internal/JarClassLoader.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/JarClassLoader.java b/geode-core/src/main/java/org/apache/geode/internal/JarClassLoader.java
deleted file mode 100644
index 9cd0589..0000000
--- a/geode-core/src/main/java/org/apache/geode/internal/JarClassLoader.java
+++ /dev/null
@@ -1,721 +0,0 @@
-/*
- * 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;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Modifier;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.channels.FileLock;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Properties;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-
-import org.apache.logging.log4j.Logger;
-
-import org.apache.geode.cache.CacheClosedException;
-import org.apache.geode.cache.CacheFactory;
-import org.apache.geode.cache.Declarable;
-import org.apache.geode.cache.execute.Function;
-import org.apache.geode.cache.execute.FunctionService;
-import org.apache.geode.internal.cache.GemFireCacheImpl;
-import org.apache.geode.internal.logging.LogService;
-import org.apache.geode.pdx.internal.TypeRegistry;
-
-/**
- * ClassLoader for a single JAR file.
- * 
- * @since GemFire 7.0
- */
-public class JarClassLoader extends ClassLoader {
-  private final static Logger logger = LogService.getLogger();
-  private final static MessageDigest messageDigest;
-
-  private final String jarName;
-  private final File file;
-  private final byte[] md5hash;
-  private FileLock fileLock;
-  private final Collection<Function> registeredFunctions = new ArrayList<Function>();
-
-  private final ThreadLocal<Boolean> alreadyScanned = new ThreadLocal<Boolean>();
-
-  // Lock used by ChannelInputStream (inner class) to prevent multiple threads from
-  // trying to use the channel simultaneously.
-  static final ReentrantLock channelLock = new ReentrantLock();
-
-  private byte[] jarByteContent;
-
-  static {
-    MessageDigest md = null;
-    try {
-      md = MessageDigest.getInstance("MD5");
-    } catch (NoSuchAlgorithmException nsaex) {
-      // Failure just means we can't do a simple compare for content equality
-    }
-    messageDigest = md;
-  }
-
-  public JarClassLoader(final File file, final String jarName, byte[] jarBytes) throws IOException {
-    Assert.assertTrue(file != null, "file cannot be null");
-    Assert.assertTrue(jarName != null, "jarName cannot be null");
-
-    final boolean isDebugEnabled = logger.isDebugEnabled();
-    try {
-      @SuppressWarnings("resource")
-      FileInputStream fileInputStream = new FileInputStream(file);
-      this.fileLock = fileInputStream.getChannel().lock(0, file.length(), true);
-
-      if (isDebugEnabled) {
-        logger.debug("Acquired shared file lock w/ channel: {}, for JAR: {}",
-            this.fileLock.channel(), file.getAbsolutePath());
-      }
-
-      if (file.length() == 0) {
-        throw new FileNotFoundException(
-            "JAR file was truncated prior to obtaining a lock: " + jarName);
-      }
-
-      final byte[] fileContent = getJarContent();
-      if (!Arrays.equals(fileContent, jarBytes)) {
-        throw new FileNotFoundException("JAR file: " + file.getAbsolutePath()
-            + ", was modified prior to obtaining a lock: " + jarName);
-      }
-
-      if (!isValidJarContent(jarBytes)) {
-        if (this.fileLock != null) {
-          this.fileLock.release();
-          this.fileLock.channel().close();
-          if (isDebugEnabled) {
-            logger.debug(
-                "Prematurely releasing shared file lock due to bad content for JAR file: {}, w/ channel: {}",
-                file.getAbsolutePath(), this.fileLock.channel());
-          }
-        }
-        throw new IllegalArgumentException(
-            "File does not contain valid JAR content: " + file.getAbsolutePath());
-      }
-
-      Assert.assertTrue(jarBytes != null, "jarBytes cannot be null");
-
-      // Temporarily save the contents of the JAR file until they can be processed by the
-      // loadClassesandRegisterFunctions() method.
-      this.jarByteContent = jarBytes;
-
-      if (messageDigest != null) {
-        this.md5hash = messageDigest.digest(this.jarByteContent);
-      } else {
-        this.md5hash = null;
-      }
-
-      this.file = file;
-      this.jarName = jarName;
-
-    } catch (FileNotFoundException fnfex) {
-      if (this.fileLock != null) {
-        this.fileLock.release();
-        this.fileLock.channel().close();
-        if (isDebugEnabled) {
-          logger.debug(
-              "Prematurely releasing shared file lock due to file not found for JAR file: {}, w/ channel: {}",
-              file.getAbsolutePath(), this.fileLock.channel());
-        }
-      }
-      throw fnfex;
-    }
-  }
-
-  /**
-   * Peek into the JAR data and make sure that it is valid JAR content.
-   * 
-   * @param inputStream InputStream containing data to be validated.
-   * 
-   * @return True if the data has JAR content, false otherwise
-   */
-  private static boolean hasValidJarContent(final InputStream inputStream) {
-    JarInputStream jarInputStream = null;
-    boolean valid = false;
-
-    try {
-      jarInputStream = new JarInputStream(inputStream);
-      valid = (jarInputStream.getNextJarEntry() != null);
-    } catch (IOException ignore) {
-      // Ignore this exception and just return false
-    } finally {
-      try {
-        jarInputStream.close();
-      } catch (IOException ioex) {
-        // Ignore this exception and just return result
-      }
-    }
-
-    return valid;
-  }
-
-  /**
-   * Peek into the JAR data and make sure that it is valid JAR content.
-   * 
-   * @param jarBytes Bytes of data to be validated.
-   * 
-   * @return True if the data has JAR content, false otherwise
-   */
-  public static boolean isValidJarContent(final byte[] jarBytes) {
-    return hasValidJarContent(new ByteArrayInputStream(jarBytes));
-  }
-
-  /**
-   * Peek into the JAR data and make sure that it is valid JAR content.
-   * 
-   * @param jarFile File whose contents should be validated.
-   * 
-   * @return True if the data has JAR content, false otherwise
-   */
-  public static boolean hasValidJarContent(final File jarFile) {
-    try {
-      return hasValidJarContent(new FileInputStream(jarFile));
-    } catch (IOException ioex) {
-      return false;
-    }
-  }
-
-  /**
-   * Scan the JAR file and attempt to load all classes and register any function classes found.
-   */
-  // This method will process the contents of the JAR file as stored in this.jarByteContent
-  // instead of reading from the original JAR file. This is done because we can't open up
-  // the original file and then close it without releasing the shared lock that was obtained
-  // in the constructor. Once this method is finished, all classes will have been loaded and
-  // there will no longer be a need to hang on to the original contents so they will be
-  // discarded.
-  public synchronized void loadClassesAndRegisterFunctions() throws ClassNotFoundException {
-    final boolean isDebugEnabled = logger.isDebugEnabled();
-    if (isDebugEnabled) {
-      logger.debug("Registering functions with JarClassLoader: {}", this);
-    }
-
-    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.jarByteContent);
-
-    JarInputStream jarInputStream = null;
-    try {
-      jarInputStream = new JarInputStream(byteArrayInputStream);
-      JarEntry jarEntry = jarInputStream.getNextJarEntry();
-
-      while (jarEntry != null) {
-        if (jarEntry.getName().endsWith(".class")) {
-          if (isDebugEnabled) {
-            logger.debug("Attempting to load class: {}, from JAR file: {}", jarEntry.getName(),
-                this.file.getAbsolutePath());
-          }
-
-          final String className = jarEntry.getName().replaceAll("/", "\\.").substring(0,
-              (jarEntry.getName().length() - 6));
-          try {
-            Class<?> clazz = loadClass(className, true, false);
-            Collection<Function> registerableFunctions = getRegisterableFunctionsFromClass(clazz);
-            for (Function function : registerableFunctions) {
-              FunctionService.registerFunction(function);
-              if (isDebugEnabled) {
-                logger.debug("Registering function class: {}, from JAR file: {}", className,
-                    this.file.getAbsolutePath());
-              }
-              this.registeredFunctions.add(function);
-            }
-          } catch (ClassNotFoundException cnfex) {
-            logger.error("Unable to load all classes from JAR file: {}",
-                this.file.getAbsolutePath(), cnfex);
-            throw cnfex;
-          } catch (NoClassDefFoundError ncdfex) {
-            logger.error("Unable to load all classes from JAR file: {}",
-                this.file.getAbsolutePath(), ncdfex);
-            throw ncdfex;
-          }
-        }
-        jarEntry = jarInputStream.getNextJarEntry();
-      }
-    } catch (IOException ioex) {
-      logger.error("Exception when trying to read class from ByteArrayInputStream", ioex);
-    } finally {
-      if (jarInputStream != null) {
-        try {
-          jarInputStream.close();
-        } catch (IOException ioex) {
-          logger.error("Exception attempting to close JAR input stream", ioex);
-        }
-      }
-    }
-    this.jarByteContent = new byte[0];
-  }
-
-  synchronized void cleanUp() {
-    for (Function function : this.registeredFunctions) {
-      FunctionService.unregisterFunction(function.getId());
-    }
-    this.registeredFunctions.clear();
-
-    try {
-      TypeRegistry typeRegistry =
-          ((GemFireCacheImpl) CacheFactory.getAnyInstance()).getPdxRegistry();
-      if (typeRegistry != null) {
-        typeRegistry.flushCache();
-      }
-    } catch (CacheClosedException ccex) {
-      // That's okay, it just means there was nothing to flush to begin with
-    }
-
-    try {
-      this.fileLock.release();
-      this.fileLock.channel().close();
-      if (logger.isDebugEnabled()) {
-        logger.debug("Released shared file lock on JAR file: {}, w/ channel: {}",
-            this.file.getAbsolutePath(), this.fileLock.channel());
-      }
-    } catch (IOException ioex) {
-      logger.error("Could not release the shared lock for JAR file: {}",
-          this.file.getAbsolutePath(), ioex);
-    }
-  }
-
-  /**
-   * Uses MD5 hashes to determine if the original byte content of this JarClassLoader is the same as
-   * that past in.
-   * 
-   * @param compareToBytes Bytes to compare the original content to
-   * @return True of the MD5 hash is the same o
-   */
-  public boolean hasSameContent(final byte[] compareToBytes) {
-    // If the MD5 hash can't be calculated then silently return no match
-    if (messageDigest == null || this.md5hash == null) {
-      return false;
-    }
-
-    byte[] compareToMd5 = messageDigest.digest(compareToBytes);
-    if (logger.isDebugEnabled()) {
-      logger.debug("For JAR file: {}, Comparing MD5 hash {} to {}", this.file.getAbsolutePath(),
-          new String(this.md5hash), new String(compareToMd5));
-    }
-    return Arrays.equals(this.md5hash, compareToMd5);
-  }
-
-  private boolean alreadyScanned() {
-    if (this.alreadyScanned.get() == null) {
-      this.alreadyScanned.set(Boolean.FALSE);
-    }
-    return this.alreadyScanned.get();
-  }
-
-  @Override
-  protected URL findResource(String resourceName) {
-    URL returnURL = null;
-    JarInputStream jarInputStream = null;
-
-    try {
-      ChannelInputStream channelInputStream = new ChannelInputStream(this.fileLock.channel());
-      jarInputStream = new JarInputStream(channelInputStream);
-
-      JarEntry jarEntry = jarInputStream.getNextJarEntry();
-      while (jarEntry != null && !jarEntry.getName().equals(resourceName)) {
-        jarEntry = jarInputStream.getNextJarEntry();
-      }
-      if (jarEntry != null) {
-        try {
-          returnURL = new URL("jar", "", this.file.toURI().toURL() + "!/" + jarEntry.getName());
-        } catch (MalformedURLException muex) {
-          logger.error("Could not create resource URL from file URL", muex);
-        }
-      }
-    } catch (IOException ioex) {
-      logger.error("Exception when trying to read class from ByteArrayInputStream", ioex);
-    } finally {
-      if (jarInputStream != null) {
-        try {
-          jarInputStream.close();
-        } catch (IOException ioex) {
-          logger.error("Unable to close JAR input stream when finding resource", ioex);
-        }
-      }
-    }
-
-    return returnURL;
-  }
-
-  @Override
-  protected Enumeration<URL> findResources(final String resourceName) {
-    return new Enumeration<URL>() {
-      private URL element = findResource(resourceName);
-
-      @Override
-      public boolean hasMoreElements() {
-        return this.element != null;
-      }
-
-      @Override
-      public URL nextElement() {
-        if (this.element != null) {
-          URL element = this.element;
-          this.element = null;
-          return element;
-        }
-        throw new NoSuchElementException();
-      }
-    };
-  }
-
-  @Override
-  public Class<?> loadClass(final String className) throws ClassNotFoundException {
-    return (loadClass(className, true));
-  }
-
-  @Override
-  public Class<?> loadClass(final String className, final boolean resolveIt)
-      throws ClassNotFoundException {
-    return loadClass(className, resolveIt, true);
-  }
-
-  Class<?> loadClass(final String className, final boolean resolveIt,
-      final boolean useClassPathLoader) throws ClassNotFoundException {
-    Class<?> clazz = findLoadedClass(className);
-    if (clazz != null) {
-      return clazz;
-    }
-
-    try {
-      clazz = findClass(className);
-      if (resolveIt) {
-        resolveClass(clazz);
-      }
-    } catch (ClassNotFoundException cnfex) {
-      if (!useClassPathLoader) {
-        throw cnfex;
-      }
-    }
-
-    if (clazz == null) {
-      try {
-        this.alreadyScanned.set(true);
-        return forName(className, ClassPathLoader.getLatest().getClassLoaders());
-      } finally {
-        this.alreadyScanned.set(false);
-      }
-    }
-
-    return clazz;
-  }
-
-  // When loadClassesAndRegisterFunctions() is called and it starts to load classes, this method
-  // may be called multiple times to resolve dependencies on other classes in the same or
-  // another JAR file. During this stage, this.jarByteContent will contain the complete contents of
-  // the JAR file and will be used when attempting to resolve these dependencies. Once
-  // loadClassesAndRegisterFunctions() is complete it discards the data in this.jarByteContent.
-  // However, at that point all of the classes available in the JAR file will already have been
-  // loaded. Future calls to loadClass(...) will return the cached Class object for any
-  // classes available in this JAR and findClass(...) will no longer be needed to find them.
-  @Override
-  protected Class<?> findClass(String className) throws ClassNotFoundException {
-    String formattedClassName = className.replaceAll("\\.", "/") + ".class";
-
-    JarInputStream jarInputStream = null;
-    if (this.jarByteContent.length == 0) {
-      throw new ClassNotFoundException(className);
-    }
-
-    try {
-      jarInputStream = new JarInputStream(new ByteArrayInputStream(this.jarByteContent));
-      JarEntry jarEntry = jarInputStream.getNextJarEntry();
-
-      while (jarEntry != null && !jarEntry.getName().equals(formattedClassName)) {
-        jarEntry = jarInputStream.getNextJarEntry();
-      }
-
-      if (jarEntry != null) {
-        byte[] buffer = new byte[1024];
-        ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(buffer.length);
-
-        int bytesRead = -1;
-        while ((bytesRead = jarInputStream.read(buffer)) != -1) {
-          byteOutStream.write(buffer, 0, bytesRead);
-        }
-
-        // Add the package first if it doesn't already exist
-        int lastDotIndex = className.lastIndexOf('.');
-        if (lastDotIndex != -1) {
-          String pkgName = className.substring(0, lastDotIndex);
-          Package pkg = getPackage(pkgName);
-          if (pkg == null) {
-            definePackage(pkgName, null, null, null, null, null, null, null);
-          }
-        }
-
-        byte[] classBytes = byteOutStream.toByteArray();
-
-        synchronized (this.file) {
-          Class<?> clazz = findLoadedClass(className);
-          if (clazz == null) {
-            clazz = defineClass(className, classBytes, 0, classBytes.length, null);
-          }
-          return clazz;
-        }
-      }
-    } catch (IOException ioex) {
-      logger.error("Exception when trying to read class from ByteArrayInputStream", ioex);
-    } finally {
-      if (jarInputStream != null) {
-        try {
-          jarInputStream.close();
-        } catch (IOException ioex) {
-          logger.error("Exception attempting to close JAR input stream", ioex);
-        }
-      }
-    }
-
-    throw new ClassNotFoundException(className);
-  }
-
-  /**
-   * Check to see if the class implements the Function interface. If so, it will be registered with
-   * FunctionService. Also, if the functions's class was originally declared in a cache.xml file
-   * then any properties specified at that time will be reused when re-registering the function.
-   * 
-   * @param clazz Class to check for implementation of the Function class
-   * @return A collection of Objects that implement the Function interface.
-   */
-  private Collection<Function> getRegisterableFunctionsFromClass(Class<?> clazz) {
-    final List<Function> registerableFunctions = new ArrayList<Function>();
-
-    try {
-      if (Function.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {
-        boolean registerUninitializedFunction = true;
-        if (Declarable.class.isAssignableFrom(clazz)) {
-          try {
-            final List<Properties> propertiesList =
-                ((GemFireCacheImpl) CacheFactory.getAnyInstance())
-                    .getDeclarableProperties(clazz.getName());
-
-            if (!propertiesList.isEmpty()) {
-              registerUninitializedFunction = false;
-              // It's possible that the same function was declared multiple times in cache.xml
-              // with different properties. So, register the function using each set of
-              // properties.
-              for (Properties properties : propertiesList) {
-                @SuppressWarnings("unchecked")
-                Function function = newFunction((Class<Function>) clazz, true);
-                if (function != null) {
-                  ((Declarable) function).init(properties);
-                  if (function.getId() != null) {
-                    registerableFunctions.add(function);
-                  }
-                }
-              }
-            }
-          } catch (CacheClosedException ccex) {
-            // That's okay, it just means there were no properties to init the function with
-          }
-        }
-
-        if (registerUninitializedFunction) {
-          @SuppressWarnings("unchecked")
-          Function function = newFunction((Class<Function>) clazz, false);
-          if (function != null && function.getId() != null) {
-            registerableFunctions.add(function);
-          }
-        }
-      }
-    } catch (Exception ex) {
-      logger.error("Attempting to register function from JAR file: " + this.file.getAbsolutePath(),
-          ex);
-    }
-
-    return registerableFunctions;
-  }
-
-  private Class<?> forName(final String className, final Collection<ClassLoader> classLoaders)
-      throws ClassNotFoundException {
-    Class<?> clazz = null;
-
-    for (ClassLoader classLoader : classLoaders) {
-      try {
-        if (classLoader instanceof JarClassLoader) {
-          if (!((JarClassLoader) classLoader).alreadyScanned()) {
-            clazz = ((JarClassLoader) classLoader).loadClass(className, true, false);
-          }
-        } else {
-          clazz = Class.forName(className, true, classLoader);
-        }
-        if (clazz != null) {
-          return clazz;
-        }
-      } catch (SecurityException sex) {
-        // Continue to next ClassLoader
-      } catch (ClassNotFoundException cnfex) {
-        // Continue to next ClassLoader
-      }
-    }
-    throw new ClassNotFoundException(className);
-  }
-
-  private Function newFunction(final Class<Function> clazz, final boolean errorOnNoSuchMethod) {
-    try {
-      final Constructor<Function> constructor = clazz.getConstructor();
-      return constructor.newInstance();
-    } catch (NoSuchMethodException nsmex) {
-      if (errorOnNoSuchMethod) {
-        logger.error("Zero-arg constructor is required, but not found for class: {}",
-            clazz.getName(), nsmex);
-      } else {
-        if (logger.isDebugEnabled()) {
-          logger.debug(
-              "Not registering function because it doesn't have a zero-arg constructor: {}",
-              clazz.getName());
-        }
-      }
-    } catch (SecurityException sex) {
-      logger.error("Zero-arg constructor of function not accessible for class: {}", clazz.getName(),
-          sex);
-    } catch (IllegalAccessException iae) {
-      logger.error("Zero-arg constructor of function not accessible for class: {}", clazz.getName(),
-          iae);
-    } catch (InvocationTargetException ite) {
-      logger.error("Error when attempting constructor for function for class: {}", clazz.getName(),
-          ite);
-    } catch (InstantiationException ie) {
-      logger.error("Unable to instantiate function for class: {}", clazz.getName(), ie);
-    } catch (ExceptionInInitializerError eiiex) {
-      logger.error("Error during function initialization for class: {}", clazz.getName(), eiiex);
-    }
-    return null;
-  }
-
-  private byte[] getJarContent() throws IOException {
-    ChannelInputStream channelInputStream = new ChannelInputStream(this.fileLock.channel());
-    final ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
-    final byte[] bytes = new byte[4096];
-
-    int bytesRead;
-    while (((bytesRead = channelInputStream.read(bytes)) != -1)) {
-      byteOutStream.write(bytes, 0, bytesRead);
-    }
-    channelInputStream.close();
-    return byteOutStream.toByteArray();
-  }
-
-  public String getJarName() {
-    return this.jarName;
-  }
-
-  public String getFileName() {
-    return this.file.getName();
-  }
-
-  public String getFileCanonicalPath() throws IOException {
-    return this.file.getCanonicalPath();
-  }
-
-  @Override
-  public int hashCode() {
-    final int prime = 31;
-    int result = 1;
-    result = prime * result + ((this.jarName == null) ? 0 : this.jarName.hashCode());
-    return result;
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (this == obj)
-      return true;
-    if (obj == null)
-      return false;
-    if (getClass() != obj.getClass())
-      return false;
-    JarClassLoader other = (JarClassLoader) obj;
-    if (this.jarName == null) {
-      if (other.jarName != null)
-        return false;
-    } else if (!this.jarName.equals(other.jarName))
-      return false;
-    return true;
-  }
-
-  @Override
-  public String toString() {
-    final StringBuilder sb = new StringBuilder(getClass().getName());
-    sb.append("@").append(System.identityHashCode(this)).append("{");
-    sb.append("jarName=").append(this.jarName);
-    sb.append(",file=").append(this.file.getAbsolutePath());
-    sb.append(",md5hash=").append(Arrays.toString(this.md5hash));
-    sb.append(",fileLock=").append(this.fileLock);
-    sb.append("}");
-    return sb.toString();
-  }
-
-  /**
-   * When a lock is acquired it is done so through an open file (FileInputStream, etc.). If for any
-   * reason that same file is used to open another input stream, when the second input stream is
-   * closed the file lock will not be held (although an OverlappingFileLock exception will be thrown
-   * if an attempt is made to acquire the lock again). To get around this problem, this class is
-   * used to wrap the original file channel used by the lock with an InputStream. When this class is
-   * instantiated a lock is obtained to prevent other threads from attempting to use the file
-   * channel at the same time. The file channel can then be read as with any other InputStream. When
-   * the input stream is closed, instead of closing the file channel, the lock is released instead.
-   * 
-   * This class is thread safe. However, multiple instances cannot be created by the same thread.
-   * The reason for this is that the lock will be obtained in all cases (it's reentrant), and then
-   * the channel position will be modified by both instances causing arbitrary values being returned
-   * from the read() method.
-   * 
-   * @since GemFire 7.0
-   */
-  private class ChannelInputStream extends InputStream {
-    private final FileChannel fileChannel;
-    private final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1);
-
-    ChannelInputStream(final FileChannel fileChannel) throws IOException {
-      channelLock.lock();
-      this.fileChannel = fileChannel;
-      this.fileChannel.position(0);
-    }
-
-    @Override
-    public int read() throws IOException {
-      this.byteBuffer.rewind();
-      if (this.fileChannel.read(this.byteBuffer) <= 0) {
-        return -1;
-      }
-      this.byteBuffer.rewind();
-      return (this.byteBuffer.get() & 255);
-    }
-
-    @Override
-    public void close() {
-      channelLock.unlock();
-    }
-  }
-}


[8/8] geode git commit: GEODE-2290: Limit scanning of deployed jars

Posted by js...@apache.org.
GEODE-2290: Limit scanning of deployed jars

 - Uses fast-classpath-scanner to scan jars for classes containing Functions without eagerly loading all classes in the jar.


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

Branch: refs/heads/develop
Commit: 6f7f943998153de1f00b1446eb80fa6d304ac40f
Parents: 8f9624d
Author: Jared Stewart <js...@pivotal.io>
Authored: Wed Apr 12 10:53:13 2017 -0700
Committer: Jared Stewart <js...@pivotal.io>
Committed: Sun Apr 16 09:10:02 2017 -0700

----------------------------------------------------------------------
 geode-assembly/build.gradle                     |   1 +
 .../src/test/resources/expected_jars.txt        |   1 +
 geode-core/build.gradle                         |  15 +-
 .../org/apache/geode/internal/DeployedJar.java  |  97 +++---
 .../org/apache/geode/internal/JarDeployer.java  |   7 +-
 .../ClassPathLoaderIntegrationTest.java         |   4 -
 .../geode/internal/DeployedJarJUnitTest.java    | 310 ++++++-------------
 .../internal/JarDeployerIntegrationTest.java    |  18 +-
 .../cache/IncrementalBackupDUnitTest.java       |   6 +-
 .../geode/management/DeployJarTestSuite.java    |   3 +
 .../DeployCommandRedeployDUnitTest.java         |  95 +++---
 .../cli/commands/DeployCommandsDUnitTest.java   |  40 +--
 .../configuration/ClusterConfigBaseTest.java    | 127 --------
 .../ClusterConfigDeployJarDUnitTest.java        |  10 +-
 .../ClusterConfigImportDUnitTest.java           |   3 +-
 .../ClusterConfigStartMemberDUnitTest.java      |   3 +-
 .../configuration/ClusterConfigTestBase.java    | 128 ++++++++
 .../dunit/rules/LocatorServerStartupRule.java   |  12 +
 ...oyCommandRedeployDUnitTest_FunctionATemplate |  30 ++
 ...oyCommandRedeployDUnitTest_FunctionBTemplate |  26 ++
 gradle/dependency-versions.properties           |   1 +
 21 files changed, 439 insertions(+), 498 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-assembly/build.gradle
----------------------------------------------------------------------
diff --git a/geode-assembly/build.gradle b/geode-assembly/build.gradle
index 304a1c4..a4f0c69 100755
--- a/geode-assembly/build.gradle
+++ b/geode-assembly/build.gradle
@@ -139,6 +139,7 @@ def cp = {
         it.contains('commons-io') ||
         it.contains('commons-lang') ||
         it.contains('commons-logging') ||
+        it.contains('fast-classpath-scanner') ||
         it.contains('fastutil') ||
         it.contains('jackson-annotations') ||
         it.contains('jackson-core') ||

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-assembly/src/test/resources/expected_jars.txt
----------------------------------------------------------------------
diff --git a/geode-assembly/src/test/resources/expected_jars.txt b/geode-assembly/src/test/resources/expected_jars.txt
index b7d1dc2..6260167 100644
--- a/geode-assembly/src/test/resources/expected_jars.txt
+++ b/geode-assembly/src/test/resources/expected_jars.txt
@@ -10,6 +10,7 @@ commons-io
 commons-lang
 commons-logging
 commons-modeler
+fast-classpath-scanner
 fastutil
 findbugs-annotations
 gfsh-dependencies.jar

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/build.gradle
----------------------------------------------------------------------
diff --git a/geode-core/build.gradle b/geode-core/build.gradle
index 757599a..f07444a 100755
--- a/geode-core/build.gradle
+++ b/geode-core/build.gradle
@@ -32,7 +32,7 @@ configurations {
 
 dependencies {
    // Source Dependencies
-  // External 
+  // External
   provided files("${System.getProperty('java.home')}/../lib/tools.jar")
   compile 'com.github.stephenc.findbugs:findbugs-annotations:' + project.'stephenc-findbugs.version'
   compile 'org.jgroups:jgroups:' + project.'jgroups.version'
@@ -104,17 +104,22 @@ dependencies {
   }
   compile ('org.iq80.snappy:snappy:' + project.'snappy-java.version') {
     ext.optional = true
-  } 
+  }
 
   compile 'org.apache.shiro:shiro-core:' + project.'shiro.version'
   // This is only added since shiro is using an old version of beanutils and we want
   // to use a standard version. Once shiro deps are updated, remove this explicit dependency
   // in favor of a transitive dependency on beanutils.
   compile 'commons-beanutils:commons-beanutils:' + project.'commons-beanutils.version'
-  
+
+  // https://mvnrepository.com/artifact/io.github.lukehutch/fast-classpath-scanner
+    compile 'io.github.lukehutch:fast-classpath-scanner:' + project.'fast-classpath-scanner.version'
+
+
+
   compile project(':geode-common')
   compile project(':geode-json')
-  
+
   jcaCompile sourceSets.main.output
 
   testCompile project(':geode-junit')
@@ -181,7 +186,7 @@ jar {
 
   from sourceSets.main.output
   from sourceSets.jca.output
-  
+
   exclude 'org/apache/geode/management/internal/web/**'
   exclude 'org/apache/geode/internal/i18n/StringIdResourceBundle_ja.txt'
   exclude 'org/apache/geode/admin/doc-files/ds4_0.dtd'

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java b/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
index 8adec1f..f96863f 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/DeployedJar.java
@@ -14,6 +14,9 @@
  */
 package org.apache.geode.internal;
 
+import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
+import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -25,7 +28,7 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 import java.net.URL;
-import java.nio.channels.FileLock;
+import java.net.URLClassLoader;
 import java.nio.file.Files;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -37,7 +40,6 @@ import java.util.Properties;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
 
-import org.apache.geode.cache.Cache;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.cache.CacheClosedException;
@@ -48,7 +50,6 @@ import org.apache.geode.cache.execute.FunctionService;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.geode.pdx.internal.TypeRegistry;
-import sun.nio.ch.ChannelInputStream;
 
 /**
  * ClassLoader for a single JAR file.
@@ -103,7 +104,7 @@ public class DeployedJar {
           + ", was modified prior to obtaining a lock: " + jarName);
     }
 
-    if (!isValidJarContent(getJarContent())) {
+    if (!hasValidJarContent(getJarContent())) {
       throw new IllegalArgumentException(
           "File does not contain valid JAR content: " + versionedJarFile.getAbsolutePath());
     }
@@ -147,23 +148,10 @@ public class DeployedJar {
    * @param jarBytes Bytes of data to be validated.
    * @return True if the data has JAR content, false otherwise
    */
-  public static boolean isValidJarContent(final byte[] jarBytes) {
+  public static boolean hasValidJarContent(final byte[] jarBytes) {
     return hasValidJarContent(new ByteArrayInputStream(jarBytes));
   }
 
-  /**
-   * Peek into the JAR data and make sure that it is valid JAR content.
-   * 
-   * @param jarFile File whose contents should be validated.
-   * @return True if the data has JAR content, false otherwise
-   */
-  public static boolean hasValidJarContent(final File jarFile) {
-    try {
-      return hasValidJarContent(new FileInputStream(jarFile));
-    } catch (IOException ioex) {
-      return false;
-    }
-  }
 
   /**
    * Scan the JAR file and attempt to load all classes and register any function classes found.
@@ -184,37 +172,42 @@ public class DeployedJar {
 
     JarInputStream jarInputStream = null;
     try {
+      List<String> functionClasses = findFunctionsInThisJar();
+
       jarInputStream = new JarInputStream(byteArrayInputStream);
       JarEntry jarEntry = jarInputStream.getNextJarEntry();
 
       while (jarEntry != null) {
         if (jarEntry.getName().endsWith(".class")) {
-          if (isDebugEnabled) {
-            logger.debug("Attempting to load class: {}, from JAR file: {}", jarEntry.getName(),
-                this.file.getAbsolutePath());
-          }
-
           final String className = jarEntry.getName().replaceAll("/", "\\.").substring(0,
               (jarEntry.getName().length() - 6));
-          try {
-            Class<?> clazz = ClassPathLoader.getLatest().forName(className);
-            Collection<Function> registerableFunctions = getRegisterableFunctionsFromClass(clazz);
-            for (Function function : registerableFunctions) {
-              FunctionService.registerFunction(function);
-              if (isDebugEnabled) {
-                logger.debug("Registering function class: {}, from JAR file: {}", className,
-                    this.file.getAbsolutePath());
+
+          if (functionClasses.contains(className)) {
+            if (isDebugEnabled) {
+              logger.debug("Attempting to load class: {}, from JAR file: {}", jarEntry.getName(),
+                  this.file.getAbsolutePath());
+            }
+            try {
+              Class<?> clazz = ClassPathLoader.getLatest().forName(className);
+              Collection<Function> registerableFunctions = getRegisterableFunctionsFromClass(clazz);
+              for (Function function : registerableFunctions) {
+                FunctionService.registerFunction(function);
+                if (isDebugEnabled) {
+                  logger.debug("Registering function class: {}, from JAR file: {}", className,
+                      this.file.getAbsolutePath());
+                }
+                this.registeredFunctions.add(function);
               }
-              this.registeredFunctions.add(function);
+            } catch (ClassNotFoundException | NoClassDefFoundError cnfex) {
+              logger.error("Unable to load all classes from JAR file: {}",
+                  this.file.getAbsolutePath(), cnfex);
+              throw cnfex;
+            }
+          } else {
+            if (isDebugEnabled) {
+              logger.debug("No functions found in class: {}, from JAR file: {}", jarEntry.getName(),
+                  this.file.getAbsolutePath());
             }
-          } catch (ClassNotFoundException cnfex) {
-            logger.error("Unable to load all classes from JAR file: {}",
-                this.file.getAbsolutePath(), cnfex);
-            throw cnfex;
-          } catch (NoClassDefFoundError ncdfex) {
-            logger.error("Unable to load all classes from JAR file: {}",
-                this.file.getAbsolutePath(), ncdfex);
-            throw ncdfex;
           }
         }
         jarEntry = jarInputStream.getNextJarEntry();
@@ -327,6 +320,15 @@ public class DeployedJar {
     return registerableFunctions;
   }
 
+  private List<String> findFunctionsInThisJar() throws IOException {
+    URLClassLoader urlClassLoader =
+        new URLClassLoader(new URL[] {this.getFile().getCanonicalFile().toURL()});
+    FastClasspathScanner fastClasspathScanner = new FastClasspathScanner()
+        .removeTemporaryFilesAfterScan(true).overrideClassLoaders(urlClassLoader);
+    ScanResult scanResult = fastClasspathScanner.scan();
+    return scanResult.getNamesOfClassesImplementing(Function.class);
+  }
+
   private Function newFunction(final Class<Function> clazz, final boolean errorOnNoSuchMethod) {
     try {
       final Constructor<Function> constructor = clazz.getConstructor();
@@ -342,20 +344,11 @@ public class DeployedJar {
               clazz.getName());
         }
       }
-    } catch (SecurityException sex) {
-      logger.error("Zero-arg constructor of function not accessible for class: {}", clazz.getName(),
-          sex);
-    } catch (IllegalAccessException iae) {
-      logger.error("Zero-arg constructor of function not accessible for class: {}", clazz.getName(),
-          iae);
-    } catch (InvocationTargetException ite) {
+    } catch (Exception ex) {
       logger.error("Error when attempting constructor for function for class: {}", clazz.getName(),
-          ite);
-    } catch (InstantiationException ie) {
-      logger.error("Unable to instantiate function for class: {}", clazz.getName(), ie);
-    } catch (ExceptionInInitializerError eiiex) {
-      logger.error("Error during function initialization for class: {}", clazz.getName(), eiiex);
+          ex);
     }
+
     return null;
   }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java b/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
index da4c136..a65cd0f 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
@@ -26,7 +26,6 @@ import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.Serializable;
@@ -35,7 +34,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -51,7 +49,6 @@ import java.util.stream.Stream;
 public class JarDeployer implements Serializable {
   private static final long serialVersionUID = 1L;
   private static final Logger logger = LogService.getLogger();
-  public static final String JAR_PREFIX = "";
   public static final String JAR_PREFIX_FOR_REGEX = "";
   private static final Lock lock = new ReentrantLock();
 
@@ -438,7 +435,7 @@ public class JarDeployer implements Serializable {
     Optional<File> latestValidDeployedJarOptional =
         Arrays.stream(jarFiles).filter(Objects::nonNull).filter(jarFile -> {
           try {
-            return DeployedJar.isValidJarContent(FileUtils.readFileToByteArray(jarFile));
+            return DeployedJar.hasValidJarContent(FileUtils.readFileToByteArray(jarFile));
           } catch (IOException e) {
             return false;
           }
@@ -501,7 +498,7 @@ public class JarDeployer implements Serializable {
     DeployedJar[] deployedJars = new DeployedJar[jarNames.length];
 
     for (int i = 0; i < jarNames.length; i++) {
-      if (!DeployedJar.isValidJarContent(jarBytes[i])) {
+      if (!DeployedJar.hasValidJarContent(jarBytes[i])) {
         throw new IllegalArgumentException(
             "File does not contain valid JAR content: " + jarNames[i]);
       }

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
index 14108c7..c783318 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
@@ -66,7 +66,6 @@ public class ClassPathLoaderIntegrationTest {
 
   private File tempFile;
   private File tempFile2;
-  private File extLibsDir;
 
   @Rule
   public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
@@ -78,9 +77,6 @@ public class ClassPathLoaderIntegrationTest {
   public void setUp() throws Exception {
     System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, "false");
 
-    extLibsDir = new File(this.temporaryFolder.getRoot(), "ext");
-    extLibsDir.mkdirs();
-
     this.tempFile = this.temporaryFolder.newFile("tempFile1.tmp");
     FileOutputStream fos = new FileOutputStream(this.tempFile);
     fos.write(new byte[TEMP_FILE_BYTES_COUNT]);

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java
index 7216463..5e7c40f 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/DeployedJarJUnitTest.java
@@ -14,12 +14,9 @@
  */
 package org.apache.geode.internal;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionContext;
@@ -27,6 +24,7 @@ import org.apache.geode.cache.execute.FunctionService;
 import org.apache.geode.cache.execute.ResultSender;
 import org.apache.geode.internal.cache.execute.FunctionContextImpl;
 import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.awaitility.Awaitility;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -43,17 +41,12 @@ import java.io.OutputStream;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Random;
-import java.util.concurrent.BrokenBarrierException;
-import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 
-/**
- * TODO: Need to fix this testDeclarableFunctionsWithParms and testClassOnClasspath on Windows:
- */
 @Category(IntegrationTest.class)
 public class DeployedJarJUnitTest {
   @Rule
@@ -62,13 +55,13 @@ public class DeployedJarJUnitTest {
   @Rule
   public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
 
-  private final ClassBuilder classBuilder = new ClassBuilder();
+  private ClassBuilder classBuilder;
 
   @Before
   public void setup() throws Exception {
     File workingDir = temporaryFolder.newFolder();
-
     ClassPathLoader.setLatestToDefault(workingDir);
+    classBuilder = new ClassBuilder();
   }
 
   @After
@@ -82,13 +75,14 @@ public class DeployedJarJUnitTest {
 
   @Test
   public void testIsValidJarContent() throws IOException {
-    assertTrue(
-        DeployedJar.isValidJarContent(this.classBuilder.createJarFromName("JarClassLoaderJUnitA")));
+    assertThat(
+        DeployedJar.hasValidJarContent(this.classBuilder.createJarFromName("JarClassLoaderJUnitA")))
+            .isTrue();
   }
 
   @Test
   public void testIsInvalidJarContent() {
-    assertFalse(DeployedJar.isValidJarContent("INVALID JAR CONTENT".getBytes()));
+    assertThat(DeployedJar.hasValidJarContent("INVALID JAR CONTENT".getBytes())).isFalse();
   }
 
   @Test
@@ -99,11 +93,7 @@ public class DeployedJarJUnitTest {
             "package com.jcljunit; public class JarClassLoaderJUnitA {}");
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
 
-    try {
-      ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitA");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
+    ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitA");
 
     // Update the JAR file and make sure the first class is no longer on the Classpath
     // and the second one is.
@@ -111,54 +101,36 @@ public class DeployedJarJUnitTest {
         "package com.jcljunit; public class JarClassLoaderJUnitB {}");
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
 
-    try {
-      ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitB");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    try {
-      ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitA");
-      fail("Class should not be found on Classpath");
-    } catch (ClassNotFoundException expected) { // expected
-    }
-
+    ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitB");
+    assertThatThrownBy(
+        () -> ClassPathLoader.getLatest().forName("com.jcljunit.JarClassLoaderJUnitA"))
+            .isInstanceOf(ClassNotFoundException.class);
   }
 
   @Test
   public void testFailingCompilation() throws Exception {
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import org.apache.geode.cache.Declarable;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer.append("public class JarClassLoaderJUnitFunction implements Function {}");
-    String functionString = stringBuffer.toString();
-
-    try {
-      this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
-      fail("This code should have failed to compile and thrown an exception");
-    } catch (Exception ex) {
-      // All good
-    }
+    String functionString = "import org.apache.geode.cache.Declarable;"
+        + "import org.apache.geode.cache.execute.Function;"
+        + "import org.apache.geode.cache.execute.FunctionContext;"
+        + "public class JarClassLoaderJUnitFunction implements Function {}";
+
+    assertThatThrownBy(() -> this.classBuilder
+        .createJarFromClassContent("JarClassLoaderJUnitFunction", functionString)).isNotNull();
   }
 
   @Test
-  public void testFunctions() throws IOException, ClassNotFoundException {
+  public void testFunctions() throws Exception {
     // Test creating a JAR file with a function
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import java.util.Properties;");
-    stringBuffer.append("import org.apache.geode.cache.Declarable;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer.append("public class JarClassLoaderJUnitFunction implements Function {");
-    stringBuffer.append("public void init(Properties props) {}");
-    stringBuffer.append("public boolean hasResult() {return true;}");
-    stringBuffer.append(
-        "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"GOODv1\");}");
-    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}");
-    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
-    stringBuffer.append("public boolean isHA() {return false;}}");
-    String functionString = stringBuffer.toString();
+    String functionString =
+        "import java.util.Properties;" + "import org.apache.geode.cache.Declarable;"
+            + "import org.apache.geode.cache.execute.Function;"
+            + "import org.apache.geode.cache.execute.FunctionContext;"
+            + "public class JarClassLoaderJUnitFunction implements Function {"
+            + "public void init(Properties props) {}" + "public boolean hasResult() {return true;}"
+            + "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"GOODv1\");}"
+            + "public String getId() {return \"JarClassLoaderJUnitFunction\";}"
+            + "public boolean optimizeForWrite() {return false;}"
+            + "public boolean isHA() {return false;}}";
 
     byte[] jarBytes =
         this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
@@ -166,11 +138,11 @@ public class DeployedJarJUnitTest {
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
 
     Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNotNull(function);
+    assertThat(function).isNotNull();
     TestResultSender resultSender = new TestResultSender();
     FunctionContext functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
     function.execute(functionContext);
-    assertEquals("GOODv1", (String) resultSender.getResults());
+    assertThat(resultSender.getResults()).isEqualTo("GOODv1");
 
     // Test updating the function with a new JAR file
     functionString = functionString.replace("v1", "v2");
@@ -179,11 +151,11 @@ public class DeployedJarJUnitTest {
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
 
     function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNotNull(function);
+    assertThat(function).isNotNull();
     resultSender = new TestResultSender();
     functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
     function.execute(functionContext);
-    assertEquals("GOODv2", (String) resultSender.getResults());
+    assertThat(resultSender.getResults()).isEqualTo("GOODv2");
 
     // Test returning null for the Id
     String functionNullIdString =
@@ -192,88 +164,71 @@ public class DeployedJarJUnitTest {
         functionNullIdString);
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnit.jar", jarBytes);
 
-    assertNull(FunctionService.getFunction("JarClassLoaderJUnitFunction"));
+    assertThat(FunctionService.getFunction("JarClassLoaderJUnitFunction")).isNull();
 
     // Test removing the JAR
     ClassPathLoader.getLatest().getJarDeployer().undeploy("JarClassLoaderJUnit.jar");
-    assertNull(FunctionService.getFunction("JarClassLoaderJUnitFunction"));
+    assertThat(FunctionService.getFunction("JarClassLoaderJUnitFunction")).isNull();
   }
 
   /**
    * Ensure that abstract functions aren't added to the Function Service.
    */
   @Test
-  public void testAbstractFunction() throws IOException, ClassNotFoundException {
+  public void testAbstractFunction() throws Exception {
     // Add an abstract Function to the Classpath
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("public abstract class JarClassLoaderJUnitFunction implements Function {");
-    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunction\";}}");
-    String functionString = stringBuffer.toString();
+    String functionString = "import org.apache.geode.cache.execute.Function;"
+        + "public abstract class JarClassLoaderJUnitFunction implements Function {"
+        + "public String getId() {return \"JarClassLoaderJUnitFunction\";}}";
 
     byte[] jarBytes =
         this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitFunction", functionString);
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitFunction.jar",
         jarBytes);
 
-    try {
-      ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunction");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
+    ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunction");
 
     Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNull(function);
+    assertThat(function).isNull();
   }
 
   @Test
   public void testDeclarableFunctionsWithNoCacheXml() throws Exception {
-
     final String jarName = "JarClassLoaderJUnitNoXml.jar";
 
     // Add a Declarable Function without parameters for the class to the Classpath
-    StringBuffer stringBuffer = new StringBuffer();
-    stringBuffer.append("import java.util.Properties;");
-    stringBuffer.append("import org.apache.geode.cache.Declarable;");
-    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
-    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
-    stringBuffer
-        .append("public class JarClassLoaderJUnitFunctionNoXml implements Function, Declarable {");
-    stringBuffer.append("public String getId() {return \"JarClassLoaderJUnitFunctionNoXml\";}");
-    stringBuffer.append("public void init(Properties props) {}");
-    stringBuffer.append(
-        "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"NOPARMSv1\");}");
-    stringBuffer.append("public boolean hasResult() {return true;}");
-    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
-    stringBuffer.append("public boolean isHA() {return false;}}");
-    String functionString = stringBuffer.toString();
+    String functionString =
+        "import java.util.Properties;" + "import org.apache.geode.cache.Declarable;"
+            + "import org.apache.geode.cache.execute.Function;"
+            + "import org.apache.geode.cache.execute.FunctionContext;"
+            + "public class JarClassLoaderJUnitFunctionNoXml implements Function, Declarable {"
+            + "public String getId() {return \"JarClassLoaderJUnitFunctionNoXml\";}"
+            + "public void init(Properties props) {}"
+            + "public void execute(FunctionContext context) {context.getResultSender().lastResult(\"NOPARMSv1\");}"
+            + "public boolean hasResult() {return true;}"
+            + "public boolean optimizeForWrite() {return false;}"
+            + "public boolean isHA() {return false;}}";
 
     byte[] jarBytes = this.classBuilder
         .createJarFromClassContent("JarClassLoaderJUnitFunctionNoXml", functionString);
 
     ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarBytes);
 
-    try {
-      ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunctionNoXml");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
+    ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunctionNoXml");
 
     // Check to see if the function without parameters executes correctly
     Function function = FunctionService.getFunction("JarClassLoaderJUnitFunctionNoXml");
-    assertNotNull(function);
+    assertThat(function).isNotNull();
     TestResultSender resultSender = new TestResultSender();
     function.execute(new FunctionContextImpl(function.getId(), null, resultSender));
-    assertEquals("NOPARMSv1", (String) resultSender.getResults());
+    assertThat((String) resultSender.getResults()).isEqualTo("NOPARMSv1");
   }
 
   @Test
-  public void testDependencyBetweenJars() throws IOException, ClassNotFoundException {
+  public void testDependencyBetweenJars() throws Exception {
     final File parentJarFile = temporaryFolder.newFile("JarClassLoaderJUnitParent.jar");
     final File usesJarFile = temporaryFolder.newFile("JarClassLoaderJUnitUses.jar");
 
-    JarDeployer jarDeployer = ClassPathLoader.getLatest().getJarDeployer();
-
     // Write out a JAR files.
     StringBuffer stringBuffer = new StringBuffer();
     stringBuffer.append("package jcljunit.parent;");
@@ -319,17 +274,15 @@ public class DeployedJarJUnitTest {
     jarBytes = functionClassBuilder.createJarFromClassContent(
         "jcljunit/function/JarClassLoaderJUnitFunction", stringBuffer.toString());
 
-
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitFunction.jar",
         jarBytes);
 
-
     Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
-    assertNotNull(function);
+    assertThat(function).isNotNull();
     TestResultSender resultSender = new TestResultSender();
     FunctionContext functionContext = new FunctionContextImpl(function.getId(), null, resultSender);
     function.execute(functionContext);
-    assertEquals("PARENT:USES", (String) resultSender.getResults());
+    assertThat((String) resultSender.getResults()).isEqualTo("PARENT:USES");
   }
 
   @Test
@@ -342,39 +295,26 @@ public class DeployedJarJUnitTest {
         jarBytes);
 
     InputStream inputStream = ClassPathLoader.getLatest().getResourceAsStream(fileName);
-    assertNotNull(inputStream);
+    assertThat(inputStream).isNotNull();
 
     final byte[] fileBytes = new byte[fileContent.length()];
     inputStream.read(fileBytes);
     inputStream.close();
-    assertTrue(fileContent.equals(new String(fileBytes)));
+    assertThat(fileContent).isEqualTo(new String(fileBytes));
   }
 
   @Test
-  public void testUpdateClassInJar() throws IOException, ClassNotFoundException {
+  public void testUpdateClassInJar() throws Exception {
     // First use of the JAR file
     byte[] jarBytes = this.classBuilder.createJarFromClassContent("JarClassLoaderJUnitTestClass",
         "public class JarClassLoaderJUnitTestClass { public Integer getValue5() { return new Integer(5); } }");
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUpdate.jar", jarBytes);
 
-    try {
-      Class<?> clazz = ClassPathLoader.getLatest().forName("JarClassLoaderJUnitTestClass");
-      Object object = clazz.newInstance();
-      Method getValue5Method = clazz.getMethod("getValue5", new Class[] {});
-      Integer value = (Integer) getValue5Method.invoke(object, new Object[] {});
-      assertEquals(value.intValue(), 5);
-
-    } catch (InvocationTargetException itex) {
-      fail("JAR file not correctly added to Classpath" + itex);
-    } catch (NoSuchMethodException nsmex) {
-      fail("JAR file not correctly added to Classpath" + nsmex);
-    } catch (InstantiationException iex) {
-      fail("JAR file not correctly added to Classpath" + iex);
-    } catch (IllegalAccessException iaex) {
-      fail("JAR file not correctly added to Classpath" + iaex);
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath" + cnfex);
-    }
+    Class<?> clazz = ClassPathLoader.getLatest().forName("JarClassLoaderJUnitTestClass");
+    Object object = clazz.newInstance();
+    Method getValue5Method = clazz.getMethod("getValue5");
+    Integer value = (Integer) getValue5Method.invoke(object);
+    assertThat(value).isEqualTo(5);
 
     // Now create an updated JAR file and make sure that the method from the new
     // class is available.
@@ -382,29 +322,15 @@ public class DeployedJarJUnitTest {
         "public class JarClassLoaderJUnitTestClass { public Integer getValue10() { return new Integer(10); } }");
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUpdate.jar", jarBytes);
 
-
-    try {
-      Class<?> clazz = ClassPathLoader.getLatest().forName("JarClassLoaderJUnitTestClass");
-      Object object = clazz.newInstance();
-      Method getValue10Method = clazz.getMethod("getValue10", new Class[] {});
-      Integer value = (Integer) getValue10Method.invoke(object, new Object[] {});
-      assertEquals(value.intValue(), 10);
-
-    } catch (InvocationTargetException itex) {
-      fail("JAR file not correctly added to Classpath" + itex);
-    } catch (NoSuchMethodException nsmex) {
-      fail("JAR file not correctly added to Classpath" + nsmex);
-    } catch (InstantiationException iex) {
-      fail("JAR file not correctly added to Classpath" + iex);
-    } catch (IllegalAccessException iaex) {
-      fail("JAR file not correctly added to Classpath" + iaex);
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath" + cnfex);
-    }
+    clazz = ClassPathLoader.getLatest().forName("JarClassLoaderJUnitTestClass");
+    object = clazz.newInstance();
+    Method getValue10Method = clazz.getMethod("getValue10");
+    value = (Integer) getValue10Method.invoke(object);
+    assertThat(value).isEqualTo(10);
   }
 
   @Test
-  public void testMultiThread() throws IOException, ClassNotFoundException {
+  public void testMultiThreadingDoesNotCauseDeadlock() throws Exception {
     // Add two JARs to the classpath
     byte[] jarBytes = this.classBuilder.createJarFromName("JarClassLoaderJUnitA");
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitA.jar", jarBytes);
@@ -416,54 +342,32 @@ public class DeployedJarJUnitTest {
     String[] classNames = new String[] {"JarClassLoaderJUnitA", "com.jcljunit.JarClassLoaderJUnitB",
         "NON-EXISTENT CLASS"};
 
-    // Spawn some threads which try to instantiate these classes
     final int threadCount = 10;
-    final int numLoops = 1000;
-    final CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount + 1);
+    ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
     for (int i = 0; i < threadCount; i++) {
-      new ForNameExerciser(cyclicBarrier, numLoops, classNames).start();
+      executorService.submit(new ForNameExerciser(classNames));
     }
 
-    // Wait for all of the threads to be ready
-    try {
-      cyclicBarrier.await();
-    } catch (InterruptedException iex) {
-      fail("Interrupted while waiting for barrier");
-    } catch (BrokenBarrierException bbex) {
-      fail("Broken barrier while waiting");
-    }
+    executorService.shutdown();
+    Awaitility.await().atMost(60, TimeUnit.SECONDS).until(executorService::isTerminated);
 
-    // Loop while each thread tries N times to instantiate a non-existent class
-    for (int i = 0; i < numLoops; i++) {
-      try {
-        cyclicBarrier.await(5, TimeUnit.SECONDS);
-      } catch (InterruptedException iex) {
-        fail("Interrupted while waiting for barrier");
-      } catch (TimeoutException tex) {
-        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
-        long[] threadIds = threadMXBean.findDeadlockedThreads();
-
-        if (threadIds != null) {
-          StringBuffer deadLockTrace = new StringBuffer();
-          for (long threadId : threadIds) {
-            ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, 100);
-            deadLockTrace.append(threadInfo.getThreadName()).append("\n");
-            for (StackTraceElement stackTraceElem : threadInfo.getStackTrace()) {
-              deadLockTrace.append("\t").append(stackTraceElem).append("\n");
-            }
-          }
-
-          fail("Deadlock with trace:\n" + deadLockTrace.toString());
-        }
+    ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
+    long[] threadIds = threadMXBean.findDeadlockedThreads();
 
-        fail("Timeout while waiting for barrier - no deadlock detected");
-      } catch (BrokenBarrierException bbex) {
-        fail("Broken barrier while waiting");
+    if (threadIds != null) {
+      StringBuilder deadLockTrace = new StringBuilder();
+      for (long threadId : threadIds) {
+        ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, 100);
+        deadLockTrace.append(threadInfo.getThreadName()).append("\n");
+        for (StackTraceElement stackTraceElem : threadInfo.getStackTrace()) {
+          deadLockTrace.append("\t").append(stackTraceElem).append("\n");
+        }
       }
+      System.out.println(deadLockTrace);
     }
+    assertThat(threadIds).isNull();
   }
 
-
   private void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
     final OutputStream outStream = new FileOutputStream(jarFile);
     outStream.write(jarBytes);
@@ -497,40 +401,24 @@ public class DeployedJarJUnitTest {
 
   static final Random random = new Random();
 
-  private class ForNameExerciser extends Thread {
-    private final CyclicBarrier cyclicBarrier;
-    private final int numLoops;
+  private class ForNameExerciser implements Runnable {
+    private final int numLoops = 1000;
     private final String[] classNames;
 
-    ForNameExerciser(final CyclicBarrier cyclicBarrier, final int numLoops,
-        final String[] classNames) {
-      this.cyclicBarrier = cyclicBarrier;
-      this.numLoops = numLoops;
+    ForNameExerciser(final String[] classNames) {
       this.classNames = classNames;
     }
 
     @Override
     public void run() {
-      try {
-        this.cyclicBarrier.await();
-      } catch (InterruptedException iex) {
-        fail("Interrupted while waiting for latch");
-      } catch (BrokenBarrierException bbex) {
-        fail("Broken barrier while waiting");
-      }
       for (int i = 0; i < this.numLoops; i++) {
         try {
           // Random select a name from the list of class names and try to load it
           String className = this.classNames[random.nextInt(this.classNames.length)];
           ClassPathLoader.getLatest().forName(className);
         } catch (ClassNotFoundException expected) { // expected
-        }
-        try {
-          this.cyclicBarrier.await();
-        } catch (InterruptedException iex) {
-          fail("Interrupted while waiting for barrrier");
-        } catch (BrokenBarrierException bbex) {
-          fail("Broken barrier while waiting");
+        } catch (Exception e) {
+          throw new RuntimeException(e);
         }
       }
     }

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
index e9af0e7..b81e3e9 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerIntegrationTest.java
@@ -21,6 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.awaitility.Awaitility;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -34,6 +35,7 @@ import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -94,8 +96,6 @@ public class JarDeployerIntegrationTest {
     alternateDir.delete();
 
     final JarDeployer jarDeployer = new JarDeployer(alternateDir);
-
-    final CyclicBarrier barrier = new CyclicBarrier(2);
     final byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDTID");
 
     // Test to verify that deployment fails if the directory doesn't exist.
@@ -106,16 +106,20 @@ public class JarDeployerIntegrationTest {
     // Test to verify that deployment succeeds if the directory doesn't
     // initially exist, but is then created while the JarDeployer is looping
     // looking for a valid directory.
-    Future<Boolean> done = Executors.newSingleThreadExecutor().submit(() -> {
+    final AtomicBoolean isDeployed = new AtomicBoolean(false);
+    final CyclicBarrier barrier = new CyclicBarrier(2);
+
+    Executors.newSingleThreadExecutor().submit(() -> {
       barrier.await();
       jarDeployer.deployWithoutRegistering("JarDeployerIntegrationTest.jar", jarBytes);
+      isDeployed.set(true);
       return true;
     });
 
     barrier.await();
-    Thread.sleep(500);
-    alternateDir.mkdir();
-    assertThat(done.get(2, TimeUnit.MINUTES)).isTrue();
+    alternateDir.mkdirs();
+    Awaitility.await().atMost(1, TimeUnit.MINUTES)
+        .until(() -> assertThat(isDeployed.get()).isTrue());
   }
 
   @Test
@@ -137,7 +141,7 @@ public class JarDeployerIntegrationTest {
   @Test
   public void testVersionNumberMatcher() throws IOException {
     int version =
-        jarDeployer.extractVersionFromFilename(temporaryFolder.newFile("MyJar.v1.jar").getName());
+        JarDeployer.extractVersionFromFilename(temporaryFolder.newFile("MyJar.v1.jar").getName());
 
     assertThat(version).isEqualTo(1);
   }

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java
index dcbbeb0..0dbe75f 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/cache/IncrementalBackupDUnitTest.java
@@ -1103,7 +1103,7 @@ public class IncrementalBackupDUnitTest extends JUnit4CacheTestCase {
       }
     });
 
-    assert (deployedJarFile.exists());
+    assertTrue(deployedJarFile.exists());
     /*
      * Perform backup. Make sure it is successful.
      */
@@ -1138,7 +1138,7 @@ public class IncrementalBackupDUnitTest extends JUnit4CacheTestCase {
     /*
      * Cleanup "dummy" jar from file system.
      */
-    Pattern pattern = Pattern.compile("^" + JarDeployer.JAR_PREFIX + jarName + ".*#\\d++$");
+    Pattern pattern = Pattern.compile("^" + jarName + ".*#\\d++$");
     deleteMatching(new File("."), pattern);
 
     // Execute the restore
@@ -1179,7 +1179,7 @@ public class IncrementalBackupDUnitTest extends JUnit4CacheTestCase {
     /*
      * Cleanup "dummy" jar from file system.
      */
-    pattern = Pattern.compile("^" + JarDeployer.JAR_PREFIX + jarName + ".*#\\d++$");
+    pattern = Pattern.compile("^" + jarName + ".*#\\d++$");
     deleteMatching(new File(vmDir), pattern);
   }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java b/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
index ee46cbf..6dfab66 100644
--- a/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
+++ b/geode-core/src/test/java/org/apache/geode/management/DeployJarTestSuite.java
@@ -21,9 +21,12 @@ import org.apache.geode.internal.JarDeployerIntegrationTest;
 import org.apache.geode.management.internal.cli.commands.DeployCommandRedeployDUnitTest;
 import org.apache.geode.management.internal.cli.commands.DeployCommandsDUnitTest;
 import org.apache.geode.management.internal.configuration.ClusterConfigDeployJarDUnitTest;
+import org.junit.Ignore;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
+
+@Ignore
 @RunWith(Suite.class)
 @Suite.SuiteClasses({DeployedJarJUnitTest.class, DeployCommandsDUnitTest.class,
     JarDeployerIntegrationTest.class, ClassPathLoaderIntegrationTest.class,

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java
index 8280f5d..7780c0e 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest.java
@@ -16,6 +16,7 @@ package org.apache.geode.management.internal.cli.commands;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.geode.cache.execute.Execution;
 import org.apache.geode.cache.execute.FunctionService;
 import org.apache.geode.distributed.DistributedSystem;
@@ -23,10 +24,8 @@ import org.apache.geode.internal.ClassBuilder;
 import org.apache.geode.internal.ClassPathLoader;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
-import org.apache.geode.test.dunit.rules.Locator;
 import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
 import org.apache.geode.test.dunit.rules.MemberVM;
-import org.apache.geode.test.dunit.rules.Server;
 import org.apache.geode.test.junit.categories.DistributedTest;
 import org.junit.Before;
 import org.junit.Rule;
@@ -34,8 +33,8 @@ import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
 import java.io.File;
-import java.io.IOException;
 import java.io.Serializable;
+import java.net.URL;
 import java.util.List;
 
 @Category(DistributedTest.class)
@@ -43,15 +42,15 @@ public class DeployCommandRedeployDUnitTest implements Serializable {
   private static final String VERSION1 = "Version1";
   private static final String VERSION2 = "Version2";
 
-  private static final String jarNameA = "DeployCommandRedeployDUnitTestA.jar";
-  private static final String functionA = "DeployCommandRedeployDUnitFunctionA";
+  private static final String JAR_NAME_A = "DeployCommandRedeployDUnitTestA.jar";
+  private static final String FUNCTION_A = "DeployCommandRedeployDUnitFunctionA";
   private File jarAVersion1;
   private File jarAVersion2;
 
-  private static final String jarNameB = "DeployCommandRedeployDUnitTestB.jar";
-  private static final String functionB = "DeployCommandRedeployDUnitFunctionB";
-  private static final String packageB = "jddunit.function";
-  private static final String fullyQualifiedFunctionB = packageB + "." + functionB;
+  private static final String JAR_NAME_B = "DeployCommandRedeployDUnitTestB.jar";
+  private static final String FUNCTION_B = "DeployCommandRedeployDUnitFunctionB";
+  private static final String PACKAGE_B = "jddunit.function";
+  private static final String FULLY_QUALIFIED_FUNCTION_B = PACKAGE_B + "." + FUNCTION_B;
   private File jarBVersion1;
   private File jarBVersion2;
 
@@ -59,7 +58,7 @@ public class DeployCommandRedeployDUnitTest implements Serializable {
   private MemberVM server;
 
   @Rule
-  public LocatorServerStartupRule lsRule = new LocatorServerStartupRule();
+  public LocatorServerStartupRule lsRule = new LocatorServerStartupRule(true);
 
   @Rule
   public transient GfshShellConnectionRule gfshConnector = new GfshShellConnectionRule();
@@ -81,64 +80,60 @@ public class DeployCommandRedeployDUnitTest implements Serializable {
   @Test
   public void redeployJarsWithNewVersionsOfFunctions() throws Exception {
     gfshConnector.executeAndVerifyCommand("deploy --jar=" + jarAVersion1.getCanonicalPath());
-    server.invoke(() -> assertThatCanLoad(jarNameA, functionA));
-    server.invoke(() -> assertThatFunctionHasVersion(functionA, VERSION1));
-
+    server.invoke(() -> assertThatCanLoad(JAR_NAME_A, FUNCTION_A));
+    server.invoke(() -> assertThatFunctionHasVersion(FUNCTION_A, VERSION1));
 
     gfshConnector.executeAndVerifyCommand("deploy --jar=" + jarBVersion1.getCanonicalPath());
-    server.invoke(() -> assertThatCanLoad(jarNameA, functionA));
-    server.invoke(() -> assertThatCanLoad(jarNameB, fullyQualifiedFunctionB));
-    server.invoke(() -> assertThatFunctionHasVersion(functionA, VERSION1));
-    server.invoke(() -> assertThatFunctionHasVersion(functionB, VERSION1));
+    server.invoke(() -> assertThatCanLoad(JAR_NAME_A, FUNCTION_A));
+    server.invoke(() -> assertThatCanLoad(JAR_NAME_B, FULLY_QUALIFIED_FUNCTION_B));
+    server.invoke(() -> assertThatFunctionHasVersion(FUNCTION_A, VERSION1));
+    server.invoke(() -> assertThatFunctionHasVersion(FUNCTION_B, VERSION1));
 
     gfshConnector.executeAndVerifyCommand("deploy --jar=" + jarBVersion2.getCanonicalPath());
-    server.invoke(() -> assertThatCanLoad(jarNameA, functionA));
-    server.invoke(() -> assertThatCanLoad(jarNameB, fullyQualifiedFunctionB));
-    server.invoke(() -> assertThatFunctionHasVersion(functionA, VERSION1));
-    server.invoke(() -> assertThatFunctionHasVersion(functionB, VERSION2));
+    server.invoke(() -> assertThatCanLoad(JAR_NAME_A, FUNCTION_A));
+    server.invoke(() -> assertThatCanLoad(JAR_NAME_B, FULLY_QUALIFIED_FUNCTION_B));
+    server.invoke(() -> assertThatFunctionHasVersion(FUNCTION_A, VERSION1));
+    server.invoke(() -> assertThatFunctionHasVersion(FUNCTION_B, VERSION2));
 
     gfshConnector.executeAndVerifyCommand("deploy --jar=" + jarAVersion2.getCanonicalPath());
-    server.invoke(() -> assertThatCanLoad(jarNameA, functionA));
-    server.invoke(() -> assertThatCanLoad(jarNameB, fullyQualifiedFunctionB));
-    server.invoke(() -> assertThatFunctionHasVersion(functionA, VERSION2));
-    server.invoke(() -> assertThatFunctionHasVersion(functionB, VERSION2));
+    server.invoke(() -> assertThatCanLoad(JAR_NAME_A, FUNCTION_A));
+    server.invoke(() -> assertThatCanLoad(JAR_NAME_B, FULLY_QUALIFIED_FUNCTION_B));
+    server.invoke(() -> assertThatFunctionHasVersion(FUNCTION_A, VERSION2));
+    server.invoke(() -> assertThatFunctionHasVersion(FUNCTION_B, VERSION2));
   }
 
   // Note that jar A is a Declarable Function, while jar B is only a Function.
   // Also, the function for jar A resides in the default package, whereas jar B specifies a package.
   // This ensures that this test has identical coverage to some tests that it replaced.
   private File createJarWithFunctionA(String version) throws Exception {
-    String classContents =
-        "import java.util.Properties;" + "import org.apache.geode.cache.Declarable;"
-            + "import org.apache.geode.cache.execute.Function;"
-            + "import org.apache.geode.cache.execute.FunctionContext;" + "public class " + functionA
-            + " implements Function, Declarable {" + "public String getId() {return \"" + functionA
-            + "\";}" + "public void init(Properties props) {}"
-            + "public void execute(FunctionContext context) {context.getResultSender().lastResult(\""
-            + version + "\");}" + "public boolean hasResult() {return true;}"
-            + "public boolean optimizeForWrite() {return false;}"
-            + "public boolean isHA() {return false;}}";
-
-    File jar = new File(lsRule.getTempFolder().newFolder(jarNameA + version), this.jarNameA);
+    URL classTemplateUrl = DeployCommandRedeployDUnitTest.class
+        .getResource("DeployCommandRedeployDUnitTest_FunctionATemplate");
+    assertThat(classTemplateUrl).isNotNull();
+
+    String classContents = FileUtils.readFileToString(new File(classTemplateUrl.toURI()), "UTF-8");
+    classContents = classContents.replaceAll("FUNCTION_A", FUNCTION_A);
+    classContents = classContents.replaceAll("VERSION", version);
+
+    File jar = new File(lsRule.getTempFolder().newFolder(JAR_NAME_A + version), this.JAR_NAME_A);
     ClassBuilder functionClassBuilder = new ClassBuilder();
-    functionClassBuilder.writeJarFromContent(functionA, classContents, jar);
+    functionClassBuilder.writeJarFromContent(FUNCTION_A, classContents, jar);
 
     return jar;
   }
 
-  private File createJarWithFunctionB(String version) throws IOException {
-    String classContents =
-        "package " + packageB + ";" + "import org.apache.geode.cache.execute.Function;"
-            + "import org.apache.geode.cache.execute.FunctionContext;" + "public class " + functionB
-            + " implements Function {" + "public boolean hasResult() {return true;}"
-            + "public void execute(FunctionContext context) {context.getResultSender().lastResult(\""
-            + version + "\");}" + "public String getId() {return \"" + functionB + "\";}"
-            + "public boolean optimizeForWrite() {return false;}"
-            + "public boolean isHA() {return false;}}";
-
-    File jar = new File(lsRule.getTempFolder().newFolder(jarNameB + version), this.jarNameB);
+  private File createJarWithFunctionB(String version) throws Exception {
+    URL classTemplateUrl = DeployCommandRedeployDUnitTest.class
+        .getResource("DeployCommandRedeployDUnitTest_FunctionATemplate");
+    assertThat(classTemplateUrl).isNotNull();
+
+    String classContents = FileUtils.readFileToString(new File(classTemplateUrl.toURI()), "UTF-8");
+    classContents = classContents.replaceAll("PACKAGE_B", PACKAGE_B);
+    classContents = classContents.replaceAll("FUNCTION_B", FUNCTION_B);
+    classContents = classContents.replaceAll("VERSION", version);
+
+    File jar = new File(lsRule.getTempFolder().newFolder(JAR_NAME_B + version), this.JAR_NAME_B);
     ClassBuilder functionClassBuilder = new ClassBuilder();
-    functionClassBuilder.writeJarFromContent("jddunit/function/" + functionB, classContents, jar);
+    functionClassBuilder.writeJarFromContent("jddunit/function/" + FUNCTION_B, classContents, jar);
 
     return jar;
   }

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java
index 6df2572..9ed5bed 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployCommandsDUnitTest.java
@@ -15,6 +15,7 @@
 package org.apache.geode.management.internal.cli.commands;
 
 import static org.apache.geode.distributed.ConfigurationProperties.GROUPS;
+import static org.apache.geode.test.dunit.Host.getHost;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertNotNull;
@@ -39,7 +40,7 @@ import java.util.Properties;
 
 /**
  * Unit tests for the DeployCommands class
- *
+ * 
  * @since GemFire 7.0
  */
 @SuppressWarnings("serial")
@@ -76,6 +77,9 @@ public class DeployCommandsDUnitTest implements Serializable {
 
   @Before
   public void setup() throws Exception {
+    getHost(0).getVM(1).bounce();
+    getHost(0).getVM(2).bounce();
+
     ClassBuilder classBuilder = new ClassBuilder();
     File jarsDir = lsRule.getTempFolder().newFolder();
     jar1 = new File(jarsDir, jarName1);
@@ -108,7 +112,7 @@ public class DeployCommandsDUnitTest implements Serializable {
     // Deploy a jar to a single group
     CommandResult cmdResult =
         gfshConnector.executeAndVerifyCommand("deploy --jar=" + jar2 + " --group=" + GROUP1);
-    String resultString = commandResultToString(cmdResult);
+    String resultString = gfshConnector.getGfshOutput();
 
     assertThat(resultString).contains(server1.getName());
     assertThat(resultString).doesNotContain(server2.getName());
@@ -121,9 +125,9 @@ public class DeployCommandsDUnitTest implements Serializable {
   @Test
   public void deployMultipleJarsToOneGroup() throws Exception {
     // Deploy of multiple JARs to a single group
-    CommandResult cmdResult = gfshConnector.executeAndVerifyCommand(
+    gfshConnector.executeAndVerifyCommand(
         "deploy --group=" + GROUP1 + " --dir=" + subdirWithJars3and4.getCanonicalPath());
-    String resultString = commandResultToString(cmdResult);
+    String resultString = gfshConnector.getGfshOutput();
 
     assertThat(resultString).describedAs(resultString).contains(server1.getName());
     assertThat(resultString).doesNotContain(server2.getName());
@@ -139,7 +143,6 @@ public class DeployCommandsDUnitTest implements Serializable {
       assertThatCannotLoad(jarName4, class4);
     });
 
-
     // Undeploy of multiple jars by specifying group
     gfshConnector.executeAndVerifyCommand("undeploy --group=" + GROUP1);
     server1.invoke(() -> {
@@ -155,9 +158,9 @@ public class DeployCommandsDUnitTest implements Serializable {
   @Test
   public void deployJarToAllServers() throws Exception {
     // Deploy a jar to all servers
-    CommandResult cmdResult = gfshConnector.executeAndVerifyCommand("deploy --jar=" + jar1);
+    gfshConnector.executeAndVerifyCommand("deploy --jar=" + jar1);
+    String resultString = gfshConnector.getGfshOutput();
 
-    String resultString = commandResultToString(cmdResult);
     assertThat(resultString).contains(server1.getName());
     assertThat(resultString).contains(server2.getName());
     assertThat(resultString).contains(jarName1);
@@ -242,16 +245,16 @@ public class DeployCommandsDUnitTest implements Serializable {
         "deploy jar --group=" + GROUP2 + " --jar=" + jar2.getCanonicalPath());
 
     // List for all members
-    CommandResult commandResult = gfshConnector.executeAndVerifyCommand("list deployed");
-    String resultString = commandResultToString(commandResult);
+    gfshConnector.executeAndVerifyCommand("list deployed");
+    String resultString = gfshConnector.getGfshOutput();
     assertThat(resultString).contains(server1.getName());
     assertThat(resultString).contains(server2.getName());
     assertThat(resultString).contains(jarName1);
     assertThat(resultString).contains(jarName2);
 
     // List for members in Group1
-    commandResult = gfshConnector.executeAndVerifyCommand("list deployed --group=" + GROUP1);
-    resultString = commandResultToString(commandResult);
+    gfshConnector.executeAndVerifyCommand("list deployed --group=" + GROUP1);
+    resultString = gfshConnector.getGfshOutput();
     assertThat(resultString).contains(server1.getName());
     assertThat(resultString).doesNotContain(server2.getName());
 
@@ -259,23 +262,12 @@ public class DeployCommandsDUnitTest implements Serializable {
     assertThat(resultString).doesNotContain(jarName2);
 
     // List for members in Group2
-    commandResult = gfshConnector.executeAndVerifyCommand("list deployed --group=" + GROUP2);
-    resultString = commandResultToString(commandResult);
+    gfshConnector.executeAndVerifyCommand("list deployed --group=" + GROUP2);
+    resultString = gfshConnector.getGfshOutput();
     assertThat(resultString).doesNotContain(server1.getName());
     assertThat(resultString).contains(server2.getName());
 
     assertThat(resultString).doesNotContain(jarName1);
     assertThat(resultString).contains(jarName2);
   }
-
-  protected static String commandResultToString(final CommandResult commandResult) {
-    assertNotNull(commandResult);
-    commandResult.resetToFirstLine();
-    StringBuilder buffer = new StringBuilder(commandResult.getHeader());
-    while (commandResult.hasNextLine()) {
-      buffer.append(commandResult.nextLine());
-    }
-    buffer.append(commandResult.getFooter());
-    return buffer.toString();
-  }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigBaseTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigBaseTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigBaseTest.java
deleted file mode 100644
index cecc8cf..0000000
--- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigBaseTest.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.management.internal.configuration;
-
-import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_CLUSTER_CONFIGURATION;
-import static org.apache.geode.distributed.ConfigurationProperties.USE_CLUSTER_CONFIGURATION;
-
-import org.apache.commons.io.FileUtils;
-import org.apache.geode.internal.ClassBuilder;
-import org.apache.geode.management.internal.configuration.utils.ZipUtils;
-import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
-import org.junit.Before;
-import org.junit.Rule;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Properties;
-
-public class ClusterConfigBaseTest {
-  public String clusterConfigZipPath;
-
-  public static final ConfigGroup CLUSTER = new ConfigGroup("cluster").regions("regionForCluster")
-      .jars("cluster.jar").maxLogFileSize("5000").configFiles("cluster.properties", "cluster.xml");
-  public static final ConfigGroup GROUP1 = new ConfigGroup("group1").regions("regionForGroup1")
-      .jars("group1.jar").maxLogFileSize("6000").configFiles("group1.properties", "group1.xml");
-  public static final ConfigGroup GROUP2 = new ConfigGroup("group2").regions("regionForGroup2")
-      .jars("group2.jar").maxLogFileSize("7000").configFiles("group2.properties", "group2.xml");
-
-  public static final ClusterConfig CONFIG_FROM_ZIP = new ClusterConfig(CLUSTER, GROUP1, GROUP2);
-
-  public static final ClusterConfig REPLICATED_CONFIG_FROM_ZIP = new ClusterConfig(
-      new ConfigGroup("cluster").maxLogFileSize("5000").jars("cluster.jar")
-          .regions("regionForCluster"),
-      new ConfigGroup("group1").maxLogFileSize("6000").jars("group1.jar")
-          .regions("regionForGroup1"),
-      new ConfigGroup("group2").maxLogFileSize("7000").jars("group2.jar")
-          .regions("regionForGroup2"));
-
-  @Rule
-  public LocatorServerStartupRule lsRule = new LocatorServerStartupRule();
-
-  protected Properties locatorProps;
-  protected Properties serverProps;
-
-  @Before
-  public void before() throws Exception {
-    clusterConfigZipPath = buildClusterZipFile();
-    locatorProps = new Properties();
-    serverProps = new Properties();
-
-    // the following are default values, we don't need to set them. We do it for clarity purpose
-    locatorProps.setProperty(ENABLE_CLUSTER_CONFIGURATION, "true");
-    serverProps.setProperty(USE_CLUSTER_CONFIGURATION, "true");
-  }
-
-  private String buildClusterZipFile() throws Exception {
-    ClassBuilder classBuilder = new ClassBuilder();
-    File clusterConfigDir = this.lsRule.getTempFolder().newFolder("cluster_config");
-
-    File clusterDir = new File(clusterConfigDir, "cluster");
-    String clusterXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
-        + "<cache xmlns=\"http://geode.apache.org/schema/cache\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" copy-on-read=\"false\" is-server=\"false\" lock-lease=\"120\" lock-timeout=\"60\" search-timeout=\"300\" version=\"1.0\" xsi:schemaLocation=\"http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd\">\n"
-        + "<region name=\"regionForCluster\">\n"
-        + "    <region-attributes data-policy=\"replicate\" scope=\"distributed-ack\"/>\n"
-        + "  </region>\n" + "</cache>\n";
-    writeFile(clusterDir, "cluster.xml", clusterXml);
-    writeFile(clusterDir, "cluster.properties", "log-file-size-limit=5000");
-    createJarFileWithClass("Cluster", "cluster.jar", clusterDir);
-
-    File group1Dir = new File(clusterConfigDir, "group1");
-    String group1Xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
-        + "<cache xmlns=\"http://geode.apache.org/schema/cache\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" copy-on-read=\"false\" is-server=\"false\" lock-lease=\"120\" lock-timeout=\"60\" search-timeout=\"300\" version=\"1.0\" xsi:schemaLocation=\"http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd\">\n"
-        + "<region name=\"regionForGroup1\">\n"
-        + "    <region-attributes data-policy=\"replicate\" scope=\"distributed-ack\"/>\n"
-        + "  </region>\n" + "</cache>\n";
-    writeFile(group1Dir, "group1.xml", group1Xml);
-    writeFile(group1Dir, "group1.properties", "log-file-size-limit=6000");
-    createJarFileWithClass("Group1", "group1.jar", group1Dir);
-
-
-    File group2Dir = new File(clusterConfigDir, "group2");
-    String group2Xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
-        + "<cache xmlns=\"http://geode.apache.org/schema/cache\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" copy-on-read=\"false\" is-server=\"false\" lock-lease=\"120\" lock-timeout=\"60\" search-timeout=\"300\" version=\"1.0\" xsi:schemaLocation=\"http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd\">\n"
-        + "<region name=\"regionForGroup2\">\n"
-        + "    <region-attributes data-policy=\"replicate\" scope=\"distributed-ack\"/>\n"
-        + "  </region>\n" + "</cache>\n";
-    writeFile(group2Dir, "group2.xml", group2Xml);
-    writeFile(group2Dir, "group2.properties", "log-file-size-limit=7000");
-    createJarFileWithClass("Group2", "group2.jar", group2Dir);
-
-
-    File clusterConfigZip = new File(lsRule.getTempFolder().newFolder(), "cluster_config.zip");
-    ZipUtils.zipDirectory(clusterConfigDir.getCanonicalPath(), clusterConfigZip.getCanonicalPath());
-
-    FileUtils.deleteDirectory(clusterConfigDir);
-    return clusterConfigZip.getCanonicalPath();
-  }
-
-  private File writeFile(File dir, String fileName, String content) throws IOException {
-    dir.mkdirs();
-    File file = new File(dir, fileName);
-    FileUtils.writeStringToFile(file, content);
-
-    return file;
-  }
-
-  protected String createJarFileWithClass(String className, String jarName, File dir)
-      throws IOException {
-    File jarFile = new File(dir, jarName);
-    new ClassBuilder().writeJarFromName(className, jarFile);
-    return jarFile.getCanonicalPath();
-  }
-}

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java
index 980f81f..3781c98 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigDeployJarDUnitTest.java
@@ -18,6 +18,7 @@ package org.apache.geode.management.internal.configuration;
 import static org.apache.geode.distributed.ConfigurationProperties.GROUPS;
 import static org.apache.geode.distributed.ConfigurationProperties.LOCATORS;
 import static org.apache.geode.distributed.ConfigurationProperties.LOG_LEVEL;
+import static org.apache.geode.test.dunit.Host.getHost;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import org.apache.geode.test.dunit.rules.GfshShellConnectionRule;
@@ -29,7 +30,7 @@ import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
 @Category(DistributedTest.class)
-public class ClusterConfigDeployJarDUnitTest extends ClusterConfigBaseTest {
+public class ClusterConfigDeployJarDUnitTest extends ClusterConfigTestBase {
   @Rule
   public GfshShellConnectionRule gfshConnector = new GfshShellConnectionRule();
 
@@ -37,7 +38,6 @@ public class ClusterConfigDeployJarDUnitTest extends ClusterConfigBaseTest {
 
   @Before
   public void before() throws Exception {
-    super.before();
     clusterJar = createJarFileWithClass("Cluster", "cluster.jar", lsRule.getTempFolder().getRoot());
     group1Jar = createJarFileWithClass("Group1", "group1.jar", lsRule.getTempFolder().getRoot());
     group2Jar = createJarFileWithClass("Group2", "group2.jar", lsRule.getTempFolder().getRoot());
@@ -180,7 +180,6 @@ public class ClusterConfigDeployJarDUnitTest extends ClusterConfigBaseTest {
     // test undeploy cluster
     gfshConnector.executeAndVerifyCommand("undeploy --jar=cluster.jar");
 
-
     cluster = cluster.removeJar("cluster.jar");
     server3Config.verify(locator);
     server1Config.verify(server1);
@@ -253,7 +252,6 @@ public class ClusterConfigDeployJarDUnitTest extends ClusterConfigBaseTest {
     gfshConnector.executeAndVerifyCommand("undeploy --jar=cluster.jar");
     server3 = lsRule.startServerVM(3, serverProps, locator.getPort());
 
-
     cluster = cluster.removeJar("cluster.jar");
     server3Config.verify(locator);
     server1Config.verify(server1);
@@ -264,8 +262,8 @@ public class ClusterConfigDeployJarDUnitTest extends ClusterConfigBaseTest {
 
     group1 = group1.removeJar("group1.jar");
     /*
-     * TODO: This is the current (weird) behavior If you started server4 with group1,group2 after
-     * this undeploy command, it would have group1.jar (brought from
+     * TODO: GEODE-2779 This is the current (weird) behavior If you started server4 with
+     * group1,group2 after this undeploy command, it would have group1.jar (brought from
      * cluster_config/group2/group1.jar on locator) whereas server3 (also in group1,group2) does not
      * have this jar.
      */

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigImportDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigImportDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigImportDUnitTest.java
index 696d22c..521e084 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigImportDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigImportDUnitTest.java
@@ -42,7 +42,7 @@ import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 @Category(DistributedTest.class)
-public class ClusterConfigImportDUnitTest extends ClusterConfigBaseTest {
+public class ClusterConfigImportDUnitTest extends ClusterConfigTestBase {
   @Rule
   public GfshShellConnectionRule gfshConnector = new GfshShellConnectionRule();
 
@@ -52,7 +52,6 @@ public class ClusterConfigImportDUnitTest extends ClusterConfigBaseTest {
 
   @Before
   public void before() throws Exception {
-    super.before();
     locatorVM = lsRule.startLocatorVM(0, locatorProps);
     INITIAL_CONFIG.verify(locatorVM);
 

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigStartMemberDUnitTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigStartMemberDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigStartMemberDUnitTest.java
index 652ec60..1cdda4c 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigStartMemberDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigStartMemberDUnitTest.java
@@ -33,12 +33,11 @@ import java.io.File;
 import java.util.Properties;
 
 @Category(DistributedTest.class)
-public class ClusterConfigStartMemberDUnitTest extends ClusterConfigBaseTest {
+public class ClusterConfigStartMemberDUnitTest extends ClusterConfigTestBase {
   protected MemberVM locator;
 
   @Before
   public void before() throws Exception {
-    super.before();
     locator = startLocatorWithLoadCCFromDir();
   }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigTestBase.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigTestBase.java b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigTestBase.java
new file mode 100644
index 0000000..c5aaa74
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/configuration/ClusterConfigTestBase.java
@@ -0,0 +1,128 @@
+/*
+ * 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.management.internal.configuration;
+
+import static org.apache.geode.distributed.ConfigurationProperties.ENABLE_CLUSTER_CONFIGURATION;
+import static org.apache.geode.distributed.ConfigurationProperties.USE_CLUSTER_CONFIGURATION;
+import static org.apache.geode.test.dunit.Host.getHost;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.geode.internal.ClassBuilder;
+import org.apache.geode.management.internal.configuration.utils.ZipUtils;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.rules.LocatorServerStartupRule;
+import org.junit.Before;
+import org.junit.Rule;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+public abstract class ClusterConfigTestBase {
+  public String clusterConfigZipPath;
+
+  public static final ConfigGroup CLUSTER = new ConfigGroup("cluster").regions("regionForCluster")
+      .jars("cluster.jar").maxLogFileSize("5000").configFiles("cluster.properties", "cluster.xml");
+  public static final ConfigGroup GROUP1 = new ConfigGroup("group1").regions("regionForGroup1")
+      .jars("group1.jar").maxLogFileSize("6000").configFiles("group1.properties", "group1.xml");
+  public static final ConfigGroup GROUP2 = new ConfigGroup("group2").regions("regionForGroup2")
+      .jars("group2.jar").maxLogFileSize("7000").configFiles("group2.properties", "group2.xml");
+
+  public static final ClusterConfig CONFIG_FROM_ZIP = new ClusterConfig(CLUSTER, GROUP1, GROUP2);
+
+  public static final ClusterConfig REPLICATED_CONFIG_FROM_ZIP = new ClusterConfig(
+      new ConfigGroup("cluster").maxLogFileSize("5000").jars("cluster.jar")
+          .regions("regionForCluster"),
+      new ConfigGroup("group1").maxLogFileSize("6000").jars("group1.jar")
+          .regions("regionForGroup1"),
+      new ConfigGroup("group2").maxLogFileSize("7000").jars("group2.jar")
+          .regions("regionForGroup2"));
+
+  @Rule
+  public LocatorServerStartupRule lsRule = new LocatorServerStartupRule(true);
+
+  protected Properties locatorProps;
+  protected Properties serverProps;
+
+  @Before
+  public void beforeClusterConfigTestBase() throws Exception {
+    clusterConfigZipPath = buildClusterZipFile();
+    locatorProps = new Properties();
+    serverProps = new Properties();
+
+    // the following are default values, we don't need to set them. We do it for clarity purpose
+    locatorProps.setProperty(ENABLE_CLUSTER_CONFIGURATION, "true");
+    serverProps.setProperty(USE_CLUSTER_CONFIGURATION, "true");
+  }
+
+  private String buildClusterZipFile() throws Exception {
+    File clusterConfigDir = this.lsRule.getTempFolder().newFolder("cluster_config");
+
+    File clusterDir = new File(clusterConfigDir, "cluster");
+    String clusterXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+        + "<cache xmlns=\"http://geode.apache.org/schema/cache\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" copy-on-read=\"false\" is-server=\"false\" lock-lease=\"120\" lock-timeout=\"60\" search-timeout=\"300\" version=\"1.0\" xsi:schemaLocation=\"http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd\">\n"
+        + "<region name=\"regionForCluster\">\n"
+        + "    <region-attributes data-policy=\"replicate\" scope=\"distributed-ack\"/>\n"
+        + "  </region>\n" + "</cache>\n";
+    writeFile(clusterDir, "cluster.xml", clusterXml);
+    writeFile(clusterDir, "cluster.properties", "log-file-size-limit=5000");
+    createJarFileWithClass("Cluster", "cluster.jar", clusterDir);
+
+    File group1Dir = new File(clusterConfigDir, "group1");
+    String group1Xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+        + "<cache xmlns=\"http://geode.apache.org/schema/cache\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" copy-on-read=\"false\" is-server=\"false\" lock-lease=\"120\" lock-timeout=\"60\" search-timeout=\"300\" version=\"1.0\" xsi:schemaLocation=\"http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd\">\n"
+        + "<region name=\"regionForGroup1\">\n"
+        + "    <region-attributes data-policy=\"replicate\" scope=\"distributed-ack\"/>\n"
+        + "  </region>\n" + "</cache>\n";
+    writeFile(group1Dir, "group1.xml", group1Xml);
+    writeFile(group1Dir, "group1.properties", "log-file-size-limit=6000");
+    createJarFileWithClass("Group1", "group1.jar", group1Dir);
+
+
+    File group2Dir = new File(clusterConfigDir, "group2");
+    String group2Xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
+        + "<cache xmlns=\"http://geode.apache.org/schema/cache\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" copy-on-read=\"false\" is-server=\"false\" lock-lease=\"120\" lock-timeout=\"60\" search-timeout=\"300\" version=\"1.0\" xsi:schemaLocation=\"http://geode.apache.org/schema/cache http://geode.apache.org/schema/cache/cache-1.0.xsd\">\n"
+        + "<region name=\"regionForGroup2\">\n"
+        + "    <region-attributes data-policy=\"replicate\" scope=\"distributed-ack\"/>\n"
+        + "  </region>\n" + "</cache>\n";
+    writeFile(group2Dir, "group2.xml", group2Xml);
+    writeFile(group2Dir, "group2.properties", "log-file-size-limit=7000");
+    createJarFileWithClass("Group2", "group2.jar", group2Dir);
+
+
+    File clusterConfigZip = new File(lsRule.getTempFolder().newFolder(), "cluster_config.zip");
+    ZipUtils.zipDirectory(clusterConfigDir.getCanonicalPath(), clusterConfigZip.getCanonicalPath());
+
+    FileUtils.deleteDirectory(clusterConfigDir);
+    return clusterConfigZip.getCanonicalPath();
+  }
+
+  private File writeFile(File dir, String fileName, String content) throws IOException {
+    dir.mkdirs();
+    File file = new File(dir, fileName);
+    FileUtils.writeStringToFile(file, content);
+
+    return file;
+  }
+
+  protected String createJarFileWithClass(String className, String jarName, File dir)
+      throws IOException {
+    File jarFile = new File(dir, jarName);
+    new ClassBuilder().writeJarFromName(className, jarFile);
+    return jarFile.getCanonicalPath();
+  }
+}

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorServerStartupRule.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorServerStartupRule.java b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorServerStartupRule.java
index 97c636b..34506c4 100644
--- a/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorServerStartupRule.java
+++ b/geode-core/src/test/java/org/apache/geode/test/dunit/rules/LocatorServerStartupRule.java
@@ -57,9 +57,18 @@ public class LocatorServerStartupRule extends ExternalResource implements Serial
 
   private TemporaryFolder temporaryFolder = new SerializableTemporaryFolder();
   private MemberVM[] members;
+  private final boolean bounceVms;
 
   public LocatorServerStartupRule() {
+    this(false);
+  }
+
+  /**
+   * If your DUnit tests fail due to insufficient cleanup, try setting bounceVms=true.
+   */
+  public LocatorServerStartupRule(boolean bounceVms) {
     DUnitLauncher.launchIfNeeded();
+    this.bounceVms = bounceVms;
   }
 
   @Override
@@ -67,6 +76,9 @@ public class LocatorServerStartupRule extends ExternalResource implements Serial
     restoreSystemProperties.before();
     temporaryFolder.create();
     Invoke.invokeInEveryVM("Stop each VM", this::cleanupVm);
+    if (bounceVms) {
+      getHost(0).getAllVMs().forEach(VM::bounce);
+    }
     members = new MemberVM[4];
   }
 

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/resources/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest_FunctionATemplate
----------------------------------------------------------------------
diff --git a/geode-core/src/test/resources/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest_FunctionATemplate b/geode-core/src/test/resources/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest_FunctionATemplate
new file mode 100644
index 0000000..766963a
--- /dev/null
+++ b/geode-core/src/test/resources/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest_FunctionATemplate
@@ -0,0 +1,30 @@
+import java.util.Properties;
+
+import org.apache.geode.cache.Declarable;
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionContext;
+
+public class FUNCTION_A implements Function, Declarable {
+  public String getId() {
+    return "FUNCTION_A";
+  }
+
+  public void init(Properties props) {
+  }
+
+  public void execute(FunctionContext context) {
+    context.getResultSender().lastResult("VERSION");
+  }
+
+  public boolean hasResult() {
+    return true;
+  }
+
+  public boolean optimizeForWrite() {
+    return false;
+  }
+
+  public boolean isHA() {
+    return false;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/geode-core/src/test/resources/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest_FunctionBTemplate
----------------------------------------------------------------------
diff --git a/geode-core/src/test/resources/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest_FunctionBTemplate b/geode-core/src/test/resources/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest_FunctionBTemplate
new file mode 100644
index 0000000..b37a6cf
--- /dev/null
+++ b/geode-core/src/test/resources/org/apache/geode/management/internal/cli/commands/DeployCommandRedeployDUnitTest_FunctionBTemplate
@@ -0,0 +1,26 @@
+package PACKAGE_B;
+
+import org.apache.geode.cache.execute.Function;
+import org.apache.geode.cache.execute.FunctionContext;
+
+public class FUNCTION_B implements Function {
+  public boolean hasResult() {
+    return true;
+  }
+
+  public void execute(FunctionContext context) {
+    context.getResultSender().lastResult("VERSION");
+  }
+
+  public String getId() {
+    return "FUNCTION_B";
+  }
+
+  public boolean optimizeForWrite() {
+    return false;
+  }
+
+  public boolean isHA() {
+    return false;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/geode/blob/6f7f9439/gradle/dependency-versions.properties
----------------------------------------------------------------------
diff --git a/gradle/dependency-versions.properties b/gradle/dependency-versions.properties
index da8bdf2..a9e3fdf 100644
--- a/gradle/dependency-versions.properties
+++ b/gradle/dependency-versions.properties
@@ -33,6 +33,7 @@ commons-digester.version=2.1
 commons-exec.version=1.3
 derby.version = 10.13.1.1
 dom4j.version = 1.6.1
+fast-classpath-scanner.version=2.0.11
 fastutil.version = 7.1.0
 google-gson.version=2.8.0
 guava.version = 21.0


[4/8] geode git commit: GEODE-2686: Remove JarClassLoader

Posted by js...@apache.org.
http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java b/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
index 18d4b42..ad5c435 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
@@ -14,44 +14,60 @@
  */
 package org.apache.geode.internal;
 
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.geode.GemFireException;
+import org.apache.geode.GemFireIOException;
 import org.apache.geode.SystemFailure;
 import org.apache.geode.internal.logging.LogService;
 import org.apache.logging.log4j.Logger;
 
 import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FilenameFilter;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.OutputStream;
-import java.io.RandomAccessFile;
 import java.io.Serializable;
-import java.nio.channels.FileLock;
+import java.net.URL;
+import java.net.URLClassLoader;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Stream;
 
 public class JarDeployer implements Serializable {
   private static final long serialVersionUID = 1L;
   private static final Logger logger = LogService.getLogger();
-  public static final String JAR_PREFIX = "vf.gf#";
-  public static final String JAR_PREFIX_FOR_REGEX = "^vf\\.gf#";
+  public static final String JAR_PREFIX = "";
+  public static final String JAR_PREFIX_FOR_REGEX = "";
   private static final Lock lock = new ReentrantLock();
 
+  private final Map<String, DeployedJar> deployedJars = new ConcurrentHashMap<>();
+
+
   // Split a versioned filename into its name and version
   public static final Pattern versionedPattern =
-      Pattern.compile(JAR_PREFIX_FOR_REGEX + "(.*)#(\\d++)$");
+      Pattern.compile(JAR_PREFIX_FOR_REGEX + "(.*)\\.v(\\d++).jar$");
 
   private final File deployDirectory;
 
@@ -63,262 +79,36 @@ public class JarDeployer implements Serializable {
     this.deployDirectory = deployDirectory;
   }
 
-  /**
-   * Re-deploy all previously deployed JAR files.
-   */
-  public void loadPreviouslyDeployedJars() {
-    List<JarClassLoader> jarClassLoaders = new ArrayList<JarClassLoader>();
-
-    lock.lock();
-    try {
-      try {
-        verifyWritableDeployDirectory();
-        final Set<String> jarNames = findDistinctDeployedJars();
-        if (!jarNames.isEmpty()) {
-          for (String jarName : jarNames) {
-            final File[] jarFiles = findSortedOldVersionsOfJar(jarName);
-
-            // It's possible the JARs were deleted by another process
-            if (jarFiles.length != 0) {
-              JarClassLoader jarClassLoader = findJarClassLoader(jarName);
-
-              try {
-                final byte[] jarBytes = getJarContent(jarFiles[0]);
-                if (!JarClassLoader.isValidJarContent(jarBytes)) {
-                  logger.warn("Invalid JAR file found and deleted: {}",
-                      jarFiles[0].getAbsolutePath());
-                  jarFiles[0].delete();
-                } else {
-                  // Test to see if the exact same file is already in use
-                  if (jarClassLoader == null
-                      || !jarClassLoader.getFileName().equals(jarFiles[0].getName())) {
-                    jarClassLoader = new JarClassLoader(jarFiles[0], jarName, jarBytes);
-                    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(jarClassLoader);
-                    jarClassLoaders.add(jarClassLoader);
-                  }
-                }
-              } catch (IOException ioex) {
-                // Another process deleted the file so don't bother doing anything else with it
-                if (logger.isDebugEnabled()) {
-                  logger.debug("Failed attempt to use JAR to create JarClassLoader for: {}",
-                      jarName);
-                }
-              }
-
-              // Remove any old left-behind versions of this JAR file
-              for (File jarFile : jarFiles) {
-                if (jarFile.exists() && (jarClassLoader == null
-                    || !jarClassLoader.getFileName().equals(jarFile.getName()))) {
-                  attemptFileLockAndDelete(jarFile);
-                }
-              }
-            }
-          }
-        }
-
-        for (JarClassLoader jarClassLoader : jarClassLoaders) {
-          jarClassLoader.loadClassesAndRegisterFunctions();
-        }
-      } catch (VirtualMachineError e) {
-        SystemFailure.initiateFailure(e);
-        throw e;
-      } catch (Throwable th) {
-        SystemFailure.checkFailure();
-        logger.error("Error when attempting to deploy JAR files on load.", th);
-      }
-    } finally {
-      lock.unlock();
-    }
+  public File getDeployDirectory() {
+    return this.deployDirectory;
   }
 
-  /**
-   * Deploy the given JAR files.
-   * 
-   * @param jarNames Array of names of the JAR files to deploy.
-   * @param jarBytes Array of contents of the JAR files to deploy.
-   * @return An array of newly created JAR class loaders. Entries will be null for an JARs that were
-   *         already deployed.
-   * @throws IOException When there's an error saving the JAR file to disk
-   */
-  public JarClassLoader[] deploy(final String jarNames[], final byte[][] jarBytes)
-      throws IOException, ClassNotFoundException {
-    JarClassLoader[] jarClassLoaders = new JarClassLoader[jarNames.length];
-    verifyWritableDeployDirectory();
-
+  public DeployedJar deployWithoutRegistering(final String jarName, final byte[] jarBytes)
+      throws IOException {
     lock.lock();
+
     try {
-      for (int i = 0; i < jarNames.length; i++) {
-        if (!JarClassLoader.isValidJarContent(jarBytes[i])) {
-          throw new IllegalArgumentException(
-              "File does not contain valid JAR content: " + jarNames[i]);
-        }
-      }
+      verifyWritableDeployDirectory();
 
-      for (int i = 0; i < jarNames.length; i++) {
-        jarClassLoaders[i] = deployWithoutRegistering(jarNames[i], jarBytes[i]);
-      }
+      File newVersionedJarFile = getNextVersionedJarFile(jarName);
+      writeJarBytesToFile(newVersionedJarFile, jarBytes);
 
-      for (JarClassLoader jarClassLoader : jarClassLoaders) {
-        if (jarClassLoader != null) {
-          jarClassLoader.loadClassesAndRegisterFunctions();
-        }
-      }
+      return new DeployedJar(newVersionedJarFile, jarName, jarBytes);
     } finally {
       lock.unlock();
     }
-    return jarClassLoaders;
   }
 
-  /**
-   * Deploy the given JAR file without registering functions.
-   * 
-   * @param jarName Name of the JAR file to deploy.
-   * @param jarBytes Contents of the JAR file to deploy.
-   * @return The newly created JarClassLoader or null if the JAR was already deployed
-   * @throws IOException When there's an error saving the JAR file to disk
-   */
-  private JarClassLoader deployWithoutRegistering(final String jarName, final byte[] jarBytes)
-      throws IOException {
-    JarClassLoader oldJarClassLoader = findJarClassLoader(jarName);
-
-    final boolean isDebugEnabled = logger.isDebugEnabled();
-    if (isDebugEnabled) {
-      logger.debug("Deploying {}: {}", jarName, (oldJarClassLoader == null ? ": not yet deployed"
-          : ": already deployed as " + oldJarClassLoader.getFileCanonicalPath()));
-    }
-
-    // Test to see if the exact same file is being deployed
-    if (oldJarClassLoader != null && oldJarClassLoader.hasSameContent(jarBytes)) {
-      return null;
-    }
-
-    JarClassLoader newJarClassLoader = null;
-
-    do {
-      File[] oldJarFiles = findSortedOldVersionsOfJar(jarName);
-
-      try {
-        // If this is the first version of this JAR file we've seen ...
-        if (oldJarFiles.length == 0) {
-          if (isDebugEnabled) {
-            logger.debug("There were no pre-existing versions for JAR: {}", jarName);
-          }
-          File nextVersionJarFile = getNextVersionJarFile(jarName);
-          if (writeJarBytesToFile(nextVersionJarFile, jarBytes)) {
-            newJarClassLoader = new JarClassLoader(nextVersionJarFile, jarName, jarBytes);
-            if (isDebugEnabled) {
-              logger.debug("Successfully created initial JarClassLoader at file: {}",
-                  nextVersionJarFile.getAbsolutePath());
-            }
-          } else {
-            if (isDebugEnabled) {
-              logger.debug("Unable to write contents for first version of JAR to file: {}",
-                  nextVersionJarFile.getAbsolutePath());
-            }
-          }
-
-        } else {
-          // Most recent is at the beginning of the list, see if this JAR matches what's
-          // already on disk.
-          if (doesFileMatchBytes(oldJarFiles[0], jarBytes)) {
-            if (isDebugEnabled) {
-              logger.debug("A version on disk was an exact match for the JAR being deployed: {}",
-                  oldJarFiles[0].getAbsolutePath());
-            }
-            newJarClassLoader = new JarClassLoader(oldJarFiles[0], jarName, jarBytes);
-            if (isDebugEnabled) {
-              logger.debug("Successfully reused JAR to create JarClassLoader from file: {}",
-                  oldJarFiles[0].getAbsolutePath());
-            }
-          } else {
-            // This JAR isn't on disk
-            if (isDebugEnabled) {
-              logger.debug("Need to create a new version for JAR: {}", jarName);
-            }
-            File nextVersionJarFile = getNextVersionJarFile(oldJarFiles[0].getName());
-            if (writeJarBytesToFile(nextVersionJarFile, jarBytes)) {
-              newJarClassLoader = new JarClassLoader(nextVersionJarFile, jarName, jarBytes);
-              if (isDebugEnabled) {
-                logger.debug("Successfully created next JarClassLoader at file: {}",
-                    nextVersionJarFile.getAbsolutePath());
-              }
-            } else {
-              if (isDebugEnabled) {
-                logger.debug("Unable to write contents for next version of JAR to file: {}",
-                    nextVersionJarFile.getAbsolutePath());
-              }
-            }
-          }
-        }
-      } catch (IOException ioex) {
-        // Another process deleted the file before we could get to it, just start again
-        logger.info("Failed attempt to use JAR to create JarClassLoader for: {} : {}", jarName,
-            ioex.getMessage());
-      }
-
-      if (isDebugEnabled) {
-        if (newJarClassLoader == null) {
-          logger.debug("Unable to determine a JAR file location, will loop and try again: {}",
-              jarName);
-        } else {
-          logger.debug("Exiting loop for JarClassLoader creation using file: {}",
-              newJarClassLoader.getFileName());
-        }
-      }
-    } while (newJarClassLoader == null);
-
-    ClassPathLoader.getLatest().addOrReplaceAndSetLatest(newJarClassLoader);
-
-    // Remove the JAR file that was undeployed as part of this redeploy
-    if (oldJarClassLoader != null) {
-      attemptFileLockAndDelete(new File(this.deployDirectory, oldJarClassLoader.getFileName()));
-    }
-
-    return newJarClassLoader;
-  }
 
   /**
-   * Undeploy the given JAR file.
+   * Get a list of all currently deployed jars.
    * 
-   * @param jarName The name of the JAR file to undeploy
-   * @return The path to the location on disk where the JAR file had been deployed
-   * @throws IOException If there's a problem deleting the file
+   * @return The list of DeployedJars
    */
-  public String undeploy(final String jarName) throws IOException {
-    JarClassLoader jarClassLoader = null;
-    verifyWritableDeployDirectory();
-
-    lock.lock();
-    try {
-      jarClassLoader = findJarClassLoader(jarName);
-      if (jarClassLoader == null) {
-        throw new IllegalArgumentException("JAR not deployed");
-      }
-
-      ClassPathLoader.getLatest().removeAndSetLatest(jarClassLoader);
-      attemptFileLockAndDelete(new File(this.deployDirectory, jarClassLoader.getFileName()));
-      return jarClassLoader.getFileCanonicalPath();
-    } finally {
-      lock.unlock();
-    }
+  public List<DeployedJar> findDeployedJars() {
+    return getDeployedJars().values().stream().collect(toList());
   }
 
-  /**
-   * Get a list of all currently deployed JarClassLoaders.
-   * 
-   * @return The list of JarClassLoaders
-   */
-  public List<JarClassLoader> findJarClassLoaders() {
-    List<JarClassLoader> returnList = new ArrayList<JarClassLoader>();
-    Collection<ClassLoader> classLoaders = ClassPathLoader.getLatest().getClassLoaders();
-    for (ClassLoader classLoader : classLoaders) {
-      if (classLoader instanceof JarClassLoader) {
-        returnList.add((JarClassLoader) classLoader);
-      }
-    }
-
-    return returnList;
-  }
 
   /**
    * Suspend all deploy and undeploy operations. This is done by acquiring and holding the lock
@@ -339,26 +129,19 @@ public class JarDeployer implements Serializable {
     lock.unlock();
   }
 
-  /**
-   * Figure out the next version of a JAR file
-   * 
-   * @param latestVersionedJarName The previous most recent version of the JAR file or original name
-   *        if there wasn't one
-   * @return The file that represents the next version
-   */
-  protected File getNextVersionJarFile(final String latestVersionedJarName) {
-    String newFileName;
-    final Matcher matcher = versionedPattern.matcher(latestVersionedJarName);
-    if (matcher.find()) {
-      newFileName = JAR_PREFIX + matcher.group(1) + "#" + (Integer.parseInt(matcher.group(2)) + 1);
+  protected File getNextVersionedJarFile(String unversionedJarName) {
+    File[] oldVersions = findSortedOldVersionsOfJar(unversionedJarName);
+
+    String nextVersionedJarName;
+    if (oldVersions == null || oldVersions.length == 0) {
+      nextVersionedJarName = removeJarExtension(unversionedJarName) + ".v1.jar";
     } else {
-      newFileName = JAR_PREFIX + latestVersionedJarName + "#1";
+      String latestVersionedJarName = oldVersions[0].getName();
+      int nextVersion = extractVersionFromFilename(latestVersionedJarName) + 1;
+      nextVersionedJarName = removeJarExtension(unversionedJarName) + ".v" + nextVersion + ".jar";
     }
 
-    if (logger.isDebugEnabled()) {
-      logger.debug("Next version file name will be: {}", newFileName);
-    }
-    return new File(this.deployDirectory, newFileName);
+    return new File(deployDirectory, nextVersionedJarName);
   }
 
   /**
@@ -370,27 +153,18 @@ public class JarDeployer implements Serializable {
    * @param jarBytes Contents of the JAR file to deploy.
    * @return True if the file was successfully written, false otherwise
    */
-  private boolean writeJarBytesToFile(final File file, final byte[] jarBytes) {
+  private boolean writeJarBytesToFile(final File file, final byte[] jarBytes) throws IOException {
     final boolean isDebugEnabled = logger.isDebugEnabled();
-    try {
-      if (file.createNewFile()) {
-        if (isDebugEnabled) {
-          logger.debug("Successfully created new JAR file: {}", file.getAbsolutePath());
-        }
-        final OutputStream outStream = new FileOutputStream(file);
-        outStream.write(jarBytes);
-        outStream.close();
-        return true;
-      }
-      return doesFileMatchBytes(file, jarBytes);
-
-    } catch (IOException ioex) {
-      // Another VM clobbered what was happening here, try again
+    if (file.createNewFile()) {
       if (isDebugEnabled) {
-        logger.debug("IOException while trying to write JAR content to file: {}", ioex);
+        logger.debug("Successfully created new JAR file: {}", file.getAbsolutePath());
       }
-      return false;
+      final OutputStream outStream = new FileOutputStream(file);
+      outStream.write(jarBytes);
+      outStream.close();
+      return true;
     }
+    return doesFileMatchBytes(file, jarBytes);
   }
 
   /**
@@ -457,86 +231,22 @@ public class JarDeployer implements Serializable {
     return true;
   }
 
-  private void attemptFileLockAndDelete(final File file) throws IOException {
-    final String absolutePath = file.getAbsolutePath();
-    FileOutputStream fileOutputStream = new FileOutputStream(file, true);
-    final boolean isDebugEnabled = logger.isDebugEnabled();
-    try {
-      FileLock fileLock = null;
-      try {
-        fileLock = fileOutputStream.getChannel().tryLock();
-
-        if (fileLock != null) {
-          if (isDebugEnabled) {
-            logger.debug("Tried and acquired exclusive lock for file: {}, w/ channel {}",
-                absolutePath, fileLock.channel());
-          }
-
-          if (file.delete()) {
-            if (isDebugEnabled) {
-              logger.debug("Deleted file with name: {}", absolutePath);
-            }
-          } else {
-            if (isDebugEnabled) {
-              logger.debug("Could not delete file, will truncate instead and delete on exit: {}",
-                  absolutePath);
-            }
-            file.deleteOnExit();
-
-            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
-            try {
-              randomAccessFile.setLength(0);
-            } finally {
-              try {
-                randomAccessFile.close();
-              } catch (IOException ioex) {
-                logger.error("Could not close file when attempting to set zero length", ioex);
-              }
-            }
-          }
-        } else {
-          if (isDebugEnabled) {
-            logger.debug("Will not delete file since exclusive lock unavailable: {}", absolutePath);
-          }
-        }
-
-      } finally {
-        if (fileLock != null) {
-          try {
-            fileLock.release();
-            fileLock.channel().close();
-            if (isDebugEnabled) {
-              logger.debug("Released file lock for file: {}, w/ channel: {}", absolutePath,
-                  fileLock.channel());
-            }
-          } catch (IOException ioex) {
-            logger.error("Could not close channel on JAR lock file", ioex);
-          }
-        }
-      }
-    } finally {
-      try {
-        fileOutputStream.close();
-      } catch (IOException ioex) {
-        logger.error("Could not close output stream on JAR file", ioex);
-      }
-    }
-  }
-
   /**
    * Find the version number that's embedded in the name of this file
    * 
-   * @param file File to get the version number from
+   * @param filename Filename to get the version number from
    * @return The version number embedded in the filename
    */
-  int extractVersionFromFilename(final File file) {
-    final Matcher matcher = versionedPattern.matcher(file.getName());
-    matcher.find();
-    return Integer.parseInt(matcher.group(2));
+  public static int extractVersionFromFilename(final String filename) {
+    final Matcher matcher = versionedPattern.matcher(filename);
+    if (matcher.find()) {
+      return Integer.parseInt(matcher.group(2));
+    } else {
+      return 0;
+    }
   }
 
   protected Set<String> findDistinctDeployedJars() {
-
     // Find all deployed JAR files
     final File[] oldFiles = this.deployDirectory.listFiles(new FilenameFilter() {
       @Override
@@ -559,41 +269,32 @@ public class JarDeployer implements Serializable {
    * Find all versions of the JAR file that are currently on disk and return them sorted from newest
    * (highest version) to oldest
    * 
-   * @param jarFilename Name of the JAR file that we want old versions of
+   * @param unversionedJarName Name of the JAR file that we want old versions of
    * @return Sorted array of files that are older versions of the given JAR
    */
-  protected File[] findSortedOldVersionsOfJar(final String jarFilename) {
+  protected File[] findSortedOldVersionsOfJar(final String unversionedJarName) {
     // Find all matching files
-    final Pattern pattern = Pattern.compile(JAR_PREFIX_FOR_REGEX + jarFilename + "#\\d++$");
-    final File[] oldJarFiles = this.deployDirectory.listFiles(new FilenameFilter() {
-      @Override
-      public boolean accept(final File file, final String name) {
-        return (pattern.matcher(name).matches());
-      }
-    });
+    final Pattern pattern = Pattern.compile(
+        JAR_PREFIX_FOR_REGEX + removeJarExtension(unversionedJarName) + "\\.v\\d++\\.jar$");
+    final File[] oldJarFiles =
+        this.deployDirectory.listFiles((file, name) -> (pattern.matcher(name).matches()));
 
     // Sort them in order from newest (highest version) to oldest
-    Arrays.sort(oldJarFiles, new Comparator<File>() {
-      @Override
-      public int compare(final File file1, final File file2) {
-        int file1Version = extractVersionFromFilename(file1);
-        int file2Version = extractVersionFromFilename(file2);
-        return file2Version - file1Version;
-      }
+    Arrays.sort(oldJarFiles, (file1, file2) -> {
+      int file1Version = extractVersionFromFilename(file1.getName());
+      int file2Version = extractVersionFromFilename(file2.getName());
+      return file2Version - file1Version;
     });
 
     return oldJarFiles;
   }
 
-  private JarClassLoader findJarClassLoader(final String jarName) {
-    Collection<ClassLoader> classLoaders = ClassPathLoader.getLatest().getClassLoaders();
-    for (ClassLoader classLoader : classLoaders) {
-      if (classLoader instanceof JarClassLoader
-          && ((JarClassLoader) classLoader).getJarName().equals(jarName)) {
-        return (JarClassLoader) classLoader;
-      }
+  protected String removeJarExtension(String jarName) {
+    if (jarName != null && jarName.endsWith(".jar")) {
+      return jarName.replaceAll("\\.jar$", "");
+    } else {
+      return jarName;
     }
-    return null;
   }
 
   /**
@@ -601,7 +302,7 @@ public class JarDeployer implements Serializable {
    * 
    * @throws IOException If the directory isn't writable
    */
-  private void verifyWritableDeployDirectory() throws IOException {
+  public void verifyWritableDeployDirectory() throws IOException {
     Exception exception = null;
     int tryCount = 0;
     do {
@@ -627,20 +328,246 @@ public class JarDeployer implements Serializable {
         "Unable to write to deploy directory: " + this.deployDirectory.getCanonicalPath());
   }
 
-  private byte[] getJarContent(File jarFile) throws IOException {
-    InputStream inputStream = new FileInputStream(jarFile);
+  final Pattern oldNamingPattern = Pattern.compile("^vf\\.gf#(.*)\\.jar#(\\d+)$");
+
+  /*
+   * In Geode 1.1.0, the deployed version of 'myjar.jar' would be named 'vf.gf#myjar.jar#1'. Now it
+   * is be named 'myjar.v1.jar'. We need to rename all existing deployed jars to the new convention
+   * if this is the first time starting up with the new naming format.
+   */
+  protected void renameJarsWithOldNamingConvention() throws IOException {
+    Set<File> jarsWithOldNamingConvention = findJarsWithOldNamingConvention();
+
+    if (jarsWithOldNamingConvention.isEmpty()) {
+      return;
+    }
+
+    for (File jar : jarsWithOldNamingConvention) {
+      renameJarWithOldNamingConvention(jar);
+    }
+  }
+
+  protected Set<File> findJarsWithOldNamingConvention() {
+    return Stream.of(this.deployDirectory.listFiles())
+        .filter((File file) -> isOldNamingConvention(file.getName())).collect(toSet());
+  }
+
+  protected boolean isOldNamingConvention(String fileName) {
+    return oldNamingPattern.matcher(fileName).matches();
+  }
+
+  private void renameJarWithOldNamingConvention(File oldJar) throws IOException {
+    Matcher matcher = oldNamingPattern.matcher(oldJar.getName());
+    if (!matcher.matches()) {
+      throw new IllegalArgumentException("The given jar " + oldJar.getCanonicalPath()
+          + " does not match the old naming convention");
+    }
+
+    String unversionedJarNameWithoutExtension = matcher.group(1);
+    String jarVersion = matcher.group(2);
+    String newJarName = unversionedJarNameWithoutExtension + ".v" + jarVersion + ".jar";
+
+    File newJar = new File(this.deployDirectory, newJarName);
+    logger.debug("Renaming deployed jar from " + oldJar.getCanonicalPath() + " to "
+        + newJar.getCanonicalPath());
+
+    FileUtils.moveFile(oldJar, newJar);
+    FileUtils.deleteQuietly(oldJar);
+  }
+
+  /**
+   * Re-deploy all previously deployed JAR files.
+   */
+  public void loadPreviouslyDeployedJars() {
+    lock.lock();
+    try {
+      verifyWritableDeployDirectory();
+      renameJarsWithOldNamingConvention();
+
+      final Set<String> jarNames = findDistinctDeployedJars();
+      if (jarNames.isEmpty()) {
+        return;
+      }
+
+      Map<String, DeployedJar> latestVersionOfEachJar = new LinkedHashMap<>();
+
+      for (String jarName : jarNames) {
+        final File[] jarFiles = findSortedOldVersionsOfJar(jarName);
+
+        Optional<File> latestValidDeployedJarOptional =
+            Arrays.stream(jarFiles).filter(Objects::nonNull).filter(jarFile -> {
+              try {
+                return DeployedJar.isValidJarContent(FileUtils.readFileToByteArray(jarFile));
+              } catch (IOException e) {
+                return false;
+              }
+            }).findFirst();
+
+        if (!latestValidDeployedJarOptional.isPresent()) {
+          // No valid version of this jar
+          continue;
+        }
+
+        File latestValidDeployedJar = latestValidDeployedJarOptional.get();
+        latestVersionOfEachJar.put(jarName, new DeployedJar(latestValidDeployedJar, jarName));
+
+        // Remove any old left-behind versions of this JAR file
+        for (File jarFile : jarFiles) {
+          if (!latestValidDeployedJar.equals(jarFile)) {
+            FileUtils.deleteQuietly(jarFile);
+          }
+        }
+      }
+
+      registerNewVersions(latestVersionOfEachJar.values().stream().collect(toList()));
+      // ClassPathLoader.getLatest().deploy(latestVersionOfEachJar.keySet().toArray(),
+      // latestVersionOfEachJar.values().toArray())
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    } finally {
+      lock.unlock();
+    }
+  }
+
+
+  public URL[] getDeployedJarURLs() {
+    return this.deployedJars.values().stream().map(DeployedJar::getFileURL).toArray(URL[]::new);
+
+  }
+
+  public List<DeployedJar> registerNewVersions(List<DeployedJar> deployedJars)
+      throws ClassNotFoundException {
+    lock.lock();
+    try {
+      for (DeployedJar deployedJar : deployedJars) {
+        if (deployedJar != null) {
+          DeployedJar oldJar = this.deployedJars.put(deployedJar.getJarName(), deployedJar);
+          if (oldJar != null) {
+            oldJar.cleanUp();
+          }
+        }
+      }
+
+      ClassPathLoader.getLatest().rebuildClassLoaderForDeployedJars();
+
+      for (DeployedJar deployedJar : deployedJars) {
+        if (deployedJar != null) {
+          deployedJar.loadClassesAndRegisterFunctions();
+        }
+      }
+    } finally {
+      lock.unlock();
+    }
+
+    return deployedJars;
+  }
+
+  /**
+   * Deploy the given JAR files.
+   * 
+   * @param jarNames Array of names of the JAR files to deploy.
+   * @param jarBytes Array of contents of the JAR files to deploy.
+   * @return An array of newly created JAR class loaders. Entries will be null for an JARs that were
+   *         already deployed.
+   * @throws IOException When there's an error saving the JAR file to disk
+   */
+  public List<DeployedJar> deploy(final String jarNames[], final byte[][] jarBytes)
+      throws IOException, ClassNotFoundException {
+    DeployedJar[] deployedJars = new DeployedJar[jarNames.length];
+
+    for (int i = 0; i < jarNames.length; i++) {
+      if (!DeployedJar.isValidJarContent(jarBytes[i])) {
+        throw new IllegalArgumentException(
+            "File does not contain valid JAR content: " + jarNames[i]);
+      }
+    }
+
+    lock.lock();
+    try {
+      for (int i = 0; i < jarNames.length; i++) {
+        String jarName = jarNames[i];
+        byte[] newJarBytes = jarBytes[i];
+
+        boolean shouldDeployNewVersion = shouldDeployNewVersion(jarName, newJarBytes);
+
+        if (shouldDeployNewVersion) {
+          deployedJars[i] = deployWithoutRegistering(jarName, newJarBytes);
+        } else {
+          deployedJars[i] = null;
+        }
+      }
+
+      return registerNewVersions(Arrays.asList(deployedJars));
+    } finally {
+      lock.unlock();
+    }
+  }
+
+  private boolean shouldDeployNewVersion(String jarName, byte[] newJarBytes) throws IOException {
+    DeployedJar oldDeployedJar = this.deployedJars.get(jarName);
+
+    if (oldDeployedJar == null) {
+      return true;
+    }
+
+    if (oldDeployedJar.hasSameContentAs(newJarBytes)) {
+      logger.warn("Jar is identical to the latest deployed version: ",
+          oldDeployedJar.getFileCanonicalPath());
+
+      return false;
+    }
+
+    return true;
+  }
+
+  public DeployedJar findDeployedJar(String jarName) {
+    return this.deployedJars.get(jarName);
+  }
+
+  public DeployedJar deploy(final String jarName, final byte[] jarBytes)
+      throws IOException, ClassNotFoundException {
+    lock.lock();
+
     try {
-      final ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
-      final byte[] bytes = new byte[4096];
+      List<DeployedJar> deployedJars = deploy(new String[] {jarName}, new byte[][] {jarBytes});
+      if (deployedJars == null || deployedJars.size() == 0) {
+        return null;
+      }
+
+      return deployedJars.get(0);
+    } finally {
+      lock.unlock();
+    }
+
+  }
 
-      int bytesRead;
-      while (((bytesRead = inputStream.read(bytes)) != -1)) {
-        byteOutStream.write(bytes, 0, bytesRead);
+  public Map<String, DeployedJar> getDeployedJars() {
+    return Collections.unmodifiableMap(this.deployedJars);
+  }
+
+  /**
+   * Undeploy the given JAR file.
+   * 
+   * @param jarName The name of the JAR file to undeploy
+   * @return The path to the location on disk where the JAR file had been deployed
+   * @throws IOException If there's a problem deleting the file
+   */
+  public String undeploy(final String jarName) throws IOException {
+    lock.lock();
+
+    try {
+      DeployedJar deployedJar = deployedJars.remove(jarName);
+      if (deployedJar == null) {
+        throw new IllegalArgumentException("JAR not deployed");
       }
 
-      return byteOutStream.toByteArray();
+      ClassPathLoader.getLatest().rebuildClassLoaderForDeployedJars();
+
+      deployedJar.cleanUp();
+
+      return deployedJar.getFileCanonicalPath();
     } finally {
-      inputStream.close();
+      lock.unlock();
     }
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java b/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java
index f904af1..2b627b2 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/ClusterConfigurationLoader.java
@@ -24,9 +24,11 @@ import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Properties;
 import java.util.Set;
 
+import org.apache.geode.internal.ClassPathLoader;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.UnmodifiableException;
@@ -35,7 +37,7 @@ import org.apache.geode.distributed.internal.DistributionConfig;
 import org.apache.geode.distributed.internal.ClusterConfigurationService;
 import org.apache.geode.distributed.internal.tcpserver.TcpClient;
 import org.apache.geode.internal.ConfigSource;
-import org.apache.geode.internal.JarClassLoader;
+import org.apache.geode.internal.DeployedJar;
 import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.admin.remote.DistributionLocatorId;
 import org.apache.geode.internal.i18n.LocalizedStrings;
@@ -56,45 +58,38 @@ public class ClusterConfigurationLoader {
    * 
    * @param cache Cache of this member
    * @param response {@link ConfigurationResponse} received from the locators
-   * @throws IOException
-   * @throws ClassNotFoundException
    */
   public static void deployJarsReceivedFromClusterConfiguration(Cache cache,
       ConfigurationResponse response) throws IOException, ClassNotFoundException {
-    if (response == null)
+    if (response == null) {
       return;
+    }
 
     String[] jarFileNames = response.getJarNames();
     byte[][] jarBytes = response.getJars();
 
-    final JarDeployer jarDeployer = new JarDeployer(
-        ((GemFireCacheImpl) cache).getDistributedSystem().getConfig().getDeployWorkingDir());
-
-    /******
-     * Un-deploy the existing jars, deployed during cache creation, do not delete anything
-     */
-
     if (jarFileNames != null && jarBytes != null) {
-      JarClassLoader[] jarClassLoaders = jarDeployer.deploy(jarFileNames, jarBytes);
-      for (int i = 0; i < jarFileNames.length; i++) {
-        if (jarClassLoaders[i] != null) {
-          logger.info("Deployed " + (jarClassLoaders[i].getFileCanonicalPath()));
-        }
-      }
+      List<DeployedJar> deployedJars =
+          ClassPathLoader.getLatest().getJarDeployer().deploy(jarFileNames, jarBytes);
+
+      deployedJars.stream().filter(Objects::nonNull)
+          .forEach((jar) -> logger.info("Deployed " + (jar.getFile().getAbsolutePath())));
     }
+    // TODO: Jared - Does this need to actually undeploy extra jars like the javadoc says?
   }
 
   /***
    * Apply the cache-xml cluster configuration on this member
-   * 
+   *
    * @param cache Cache created for this member
    * @param response {@link ConfigurationResponse} containing the requested {@link Configuration}
    * @param config this member's config.
    */
   public static void applyClusterXmlConfiguration(Cache cache, ConfigurationResponse response,
       DistributionConfig config) {
-    if (response == null || response.getRequestedConfiguration().isEmpty())
+    if (response == null || response.getRequestedConfiguration().isEmpty()) {
       return;
+    }
 
     List<String> groups = getGroups(config);
     Map<String, Configuration> requestedConfiguration = response.getRequestedConfiguration();
@@ -138,15 +133,16 @@ public class ClusterConfigurationLoader {
 
   /***
    * Apply the gemfire properties cluster configuration on this member
-   * 
+   *
    * @param cache Cache created for this member
    * @param response {@link ConfigurationResponse} containing the requested {@link Configuration}
    * @param config this member's config
    */
   public static void applyClusterPropertiesConfiguration(Cache cache,
       ConfigurationResponse response, DistributionConfig config) {
-    if (response == null || response.getRequestedConfiguration().isEmpty())
+    if (response == null || response.getRequestedConfiguration().isEmpty()) {
       return;
+    }
 
     List<String> groups = getGroups(config);
     Map<String, Configuration> requestedConfiguration = response.getRequestedConfiguration();
@@ -189,8 +185,6 @@ public class ClusterConfigurationLoader {
    * 
    * @param config this member's configuration.
    * @return {@link ConfigurationResponse}
-   * @throws ClusterConfigurationNotAvailableException
-   * @throws UnknownHostException
    */
   public static ConfigurationResponse requestConfigurationFromLocators(DistributionConfig config,
       List<String> locatorList)

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
index 08f916b..fb311e7 100755
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java
@@ -1192,7 +1192,7 @@ public class GemFireCacheImpl
       listener.cacheCreated(this);
     }
 
-    ClassPathLoader.setLatestToDefault();
+    ClassPathLoader.setLatestToDefault(this.system.getConfig().getDeployWorkingDir());
 
     // request and check cluster configuration
     ConfigurationResponse configurationResponse = requestSharedConfiguration();
@@ -1239,7 +1239,7 @@ public class GemFireCacheImpl
 
     try {
       // Deploy all the jars from the deploy working dir.
-      new JarDeployer(this.system.getConfig().getDeployWorkingDir()).loadPreviouslyDeployedJars();
+      ClassPathLoader.getLatest().getJarDeployer().loadPreviouslyDeployedJars();
       ClusterConfigurationLoader.applyClusterXmlConfiguration(this, configurationResponse,
           system.getConfig());
       initializeDeclarativeCache();

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/internal/cache/persistence/BackupManager.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/persistence/BackupManager.java b/geode-core/src/main/java/org/apache/geode/internal/cache/persistence/BackupManager.java
index d052551..deb53cb 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/persistence/BackupManager.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/persistence/BackupManager.java
@@ -22,7 +22,8 @@ import org.apache.geode.distributed.internal.DM;
 import org.apache.geode.distributed.internal.DistributionConfig;
 import org.apache.geode.distributed.internal.MembershipListener;
 import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
-import org.apache.geode.internal.JarClassLoader;
+import org.apache.geode.internal.ClassPathLoader;
+import org.apache.geode.internal.DeployedJar;
 import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.cache.DiskStoreImpl;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
@@ -290,21 +291,20 @@ public class BackupManager implements MembershipListener {
     JarDeployer deployer = null;
 
     try {
-      deployer = new JarDeployer();
-
       /*
        * Suspend any user deployed jar file updates during this backup.
        */
+      deployer = ClassPathLoader.getLatest().getJarDeployer();
       deployer.suspendAll();
 
-      List<JarClassLoader> jarList = deployer.findJarClassLoaders();
+      List<DeployedJar> jarList = deployer.findDeployedJars();
       if (!jarList.isEmpty()) {
         File userBackupDir = new File(backupDir, USER_FILES);
         if (!userBackupDir.exists()) {
           userBackupDir.mkdir();
         }
 
-        for (JarClassLoader loader : jarList) {
+        for (DeployedJar loader : jarList) {
           File source = new File(loader.getFileCanonicalPath());
           File dest = new File(userBackupDir, source.getName());
           if (source.isDirectory()) {

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DeployFunction.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DeployFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DeployFunction.java
index 5f1f161..148aa5f 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DeployFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/DeployFunction.java
@@ -15,8 +15,10 @@
 package org.apache.geode.management.internal.cli.functions;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
+import org.apache.geode.internal.ClassPathLoader;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.SystemFailure;
@@ -27,7 +29,7 @@ import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.internal.InternalEntity;
-import org.apache.geode.internal.JarClassLoader;
+import org.apache.geode.internal.DeployedJar;
 import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.internal.logging.LogService;
@@ -62,11 +64,12 @@ public class DeployFunction implements Function, InternalEntity {
       }
 
       List<String> deployedList = new ArrayList<String>();
-      JarClassLoader[] jarClassLoaders = jarDeployer.deploy(jarFilenames, jarBytes);
+      List<DeployedJar> jarClassLoaders =
+          ClassPathLoader.getLatest().getJarDeployer().deploy(jarFilenames, jarBytes);
       for (int i = 0; i < jarFilenames.length; i++) {
         deployedList.add(jarFilenames[i]);
-        if (jarClassLoaders[i] != null) {
-          deployedList.add(jarClassLoaders[i].getFileCanonicalPath());
+        if (jarClassLoaders.get(i) != null) {
+          deployedList.add(jarClassLoaders.get(i).getFileCanonicalPath());
         } else {
           deployedList.add("Already deployed");
         }

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ListDeployedFunction.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ListDeployedFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ListDeployedFunction.java
index 8df24db..3d6a321 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ListDeployedFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/ListDeployedFunction.java
@@ -16,6 +16,8 @@ package org.apache.geode.management.internal.cli.functions;
 
 import java.util.List;
 
+import org.apache.geode.internal.ClassPathLoader;
+import org.apache.geode.internal.DeployedJar;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.SystemFailure;
@@ -26,7 +28,6 @@ import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.internal.InternalEntity;
-import org.apache.geode.internal.JarClassLoader;
 import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.internal.logging.LogService;
@@ -45,8 +46,7 @@ public class ListDeployedFunction implements Function, InternalEntity {
 
     try {
       Cache cache = CacheFactory.getAnyInstance();
-      final JarDeployer jarDeployer = new JarDeployer(
-          ((GemFireCacheImpl) cache).getDistributedSystem().getConfig().getDeployWorkingDir());
+      final JarDeployer jarDeployer = ClassPathLoader.getLatest().getJarDeployer();
 
       DistributedMember member = cache.getDistributedSystem().getDistributedMember();
 
@@ -56,10 +56,10 @@ public class ListDeployedFunction implements Function, InternalEntity {
         memberId = member.getName();
       }
 
-      final List<JarClassLoader> jarClassLoaders = jarDeployer.findJarClassLoaders();
+      final List<DeployedJar> jarClassLoaders = jarDeployer.findDeployedJars();
       final String[] jars = new String[jarClassLoaders.size() * 2];
       int index = 0;
-      for (JarClassLoader jarClassLoader : jarClassLoaders) {
+      for (DeployedJar jarClassLoader : jarClassLoaders) {
         jars[index++] = jarClassLoader.getJarName();
         jars[index++] = jarClassLoader.getFileCanonicalPath();
       }

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/UndeployFunction.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/UndeployFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/UndeployFunction.java
index 3f05082..14d875e 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/UndeployFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/functions/UndeployFunction.java
@@ -18,6 +18,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.StringTokenizer;
 
+import org.apache.geode.internal.ClassPathLoader;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.SystemFailure;
@@ -28,7 +29,7 @@ import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionContext;
 import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.internal.InternalEntity;
-import org.apache.geode.internal.JarClassLoader;
+import org.apache.geode.internal.DeployedJar;
 import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.cache.GemFireCacheImpl;
 import org.apache.geode.internal.logging.LogService;
@@ -50,8 +51,7 @@ public class UndeployFunction implements Function, InternalEntity {
       final String jarFilenameList = (String) args[0]; // Comma separated
       Cache cache = CacheFactory.getAnyInstance();
 
-      final JarDeployer jarDeployer = new JarDeployer(
-          ((GemFireCacheImpl) cache).getDistributedSystem().getConfig().getDeployWorkingDir());
+      final JarDeployer jarDeployer = ClassPathLoader.getLatest().getJarDeployer();
 
       DistributedMember member = cache.getDistributedSystem().getDistributedMember();
 
@@ -63,13 +63,14 @@ public class UndeployFunction implements Function, InternalEntity {
 
       String[] undeployedJars = new String[0];
       if (jarFilenameList == null || jarFilenameList.equals("")) {
-        final List<JarClassLoader> jarClassLoaders = jarDeployer.findJarClassLoaders();
+        final List<DeployedJar> jarClassLoaders = jarDeployer.findDeployedJars();
         undeployedJars = new String[jarClassLoaders.size() * 2];
         int index = 0;
-        for (JarClassLoader jarClassLoader : jarClassLoaders) {
+        for (DeployedJar jarClassLoader : jarClassLoaders) {
           undeployedJars[index++] = jarClassLoader.getJarName();
           try {
-            undeployedJars[index++] = jarDeployer.undeploy(jarClassLoader.getJarName());
+            undeployedJars[index++] =
+                ClassPathLoader.getLatest().getJarDeployer().undeploy(jarClassLoader.getJarName());
           } catch (IllegalArgumentException iaex) {
             // It's okay for it to have have been uneployed from this server
             undeployedJars[index++] = iaex.getMessage();
@@ -82,7 +83,7 @@ public class UndeployFunction implements Function, InternalEntity {
           String jarFilename = jarTokenizer.nextToken().trim();
           try {
             undeployedList.add(jarFilename);
-            undeployedList.add(jarDeployer.undeploy(jarFilename));
+            undeployedList.add(ClassPathLoader.getLatest().getJarDeployer().undeploy(jarFilename));
           } catch (IllegalArgumentException iaex) {
             // It's okay for it to not have been deployed to this server
             undeployedList.add(iaex.getMessage());

http://git-wip-us.apache.org/repos/asf/geode/blob/6fd2d123/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
index c52d575..d30feb6 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/ClassPathLoaderIntegrationTest.java
@@ -14,6 +14,8 @@
  */
 package org.apache.geode.internal;
 
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.*;
 
 import java.io.BufferedInputStream;
@@ -24,11 +26,20 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.URL;
 import java.util.Enumeration;
+import java.util.List;
+import java.util.Properties;
 import java.util.Vector;
 
 import org.apache.bcel.Constants;
 import org.apache.bcel.classfile.JavaClass;
 import org.apache.bcel.generic.ClassGen;
+import org.apache.commons.io.FileUtils;
+import org.apache.geode.cache.execute.Execution;
+import org.apache.geode.cache.execute.FunctionService;
+import org.apache.geode.cache.execute.ResultCollector;
+import org.apache.geode.distributed.DistributedSystem;
+import org.apache.geode.internal.cache.GemFireCacheImpl;
+import org.apache.geode.test.dunit.rules.ServerStarterRule;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -53,8 +64,9 @@ public class ClassPathLoaderIntegrationTest {
 
   private static final int TEMP_FILE_BYTES_COUNT = 256;
 
-  private volatile File tempFile;
-  private volatile File tempFile2;
+  private File tempFile;
+  private File tempFile2;
+  private File extLibsDir;
 
   @Rule
   public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
@@ -65,8 +77,9 @@ public class ClassPathLoaderIntegrationTest {
   @Before
   public void setUp() throws Exception {
     System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, "false");
-    System.setProperty(ClassPathLoader.EXT_LIB_DIR_PARENT_PROPERTY,
-        this.temporaryFolder.getRoot().getAbsolutePath());
+
+    extLibsDir = new File(this.temporaryFolder.getRoot(), "ext");
+    extLibsDir.mkdirs();
 
     this.tempFile = this.temporaryFolder.newFile("tempFile1.tmp");
     FileOutputStream fos = new FileOutputStream(this.tempFile);
@@ -77,98 +90,248 @@ public class ClassPathLoaderIntegrationTest {
     fos = new FileOutputStream(this.tempFile2);
     fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
     fos.close();
+
+    System.setProperty("user.dir", temporaryFolder.getRoot().getAbsolutePath());
+    ClassPathLoader.setLatestToDefault(temporaryFolder.getRoot());
   }
 
-  /**
-   * Verifies that <tt>getResource</tt> works with custom loader from {@link ClassPathLoader}.
-   */
+
   @Test
-  public void testGetResourceWithCustomLoader() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithCustomLoader");
+  public void testDeployFileAndChange() throws IOException, ClassNotFoundException {
+    String jarName = "JarDeployerIntegrationTest.jar";
 
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+    String classAResource = "integration/parent/ClassA.class";
+    String classBResource = "integration/parent/ClassB.class";
 
-    String resourceToGet = "com/nowhere/testGetResourceWithCustomLoader.rsc";
-    URL url = dcl.getResource(resourceToGet);
-    assertNotNull(url);
+    String classAName = "integration.parent.ClassA";
+    String classBName = "integration.parent.ClassB";
 
-    InputStream is = url != null ? url.openStream() : null;
-    assertNotNull(is);
+    byte[] firstJarBytes = createJarWithClass("ClassA");
 
-    int totalBytesRead = 0;
-    byte[] input = new byte[128];
+    // First deploy of the JAR file
+    File firstDeployedJarFile =
+        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, firstJarBytes).getFile();
 
-    BufferedInputStream bis = new BufferedInputStream(is);
-    for (int bytesRead = bis.read(input); bytesRead > -1;) {
-      totalBytesRead += bytesRead;
-      bytesRead = bis.read(input);
-    }
-    bis.close();
+    assertThat(firstDeployedJarFile).exists().hasBinaryContent(firstJarBytes);
+    assertThat(firstDeployedJarFile.getName()).contains(".v1.").doesNotContain(".v2.");
+
+    assertThatClassCanBeLoaded(classAName);
+    assertThatClassCannotBeLoaded(classBName);
+
+    assertThatResourceCanBeLoaded(classAResource);
+    assertThatResourceCannotBeLoaded(classBResource);
 
-    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+    // Now deploy an updated JAR file and make sure that the next version of the JAR file
+    // was created and the first one is no longer used
+    byte[] secondJarBytes = createJarWithClass("ClassB");
+
+    File secondDeployedJarFile =
+        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, secondJarBytes).getFile();
+
+    assertThat(secondDeployedJarFile).exists().hasBinaryContent(secondJarBytes);
+    assertThat(secondDeployedJarFile.getName()).contains(".v2.").doesNotContain(".v1.");
+
+    assertThatClassCanBeLoaded(classBName);
+    assertThatClassCannotBeLoaded(classAName);
+
+    assertThatResourceCanBeLoaded(classBResource);
+    assertThatResourceCannotBeLoaded(classAResource);
+
+    // Now undeploy JAR and make sure it gets cleaned up
+    ClassPathLoader.getLatest().getJarDeployer().undeploy(jarName);
+    assertThatClassCannotBeLoaded(classBName);
+    assertThatClassCannotBeLoaded(classAName);
+
+    assertThatResourceCannotBeLoaded(classBResource);
+    assertThatResourceCannotBeLoaded(classAResource);
   }
 
-  /**
-   * Verifies that <tt>getResources</tt> works with custom loader from {@link ClassPathLoader}.
-   */
   @Test
-  public void testGetResourcesWithCustomLoader() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithCustomLoader");
+  public void testDeployNoUpdateWhenNoChange() throws IOException, ClassNotFoundException {
+    String jarName = "JarDeployerIntegrationTest.jar";
 
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+    // First deploy of the JAR file
+    byte[] jarBytes = new ClassBuilder().createJarFromName("JarDeployerDUnitDNUWNC");
+    DeployedJar jarClassLoader =
+        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarBytes);
+    File deployedJar = new File(jarClassLoader.getFileCanonicalPath());
 
-    String resourceToGet = "com/nowhere/testGetResourceWithCustomLoader.rsc";
-    Enumeration<URL> urls = dcl.getResources(resourceToGet);
-    assertNotNull(urls);
-    assertTrue(urls.hasMoreElements());
+    assertThat(deployedJar).exists();
+    assertThat(deployedJar.getName()).contains(".v1.");
 
-    URL url = urls.nextElement();
-    InputStream is = url != null ? url.openStream() : null;
-    assertNotNull(is);
+    // Re-deploy of the same JAR should do nothing
+    DeployedJar newJarClassLoader =
+        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarBytes);
+    assertThat(newJarClassLoader).isNull();
+    assertThat(deployedJar).exists();
 
-    int totalBytesRead = 0;
-    byte[] input = new byte[128];
+  }
 
-    BufferedInputStream bis = new BufferedInputStream(is);
-    for (int bytesRead = bis.read(input); bytesRead > -1;) {
-      totalBytesRead += bytesRead;
-      bytesRead = bis.read(input);
-    }
-    bis.close();
+  @Test
+  public void testDeployWithExistingDependentJars() throws Exception {
+    ClassBuilder classBuilder = new ClassBuilder();
+    final File parentJarFile =
+        new File(temporaryFolder.getRoot(), "JarDeployerDUnitAParent.v1.jar");
+    final File usesJarFile = new File(temporaryFolder.getRoot(), "JarDeployerDUnitUses.v1.jar");
+    final File functionJarFile =
+        new File(temporaryFolder.getRoot(), "JarDeployerDUnitFunction.v1.jar");
+
+    // Write out a JAR files.
+    StringBuffer stringBuffer = new StringBuffer();
+    stringBuffer.append("package jddunit.parent;");
+    stringBuffer.append("public class JarDeployerDUnitParent {");
+    stringBuffer.append("public String getValueParent() {");
+    stringBuffer.append("return \"PARENT\";}}");
+
+    byte[] jarBytes = classBuilder.createJarFromClassContent(
+        "jddunit/parent/JarDeployerDUnitParent", stringBuffer.toString());
+    FileOutputStream outStream = new FileOutputStream(parentJarFile);
+    outStream.write(jarBytes);
+    outStream.close();
+
+    stringBuffer = new StringBuffer();
+    stringBuffer.append("package jddunit.uses;");
+    stringBuffer.append("public class JarDeployerDUnitUses {");
+    stringBuffer.append("public String getValueUses() {");
+    stringBuffer.append("return \"USES\";}}");
 
-    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+    jarBytes = classBuilder.createJarFromClassContent("jddunit/uses/JarDeployerDUnitUses",
+        stringBuffer.toString());
+    outStream = new FileOutputStream(usesJarFile);
+    outStream.write(jarBytes);
+    outStream.close();
+
+    stringBuffer = new StringBuffer();
+    stringBuffer.append("package jddunit.function;");
+    stringBuffer.append("import jddunit.parent.JarDeployerDUnitParent;");
+    stringBuffer.append("import jddunit.uses.JarDeployerDUnitUses;");
+    stringBuffer.append("import org.apache.geode.cache.execute.Function;");
+    stringBuffer.append("import org.apache.geode.cache.execute.FunctionContext;");
+    stringBuffer.append(
+        "public class JarDeployerDUnitFunction  extends JarDeployerDUnitParent implements Function {");
+    stringBuffer.append("private JarDeployerDUnitUses uses = new JarDeployerDUnitUses();");
+    stringBuffer.append("public boolean hasResult() {return true;}");
+    stringBuffer.append(
+        "public void execute(FunctionContext context) {context.getResultSender().lastResult(getValueParent() + \":\" + uses.getValueUses());}");
+    stringBuffer.append("public String getId() {return \"JarDeployerDUnitFunction\";}");
+    stringBuffer.append("public boolean optimizeForWrite() {return false;}");
+    stringBuffer.append("public boolean isHA() {return false;}}");
+
+    ClassBuilder functionClassBuilder = new ClassBuilder();
+    functionClassBuilder.addToClassPath(parentJarFile.getAbsolutePath());
+    functionClassBuilder.addToClassPath(usesJarFile.getAbsolutePath());
+    jarBytes = functionClassBuilder.createJarFromClassContent(
+        "jddunit/function/JarDeployerDUnitFunction", stringBuffer.toString());
+    outStream = new FileOutputStream(functionJarFile);
+    outStream.write(jarBytes);
+    outStream.close();
+
+    Properties properties = new Properties();
+    properties.setProperty("user.dir", temporaryFolder.getRoot().getAbsolutePath());
+    ServerStarterRule serverStarterRule = new ServerStarterRule();
+    serverStarterRule.startServer();
+
+    GemFireCacheImpl gemFireCache = GemFireCacheImpl.getInstance();
+    DistributedSystem distributedSystem = gemFireCache.getDistributedSystem();
+    Execution execution =
+        FunctionService.onMember(distributedSystem, distributedSystem.getDistributedMember());
+    ResultCollector resultCollector = execution.execute("JarDeployerDUnitFunction");
+    @SuppressWarnings("unchecked")
+    List<String> result = (List<String>) resultCollector.getResult();
+    assertEquals("PARENT:USES", result.get(0));
+
+    serverStarterRule.after();
   }
 
-  /**
-   * Verifies that <tt>getResourceAsStream</tt> works with custom loader from
-   * {@link ClassPathLoader}.
-   */
   @Test
-  public void testGetResourceAsStreamWithCustomLoader() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceAsStreamWithCustomLoader");
+  public void deployNewVersionOfFunctionOverOldVersion() throws Exception {
+    File jarVersion1 = createVersionOfJar("Version1", "MyFunction", "MyJar.jar");
+    File jarVersion2 = createVersionOfJar("Version2", "MyFunction", "MyJar.jar");
 
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl = dcl.addOrReplace(new GeneratingClassLoader());
+    Properties properties = new Properties();
+    properties.setProperty("user.dir", temporaryFolder.getRoot().getAbsolutePath());
+    ServerStarterRule serverStarterRule = new ServerStarterRule();
+    serverStarterRule.startServer();
 
-    String resourceToGet = "com/nowhere/testGetResourceAsStreamWithCustomLoader.rsc";
-    InputStream is = dcl.getResourceAsStream(resourceToGet);
-    assertNotNull(is);
+    GemFireCacheImpl gemFireCache = GemFireCacheImpl.getInstance();
+    DistributedSystem distributedSystem = gemFireCache.getDistributedSystem();
 
-    int totalBytesRead = 0;
-    byte[] input = new byte[128];
+    ClassPathLoader.getLatest().getJarDeployer().deploy("MyJar.jar",
+        FileUtils.readFileToByteArray(jarVersion1));
 
-    BufferedInputStream bis = new BufferedInputStream(is);
-    for (int bytesRead = bis.read(input); bytesRead > -1;) {
-      totalBytesRead += bytesRead;
-      bytesRead = bis.read(input);
-    }
-    bis.close();
+    assertThatClassCanBeLoaded("jddunit.function.MyFunction");
+    Execution execution =
+        FunctionService.onMember(distributedSystem, distributedSystem.getDistributedMember());
+
+    List<String> result = (List<String>) execution.execute("MyFunction").getResult();
+    assertThat(result.get(0)).isEqualTo("Version1");
+
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("MyJar.jar",
+        FileUtils.readFileToByteArray(jarVersion2));
+    result = (List<String>) execution.execute("MyFunction").getResult();
+    assertThat(result.get(0)).isEqualTo("Version2");
 
-    assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
+
+    serverStarterRule.after();
   }
 
+
+  private File createVersionOfJar(String version, String functionName, String jarName)
+      throws IOException {
+    String classContents =
+        "package jddunit.function;" + "import org.apache.geode.cache.execute.Function;"
+            + "import org.apache.geode.cache.execute.FunctionContext;" + "public class "
+            + functionName + " implements Function {" + "public boolean hasResult() {return true;}"
+            + "public String getId() {return \"" + functionName + "\";}"
+            + "public void execute(FunctionContext context) {context.getResultSender().lastResult(\""
+            + version + "\");}}";
+
+    File jar = new File(this.temporaryFolder.newFolder(version), jarName);
+    ClassBuilder functionClassBuilder = new ClassBuilder();
+    functionClassBuilder.writeJarFromContent("jddunit/function/" + functionName, classContents,
+        jar);
+
+    return jar;
+  }
+
+  private void assertThatClassCanBeLoaded(String className) throws ClassNotFoundException {
+    assertThat(ClassPathLoader.getLatest().forName(className)).isNotNull();
+  }
+
+  private void assertThatClassCannotBeLoaded(String className) throws ClassNotFoundException {
+    assertThatThrownBy(() -> ClassPathLoader.getLatest().forName(className))
+        .isExactlyInstanceOf(ClassNotFoundException.class);
+  }
+
+  private void assertThatResourceCanBeLoaded(String resourceName) throws IOException {
+    // ClassPathLoader.getResource
+    assertThat(ClassPathLoader.getLatest().getResource(resourceName)).isNotNull();
+
+    // ClassPathLoader.getResources
+    Enumeration<URL> urls = ClassPathLoader.getLatest().getResources(resourceName);
+    assertThat(urls).isNotNull();
+    assertThat(urls.hasMoreElements()).isTrue();
+
+    // ClassPathLoader.getResourceAsStream
+    InputStream is = ClassPathLoader.getLatest().getResourceAsStream(resourceName);
+    assertThat(is).isNotNull();
+  }
+
+  private void assertThatResourceCannotBeLoaded(String resourceName) throws IOException {
+    // ClassPathLoader.getResource
+    assertThat(ClassPathLoader.getLatest().getResource(resourceName)).isNull();
+
+    // ClassPathLoader.getResources
+    Enumeration<URL> urls = ClassPathLoader.getLatest().getResources(resourceName);
+    assertThat(urls.hasMoreElements()).isFalse();
+
+    // ClassPathLoader.getResourceAsStream
+    InputStream is = ClassPathLoader.getLatest().getResourceAsStream(resourceName);
+    assertThat(is).isNull();
+  }
+
+
   /**
    * Verifies that <tt>getResource</tt> works with TCCL from {@link ClassPathLoader}.
    */
@@ -281,154 +444,6 @@ public class ClassPathLoaderIntegrationTest {
     }
   }
 
-  /**
-   * Verifies that JAR files found in the extlib directory will be correctly added to the
-   * {@link ClassPathLoader}.
-   */
-  @Test
-  public void testJarsInExtLib() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testJarsInExtLib");
-
-    File EXT_LIB_DIR = ClassPathLoader.defineEXT_LIB_DIR();
-    EXT_LIB_DIR.mkdir();
-
-    File subdir = new File(EXT_LIB_DIR, "cplju");
-    subdir.mkdir();
-
-    final ClassBuilder classBuilder = new ClassBuilder();
-
-    writeJarBytesToFile(new File(EXT_LIB_DIR, "ClassPathLoaderJUnit1.jar"),
-        classBuilder.createJarFromClassContent("com/cpljunit1/ClassPathLoaderJUnit1",
-            "package com.cpljunit1; public class ClassPathLoaderJUnit1 {}"));
-    writeJarBytesToFile(new File(subdir, "ClassPathLoaderJUnit2.jar"),
-        classBuilder.createJarFromClassContent("com/cpljunit2/ClassPathLoaderJUnit2",
-            "package com.cpljunit2; public class ClassPathLoaderJUnit2 {}"));
-
-    ClassPathLoader classPathLoader = ClassPathLoader.createWithDefaults(false);
-    try {
-      classPathLoader.forName("com.cpljunit1.ClassPathLoaderJUnit1");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    try {
-      classPathLoader.forName("com.cpljunit2.ClassPathLoaderJUnit2");
-    } catch (ClassNotFoundException cnfex) {
-      fail("JAR file not correctly added to Classpath");
-    }
-
-    assertNotNull(classPathLoader.getResource("com/cpljunit2/ClassPathLoaderJUnit2.class"));
-
-    Enumeration<URL> urls = classPathLoader.getResources("com/cpljunit1");
-    if (!urls.hasMoreElements()) {
-      fail("Resources should return one element");
-    }
-  }
-
-  /**
-   * Verifies that the 3rd custom loader will get the resource. Parent cannot find it and TCCL is
-   * broken. This verifies that all custom loaders are checked and that the custom loaders are all
-   * checked before TCCL.
-   */
-  @Test
-  public void testGetResourceAsStreamWithMultipleCustomLoaders() throws Exception {
-    System.out
-        .println("\nStarting ClassPathLoaderTest#testGetResourceAsStreamWithMultipleCustomLoaders");
-
-    // create DCL such that the 3rd loader should find the resource
-    // first custom loader becomes parent which won't find anything
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl = dcl.addOrReplace(new GeneratingClassLoader());
-    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
-    dcl = dcl.addOrReplace(new NullClassLoader());
-
-    String resourceToGet = "com/nowhere/testGetResourceAsStreamWithMultipleCustomLoaders.rsc";
-
-    ClassLoader cl = Thread.currentThread().getContextClassLoader();
-    try {
-      // set TCCL to throw errors which makes sure we find before checking TCCL
-      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
-
-      InputStream is = dcl.getResourceAsStream(resourceToGet);
-      assertNotNull(is);
-      is.close();
-    } finally {
-      Thread.currentThread().setContextClassLoader(cl);
-    }
-  }
-
-  /**
-   * Verifies that the 3rd custom loader will get the resource. Parent cannot find it and TCCL is
-   * broken. This verifies that all custom loaders are checked and that the custom loaders are all
-   * checked before TCCL.
-   */
-  @Test
-  public void testGetResourceWithMultipleCustomLoaders() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithMultipleCustomLoaders");
-
-    // create DCL such that the 3rd loader should find the resource
-    // first custom loader becomes parent which won't find anything
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl = dcl.addOrReplace(new GeneratingClassLoader());
-    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
-    dcl = dcl.addOrReplace(new NullClassLoader());
-
-    String resourceToGet = "com/nowhere/testGetResourceWithMultipleCustomLoaders.rsc";
-
-    ClassLoader cl = Thread.currentThread().getContextClassLoader();
-    try {
-      // set TCCL to throw errors which makes sure we find before checking TCCL
-      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
-
-      URL url = dcl.getResource(resourceToGet);
-      assertNotNull(url);
-    } finally {
-      Thread.currentThread().setContextClassLoader(cl);
-    }
-  }
-
-  /**
-   * Verifies that the 3rd custom loader will get the resources. Parent cannot find it and TCCL is
-   * broken. This verifies that all custom loaders are checked and that the custom loaders are all
-   * checked before TCCL.
-   */
-  @Test
-  public void testGetResourcesWithMultipleCustomLoaders() throws Exception {
-    System.out.println("\nStarting ClassPathLoaderTest#testGetResourceWithMultipleCustomLoaders");
-
-    // create DCL such that the 3rd loader should find the resource
-    // first custom loader becomes parent which won't find anything
-    ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
-    dcl = dcl.addOrReplace(new GeneratingClassLoader());
-    dcl = dcl.addOrReplace(new GeneratingClassLoader2());
-    dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
-    dcl = dcl.addOrReplace(new NullClassLoader());
-
-    String resourceToGet = "com/nowhere/testGetResourceWithMultipleCustomLoaders.rsc";
-
-    ClassLoader cl = Thread.currentThread().getContextClassLoader();
-    try {
-      // set TCCL to throw errors which makes sure we find before checking TCCL
-      Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
-
-      Enumeration<URL> urls = dcl.getResources(resourceToGet);
-      assertNotNull(urls);
-      assertTrue(urls.hasMoreElements());
-
-      URL url = urls.nextElement();
-      assertNotNull(url);
-
-      // Should find two with unique URLs
-      assertTrue("Did not find all resources.", urls.hasMoreElements());
-      URL url2 = urls.nextElement();
-      assertNotNull(url2);
-      assertTrue("Resource URLs should be unique.", !url.equals(url2));
-
-    } finally {
-      Thread.currentThread().setContextClassLoader(cl);
-    }
-  }
-
   private void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
     final OutputStream outStream = new FileOutputStream(jarFile);
     outStream.write(jarBytes);
@@ -508,4 +523,11 @@ public class ClassPathLoaderIntegrationTest {
       return tempFile2;
     }
   }
+
+  private byte[] createJarWithClass(String className) throws IOException {
+    String stringBuilder = "package integration.parent;" + "public class " + className + " {}";
+
+    return new ClassBuilder().createJarFromClassContent("integration/parent/" + className,
+        stringBuilder);
+  }
 }


[7/8] geode git commit: GEODE-2686: Add more logging to JarDeployer

Posted by js...@apache.org.
GEODE-2686: Add more logging to JarDeployer


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

Branch: refs/heads/develop
Commit: 8f9624d05abf6a9dea20a9cf7aee2b6a0b7ccb8b
Parents: ee11b0a
Author: Jared Stewart <js...@pivotal.io>
Authored: Mon Apr 10 14:49:09 2017 -0700
Committer: Jared Stewart <js...@pivotal.io>
Committed: Sun Apr 16 09:10:01 2017 -0700

----------------------------------------------------------------------
 .../main/java/org/apache/geode/internal/JarDeployer.java    | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/8f9624d0/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
----------------------------------------------------------------------
diff --git a/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java b/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
index 669802c..da4c136 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/JarDeployer.java
@@ -14,6 +14,7 @@
  */
 package org.apache.geode.internal;
 
+import static java.util.stream.Collectors.joining;
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toSet;
 
@@ -89,6 +90,7 @@ public class JarDeployer implements Serializable {
     try {
       boolean shouldDeployNewVersion = shouldDeployNewVersion(jarName, jarBytes);
       if (!shouldDeployNewVersion) {
+        logger.debug("No need to deploy a new version of {}", jarName);
         return null;
       }
 
@@ -146,6 +148,8 @@ public class JarDeployer implements Serializable {
       nextVersionedJarName = removeJarExtension(unversionedJarName) + ".v" + nextVersion + ".jar";
     }
 
+    logger.debug("Next versioned jar name for {} is {}", unversionedJarName, nextVersionedJarName);
+
     return new File(deployDirectory, nextVersionedJarName);
   }
 
@@ -274,6 +278,7 @@ public class JarDeployer implements Serializable {
    * @return Sorted array of files that are older versions of the given JAR
    */
   protected File[] findSortedOldVersionsOfJar(final String unversionedJarName) {
+    logger.debug("Finding sorted old versions of {}", unversionedJarName);
     // Find all matching files
     final Pattern pattern = Pattern.compile(
         JAR_PREFIX_FOR_REGEX + removeJarExtension(unversionedJarName) + "\\.v\\d++\\.jar$");
@@ -287,6 +292,8 @@ public class JarDeployer implements Serializable {
       return file2Version - file1Version;
     });
 
+    logger.debug("Found [{}]",
+        Arrays.stream(oldJarFiles).map(File::getAbsolutePath).collect(joining(",")));
     return oldJarFiles;
   }
 
@@ -458,7 +465,7 @@ public class JarDeployer implements Serializable {
     try {
       for (DeployedJar deployedJar : deployedJars) {
         if (deployedJar != null) {
-          logger.info("Registering new version of jar: {}", deployedJar.toString());
+          logger.info("Registering new version of jar: {}", deployedJar);
           DeployedJar oldJar = this.deployedJars.put(deployedJar.getJarName(), deployedJar);
           if (oldJar != null) {
             oldJar.cleanUp();