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 <huyn...@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);
 

Reply via email to