You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ja...@apache.org on 2019/06/18 21:02:01 UTC

[geode] branch develop updated: GEODE-6822: Deploying jars only creates new classloader for the newly deployed jar (#3537)

This is an automated email from the ASF dual-hosted git repository.

jasonhuynh pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/develop by this push:
     new a51015a  GEODE-6822: Deploying jars only creates new classloader for the newly deployed jar (#3537)
a51015a is described below

commit a51015ac467336c0731c010f8918e4c6364e4d26
Author: Jason Huynh <hu...@gmail.com>
AuthorDate: Tue Jun 18 14:01:48 2019 -0700

    GEODE-6822: Deploying jars only creates new classloader for the newly deployed jar (#3537)
    
      * Deserialized objects of classes unrelated to the latest deployed jar should now compare correctly after an unrelated new jar is deployed
      * This solution causes each deployed jar to create it's own class loader and chains them together.
      * The class loaders are now child first class loaders and If a class cannot be found, it will search all 'sibling'/other deployed jars
      * When a jar is redeployed (new version), we keep some meta data in a map so we no longer crawl the 'old' jar when possible and only search through the latest version of a specific jar.
      * Java does some caching of classes and in the undeploy scenario, we may still be able to load classes that have been "removed" from the new jar (only if it was already cached by some other class loader). This can happen when we have inter jar dependencies.
      * Geode also caches classes, so whenever a redeploy occurs, the geode class cache gets wiped, and first lookups take a minor performance hit, but subsequent lookups go through geodes caching of the classes (so the for name and class lookups through chaining should have a minimal impact)
---
 .../geode/internal/ClassPathLoaderDeployTest.java  | 360 +++++++++++++++-
 .../geode/internal/ClassPathLoaderJUnitTest.java   | 460 +++++++++++++++++++++
 .../geode/internal/ChildFirstClassLoader.java      | 103 +++++
 .../org/apache/geode/internal/ClassPathLoader.java |  61 ++-
 .../internal/DeployJarChildFirstClassLoader.java   | 120 ++++++
 .../org/apache/geode/internal/DeployedJar.java     |   1 -
 .../org/apache/geode/internal/JarDeployer.java     |   5 +-
 7 files changed, 1089 insertions(+), 21 deletions(-)

diff --git a/geode-core/src/integrationTest/java/org/apache/geode/internal/ClassPathLoaderDeployTest.java b/geode-core/src/integrationTest/java/org/apache/geode/internal/ClassPathLoaderDeployTest.java
index dead032..515fdbb 100644
--- a/geode-core/src/integrationTest/java/org/apache/geode/internal/ClassPathLoaderDeployTest.java
+++ b/geode-core/src/integrationTest/java/org/apache/geode/internal/ClassPathLoaderDeployTest.java
@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.List;
 
+import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -44,6 +45,11 @@ public class ClassPathLoaderDeployTest {
   @Rule
   public ServerStarterRule server = new ServerStarterRule();
 
+  @After
+  public void resetClassPathLoader() {
+    ClassPathLoader.setLatestToDefault();
+  }
+
   @Test
   public void testDeployWithExistingDependentJars() throws Exception {
     ClassBuilder classBuilder = new ClassBuilder();
@@ -136,6 +142,336 @@ public class ClassPathLoaderDeployTest {
     assertThat(result.get(0)).isEqualTo("Version2");
   }
 
+  @Test
+  public void deployingJarsUnrelatedToDeserializedObjectsShouldNotCauseFailingInstanceOfChecks()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    File jarA = createVersionOfJar("JarAVersion11", classAName, "JarA.jar");
+    File jarB = createVersionOfJar("JarBVersion11", classBName, "JarB.jar");
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class class1 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = class1.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class class2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object anotherClassAInstance = class2.newInstance();
+
+    assertThat(classAInstance).isInstanceOf(anotherClassAInstance.getClass());
+  }
+
+  @Test
+  public void shouldBeAbleToLoadClassesWithInterdependenciesAcrossDifferentJars() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    assertThat(classAName).isEqualTo(classAInstance.getClass().getSimpleName());
+  }
+
+  @Test
+  public void loadingInterdependentJarsShouldNotCauseClassIncompatibilities() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    assertThat(classBInstance.getClass()).isAssignableFrom(classAInstance.getClass());
+  }
+
+  @Test
+  public void loadingParentClassFirstFromInterdependentJarsShouldNotCauseClassIncompatibilities()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    Class classB2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classB2Instance = classB2.newInstance();
+
+    assertThat(classB2Instance).isInstanceOf(classBInstance.getClass());
+    assertThat(classBInstance.getClass()).isAssignableFrom(classAInstance.getClass());
+  }
+
+  @Test
+  public void redeployingParentClassDoesNotCauseSubclassIncompatibilities() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    String classB2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return false;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+    File jarBV2 =
+        createJarFromClassContents("JarBVersion2", classBName, "JarB.jar", classB2Contents);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarBV2);
+
+    Class classA2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classA2Instance = classA2.newInstance();
+
+    assertThat(classA2Instance).isInstanceOf(classAInstance.getClass());
+  }
+
+  @Test
+  public void redeployingParentClassIfParentDeployedLastDoesNotCauseSubclassIncompatibilities()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    String classB2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return false;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+    File jarBV2 =
+        createJarFromClassContents("JarBVersion2", classBName, "JarB.jar", classB2Contents);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarBV2);
+    Class classA2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classA2Instance = classA2.newInstance();
+
+    assertThat(classA2Instance).isInstanceOf(classAInstance.getClass());
+  }
+
+
+  @Test(expected = ClassNotFoundException.class)
+  public void redeployingJarWithRemovedClassShouldNoLongerAllowLoadingRemovedClass()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+    String classB2Name = "classB2";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    String classB2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classB2Name + " { public boolean someMethod() {return false;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+    File jarBV2 =
+        createJarFromClassContents("JarBVersion2", classB2Name, "JarB.jar", classB2Contents);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarBV2);
+    Class classB2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+  }
+
+  @Test
+  public void undeployedUnrelatedJarShouldNotAffectDeserializedObjectComparison() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().undeploy("JarB.jar");
+    Object classA2Instance = classA.newInstance();
+    assertThat(classA2Instance).isInstanceOf(classAInstance.getClass());
+  }
+
+  @Test(expected = ClassNotFoundException.class)
+  public void undeployedJarShouldNoLongerAllowLoadingUndeployedClass() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+    String classB2Name = "classB2";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    String classB2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classB2Name + " { public boolean someMethod() {return false;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().undeploy("JarB.jar");
+    classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    classBInstance = classB.newInstance();
+
+  }
 
   private File createVersionOfJar(String version, String functionName, String jarName)
       throws IOException {
@@ -147,9 +483,29 @@ public class ClassPathLoaderDeployTest {
             + "public void execute(FunctionContext context) {context.getResultSender().lastResult(\""
             + version + "\");}}";
 
+    return createJarFromClassContents(version, functionName, jarName, classContents);
+  }
+
+  private File createJarFromClassContents(String version, String functionName, String jarName,
+      String classContents)
+      throws IOException {
+
     File jar = new File(this.temporaryFolder.newFolder(version), jarName);
-    ClassBuilder functionClassBuilder = new ClassBuilder();
-    functionClassBuilder.writeJarFromContent("jddunit/function/" + functionName, classContents,
+    ClassBuilder classBuilder = new ClassBuilder();
+    classBuilder.writeJarFromContent("jddunit/function/" + functionName, classContents,
+        jar);
+
+    return jar;
+  }
+
+  private File createJarFromClassContents(String version, String functionName, String jarName,
+      String classContents, String additionalClassPath)
+      throws IOException {
+
+    File jar = new File(this.temporaryFolder.newFolder(version), jarName);
+    ClassBuilder classBuilder = new ClassBuilder();
+    classBuilder.addToClassPath(additionalClassPath);
+    classBuilder.writeJarFromContent("jddunit/function/" + functionName, classContents,
         jar);
 
     return jar;
diff --git a/geode-core/src/integrationTest/java/org/apache/geode/internal/ClassPathLoaderJUnitTest.java b/geode-core/src/integrationTest/java/org/apache/geode/internal/ClassPathLoaderJUnitTest.java
new file mode 100644
index 0000000..bf69f54
--- /dev/null
+++ b/geode-core/src/integrationTest/java/org/apache/geode/internal/ClassPathLoaderJUnitTest.java
@@ -0,0 +1,460 @@
+/*
+ * 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.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import org.apache.geode.test.compiler.ClassBuilder;
+
+/**
+ * Integration tests for {@link ClassPathLoader}.
+ */
+public class ClassPathLoaderJUnitTest {
+  @Rule
+  public TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+  @After
+  public void resetClassPathLoader() {
+    ClassPathLoader.setLatestToDefault();
+  }
+
+  @Test
+  public void deployingJarsUnrelatedToDeserializedObjectsShouldNotCauseFailingInstanceOfChecks()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    File jarA = createVersionOfJar("JarAVersion11", classAName, "JarA.jar");
+    File jarB = createVersionOfJar("JarBVersion11", classBName, "JarB.jar");
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class class1 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = class1.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class class2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object anotherClassAInstance = class2.newInstance();
+
+    assertThat(classAInstance).isInstanceOf(anotherClassAInstance.getClass());
+  }
+
+  @Test
+  public void shouldBeAbleToLoadClassesWithInterdependenciesAcrossDifferentJars() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    assertThat(classAName).isEqualTo(classAInstance.getClass().getSimpleName());
+  }
+
+  @Test
+  public void loadingInterdependentJarsShouldNotCauseClassIncompatibilities() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    assertThat(classBInstance.getClass()).isAssignableFrom(classAInstance.getClass());
+  }
+
+  @Test
+  public void loadingParentClassFirstFromInterdependentJarsShouldNotCauseClassIncompatibilities()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    Class classB2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classB2Instance = classB2.newInstance();
+
+    assertThat(classB2Instance).isInstanceOf(classBInstance.getClass());
+    assertThat(classBInstance.getClass()).isAssignableFrom(classAInstance.getClass());
+  }
+
+  @Test
+  public void redeploySubclassJarThatExtendsInterdependentJarShouldNowLoadNewSubclass()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classA2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return false;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+    File jarAV2 =
+        createJarFromClassContents("JarAVersion2", classAName, "JarA.jar", classA2Contents,
+            jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarAV2);
+    Class classA2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classA2Instance = classA2.newInstance();
+
+    assertThat(classA2Instance).isNotInstanceOf(classA);
+  }
+
+  @Test
+  public void redeployingParentClassDoesNotCauseSubclassIncompatibilities() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    String classB2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return false;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+    File jarBV2 =
+        createJarFromClassContents("JarBVersion2", classBName, "JarB.jar", classB2Contents);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarBV2);
+
+    Class classA2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classA2Instance = classA2.newInstance();
+
+    assertThat(classA2Instance).isInstanceOf(classAInstance.getClass());
+  }
+
+  @Test
+  public void redeployingParentClassIfParentDeployedLastDoesNotCauseSubclassIncompatibilities()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    String classB2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return false;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+    File jarBV2 =
+        createJarFromClassContents("JarBVersion2", classBName, "JarB.jar", classB2Contents);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarBV2);
+    Class classA2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classA2Instance = classA2.newInstance();
+
+    assertThat(classA2Instance).isInstanceOf(classAInstance.getClass());
+  }
+
+  @Test(expected = ClassNotFoundException.class)
+  public void redeployingJarWithRemovedClassShouldNoLongerAllowLoadingRemovedClass()
+      throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+    String classB2Name = "classB2";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " extends " + classBName
+            + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    String classB2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classB2Name + " { public boolean someMethod() {return false;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+    File jarBV2 =
+        createJarFromClassContents("JarBVersion2", classB2Name, "JarB.jar", classB2Contents);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarBV2);
+    Class classB2 =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+  }
+
+  @Test
+  public void undeployedUnrelatedJarShouldNotAffectDeserializedObjectComparison() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents);
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarA.jar", jarA);
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().undeploy("JarB.jar");
+    Object classA2Instance = classA.newInstance();
+    assertThat(classA2Instance).isInstanceOf(classAInstance.getClass());
+  }
+
+  @Test(expected = ClassNotFoundException.class)
+  public void undeployedJarShouldNoLongerAllowLoadingUndeployedClass() throws Exception {
+    String classAName = "classA";
+    String classBName = "classB";
+    String classB2Name = "classB2";
+
+    String classAContents =
+        "package jddunit.function;"
+            + "public class "
+            + classAName + " { public boolean hasResult() {return true;}}";
+
+    String classBContents =
+        "package jddunit.function;"
+            + "public class "
+            + classBName + " { public boolean someMethod() {return true;}}";
+
+    String classB2Contents =
+        "package jddunit.function;"
+            + "public class "
+            + classB2Name + " { public boolean someMethod() {return false;}}";
+
+    File jarB = createJarFromClassContents("JarBVersion1", classBName, "JarB.jar", classBContents);
+    File jarA = createJarFromClassContents("JarAVersion1", classAName, "JarA.jar", classAContents,
+        jarB.getAbsolutePath());
+
+    ClassPathLoader.getLatest().getJarDeployer().deploy("JarB.jar", jarB);
+    Class classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    Object classBInstance = classB.newInstance();
+
+    Class classA =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classAName);
+    Object classAInstance = classA.newInstance();
+
+    ClassPathLoader.getLatest().getJarDeployer().undeploy("JarB.jar");
+    classB =
+        ClassPathLoader.getLatest().asClassLoader().loadClass("jddunit.function." + classBName);
+    classBInstance = classB.newInstance();
+
+  }
+
+  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 + "\");}}";
+
+    return createJarFromClassContents(version, functionName, jarName, classContents);
+  }
+
+  private File createJarFromClassContents(String version, String functionName, String jarName,
+      String classContents)
+      throws IOException {
+
+    File jar = new File(this.temporaryFolder.newFolder(version), jarName);
+    ClassBuilder classBuilder = new ClassBuilder();
+    classBuilder.writeJarFromContent("jddunit/function/" + functionName, classContents,
+        jar);
+
+    return jar;
+  }
+
+  private File createJarFromClassContents(String version, String functionName, String jarName,
+      String classContents, String additionalClassPath)
+      throws IOException {
+
+    File jar = new File(this.temporaryFolder.newFolder(version), jarName);
+    ClassBuilder classBuilder = new ClassBuilder();
+    classBuilder.addToClassPath(additionalClassPath);
+    classBuilder.writeJarFromContent("jddunit/function/" + functionName, classContents,
+        jar);
+
+    return jar;
+  }
+
+  private void assertThatClassCanBeLoaded(String className) throws ClassNotFoundException {
+    assertThat(ClassPathLoader.getLatest().forName(className)).isNotNull();
+  }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/internal/ChildFirstClassLoader.java b/geode-core/src/main/java/org/apache/geode/internal/ChildFirstClassLoader.java
new file mode 100644
index 0000000..ccae0a1
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/ChildFirstClassLoader.java
@@ -0,0 +1,103 @@
+/*
+ * 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.net.URL;
+import java.net.URLClassLoader;
+
+public class ChildFirstClassLoader extends URLClassLoader {
+
+  public ChildFirstClassLoader() {
+    super(new URL[] {});
+  }
+
+  public ChildFirstClassLoader(URL[] urls) {
+    super(urls);
+  }
+
+  public ChildFirstClassLoader(URL[] urls, ClassLoader parent) {
+    super(urls, parent);
+  }
+
+  @Override
+  public void addURL(URL url) {
+    super.addURL(url);
+  }
+
+  @Override
+  public Class loadClass(String name) throws ClassNotFoundException {
+    return loadClass(name, false);
+  }
+
+  /**
+   * We override the parent-first behavior established by java.lang.Classloader.
+   */
+  @Override
+  protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+    Class c = null;
+
+    // First, check if the class has already been loaded
+    c = findLoadedClass(name);
+
+    // if not loaded, search the local (child) resources
+    if (c == null) {
+      try {
+        c = findClass(name);
+      } catch (ClassNotFoundException cnfe) {
+        // ignore
+      }
+    }
+
+    // if we could not find it, delegate to parent
+    // Note that we don't attempt to catch any ClassNotFoundException
+    if (c == null) {
+      try {
+        c = searchParent(name);
+      } catch (ClassNotFoundException | NoClassDefFoundError cnfe) {
+        // ignore
+      }
+    }
+
+    if (resolve) {
+      resolveClass(c);
+    }
+
+    return c;
+  }
+
+  @Override
+  public URL getResource(String name) {
+    URL url = null;
+    if (url == null) {
+      url = findResource(name);
+    }
+    if (url == null) {
+      url = super.getResource(name);
+    }
+    return url;
+  }
+
+  protected Class searchParent(String name) throws ClassNotFoundException {
+    Class c;
+    if (getParent() != null) {
+      c = getParent().loadClass(name);
+    } else {
+      c = getSystemClassLoader().loadClass(name);
+    }
+    return c;
+  }
+}
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 85d3add..0bab739 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
@@ -21,10 +21,11 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Proxy;
 import java.net.URL;
