geode-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jensde...@apache.org
Subject [geode] 01/01: GEODE-4131: Do not reference deployed jars as byte arrays anymore
Date Wed, 27 Dec 2017 22:13:54 GMT
This is an automated email from the ASF dual-hosted git repository.

jensdeppe pushed a commit to branch feature/GEODE-4131
in repository https://gitbox.apache.org/repos/asf/geode.git

commit 9f338eb5113bb8b432694f57a72c792eaa806c20
Author: Jens Deppe <jdeppe@pivotal.io>
AuthorDate: Wed Dec 27 13:45:59 2017 -0800

    GEODE-4131: Do not reference deployed jars as byte arrays anymore
    
    - All jars are streamed between locators and servers using the RMIIO library.
---
 geode-core/build.gradle                            |   1 +
 .../internal/ClusterConfigurationService.java      | 108 ++++++++-------
 .../org/apache/geode/internal/DeployedJar.java     | 100 ++++++--------
 .../geode/internal/InternalDataSerializer.java     |   1 +
 .../org/apache/geode/internal/JarDeployer.java     | 148 ++++-----------------
 .../internal/cache/ClusterConfigurationLoader.java |  83 ++++++++++--
 .../geode/internal/cache/GemFireCacheImpl.java     |  17 +--
 .../geode/management/internal/cli/CliUtil.java     |  20 ++-
 .../internal/cli/commands/DeployCommand.java       |  58 +++++---
 .../internal/cli/functions/DeployFunction.java     |  79 ++++++++++-
 ...adJarFunction.java => DownloadJarFunction.java} |  38 +++++-
 .../functions/GetClusterConfigurationFunction.java |   2 +-
 .../messages/ConfigurationResponse.java            |  35 ++---
 .../sanctioned-geode-core-serializables.txt        |   2 +-
 .../internal/ClassPathLoaderIntegrationTest.java   |  75 +++++++----
 .../apache/geode/internal/ClassPathLoaderTest.java |  37 +++++-
 .../geode/internal/DeployedJarJUnitTest.java       |  13 +-
 .../geode/internal/JarDeployerDeadlockTest.java    |  11 +-
 .../geode/internal/JarDeployerIntegrationTest.java |  22 ++-
 .../backup/IncrementalBackupDistributedTest.java   |   8 +-
 .../commands/DeployCommandRedeployDUnitTest.java   |   2 +-
 .../cli/commands/DeployWithGroupsDUnitTest.java    |   4 +-
 .../internal/configuration/ClusterConfig.java      |   4 +-
 .../codeAnalysis/sanctionedDataSerializables.txt   |   5 +-
 gradle/dependency-versions.properties              |   1 +
 25 files changed, 514 insertions(+), 360 deletions(-)

diff --git a/geode-core/build.gradle b/geode-core/build.gradle
index 041e411..77c02ba 100755
--- a/geode-core/build.gradle
+++ b/geode-core/build.gradle
@@ -132,6 +132,7 @@ dependencies {
   // https://mvnrepository.com/artifact/io.github.lukehutch/fast-classpath-scanner
   compile 'io.github.lukehutch:fast-classpath-scanner:' + project.'fast-classpath-scanner.version'
 
+  compile 'com.healthmarketscience.rmiio:rmiio:' + project.'rmiio.version'
 
   compile project(':geode-common')
   compile project(':geode-json')
diff --git a/geode-core/src/main/java/org/apache/geode/distributed/internal/ClusterConfigurationService.java b/geode-core/src/main/java/org/apache/geode/distributed/internal/ClusterConfigurationService.java
index beb6ae5..3942aba 100644
--- a/geode-core/src/main/java/org/apache/geode/distributed/internal/ClusterConfigurationService.java
+++ b/geode-core/src/main/java/org/apache/geode/distributed/internal/ClusterConfigurationService.java
@@ -21,11 +21,17 @@ import static org.apache.geode.distributed.ConfigurationProperties.SECURITY_POST
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileFilter;
+import java.io.FileOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -45,6 +51,7 @@ import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactoryConfigurationError;
 
+import com.healthmarketscience.rmiio.RemoteInputStream;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.FilenameUtils;
 import org.apache.commons.io.filefilter.DirectoryFileFilter;
@@ -78,7 +85,7 @@ import org.apache.geode.management.internal.configuration.callbacks.Configuratio
 import org.apache.geode.management.internal.configuration.domain.Configuration;
 import org.apache.geode.management.internal.configuration.domain.SharedConfigurationStatus;
 import org.apache.geode.management.internal.configuration.domain.XmlEntity;
-import org.apache.geode.management.internal.configuration.functions.UploadJarFunction;
+import org.apache.geode.management.internal.configuration.functions.DownloadJarFunction;
 import org.apache.geode.management.internal.configuration.messages.ConfigurationResponse;
 import org.apache.geode.management.internal.configuration.messages.SharedConfigurationStatusResponse;
 import org.apache.geode.management.internal.configuration.utils.XmlUtils;
@@ -382,23 +389,6 @@ public class ClusterConfigurationService {
     return success;
   }
 
-  /**
-   * read the jar bytes in the file system
-   * <p>
-   * used when creating cluster config response and used when uploading the jars to another locator
-   */
-  public byte[] getJarBytesFromThisLocator(String group, String jarName) throws IOException {
-    Configuration configuration = getConfiguration(group);
-
-    File jar = getPathToJarOnThisLocator(group, jarName).toFile();
-
-    if (configuration == null || !configuration.getJarNames().contains(jarName) || !jar.exists()) {
-      return null;
-    }
-
-    return FileUtils.readFileToByteArray(jar);
-  }
-
   // Only used when a locator is initially starting up
   public void downloadJarFromOtherLocators(String groupName, String jarName)
       throws IllegalStateException, IOException {
@@ -416,10 +406,7 @@ public class ClusterConfigurationService {
           "Request to download jar " + jarName + " but no other locators are present");
     }
 
-    byte[] jarBytes = downloadJar(locators.get(0), groupName, jarName);
-
-    File jarToWrite = getPathToJarOnThisLocator(groupName, jarName).toFile();
-    FileUtils.writeByteArrayToFile(jarToWrite, jarBytes);
+    downloadJarFromLocator(groupName, jarName, locators.get(0));
   }
 
   // used in the cluster config change listener when jarnames are changed in the internal region
@@ -429,46 +416,67 @@ public class ClusterConfigurationService {
 
     createConfigDirIfNecessary(groupName);
 
-    byte[] jarBytes = downloadJar(sourceLocator, groupName, jarName);
-
-    if (jarBytes == null) {
-      throw new IllegalStateException("Could not download jar " + jarName + " in " + groupName
-          + " from " + sourceLocator.getName());
-    }
+    File jarFile = downloadJar(sourceLocator, groupName, jarName);
 
     File jarToWrite = getPathToJarOnThisLocator(groupName, jarName).toFile();
-    FileUtils.writeByteArrayToFile(jarToWrite, jarBytes);
+    Files.copy(jarFile.toPath(), jarToWrite.toPath(), StandardCopyOption.REPLACE_EXISTING);
   }
 
