You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2016/12/14 22:47:25 UTC

geode git commit: GEODE-2214: fall back to TCCL if target CL fails to resolve class

Repository: geode
Updated Branches:
  refs/heads/feature/GEODE-2214 213d8bc1c -> f122daeb2


GEODE-2214: fall back to TCCL if target CL fails to resolve class


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

Branch: refs/heads/feature/GEODE-2214
Commit: f122daeb2d1c9f505ff550cf9ca6ea7d452d5c93
Parents: 213d8bc
Author: Kirk Lund <kl...@apache.org>
Authored: Wed Dec 14 14:46:04 2016 -0800
Committer: Kirk Lund <kl...@apache.org>
Committed: Wed Dec 14 14:46:04 2016 -0800

----------------------------------------------------------------------
 extensions/geode-modules/build.gradle           |   1 +
 .../util/ClassLoaderObjectInputStream.java      |   8 +-
 .../util/ClassLoaderObjectInputStreamTest.java  | 169 +++++++++++++++++++
 3 files changed, 177 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/geode/blob/f122daeb/extensions/geode-modules/build.gradle
----------------------------------------------------------------------
diff --git a/extensions/geode-modules/build.gradle b/extensions/geode-modules/build.gradle
index 0ac8c9a..55355fb 100644
--- a/extensions/geode-modules/build.gradle
+++ b/extensions/geode-modules/build.gradle
@@ -26,6 +26,7 @@ dependencies {
   }
   compile 'org.apache.tomcat:juli:' + project.'tomcat6.version'
 
+  testCompile 'org.apache.bcel:bcel:' + project.'bcel.version'
   testCompile 'org.httpunit:httpunit:' + project.'httpunit.version'
   testRuntime 'org.apache.tomcat:coyote:' + project.'tomcat6.version'
 

http://git-wip-us.apache.org/repos/asf/geode/blob/f122daeb/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/ClassLoaderObjectInputStream.java
----------------------------------------------------------------------
diff --git a/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/ClassLoaderObjectInputStream.java b/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/ClassLoaderObjectInputStream.java
index d5ebbab..6368bf6 100644
--- a/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/ClassLoaderObjectInputStream.java
+++ b/extensions/geode-modules/src/main/java/org/apache/geode/modules/util/ClassLoaderObjectInputStream.java
@@ -33,6 +33,12 @@ public class ClassLoaderObjectInputStream extends ObjectInputStream {
 
   @Override
   public Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
-    return Class.forName(desc.getName(), false, loader);
+    Class<?> theClass;
+    try {
+      theClass = Class.forName(desc.getName(), false, loader);
+    } catch (ClassNotFoundException cnfe) {
+      theClass = Thread.currentThread().getContextClassLoader().loadClass(desc.getName());
+    }
+    return theClass;
   }
 }