-import java.net.URLClassLoader;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 
@@ -73,18 +74,15 @@ public class ClassPathLoader {
   @MakeNotStatic
   private static volatile ClassPathLoader latest;
 
-  private volatile URLClassLoader classLoaderForDeployedJars;
+  public final HashMap<String, DeployJarChildFirstClassLoader> latestJarNamesToClassLoader =
+      new HashMap<>();
+
+  private volatile DeployJarChildFirstClassLoader leafLoader;
 
   private final JarDeployer jarDeployer;
 
   private boolean excludeTCCL;
 
-  void rebuildClassLoaderForDeployedJars() {
-    ClassLoader parent = ClassPathLoader.class.getClassLoader();
-
-    this.classLoaderForDeployedJars = new URLClassLoader(jarDeployer.getDeployedJarURLs(), parent);
-  }
-
   public ClassPathLoader(boolean excludeTCCL) {
     this.excludeTCCL = excludeTCCL;
     this.jarDeployer = new JarDeployer();
@@ -118,6 +116,30 @@ public class ClassPathLoader {
     return new ClassPathLoader(excludeTCCL);
   }
 
+  synchronized void rebuildClassLoaderForDeployedJars() {
+    leafLoader = null;
+    Collection<DeployedJar> deployedJars = jarDeployer.getDeployedJars().values();
+    for (DeployedJar deployedJar : deployedJars) {
+      chainClassloader(deployedJar);
+    }
+  }
+
+  ClassLoader getLeafLoader() {
+    if (leafLoader == null) {
+      return ClassPathLoader.class.getClassLoader();
+    }
+    return leafLoader;
+  }
+
+  synchronized void chainClassloader(DeployedJar jar) {
+    this.leafLoader = new DeployJarChildFirstClassLoader(latestJarNamesToClassLoader,
+        new URL[] {jar.getFileURL()}, jar.getJarName(), getLeafLoader());
+  }
+
+  synchronized void unloadClassloaderForJar(String jarName) {
+    latestJarNamesToClassLoader.put(jarName, null);
+  }
+
   public URL getResource(final String name) {
     final boolean isDebugEnabled = logger.isTraceEnabled();
     if (isDebugEnabled) {
@@ -151,13 +173,26 @@ public class ClassPathLoader {
       logger.trace("forName({})", name);
     }
 
+    Class<?> clazz = forName(name, isDebugEnabled);
+    if (clazz != null)
+      return clazz;
+
+    throw new ClassNotFoundException(name);
+  }
+
+  private Class<?> forName(String name, boolean isDebugEnabled) {
     for (ClassLoader classLoader : this.getClassLoaders()) {
       if (isDebugEnabled) {
         logger.trace("forName trying: {}", classLoader);
       }
       try {
+        // Do not look up class definitions in jars that have been unloaded or are old
+        if (classLoader instanceof DeployJarChildFirstClassLoader) {
+          if (((DeployJarChildFirstClassLoader) classLoader).thisIsOld()) {
+            return null;
+          }
+        }
         Class<?> clazz = Class.forName(name, true, classLoader);
-
         if (clazz != null) {
           if (isDebugEnabled) {
             logger.trace("forName found by: {}", classLoader);
@@ -168,8 +203,7 @@ public class ClassPathLoader {
         // try next classLoader
       }
     }
-
-    throw new ClassNotFoundException(name);
+    return null;
   }
 
   /**
@@ -308,10 +342,7 @@ public class ClassPathLoader {
       }
     }
 
-    if (classLoaderForDeployedJars != null) {
-      classLoaders.add(classLoaderForDeployedJars);
-    }
-
+    classLoaders.add(getLeafLoader());
     return classLoaders;
   }
 
diff --git a/geode-core/src/main/java/org/apache/geode/internal/DeployJarChildFirstClassLoader.java b/geode-core/src/main/java/org/apache/geode/internal/DeployJarChildFirstClassLoader.java
new file mode 100644
index 0000000..489add6
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/DeployJarChildFirstClassLoader.java
@@ -0,0 +1,120 @@
+/*
+ * 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.IOException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.stream.Collectors;
+
+/**
+ * This class loader loads from itself/child first. It also crawls the rest of the deployed jars
+ * when attempting
+ * to do a class look up (for inter jar dependencies).
+ *
+ * As new jars versions of jars are deployed, the jar deployer does a little book keeping in the
+ * latestJarNamesToClassLoader map.
+ * This map is then used to ignore any old versions of a jar. For example if we have deployed
+ * foov2.jar we should no longer crawl
+ * foov1.jar when doing class look ups (even in the inter jar dependency case)
+ *
+ */
+public class DeployJarChildFirstClassLoader extends ChildFirstClassLoader {
+
+  private String jarName;
+  public final HashMap<String, DeployJarChildFirstClassLoader> latestJarNamesToClassLoader;
+
+  public DeployJarChildFirstClassLoader(
+      HashMap<String, DeployJarChildFirstClassLoader> latestJarNamesToClassLoader, URL[] urls,
+      String jarName, ClassLoader parent) {
+    super(urls, parent);
+    this.latestJarNamesToClassLoader = latestJarNamesToClassLoader;
+    this.jarName = jarName;
+    updateLatestJarNamesToClassLoaderMap();
+  }
+
+  private void updateLatestJarNamesToClassLoaderMap() {
+    latestJarNamesToClassLoader.put(jarName, this);
+  }
+
+
+  @Override
+  public Class loadClass(String name) throws ClassNotFoundException {
+    return loadClass(name, false);
+  }
+
+
+  /**
+   * We override the parent-first behavior established by java.lang.Classloader.
+   */
+  @Override
+  protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+    Class c;
+    if (thisIsOld()) {
+      c = searchParent(name);
+    } else {
+      c = super.loadClass(name, resolve);
+    }
+    if (c == null) {
+      for (DeployJarChildFirstClassLoader sibling : latestJarNamesToClassLoader.values().stream()
+          .filter(s -> s != null).collect(Collectors.toList())) {
+        try {
+          c = sibling.findClass(name);
+          if (c != null) {
+            break;
+          }
+        } catch (ClassNotFoundException | NoClassDefFoundError e) {
+        }
+      }
+    }
+    return c;
+  }
+
+  @Override
+  public Class findClass(String name) throws ClassNotFoundException {
+    if (thisIsOld()) {
+      return null;
+    }
+    return super.findClass(name);
+  }
+
+  @Override
+  public URL findResource(String name) {
+    if (thisIsOld()) {
+      return null;
+    }
+    return super.findResource(name);
+  }
+
+  @Override
+  public Enumeration<URL> findResources(final String name) throws IOException {
+    if (thisIsOld()) {
+      return Collections.enumeration(Collections.EMPTY_LIST);
+    } else {
+      return super.findResources(name);
+    }
+  }
+
+  boolean thisIsOld() {
+    return latestJarNamesToClassLoader.get(jarName) != this;
+  }
+
+  public String getJarName() {
+    return jarName;
+  }
+}
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 a2dd2b5..395a519 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
@@ -156,7 +156,6 @@ public class DeployedJar {
     JarInputStream jarInputStream = null;
     try {
       Collection<String> functionClasses = findFunctionsInThisJar();
-
       jarInputStream = new JarInputStream(bufferedInputStream);
       JarEntry jarEntry = jarInputStream.getNextJarEntry();
 
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 24e1633..73a78ff 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
@@ -361,12 +361,11 @@ public class JarDeployer implements Serializable {
         if (deployedJar != null) {
           logger.info("Registering new version of jar: {}", deployedJar);
           DeployedJar oldJar = this.deployedJars.put(deployedJar.getJarName(), deployedJar);
+          ClassPathLoader.getLatest().chainClassloader(deployedJar);
           newVersionToOldVersion.put(deployedJar, oldJar);
         }
       }
 
-      ClassPathLoader.getLatest().rebuildClassLoaderForDeployedJars();
-
       // Finally, unregister functions that were removed
       for (Map.Entry<DeployedJar, DeployedJar> entry : newVersionToOldVersion.entrySet()) {
         DeployedJar newjar = entry.getKey();
@@ -485,7 +484,7 @@ public class JarDeployer implements Serializable {
         throw new IllegalArgumentException("JAR not deployed");
       }
 
-      ClassPathLoader.getLatest().rebuildClassLoaderForDeployedJars();
+      ClassPathLoader.getLatest().unloadClassloaderForJar(jarName);
 
       deployedJar.cleanUp(null);