-  private byte[] downloadJar(DistributedMember locator, String groupName, String jarName) {
-    ResultCollector<byte[], List<byte[]>> rc =
-        (ResultCollector<byte[], List<byte[]>>) CliUtil.executeFunction(new UploadJarFunction(),
-            new Object[] {groupName, jarName}, Collections.singleton(locator));
-
-    List<byte[]> result = rc.getResult();
+  /**
+   * Retrieve a deployed jar from a locator. The retrieved file is staged in a temporary location.
+   *
+   * @param locator the DistributedMember
+   * @param groupName the group to use when retrieving the jar
+   * @param jarName the name of the deployed jar
+   * @return a File referencing the downloaded jar. The File is downloaded to a temporary location.
+   */
+  public File downloadJar(DistributedMember locator, String groupName, String jarName)
+      throws IOException {
+    ResultCollector<RemoteInputStream, List<RemoteInputStream>> rc =
+        (ResultCollector<RemoteInputStream, List<RemoteInputStream>>) CliUtil.executeFunction(
+            new DownloadJarFunction(), new Object[] {groupName, jarName},
+            Collections.singleton(locator));
+
+    List<RemoteInputStream> result = rc.getResult();
+    RemoteInputStream jarStream = result.get(0);
+
+    Set<PosixFilePermission> perms = new HashSet<>();
+    perms.add(PosixFilePermission.OWNER_READ);
+    perms.add(PosixFilePermission.OWNER_WRITE);
+    perms.add(PosixFilePermission.OWNER_EXECUTE);
+    Path tempDir =
+        Files.createTempDirectory("deploy-", PosixFilePermissions.asFileAttribute(perms));
+    Path tempJar = Paths.get(tempDir.toString(), jarName);
+    FileOutputStream fos = new FileOutputStream(tempJar.toString());
+
+    int packetId = 0;
+    while (true) {
+      byte[] data = jarStream.readPacket(packetId);
+      if (data == null) {
+        break;
+      }
+      fos.write(data);
+      packetId++;
+    }
+    fos.close();
+    jarStream.close(true);
 
-    // we should only get one byte[] back in the list
-    return result.get(0);
+    return tempJar.toFile();
   }
 
   // used when creating cluster config response
-  public Map<String, byte[]> getAllJarsFromThisLocator(Set<String> groups) throws IOException {
-    Map<String, byte[]> jarNamesToJarBytes = new HashMap<>();
-
+  public Set<String> getAllJarNamesFromThisLocator(Set<String> groups) {
+    Set<String> jarNames = new HashSet<>();
     for (String group : groups) {
       Configuration groupConfig = getConfiguration(group);
       if (groupConfig == null) {
         break;
       }
 
-      Set<String> jars = groupConfig.getJarNames();
-      for (String jar : jars) {
-        byte[] jarBytes = getJarBytesFromThisLocator(group, jar);
-        jarNamesToJarBytes.put(jar, jarBytes);
-      }
+      jarNames.addAll(groupConfig.getJarNames());
     }
 
-    return jarNamesToJarBytes;
+    return jarNames;
   }
 
   /**
@@ -547,13 +555,11 @@ public class ClusterConfigurationService {
         for (String group : groups) {
           Configuration configuration = getConfiguration(group);
           configResponse.addConfiguration(configuration);
+          if (configuration != null) {
+            configResponse.addJar(group, configuration.getJarNames());
+          }
         }
 
-        Map<String, byte[]> jarNamesToJarBytes = getAllJarsFromThisLocator(groups);
-        String[] jarNames = jarNamesToJarBytes.keySet().stream().toArray(String[]::new);
-        byte[][] jarBytes = jarNamesToJarBytes.values().toArray(new byte[jarNames.length][]);
-
-        configResponse.addJarsToBeDeployed(jarNames, jarBytes);
         return configResponse;
       }
     } finally {
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 a341ee3..f48edc8 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,7 @@
  */
 package org.apache.geode.internal;
 
+import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -86,52 +87,44 @@ public class DeployedJar {
     return JarDeployer.extractVersionFromFilename(this.file.getName());
   }
 
-  public DeployedJar(File versionedJarFile, String jarName) throws IOException {
-    this(versionedJarFile, jarName, Files.readAllBytes(versionedJarFile.toPath()));
-  }
-
   /**
    * Writes the given jarBytes to versionedJarFile
    */
-  public DeployedJar(File versionedJarFile, final String jarName, byte[] jarBytes)
-      throws FileNotFoundException {
-    Assert.assertTrue(jarBytes != null, "jarBytes cannot be null");
+  public DeployedJar(File versionedJarFile, final String jarName) {
     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 IllegalStateException("JAR file: " + versionedJarFile.getAbsolutePath()
-          + ", does not have the expected content.");
-    }
-
-    if (!hasValidJarContent(fileContent)) {
+    if (!hasValidJarContent(versionedJarFile)) {
       throw new IllegalArgumentException(
           "File does not contain valid JAR content: " + versionedJarFile.getAbsolutePath());
     }
 
-    if (messageDigest != null) {
-      this.md5hash = messageDigest.digest(jarBytes);
-    } else {
-      this.md5hash = null;
+    byte[] digest = null;
+    try {
+      if (messageDigest != null) {
+        digest = fileDigest(this.file);
+      }
+    } catch (IOException e) {
+      // Ignored
     }
+    this.md5hash = digest;
   }
 
   /**
    * Peek into the JAR data and make sure that it is valid JAR content.
    *
-   * @param inputStream InputStream containing data to be validated.
+   * @param jarFile Jar containing data to be validated.
    * @return True if the data has JAR content, false otherwise
    */
-  private static boolean hasValidJarContent(final InputStream inputStream) {
+  public static boolean hasValidJarContent(File jarFile) {
     JarInputStream jarInputStream = null;
     boolean valid = false;
 
     try {
-      jarInputStream = new JarInputStream(inputStream);
+      jarInputStream = new JarInputStream(new FileInputStream(jarFile));
       valid = jarInputStream.getNextJarEntry() != null;
     } catch (IOException ignore) {
       // Ignore this exception and just return false
@@ -147,16 +140,6 @@ public class DeployedJar {
   }
 
   /**
-   * 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
-   */
-  static boolean hasValidJarContent(final byte[] jarBytes) {
-    return hasValidJarContent(new ByteArrayInputStream(jarBytes));
-  }
-
-  /**
    * Scan the JAR file and attempt to register any function classes found.
    */
 
@@ -166,13 +149,19 @@ public class DeployedJar {
       logger.debug("Registering functions with DeployedJar: {}", this);
     }
 
-    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.getJarContent());
+    BufferedInputStream bufferedInputStream;
+    try {
+      bufferedInputStream = new BufferedInputStream(new FileInputStream(this.file));
+    } catch (Exception ex) {
+      logger.error("Unable to scan jar file for functions");
+      return;
+    }
 
     JarInputStream jarInputStream = null;
     try {
       Collection<String> functionClasses = findFunctionsInThisJar();
 
-      jarInputStream = new JarInputStream(byteArrayInputStream);
+      jarInputStream = new JarInputStream(bufferedInputStream);
       JarEntry jarEntry = jarInputStream.getNextJarEntry();
 
       while (jarEntry != null) {
@@ -259,16 +248,21 @@ public class DeployedJar {
    * 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
+   * @param stagedFile File to compare the original content to
    * @return True of the MD5 hash is the same o
    */
-  boolean hasSameContentAs(final byte[] compareToBytes) {
+  boolean hasSameContentAs(final File stagedFile) {
     // If the MD5 hash can't be calculated then silently return no match
     if (messageDigest == null || this.md5hash == null) {
-      return Arrays.equals(compareToBytes, getJarContent());
+      return false;
     }
 
-    byte[] compareToMd5 = messageDigest.digest(compareToBytes);
+    byte[] compareToMd5;
+    try {
+      compareToMd5 = fileDigest(stagedFile);
+    } catch (IOException ex) {
+      return false;
+    }
     if (logger.isDebugEnabled()) {
       logger.debug("For JAR file: {}, Comparing MD5 hash {} to {}", this.file.getAbsolutePath(),
           new String(this.md5hash), new String(compareToMd5));
@@ -276,6 +270,18 @@ public class DeployedJar {
     return Arrays.equals(this.md5hash, compareToMd5);
   }
 
+  private byte[] fileDigest(File file) throws IOException {
+    BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file));
+    byte[] data = new byte[8192];
+
+    int read;
+    while ((read = fis.read(data)) > 0) {
+      messageDigest.update(data, 0, read);
+    }
+
+    return messageDigest.digest();
+  }
+
   /**
    * 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
@@ -360,26 +366,6 @@ public class DeployedJar {
     return null;
   }
 
-  private byte[] getJarContent() {
-    try {
-      InputStream 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 ZERO_BYTES;
-  }
-
   /**
    * @return the unversioned name of this jar file, e.g. myJar.jar
    */
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 5079c07..c3b890b 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
@@ -156,6 +156,7 @@ public abstract class InternalDataSerializer extends DataSerializer implements D
           + ";org.apache.shiro.*;org.apache.shiro.authz.*;org.apache.shiro.authc.*" // security
                                                                                     // services
           + ";org.apache.geode.modules.util.SessionCustomExpiry" // geode-modules
+          + ";com.healthmarketscience.rmiio.*;com.sun.proxy.*" // Jar deployment
           + ";";
 
 
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 61650a7..47e11d0 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,14 +18,11 @@ import static java.util.stream.Collectors.joining;
 import static java.util.stream.Collectors.toList;
 import static java.util.stream.Collectors.toSet;
 
-import java.io.BufferedInputStream;
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
 import java.io.Serializable;
 import java.net.URL;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -46,6 +43,7 @@ import java.util.stream.Stream;
 import org.apache.commons.io.FileUtils;
 import org.apache.logging.log4j.Logger;
 
+import org.apache.geode.annotations.TestingOnly;
 import org.apache.geode.internal.logging.LogService;
 
 public class JarDeployer implements Serializable {
@@ -82,12 +80,12 @@ public class JarDeployer implements Serializable {
    * @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)
+  public DeployedJar deployWithoutRegistering(final String jarName, final File stagedJar)
       throws IOException {
     lock.lock();
 
     try {
-      boolean shouldDeployNewVersion = shouldDeployNewVersion(jarName, jarBytes);
+      boolean shouldDeployNewVersion = shouldDeployNewVersion(jarName, stagedJar);
       if (!shouldDeployNewVersion) {
         logger.debug("No need to deploy a new version of {}", jarName);
         return null;
@@ -96,9 +94,9 @@ public class JarDeployer implements Serializable {
       verifyWritableDeployDirectory();
 
       File newVersionedJarFile = getNextVersionedJarFile(jarName);
-      writeJarBytesToFile(newVersionedJarFile, jarBytes);
+      Files.copy(stagedJar.toPath(), newVersionedJarFile.toPath());
 
-      return new DeployedJar(newVersionedJarFile, jarName, jarBytes);
+      return new DeployedJar(newVersionedJarFile, jarName);
     } finally {
       lock.unlock();
     }
@@ -152,93 +150,6 @@ public class JarDeployer implements Serializable {
   }
 
   /**
-   * Attempt to write the given bytes to the given file. If this VM is able to successfully write
-   * the contents to the file, or another VM writes the exact same contents, then the write is
-   * considered to be successful.
-   *
-   * @param file File of the JAR file to deploy.
-   * @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) throws IOException {
-    final boolean isDebugEnabled = logger.isDebugEnabled();
-    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);
-  }
-
-  /**
-   * Determine if the contents of the file referenced is an exact match for the bytes provided. The
-   * method first checks to see if the file is actively being written by checking the length over
-   * time. If it appears that the file is actively being written, then it loops waiting for that to
-   * complete before doing the comparison.
-   *
-   * @param file File to compare
-   * @param bytes Bytes to compare
-   * @return True if there's an exact match, false otherwise
-   * @throws IOException If there's a problem reading the file
-   */
-  private boolean doesFileMatchBytes(final File file, final byte[] bytes) throws IOException {
-    // First check to see if the file is actively being written (if it's not big enough)
-    final String absolutePath = file.getAbsolutePath();
-    boolean keepTrying = true;
-    final boolean isDebugEnabled = logger.isDebugEnabled();
-    while (file.length() < bytes.length && keepTrying) {
-      if (isDebugEnabled) {
-        logger.debug("Loop waiting for another to write file: {}", absolutePath);
-      }
-      long startingFileLength = file.length();
-      try {
-        Thread.sleep(500);
-      } catch (InterruptedException iex) {
-        // Just keep looping
-      }
-      if (startingFileLength == file.length()) {
-        if (isDebugEnabled) {
-          logger.debug("Done waiting for another to write file: {}", absolutePath);
-        }
-        // Assume the other process has finished writing
-        keepTrying = false;
-      }
-    }
-
-    // If they don't have the same number of bytes then nothing to do
-    if (file.length() != bytes.length) {
-      if (isDebugEnabled) {
-        logger.debug("Unmatching file length when waiting for another to write file: {}",
-            absolutePath);
-      }
-      return false;
-    }
-
-    // Open the file then loop comparing each byte
-    BufferedInputStream inStream = new BufferedInputStream(new FileInputStream(file));
-    int index = 0;
-    try {
-      for (; index < bytes.length; index++) {
-        if (((byte) inStream.read()) != bytes[index]) {
-          if (isDebugEnabled) {
-            logger.debug("Did not find a match when waiting for another to write file: {}",
-                absolutePath);
-          }
-          return false;
-        }
-      }
-    } finally {
-      inStream.close();
-    }
-
-    return true;
-  }
-
-  /**
    * Find the version number that's embedded in the name of this file
    *
    * @param filename Filename to get the version number from
@@ -420,14 +331,8 @@ public class JarDeployer implements Serializable {
       throws IOException {
     final File[] jarFiles = findSortedOldVersionsOfJar(unversionedJarName);
 
-    Optional<File> latestValidDeployedJarOptional =
-        Arrays.stream(jarFiles).filter(Objects::nonNull).filter(jarFile -> {
-          try {
-            return DeployedJar.hasValidJarContent(FileUtils.readFileToByteArray(jarFile));
-          } catch (IOException e) {
-            return false;
-          }
-        }).findFirst();
+    Optional<File> latestValidDeployedJarOptional = Arrays.stream(jarFiles).filter(Objects::nonNull)
+        .filter(jarFile -> DeployedJar.hasValidJarContent(jarFile)).findFirst();
 
     if (!latestValidDeployedJarOptional.isPresent()) {
       // No valid version of this jar
@@ -482,46 +387,43 @@ public class JarDeployer implements Serializable {
   /**
    * 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.
+   * @param stagedJarFiles A map of Files which have been staged in another location and are ready
+   *        to be deployed as a unit.
    * @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)
+  public List<DeployedJar> deploy(final Map<String, File> stagedJarFiles)
       throws IOException, ClassNotFoundException {
-    DeployedJar[] deployedJars = new DeployedJar[jarNames.length];
+    List<DeployedJar> deployedJars = new ArrayList<>(stagedJarFiles.size());
 
-    for (int i = 0; i < jarNames.length; i++) {
-      if (!DeployedJar.hasValidJarContent(jarBytes[i])) {
+    for (File jar : stagedJarFiles.values()) {
+      if (!DeployedJar.hasValidJarContent(jar)) {
         throw new IllegalArgumentException(
-            "File does not contain valid JAR content: " + jarNames[i]);
+            "File does not contain valid JAR content: " + jar.getName());
       }
     }
 
     lock.lock();
     try {
-      for (int i = 0; i < jarNames.length; i++) {
-        String jarName = jarNames[i];
-        byte[] newJarBytes = jarBytes[i];
-
-        deployedJars[i] = deployWithoutRegistering(jarName, newJarBytes);
+      for (String fileName : stagedJarFiles.keySet()) {
+        deployedJars.add(deployWithoutRegistering(fileName, stagedJarFiles.get(fileName)));
       }
 
-      return registerNewVersions(Arrays.asList(deployedJars));
+      return registerNewVersions(deployedJars);
     } finally {
       lock.unlock();
     }
   }
 
-  private boolean shouldDeployNewVersion(String jarName, byte[] newJarBytes) throws IOException {
+  private boolean shouldDeployNewVersion(String jarName, File stagedJar) throws IOException {
     DeployedJar oldDeployedJar = this.deployedJars.get(jarName);
 
     if (oldDeployedJar == null) {
       return true;
     }
 
-    if (oldDeployedJar.hasSameContentAs(newJarBytes)) {
+    if (oldDeployedJar.hasSameContentAs(stagedJar)) {
       logger.warn("Jar is identical to the latest deployed version: {}",
           oldDeployedJar.getFileCanonicalPath());
 
@@ -536,16 +438,20 @@ public class JarDeployer implements Serializable {
    *
    * @param jarName - the unversioned jar name, e.g. myJar.jar
    */
-  public DeployedJar findDeployedJar(String jarName) {
+  public DeployedJar getDeployedJar(String jarName) {
     return this.deployedJars.get(jarName);
   }
 
-  public DeployedJar deploy(final String jarName, final byte[] jarBytes)
+  @TestingOnly
+  public DeployedJar deploy(final String jarName, final File stagedJarFile)
       throws IOException, ClassNotFoundException {
     lock.lock();
 
+    Map<String, File> jarFiles = new HashMap<>();
+    jarFiles.put(jarName, stagedJarFile);
+
     try {
-      List<DeployedJar> deployedJars = deploy(new String[] {jarName}, new byte[][] {jarBytes});
+      List<DeployedJar> deployedJars = deploy(jarFiles);
       if (deployedJars == null || deployedJars.size() == 0) {
         return null;
       }
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 cd1a85a..7716c13 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
@@ -18,11 +18,20 @@ import static java.util.stream.Collectors.joining;
 import static java.util.stream.Collectors.toList;
 
 import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -31,9 +40,8 @@ import java.util.Objects;
 import java.util.Properties;
 import java.util.Set;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
-import org.apache.commons.lang.ArrayUtils;
+import com.healthmarketscience.rmiio.RemoteInputStream;
 import org.apache.commons.lang.StringUtils;
 import org.apache.logging.log4j.Logger;
 
@@ -41,6 +49,7 @@ import org.apache.geode.UnmodifiableException;
 import org.apache.geode.cache.Cache;
 import org.apache.geode.cache.execute.FunctionService;
 import org.apache.geode.cache.execute.ResultCollector;
+import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.distributed.internal.ClusterConfigurationService;
 import org.apache.geode.distributed.internal.DistributionConfig;
 import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
@@ -50,7 +59,9 @@ import org.apache.geode.internal.DeployedJar;
 import org.apache.geode.internal.JarDeployer;
 import org.apache.geode.internal.config.ClusterConfigurationNotAvailableException;
 import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.management.internal.cli.CliUtil;
 import org.apache.geode.management.internal.configuration.domain.Configuration;
+import org.apache.geode.management.internal.configuration.functions.DownloadJarFunction;
 import org.apache.geode.management.internal.configuration.functions.GetClusterConfigurationFunction;
 import org.apache.geode.management.internal.configuration.messages.ConfigurationResponse;
 
@@ -62,34 +73,36 @@ public class ClusterConfigurationLoader {
    * Deploys the jars received from shared configuration, it undeploys any other jars that were not
    * part of shared configuration
    *
-   * @param cache Cache of this member
    * @param response {@link ConfigurationResponse} received from the locators
    */
-  public void deployJarsReceivedFromClusterConfiguration(Cache cache,
-      ConfigurationResponse response) throws IOException, ClassNotFoundException {
+  public void deployJarsReceivedFromClusterConfiguration(ConfigurationResponse response)
+      throws IOException, ClassNotFoundException {
     logger.info("Requesting cluster configuration");
     if (response == null) {
       return;
     }
 
-    String[] jarFileNames = response.getJarNames();
-    byte[][] jarBytes = response.getJars();
+    List<String> jarFileNames =
+        response.getJarNames().values().stream().flatMap(Set::stream).collect(Collectors.toList());
 
-    if (jarFileNames != null && jarBytes != null) {
-      logger.info("Got response with jars: {}", Stream.of(jarFileNames).collect(joining(",")));
+    if (jarFileNames != null && !jarFileNames.isEmpty()) {
+      logger.info("Got response with jars: {}", jarFileNames.stream().collect(joining(",")));
       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());
+                .filter(jarName -> !jarFileNames.contains(jarName)).collect(toList());
 
         for (String extraJar : extraJarsOnServer) {
           logger.info("Removing jar not present in cluster configuration: {}", extraJar);
           jarDeployer.deleteAllVersionsOfJar(extraJar);
         }
 
-        List<DeployedJar> deployedJars = jarDeployer.deploy(jarFileNames, jarBytes);
+        Map<String, File> stagedJarFiles =
+            getJarsFromLocator(response.getMember(), response.getJarNames());
+
+        List<DeployedJar> deployedJars = jarDeployer.deploy(stagedJarFiles);
 
         deployedJars.stream().filter(Objects::nonNull)
             .forEach((jar) -> logger.info("Deployed: {}", jar.getFile().getAbsolutePath()));
@@ -99,6 +112,53 @@ public class ClusterConfigurationLoader {
     }
   }
 
+  private Map<String, File> getJarsFromLocator(DistributedMember locator,
+      Map<String, Set<String>> jarNames) throws IOException {
+    Map<String, File> results = new HashMap<>();
+
+    for (String group : jarNames.keySet()) {
+      for (String jar : jarNames.get(group)) {
+        results.put(jar, downloadJar(locator, group, jar));
+      }
+    }
+
+    return results;
+  }
+
+  public File downloadJar(DistributedMember locator, String groupName, String jarName)
+      throws IOException {
+    ResultCollector<RemoteInputStream, List<RemoteInputStream>> rc =
+        (ResultCollector<RemoteInputStream, List<RemoteInputStream>>) CliUtil.executeFunction(
+            new DownloadJarFunction(), new Object[] {groupName, jarName},
+            Collections.singleton(locator));
+
+    List<RemoteInputStream> result = rc.getResult();
+    RemoteInputStream jarStream = result.get(0);
+
+    Set<PosixFilePermission> perms = new HashSet<>();
+    perms.add(PosixFilePermission.OWNER_READ);
+    perms.add(PosixFilePermission.OWNER_WRITE);
+    perms.add(PosixFilePermission.OWNER_EXECUTE);
+    Path tempDir =
+        Files.createTempDirectory("deploy-", PosixFilePermissions.asFileAttribute(perms));
+    Path tempJar = Paths.get(tempDir.toString(), jarName);
+    FileOutputStream fos = new FileOutputStream(tempJar.toString());
+
+    int packetId = 0;
+    while (true) {
+      byte[] data = jarStream.readPacket(packetId);
+      if (data == null) {
+        break;
+      }
+      fos.write(data);
+      packetId++;
+    }
+    fos.close();
+    jarStream.close(true);
+
+    return tempJar.toFile();
+  }
+
   /***
    * Apply the cache-xml cluster configuration on this member
    */
@@ -225,6 +285,7 @@ public class ClusterConfigurationLoader {
       Object result = ((ArrayList) resultCollector.getResult()).get(0);
       if (result instanceof ConfigurationResponse) {
         response = (ConfigurationResponse) result;
+        response.setMember(locator);
         break;
       } else {
         logger.error("Received invalid result from {}: {}", locator.toString(), result);
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 76113f1..8384b27 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
@@ -1018,25 +1018,20 @@ public class GemFireCacheImpl implements InternalCache, InternalClientCache, Has
     }
 
     // can't simply return null if server is not using shared configuration, since we need to find
-    // out
-    // if the locator is running in secure mode or not, if yes, then we need to throw an exception
-    // if server is not using cluster config
+    // out if the locator is running in secure mode or not, if yes, then we need to throw an
+    // exception if server is not using cluster config.
 
-    Map<InternalDistributedMember, Collection<String>> scl =
+    Map<InternalDistributedMember, Collection<String>> locatorsWithClusterConfig =
         getDistributionManager().getAllHostedLocatorsWithSharedConfiguration();
 
     // If there are no locators with Shared configuration, that means the system has been started
-    // without shared configuration
-    // then do not make requests to the locators
-    if (scl.isEmpty()) {
+    // without shared configuration then do not make requests to the locators.
+    if (locatorsWithClusterConfig.isEmpty()) {
       logger.info(LocalizedMessage
           .create(LocalizedStrings.GemFireCache_NO_LOCATORS_FOUND_WITH_SHARED_CONFIGURATION));
       return null;
     }
 
-    Map<InternalDistributedMember, Collection<String>> locatorsWithClusterConfig =
-        getDistributionManager().getAllHostedLocatorsWithSharedConfiguration();
-
     try {
       ConfigurationResponse response = ccLoader.requestConfigurationFromLocators(
           this.system.getConfig().getGroups(), locatorsWithClusterConfig.keySet());
@@ -1169,7 +1164,7 @@ public class GemFireCacheImpl implements InternalCache, InternalClientCache, Has
     ClassPathLoader.setLatestToDefault(this.system.getConfig().getDeployWorkingDir());
 
     try {
-      ccLoader.deployJarsReceivedFromClusterConfiguration(this, this.configurationResponse);
+      ccLoader.deployJarsReceivedFromClusterConfiguration(this.configurationResponse);
     } catch (IOException | ClassNotFoundException e) {
       throw new GemFireConfigException(
           LocalizedStrings.GemFireCache_EXCEPTION_OCCURRED_WHILE_DEPLOYING_JARS_FROM_SHARED_CONDFIGURATION
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliUtil.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliUtil.java
index 8ad2282..0ca70b0 100755
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliUtil.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/CliUtil.java
@@ -34,6 +34,7 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.zip.DataFormatException;
@@ -537,7 +538,7 @@ public class CliUtil {
   }
 
   /***
-   * Executes a function with arguments on a set of members , ignores the departed members.
+   * Executes a function with arguments on a set of members, ignoring the departed members.
    *
    * @param function Function to be executed.
    * @param args Arguments passed to the function, pass null if you wish to pass no arguments to the
@@ -560,6 +561,23 @@ public class CliUtil {
     return execution.execute(function);
   }
 
+  /***
+   * Executes a function with arguments on a single member, ignoring the departed members.
+   *
+   * @param function Function to be executed.
+   * @param args Arguments passed to the function, pass null if you wish to pass no arguments to the
+   *        function.
+   * @param member The member on which the function is to be executed.
+   *
+   * @return ResultCollector
+   */
+  public static ResultCollector<?, ?> executeFunction(Function function, Object args,
+      DistributedMember member) {
+    Set<DistributedMember> members = new HashSet<>();
+    members.add(member);
+    return executeFunction(function, args, members);
+  }
+
   /**
    * Returns a Set of DistributedMember for members that have the specified <code>region</code>.
    * <code>returnAll</code> indicates whether to return all members or only the first member we
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DeployCommand.java b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DeployCommand.java
index 5ee16fa..d2e3025 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DeployCommand.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/cli/commands/DeployCommand.java
@@ -17,13 +17,19 @@ package org.apache.geode.management.internal.cli.commands;
 
 import static org.apache.commons.io.FileUtils.ONE_MB;
 
+import java.io.ByteArrayInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.rmi.RemoteException;
 import java.text.DecimalFormat;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
+import com.healthmarketscience.rmiio.RemoteInputStream;
+import com.healthmarketscience.rmiio.SimpleRemoteInputStream;
 import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.StringUtils;
 import org.springframework.shell.core.annotation.CliCommand;
@@ -78,32 +84,46 @@ public class DeployCommand implements GfshCommand {
     byte[][] jarBytes = CliUtil.bytesToData(shellBytesData);
 
     Set<DistributedMember> targetMembers;
-
     targetMembers = CliUtil.findMembers(groups, null);
 
-    if (targetMembers.size() > 0) {
+    List results = new ArrayList();
+    for (DistributedMember member : targetMembers) {
+
+      List<RemoteInputStream> remoteStreams = new ArrayList<>();
+
+      for (int i = 0; i < jarNames.length; i++) {
+        try {
+          remoteStreams
+              .add(new SimpleRemoteInputStream(new ByteArrayInputStream(jarBytes[i])).export());
+        } catch (RemoteException rex) {
+          rex.printStackTrace();
+        }
+      }
+
       // this deploys the jars to all the matching servers
       ResultCollector<?, ?> resultCollector = CliUtil.executeFunction(this.deployFunction,
-          new Object[] {jarNames, jarBytes}, targetMembers);
+          new Object[] {Arrays.stream(jarNames).collect(Collectors.toList()), remoteStreams},
+          member);
 
-      List<CliFunctionResult> results =
-          CliFunctionResult.cleanResults((List<?>) resultCollector.getResult());
+      results.add(((List) resultCollector.getResult()).get(0));
+    }
 
-      for (CliFunctionResult result : results) {
-        if (result.getThrowable() != null) {
+    List<CliFunctionResult> cleanedResults = CliFunctionResult.cleanResults(results);
+
+    for (CliFunctionResult result : cleanedResults) {
+      if (result.getThrowable() != null) {
+        tabularData.accumulate("Member", result.getMemberIdOrName());
+        tabularData.accumulate("Deployed JAR", "");
+        tabularData.accumulate("Deployed JAR Location",
+            "ERROR: " + result.getThrowable().getClass().getName() + ": "
+                + result.getThrowable().getMessage());
+        tabularData.setStatus(Result.Status.ERROR);
+      } else {
+        String[] strings = (String[]) result.getSerializables();
+        for (int i = 0; i < strings.length; i += 2) {
           tabularData.accumulate("Member", result.getMemberIdOrName());
-          tabularData.accumulate("Deployed JAR", "");
-          tabularData.accumulate("Deployed JAR Location",
-              "ERROR: " + result.getThrowable().getClass().getName() + ": "
-                  + result.getThrowable().getMessage());
-          tabularData.setStatus(Result.Status.ERROR);
-        } else {
-          String[] strings = (String[]) result.getSerializables();
-          for (int i = 0; i < strings.length; i += 2) {
-            tabularData.accumulate("Member", result.getMemberIdOrName());
-            tabularData.accumulate("Deployed JAR", strings[i]);
-            tabularData.accumulate("Deployed JAR Location", strings[i + 1]);
-          }
+          tabularData.accumulate("Deployed JAR", strings[i]);
+          tabularData.accumulate("Deployed JAR Location", strings[i + 1]);
         }
       }
     }
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 c9ef06a..ad1053f 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
@@ -14,9 +14,22 @@
  */
 package org.apache.geode.management.internal.cli.functions;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
+import com.healthmarketscience.rmiio.RemoteInputStream;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.SystemFailure;
@@ -44,10 +57,10 @@ public class DeployFunction implements Function, InternalEntity {
 
     try {
       final Object[] args = (Object[]) context.getArguments();
-      final String[] jarFilenames = (String[]) args[0];
-      final byte[][] jarBytes = (byte[][]) args[1];
-      InternalCache cache = (InternalCache) context.getCache();
+      final List<String> jarFilenames = (List<String>) args[0];
+      final List<RemoteInputStream> jarStreams = (List<RemoteInputStream>) args[1];
 
+      InternalCache cache = (InternalCache) context.getCache();
       DistributedMember member = cache.getDistributedSystem().getDistributedMember();
 
       memberId = member.getId();
@@ -56,11 +69,21 @@ public class DeployFunction implements Function, InternalEntity {
         memberId = member.getName();
       }
 
+      Map<String, File> stagedFiles;
+      try {
+        stagedFiles = stageJarContent(jarFilenames, jarStreams);
+      } catch (IOException ex) {
+        CliFunctionResult result =
+            new CliFunctionResult(memberId, ex, "error staging jars for deployment");
+        context.getResultSender().lastResult(result);
+        return;
+      }
+
       List<String> deployedList = new ArrayList<String>();
       List<DeployedJar> jarClassLoaders =
-          ClassPathLoader.getLatest().getJarDeployer().deploy(jarFilenames, jarBytes);
-      for (int i = 0; i < jarFilenames.length; i++) {
-        deployedList.add(jarFilenames[i]);
+          ClassPathLoader.getLatest().getJarDeployer().deploy(stagedFiles);
+      for (int i = 0; i < jarFilenames.size(); i++) {
+        deployedList.add(jarFilenames.get(i));
         if (jarClassLoaders.get(i) != null) {
           deployedList.add(jarClassLoaders.get(i).getFileCanonicalPath());
         } else {
@@ -108,4 +131,48 @@ public class DeployFunction implements Function, InternalEntity {
   public boolean isHA() {
     return false;
   }
+
+  private Map<String, File> stageJarContent(List<String> jarNames,
+      List<RemoteInputStream> jarStreams) throws IOException {
+    Map<String, File> stagedJars = new HashMap<>();
+
+    try {
+      Set<PosixFilePermission> perms = new HashSet<>();
+      perms.add(PosixFilePermission.OWNER_READ);
+      perms.add(PosixFilePermission.OWNER_WRITE);
+      perms.add(PosixFilePermission.OWNER_EXECUTE);
+      Path tempDir =
+          Files.createTempDirectory("deploy-", PosixFilePermissions.asFileAttribute(perms));
+
+      for (int i = 0; i < jarNames.size(); i++) {
+        Path tempJar = Paths.get(tempDir.toString(), jarNames.get(i));
+        FileOutputStream fos = new FileOutputStream(tempJar.toString());
+
+        int packetId = 0;
+        while (true) {
+          byte[] data = jarStreams.get(i).readPacket(packetId);
+          if (data == null) {
+            break;
+          }
+          fos.write(data);
+          packetId++;
+        }
+        fos.close();
+        jarStreams.get(i).close(true);
+
+        stagedJars.put(jarNames.get(i), tempJar.toFile());
+      }
+    } catch (IOException iox) {
+      for (int i = 0; i < jarStreams.size(); i++) {
+        try {
+          jarStreams.get(i).close(true);
+        } catch (IOException ex) {
+          // Ignored
+        }
+      }
+      throw iox;
+    }
+
+    return stagedJars;
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/UploadJarFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/DownloadJarFunction.java
similarity index 60%
rename from geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/UploadJarFunction.java
rename to geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/DownloadJarFunction.java
index 56225ca..fd93ecb 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/UploadJarFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/DownloadJarFunction.java
@@ -15,19 +15,28 @@
  */
 package org.apache.geode.management.internal.configuration.functions;
 
-import java.util.List;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.rmi.RemoteException;
 
+import com.healthmarketscience.rmiio.GZIPRemoteInputStream;
+import com.healthmarketscience.rmiio.RemoteInputStream;
+import com.healthmarketscience.rmiio.RemoteInputStreamServer;
+import com.healthmarketscience.rmiio.SimpleRemoteInputStream;
 import org.apache.logging.log4j.Logger;
 
 import org.apache.geode.cache.execute.Function;
 import org.apache.geode.cache.execute.FunctionContext;
+import org.apache.geode.cache.execute.FunctionException;
 import org.apache.geode.distributed.Locator;
 import org.apache.geode.distributed.internal.ClusterConfigurationService;
 import org.apache.geode.distributed.internal.InternalLocator;
 import org.apache.geode.internal.InternalEntity;
 import org.apache.geode.internal.logging.LogService;
 
-public class UploadJarFunction implements Function<Object[]>, InternalEntity {
+public class DownloadJarFunction implements Function<Object[]>, InternalEntity {
   private static final Logger logger = LogService.getLogger();
 
   private static final long serialVersionUID = 1L;
@@ -39,24 +48,41 @@ public class UploadJarFunction implements Function<Object[]>, InternalEntity {
     String group = (String) args[0];
     String jarName = (String) args[1];
 
-    byte[] jarBytes = null;
+    RemoteInputStream result = null;
     if (locator != null && group != null && jarName != null) {
       ClusterConfigurationService sharedConfig = locator.getSharedConfiguration();
       if (sharedConfig != null) {
         try {
-          jarBytes = sharedConfig.getJarBytesFromThisLocator(group, jarName);
-          context.getResultSender().lastResult(jarBytes);
+          File jarFile = sharedConfig.getPathToJarOnThisLocator(group, jarName).toFile();
+
+          RemoteInputStreamServer istream = null;
+          try {
+            istream =
+                new SimpleRemoteInputStream(new BufferedInputStream(new FileInputStream(jarFile)));
+            result = istream.export();
+            istream = null;
+          } catch (FileNotFoundException | RemoteException ex) {
+            throw new FunctionException(ex);
+          } finally {
+            // we will only close the stream here if the server fails before
+            // returning an exported stream
+            if (istream != null) {
+              istream.close();
+            }
+          }
         } catch (Exception e) {
           logger.error(e);
           throw new IllegalStateException(e.getMessage());
         }
       }
     }
+
+    context.getResultSender().lastResult(result);
   }
 
   @Override
   public String getId() {
-    return UploadJarFunction.class.getName();
+    return DownloadJarFunction.class.getName();
   }
 
 }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/GetClusterConfigurationFunction.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/GetClusterConfigurationFunction.java
index bc7ab6b..c64e876 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/GetClusterConfigurationFunction.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/functions/GetClusterConfigurationFunction.java
@@ -55,7 +55,7 @@ public class GetClusterConfigurationFunction implements Function {
           clusterConfigurationService.createConfigurationResponse(groups);
       context.getResultSender().lastResult(response);
     } catch (IOException e) {
-      logger.error("Unable to retrieve the cluster configuraton", e);
+      logger.error("Unable to retrieve the cluster configuration", e);
       context.getResultSender().lastResult(e);
     }
   }
diff --git a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/messages/ConfigurationResponse.java b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/messages/ConfigurationResponse.java
index a7188b2..bfc3c14 100644
--- a/geode-core/src/main/java/org/apache/geode/management/internal/configuration/messages/ConfigurationResponse.java
+++ b/geode-core/src/main/java/org/apache/geode/management/internal/configuration/messages/ConfigurationResponse.java
@@ -18,6 +18,7 @@ import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -30,8 +31,10 @@ import javax.xml.transform.TransformerFactoryConfigurationError;
 import org.apache.commons.lang.StringUtils;
 import org.xml.sax.SAXException;
 
+import org.apache.geode.DataSerializable;
 import org.apache.geode.DataSerializer;
 import org.apache.geode.InternalGemFireError;
+import org.apache.geode.distributed.DistributedMember;
 import org.apache.geode.internal.DataSerializableFixedID;
 import org.apache.geode.internal.Version;
 import org.apache.geode.management.internal.configuration.domain.Configuration;
@@ -39,11 +42,13 @@ import org.apache.geode.management.internal.configuration.utils.XmlUtils;
 
 public class ConfigurationResponse implements DataSerializableFixedID {
 
-  private Map<String, Configuration> requestedConfiguration = new HashMap<String, Configuration>();
-  private byte[][] jarBytes;
-  private String[] jarNames;
+  private Map<String, Configuration> requestedConfiguration = new HashMap<>();
+  private Map<String, Set<String>> jarNames = new HashMap<>();
   private boolean failedToGetSharedConfig = false;
 
+  // This is set to the member from which this object was received
+  private transient DistributedMember member;
+
   @Override
   public int getDSFID() {
     return DataSerializableFixedID.CONFIGURATION_RESPONSE;
@@ -51,17 +56,15 @@ public class ConfigurationResponse implements DataSerializableFixedID {
 
   @Override
   public void toData(DataOutput out) throws IOException {
-    DataSerializer.writeHashMap((HashMap<?, ?>) requestedConfiguration, out);
-    DataSerializer.writeStringArray(jarNames, out);
-    DataSerializer.writeArrayOfByteArrays(jarBytes, out);
+    DataSerializer.writeHashMap(requestedConfiguration, out);
+    DataSerializer.writeHashMap(jarNames, out);
     DataSerializer.writeBoolean(Boolean.valueOf(failedToGetSharedConfig), out);
   }
 
   @Override
   public void fromData(DataInput in) throws IOException, ClassNotFoundException {
     this.requestedConfiguration = DataSerializer.readHashMap(in);
-    this.jarNames = DataSerializer.readStringArray(in);
-    this.jarBytes = DataSerializer.readArrayOfByteArrays(in);
+    this.jarNames = DataSerializer.readHashMap(in);
     this.failedToGetSharedConfig = DataSerializer.readBoolean(in);
   }
 
@@ -126,18 +129,20 @@ public class ConfigurationResponse implements DataSerializableFixedID {
     return sb.toString();
   }
 
+  public void addJar(String group, Set<String> jarNames) {
+    this.jarNames.put(group, jarNames);
+  }
 
-  public String[] getJarNames() {
-    return this.jarNames;
+  public Map<String, Set<String>> getJarNames() {
+    return jarNames;
   }
 
-  public byte[][] getJars() {
-    return this.jarBytes;
+  public DistributedMember getMember() {
+    return member;
   }
 
-  public void addJarsToBeDeployed(String[] jarNames, byte[][] jarBytes) {
-    this.jarNames = jarNames;
-    this.jarBytes = jarBytes;
+  public void setMember(DistributedMember member) {
+    this.member = member;
   }
 
   public Version[] getSerializationVersions() {
diff --git a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
index 19fea74..84b2721 100644
--- a/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
+++ b/geode-core/src/main/resources/org/apache/geode/internal/sanctioned-geode-core-serializables.txt
@@ -587,7 +587,7 @@ org/apache/geode/management/internal/configuration/domain/SharedConfigurationSta
 org/apache/geode/management/internal/configuration/functions/GetClusterConfigurationFunction,false
 org/apache/geode/management/internal/configuration/functions/GetRegionNamesFunction,false
 org/apache/geode/management/internal/configuration/functions/RecreateCacheFunction,false
-org/apache/geode/management/internal/configuration/functions/UploadJarFunction,true,1
+org/apache/geode/management/internal/configuration/functions/DownloadJarFunction,true,1
 org/apache/geode/management/internal/web/domain/QueryParameterSource,true,34131123582155,objectName:javax/management/ObjectName,queryExpression:javax/management/QueryExp
 org/apache/geode/management/internal/web/shell/MBeanAccessException,true,813768898269516238
 org/apache/geode/memcached/GemFireMemcachedServer$Protocol,false
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 c442b19..a5aa996 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
@@ -22,7 +22,10 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
 import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -37,6 +40,7 @@ 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.commons.io.IOUtils;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -109,10 +113,10 @@ public class ClassPathLoaderIntegrationTest {
 
     String classAName = "integration.parent.ClassA";
 
-    byte[] firstJarBytes = createJarWithClass("ClassA");
+    File firstJar = createJarWithClass("ClassA");
 
     // First deploy of the JAR file
-    ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, firstJarBytes).getFile();
+    ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, firstJar).getFile();
 
     assertThatClassCanBeLoaded(classAName);
     assertThatResourceCanBeLoaded(classAResource);
@@ -128,13 +132,15 @@ public class ClassPathLoaderIntegrationTest {
     String classAName = "integration.parent.ClassA";
     String classBName = "integration.parent.ClassB";
 
-    byte[] firstJarBytes = createJarWithClass("ClassA");
+    File firstJar = createJarWithClass("ClassA");
+    ByteArrayOutputStream firstJarBytes = new ByteArrayOutputStream();
+    IOUtils.copy(new FileInputStream(firstJar), firstJarBytes);
 
     // First deploy of the JAR file
     File firstDeployedJarFile =
-        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, firstJarBytes).getFile();
+        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, firstJar).getFile();
 
-    assertThat(firstDeployedJarFile).exists().hasBinaryContent(firstJarBytes);
+    assertThat(firstDeployedJarFile).exists().hasBinaryContent(firstJarBytes.toByteArray());
     assertThat(firstDeployedJarFile.getName()).contains(".v1.").doesNotContain(".v2.");
 
     assertThatClassCanBeLoaded(classAName);
@@ -145,12 +151,14 @@ public class ClassPathLoaderIntegrationTest {
 
     // 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 secondJar = createJarWithClass("ClassB");
+    ByteArrayOutputStream secondJarBytes = new ByteArrayOutputStream();
+    IOUtils.copy(new FileInputStream(secondJar), secondJarBytes);
 
     File secondDeployedJarFile =
-        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, secondJarBytes).getFile();
+        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, secondJar).getFile();
 
-    assertThat(secondDeployedJarFile).exists().hasBinaryContent(secondJarBytes);
+    assertThat(secondDeployedJarFile).exists().hasBinaryContent(secondJarBytes.toByteArray());
     assertThat(secondDeployedJarFile.getName()).contains(".v2.").doesNotContain(".v1.");
 
     assertThatClassCanBeLoaded(classBName);
@@ -174,8 +182,10 @@ public class ClassPathLoaderIntegrationTest {
 
     // First deploy of the JAR file
     byte[] jarBytes = new ClassBuilder().createJarFromName("JarDeployerDUnitDNUWNC");
+    File jarFile = temporaryFolder.newFile();
+    writeJarBytesToFile(jarFile, jarBytes);
     DeployedJar jarClassLoader =
-        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarBytes);
+        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarFile);
     File deployedJar = new File(jarClassLoader.getFileCanonicalPath());
 
     assertThat(deployedJar).exists();
@@ -183,7 +193,7 @@ public class ClassPathLoaderIntegrationTest {
 
     // Re-deploy of the same JAR should do nothing
     DeployedJar newJarClassLoader =
-        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarBytes);
+        ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarFile);
     assertThat(newJarClassLoader).isNull();
     assertThat(deployedJar).exists();
 
@@ -274,8 +284,7 @@ public class ClassPathLoaderIntegrationTest {
     GemFireCacheImpl gemFireCache = GemFireCacheImpl.getInstance();
     DistributedSystem distributedSystem = gemFireCache.getDistributedSystem();
 
-    ClassPathLoader.getLatest().getJarDeployer().deploy("MyJar.jar",
-        FileUtils.readFileToByteArray(jarVersion1));
+    ClassPathLoader.getLatest().getJarDeployer().deploy("MyJar.jar", jarVersion1);
 
     assertThatClassCanBeLoaded("jddunit.function.MyFunction");
     Execution execution = FunctionService.onMember(distributedSystem.getDistributedMember());
@@ -283,8 +292,7 @@ public class ClassPathLoaderIntegrationTest {
     List<String> result = (List<String>) execution.execute("MyFunction").getResult();
     assertThat(result.get(0)).isEqualTo("Version1");
 
-    ClassPathLoader.getLatest().getJarDeployer().deploy("MyJar.jar",
-        FileUtils.readFileToByteArray(jarVersion2));
+    ClassPathLoader.getLatest().getJarDeployer().deploy("MyJar.jar", jarVersion2);
     result = (List<String>) execution.execute("MyFunction").getResult();
     assertThat(result.get(0)).isEqualTo("Version2");
 
@@ -461,7 +469,7 @@ public class ClassPathLoaderIntegrationTest {
 
   @Test
   public void testDeclarableFunctionsWithNoCacheXml() throws Exception {
-    final String jarName = "JarClassLoaderJUnitNoXml.jar";
+    final String jarFilename = "JarClassLoaderJUnitNoXml.jar";
 
     // Add a Declarable Function without parameters for the class to the Classpath
     String functionString =
@@ -478,8 +486,10 @@ public class ClassPathLoaderIntegrationTest {
 
     byte[] jarBytes = this.classBuilder
         .createJarFromClassContent("JarClassLoaderJUnitFunctionNoXml", functionString);
+    File jarFile = temporaryFolder.newFile();
+    writeJarBytesToFile(jarFile, jarBytes);
 
-    ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarBytes);
+    ClassPathLoader.getLatest().getJarDeployer().deploy(jarFilename, jarFile);
 
     ClassPathLoader.getLatest().forName("JarClassLoaderJUnitFunctionNoXml");
 
@@ -506,7 +516,8 @@ public class ClassPathLoaderIntegrationTest {
     byte[] jarBytes = this.classBuilder.createJarFromClassContent(
         "jcljunit/parent/JarClassLoaderJUnitParent", stringBuffer.toString());
     writeJarBytesToFile(parentJarFile, jarBytes);
-    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitParent.jar", jarBytes);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitParent.jar",
+        parentJarFile);
 
     stringBuffer = new StringBuffer();
     stringBuffer.append("package jcljunit.uses;");
@@ -517,7 +528,7 @@ public class ClassPathLoaderIntegrationTest {
     jarBytes = this.classBuilder.createJarFromClassContent("jcljunit/uses/JarClassLoaderJUnitUses",
         stringBuffer.toString());
     writeJarBytesToFile(usesJarFile, jarBytes);
-    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUses.jar", jarBytes);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUses.jar", usesJarFile);
 
     stringBuffer = new StringBuffer();
     stringBuffer.append("package jcljunit.function;");
@@ -540,9 +551,11 @@ public class ClassPathLoaderIntegrationTest {
     functionClassBuilder.addToClassPath(usesJarFile.getAbsolutePath());
     jarBytes = functionClassBuilder.createJarFromClassContent(
         "jcljunit/function/JarClassLoaderJUnitFunction", stringBuffer.toString());
+    File jarFunction = temporaryFolder.newFile();
+    writeJarBytesToFile(jarFunction, jarBytes);
 
     ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitFunction.jar",
-        jarBytes);
+        jarFunction);
 
     Function function = FunctionService.getFunction("JarClassLoaderJUnitFunction");
     assertThat(function).isNotNull();
@@ -559,8 +572,9 @@ public class ClassPathLoaderIntegrationTest {
     final String fileContent = "FILE CONTENT";
 
     byte[] jarBytes = this.classBuilder.createJarFromFileContent(fileName, fileContent);
-    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitResource.jar",
-        jarBytes);
+    File tempJar = temporaryFolder.newFile();
+    writeJarBytesToFile(tempJar, jarBytes);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitResource.jar", tempJar);
 
     InputStream inputStream = ClassPathLoader.getLatest().getResourceAsStream(fileName);
     assertThat(inputStream).isNotNull();
@@ -577,7 +591,9 @@ public class ClassPathLoaderIntegrationTest {
     // 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);
+    File jarFile = temporaryFolder.newFile();
+    writeJarBytesToFile(jarFile, jarBytes);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUpdate.jar", jarFile);
 
     Class<?> clazz = ClassPathLoader.getLatest().forName("JarClassLoaderJUnitTestClass");
     Object object = clazz.newInstance();
@@ -589,7 +605,9 @@ public class ClassPathLoaderIntegrationTest {
     // 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);
+    File jarFile2 = temporaryFolder.newFile();
+    writeJarBytesToFile(jarFile2, jarBytes);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitUpdate.jar", jarFile2);
 
     clazz = ClassPathLoader.getLatest().forName("JarClassLoaderJUnitTestClass");
     object = clazz.newInstance();
@@ -678,11 +696,16 @@ public class ClassPathLoaderIntegrationTest {
     }
   }
 
-  private byte[] createJarWithClass(String className) throws IOException {
+  private File createJarWithClass(String className) throws IOException {
     String stringBuilder = "package integration.parent;" + "public class " + className + " {}";
 
-    return new ClassBuilder().createJarFromClassContent("integration/parent/" + className,
-        stringBuilder);
+    byte[] jarBytes = new ClassBuilder()
+        .createJarFromClassContent("integration/parent/" + className, stringBuilder);
+
+    File jarFile = temporaryFolder.newFile();
+    IOUtils.copy(new ByteArrayInputStream(jarBytes), new FileOutputStream(jarFile));
+
+    return jarFile;
   }
 
   private static class TestResultSender implements ResultSender<Object> {
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 99acbc9..5525589 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
@@ -19,24 +19,33 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 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.IOUtils;
 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 org.apache.geode.test.compiler.ClassBuilder;
 import org.apache.geode.test.junit.categories.UnitTest;
+import org.apache.geode.test.junit.rules.TemporaryFileRule;
 
 /**
  * Unit tests for {@link ClassPathLoader}.
@@ -51,6 +60,9 @@ public class ClassPathLoaderTest {
   @Rule
   public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
 
+  @Rule
+  public TemporaryFolder tempFolder = new TemporaryFolder();
+
   @Before
   public void setUp() throws Exception {
     System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, "false");
@@ -68,17 +80,28 @@ public class ClassPathLoaderTest {
   }
 
   @Test
-  public void testZeroLengthFile() throws IOException, ClassNotFoundException {
+  public void testZeroLengthFile() throws IOException {
+    File zeroFile = tempFolder.newFile();
+    zeroFile.createNewFile();
+
+    Map<String, File> jarFiles = new HashMap<>();
+    jarFiles.put("JarDeployerDUnitZLF.jar", zeroFile);
+
     assertThatThrownBy(() -> {
-      ClassPathLoader.getLatest().getJarDeployer().deploy(new String[] {"JarDeployerDUnitZLF.jar"},
-          new byte[][] {new byte[0]});
+      ClassPathLoader.getLatest().getJarDeployer().deploy(jarFiles);
     }).isInstanceOf(IllegalArgumentException.class);
 
-    assertThatThrownBy(() -> {
-      ClassPathLoader.getLatest().getJarDeployer().deploy(
-          new String[] {"JarDeployerDUnitZLF1.jar", "JarDeployerDUnitZLF2.jar"},
-          new byte[][] {new ClassBuilder().createJarFromName("JarDeployerDUnitZLF1"), new byte[0]});
+    byte[] validBytes = new ClassBuilder().createJarFromName("JarDeployerDUnitZLF1");
+    File validFile = tempFolder.newFile();
 
+    IOUtils.copy(new ByteArrayInputStream(validBytes), new FileOutputStream(validFile));
+
+    jarFiles.put("JarDeployerDUnitZLF1.jar", validFile);
+
+    jarFiles.put("JarDeployerDUnitZLF2.jar", zeroFile);
+
+    assertThatThrownBy(() -> {
+      ClassPathLoader.getLatest().getJarDeployer().deploy(jarFiles);
     }).isInstanceOf(IllegalArgumentException.class);
   }
 
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 abaa458..f78a6d6 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
@@ -15,7 +15,6 @@
 package org.apache.geode.internal;
 
 
-import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.io.File;
@@ -51,22 +50,14 @@ public class DeployedJarJUnitTest {
 
   @Test
   public void validJarContentDoesNotThrow() throws Exception {
-    new DeployedJar(jarFile, JAR_NAME, expectedJarBytes);
-  }
-
-  @Test
-  public void unexpectedContentThrowsException() throws Exception {
-    givenUnexpectedJarFileContents();
-
-    assertThatThrownBy(() -> new DeployedJar(jarFile, JAR_NAME, expectedJarBytes))
-        .isInstanceOf(IllegalStateException.class);
+    new DeployedJar(jarFile, JAR_NAME);
   }
 
   @Test
   public void invalidContentThrowsException() throws Exception {
     byte[] invalidJarBytes = givenInvalidJarBytes();
 
-    assertThatThrownBy(() -> new DeployedJar(jarFile, JAR_NAME, invalidJarBytes))
+    assertThatThrownBy(() -> new DeployedJar(jarFile, JAR_NAME))
         .isInstanceOf(IllegalArgumentException.class);
   }
 
diff --git a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerDeadlockTest.java b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerDeadlockTest.java
index c03b114..e20ca62 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/JarDeployerDeadlockTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/JarDeployerDeadlockTest.java
@@ -16,7 +16,9 @@ package org.apache.geode.internal;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.lang.management.ManagementFactory;
 import java.lang.management.ThreadInfo;
 import java.lang.management.ThreadMXBean;
@@ -25,6 +27,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.commons.io.IOUtils;
 import org.awaitility.Awaitility;
 import org.junit.After;
 import org.junit.Before;
@@ -68,11 +71,15 @@ public class JarDeployerDeadlockTest {
   public void testMultiThreadingDoesNotCauseDeadlock() throws Exception {
     // Add two JARs to the classpath
     byte[] jarBytes = this.classBuilder.createJarFromName("JarClassLoaderJUnitA");
-    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitA.jar", jarBytes);
+    File jarFile = temporaryFolder.newFile();
+    IOUtils.copy(new ByteArrayInputStream(jarBytes), new FileOutputStream(jarFile));
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitA.jar", jarFile);
 
     jarBytes = this.classBuilder.createJarFromClassContent("com/jcljunit/JarClassLoaderJUnitB",
         "package com.jcljunit; public class JarClassLoaderJUnitB {}");
-    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitB.jar", jarBytes);
+    File jarFile2 = temporaryFolder.newFile();
+    IOUtils.copy(new ByteArrayInputStream(jarBytes), new FileOutputStream(jarFile2));
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarClassLoaderJUnitB.jar", jarFile2);
 
     String[] classNames = new String[] {"JarClassLoaderJUnitA", "com.jcljunit.JarClassLoaderJUnitB",
         "NON-EXISTENT CLASS"};
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 ed7d23f..90402c1 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
@@ -20,12 +20,15 @@ package org.apache.geode.internal;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
+import org.apache.commons.io.IOUtils;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -62,9 +65,10 @@ public class JarDeployerIntegrationTest {
     String jarName = "JarDeployerIntegrationTest.jar";
 
     byte[] firstJarBytes = createJarWithClass("ClassA");
+    File jarFile1 = writeJarBytes(firstJarBytes);
 
     // First deploy of the JAR file
-    DeployedJar firstDeployedJar = jarDeployer.deployWithoutRegistering(jarName, firstJarBytes);
+    DeployedJar firstDeployedJar = jarDeployer.deployWithoutRegistering(jarName, jarFile1);
 
     assertThat(firstDeployedJar.getFile()).exists().hasBinaryContent(firstJarBytes);
     assertThat(firstDeployedJar.getFile().getName()).contains(".v1.").doesNotContain(".v2.");
@@ -72,8 +76,9 @@ public class JarDeployerIntegrationTest {
     // Now deploy an updated JAR file and make sure that the next version of the JAR file
     // was created
     byte[] secondJarBytes = createJarWithClass("ClassB");
+    File jarFile2 = writeJarBytes(secondJarBytes);
 
-    DeployedJar secondDeployedJar = jarDeployer.deployWithoutRegistering(jarName, secondJarBytes);
+    DeployedJar secondDeployedJar = jarDeployer.deployWithoutRegistering(jarName, jarFile2);
     File secondDeployedJarFile = new File(secondDeployedJar.getFileCanonicalPath());
 
     assertThat(secondDeployedJarFile).exists().hasBinaryContent(secondJarBytes);
@@ -93,10 +98,11 @@ public class JarDeployerIntegrationTest {
 
     final JarDeployer jarDeployer = new JarDeployer(alternateDir);
     final byte[] jarBytes = this.classBuilder.createJarFromName("JarDeployerDUnitDTID");
+    File jarFile = writeJarBytes(jarBytes);
 
     // Test to verify that deployment fails if the directory doesn't exist.
     assertThatThrownBy(() -> {
-      jarDeployer.deployWithoutRegistering("JarDeployerIntegrationTest.jar", jarBytes);
+      jarDeployer.deployWithoutRegistering("JarDeployerIntegrationTest.jar", jarFile);
     }).isInstanceOf(IOException.class).hasMessageContaining("Unable to write to deploy directory:");
   }
 
@@ -106,12 +112,13 @@ public class JarDeployerIntegrationTest {
     assertThat(versionedName.getName()).isEqualTo("myJar.v1.jar");
 
     byte[] jarBytes = this.classBuilder.createJarFromName("ClassA");
-    File deployedJarFile = jarDeployer.deployWithoutRegistering("myJar.jar", jarBytes).getFile();
+    File jarFile = writeJarBytes(jarBytes);
+    File deployedJarFile = jarDeployer.deployWithoutRegistering("myJar.jar", jarFile).getFile();
 
     assertThat(deployedJarFile.getName()).isEqualTo("myJar.v1.jar");
 
     File secondDeployedJarFile =
-        jarDeployer.deployWithoutRegistering("myJar.jar", jarBytes).getFile();
+        jarDeployer.deployWithoutRegistering("myJar.jar", deployedJarFile).getFile();
 
     assertThat(secondDeployedJarFile.getName()).isEqualTo("myJar.v2.jar");
   }
@@ -246,5 +253,10 @@ public class JarDeployerIntegrationTest {
     assertThat(jarBVersion3).exists();
   }
 
+  private File writeJarBytes(byte[] content) throws IOException {
+    File tempJar = temporaryFolder.newFile();
+    IOUtils.copy(new ByteArrayInputStream(content), new FileOutputStream(tempJar));
+    return tempJar;
+  }
 
 }
diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/backup/IncrementalBackupDistributedTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/backup/IncrementalBackupDistributedTest.java
index d71bb85..6b09aed 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/cache/backup/IncrementalBackupDistributedTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/cache/backup/IncrementalBackupDistributedTest.java
@@ -21,8 +21,10 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileFilter;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.nio.file.Files;
@@ -36,6 +38,7 @@ import java.util.Set;
 import java.util.regex.Pattern;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.filefilter.DirectoryFileFilter;
 import org.apache.commons.io.filefilter.RegexFileFilter;
 import org.apache.logging.log4j.Logger;
@@ -1002,6 +1005,9 @@ public class IncrementalBackupDistributedTest extends JUnit4CacheTestCase {
     final ClassBuilder classBuilder = new ClassBuilder();
     final byte[] classBytes = classBuilder.createJarFromName(jarName);
 
+    File jarFile = tempDir.newFile();
+    IOUtils.copyLarge(new ByteArrayInputStream(classBytes), new FileOutputStream(jarFile));
+
     VM vm0 = Host.getHost(0).getVM(0);
 
     /*
@@ -1009,7 +1015,7 @@ public class IncrementalBackupDistributedTest extends JUnit4CacheTestCase {
      */
     File deployedJarFile = vm0.invoke(() -> {
       DeployedJar deployedJar =
-          ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, classBytes);
+          ClassPathLoader.getLatest().getJarDeployer().deploy(jarName, jarFile);
       return deployedJar.getFile();
     });
 
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 45e832f..91c209d 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
@@ -209,7 +209,7 @@ public class DeployCommandRedeployDUnitTest {
 
   private static void assertThatCanLoad(String jarName, String className)
       throws ClassNotFoundException {
-    assertThat(ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jarName)).isNotNull();
+    assertThat(ClassPathLoader.getLatest().getJarDeployer().getDeployedJar(jarName)).isNotNull();
     assertThat(ClassPathLoader.getLatest().forName(className)).isNotNull();
   }
 
diff --git a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployWithGroupsDUnitTest.java b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployWithGroupsDUnitTest.java
index 145bbe4..a38d142 100644
--- a/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployWithGroupsDUnitTest.java
+++ b/geode-core/src/test/java/org/apache/geode/management/internal/cli/commands/DeployWithGroupsDUnitTest.java
@@ -253,12 +253,12 @@ public class DeployWithGroupsDUnitTest implements Serializable {
   }
 
   private void assertThatCanLoad(String jarName, String className) throws ClassNotFoundException {
-    assertThat(ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jarName)).isNotNull();
+    assertThat(ClassPathLoader.getLatest().getJarDeployer().getDeployedJar(jarName)).isNotNull();
     assertThat(ClassPathLoader.getLatest().forName(className)).isNotNull();
   }
 
   private void assertThatCannotLoad(String jarName, String className) {
-    assertThat(ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jarName)).isNull();
+    assertThat(ClassPathLoader.getLatest().getJarDeployer().getDeployedJar(jarName)).isNull();
     assertThatThrownBy(() -> ClassPathLoader.getLatest().forName(className))
         .isExactlyInstanceOf(ClassNotFoundException.class);
   }
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 2a6f962..99bbafc 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
@@ -160,7 +160,7 @@ public class ClusterConfig implements Serializable {
       }
 
       for (String jar : this.getJarNames()) {
-        DeployedJar deployedJar = ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jar);
+        DeployedJar deployedJar = ClassPathLoader.getLatest().getJarDeployer().getDeployedJar(jar);
         assertThat(deployedJar).isNotNull();
         assertThat(Class.forName(nameOfClassContainedInJar(jar), true,
             new URLClassLoader(new URL[] {deployedJar.getFileURL()}))).isNotNull();
@@ -172,7 +172,7 @@ public class ClusterConfig implements Serializable {
       for (String jar : undeployedJarNames) {
         System.out.println("Verifying undeployed jar: " + jar);
         DeployedJar undeployedJar =
-            ClassPathLoader.getLatest().getJarDeployer().findDeployedJar(jar);
+            ClassPathLoader.getLatest().getJarDeployer().getDeployedJar(jar);
         assertThat(undeployedJar).isNull();
       }
     });
diff --git a/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt b/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
index e9a08a5..5194f46 100644
--- a/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
+++ b/geode-core/src/test/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
@@ -2089,8 +2089,8 @@ toData,22,2a2bb600682ab400162bb800692ab400172bb80069b1
 toDataPre_GEODE_1_1_1_0,49,2ab400062bb800692ab400032bb8006a2ab400022bb800692ab4001f2bb800692ab400052bb800692ab400042bb80069b1
 
 org/apache/geode/management/internal/configuration/messages/ConfigurationResponse,2
-fromData,36,2a2bb8000eb500042a2bb8000fb500082a2bb80010b5000a2a2bb80011b60012b50005b1
-toData,39,2ab40004c000022bb800072ab400082bb800092ab4000a2bb8000b2ab40005b8000c2bb8000db1
+fromData,28,2a2bb8000bb500042a2bb8000bb500052a2bb8000cb6000db50006b1
+toData,28,2ab400042bb800082ab400052bb800082ab40006b800092bb8000ab1
 
 org/apache/geode/management/internal/configuration/messages/SharedConfigurationStatusRequest,2
 fromData,1,b1
@@ -2135,4 +2135,3 @@ toData,9,2ab400022bb80005b1
 org/apache/geode/redis/internal/DoubleWrapper,2
 fromData,9,2a2bb80004b50002b1
 toData,9,2ab400022bb80003b1
-
diff --git a/gradle/dependency-versions.properties b/gradle/dependency-versions.properties
index 723a2e5..f5bf44a 100644
--- a/gradle/dependency-versions.properties
+++ b/gradle/dependency-versions.properties
@@ -80,6 +80,7 @@ powermock.version = 1.7.1
 protobuf-gradle-plugin.version = 0.8.1
 protobuf-java.version = 3.3.1
 protoc.version = 3.0.0
+rmiio.version = 2.1.2
 selenium.version=3.0.1
 shiro.version=1.3.2
 slf4j-api.version = 1.7.24

-- 
To stop receiving notification emails like this one, please contact
"commits@geode.apache.org" <commits@geode.apache.org>.

Mime
View raw message