http://git-wip-us.apache.org/repos/asf/geode/blob/f122daeb/extensions/geode-modules/src/test/java/org/apache/geode/modules/util/ClassLoaderObjectInputStreamTest.java
----------------------------------------------------------------------
diff --git a/extensions/geode-modules/src/test/java/org/apache/geode/modules/util/ClassLoaderObjectInputStreamTest.java b/extensions/geode-modules/src/test/java/org/apache/geode/modules/util/ClassLoaderObjectInputStreamTest.java
new file mode 100644
index 0000000..0e76aad
--- /dev/null
+++ b/extensions/geode-modules/src/test/java/org/apache/geode/modules/util/ClassLoaderObjectInputStreamTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.modules.util;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.apache.bcel.Constants;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.generic.ClassGen;
+import org.apache.geode.internal.ClassPathLoader;
+import org.apache.geode.test.junit.categories.UnitTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.TestName;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Vector;
+
+@Category(UnitTest.class)
+@SuppressWarnings("unused")
+public class ClassLoaderObjectInputStreamTest {
+
+  private ClassLoader originalTCCL;
+  private ClassLoader newTCCL;
+  private String classToLoad;
+  private Object instanceOfTCCLClass;
+
+  @Rule
+  public TestName testName = new TestName();
+
+  @Before
+  public void setUp() throws Exception {
+    this.originalTCCL = Thread.currentThread().getContextClassLoader();
+    this.newTCCL = new GeneratingClassLoader();
+    this.classToLoad = "com.nowhere." + getClass().getSimpleName() + "_" + testName.getMethodName();
+    this.instanceOfTCCLClass = createInstanceOfTCCLClass();
+  }
+
+  @After
+  public void unsetTCCL() throws Exception {
+    Thread.currentThread().setContextClassLoader(this.originalTCCL);
+  }
+
+  @Test
+  public void resolveClassFromTCCLThrowsIfTCCLDisabled() throws Exception {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    ObjectOutputStream oos = new ObjectOutputStream(baos);
+    oos.writeObject(this.instanceOfTCCLClass);
+    oos.close();
+
+    ObjectInputStream ois = new ClassLoaderObjectInputStream(
+        new ByteArrayInputStream(baos.toByteArray()), getClass().getClassLoader());
+
+    assertThatThrownBy(() -> ois.readObject()).isExactlyInstanceOf(ClassNotFoundException.class);
+  }
+
+  @Test
+  public void resolveClassFindsClassFromTCCLIfTCCLEnabled() throws Exception {
+    Thread.currentThread().setContextClassLoader(this.newTCCL);
+
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    ObjectOutputStream oos = new ObjectOutputStream(baos);
+    oos.writeObject(this.instanceOfTCCLClass);
+    oos.close();
+
+    ObjectInputStream ois = new ClassLoaderObjectInputStream(
+        new ByteArrayInputStream(baos.toByteArray()), getClass().getClassLoader());
+
+    Object objectFromTCCL = ois.readObject(); // fails due to new bug
+
+    assertThat(objectFromTCCL).isNotNull();
+    assertThat(objectFromTCCL.getClass()).isNotNull();
+    assertThat(objectFromTCCL.getClass().getName()).isEqualTo(this.classToLoad);
+  }
+
+  private Object createInstanceOfTCCLClass()
+      throws ClassNotFoundException, IllegalAccessException, InstantiationException {
+    Class<?> clazz = Class.forName(this.classToLoad, false, this.newTCCL);
+    return clazz.newInstance();
+  }
+
+  /**
+   * Custom class loader which uses BCEL to always dynamically generate a class for any class name
+   * it tries to load.
+   */
+  private static class GeneratingClassLoader extends ClassLoader {
+
+    /**
+     * Currently unused but potentially useful for some future test. This causes this loader to only
+     * generate a class that the parent could not find.
+     *
+     * @param parent the parent class loader to check with first
+     */
+    public GeneratingClassLoader(ClassLoader parent) {
+      super(parent);
+    }
+
+    /**
+     * Specifies no parent to ensure that this loader generates the named class.
+     */
+    public GeneratingClassLoader() {
+      super(null); // no parent
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+      ClassGen cg = new ClassGen(name, Object.class.getName(), "<generated>",
+          Constants.ACC_PUBLIC | Constants.ACC_SUPER, new String[] {Serializable.class.getName()});
+      cg.addEmptyConstructor(Constants.ACC_PUBLIC);
+      JavaClass jClazz = cg.getJavaClass();
+      byte[] bytes = jClazz.getBytes();
+      return defineClass(jClazz.getClassName(), bytes, 0, bytes.length);
+    }
+
+    @Override
+    protected URL findResource(String name) {
+      URL url = null;
+      try {
+        url = getTempFile().getAbsoluteFile().toURI().toURL();
+        System.out.println("GeneratingClassLoader#findResource returning " + url);
+      } catch (IOException e) {
+        throw new Error(e);
+      }
+      return url;
+    }
+
+    @Override
+    protected Enumeration<URL> findResources(String name) throws IOException {
+      URL url;
+      try {
+        url = getTempFile().getAbsoluteFile().toURI().toURL();
+        System.out.println("GeneratingClassLoader#findResources returning " + url);
+      } catch (IOException e) {
+        throw new Error(e);
+      }
+      Vector<URL> urls = new Vector<URL>();
+      urls.add(url);
+      return urls.elements();
+    }
+
+    protected File getTempFile() {
+      return null;
+    }
+  }
+
+}