/*
* Copyright © 2016 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.app.runtime.spark;
import co.cask.cdap.common.lang.ClassLoaders;
import com.google.common.io.Closeables;
import org.junit.Test;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
/**
* Unit tests for {@link SparkRunnerClassLoader}.
*/
public class SparkRunnerClassLoaderTest {
@Test
public void testConcurrentLoadClose() throws Exception {
// This is for testing CDAP-5822, which apparently is a JDK bug.
// Since the problem manifest as a race between loading class and closing of another ClassLoader instance,
// we loop 100 times, which give a very high chance that the test would fail when the bug was not fixed.
for (int i = 0; i < 100; i++) {
List<URL> urls = ClassLoaders.getClassLoaderURLs(getClass().getClassLoader(), new ArrayList<URL>());
final URL[] urlArray = urls.toArray(new URL[urls.size()]);
SparkRunnerClassLoader firstCL = new SparkRunnerClassLoader(urlArray,
getClass().getClassLoader(), false, false);
// Load a class from the first CL.
firstCL.loadClass("org.apache.spark.SparkContext");
// Create a thread to load the same class name from a different CL instance
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Exception> exception = new AtomicReference<>();
Thread t = new Thread() {
@Override
public void run() {
SparkRunnerClassLoader secondCL = new SparkRunnerClassLoader(urlArray,
getClass().getClassLoader(), false, false);
try {
latch.countDown();
secondCL.loadClass("org.apache.spark.SparkContext");
} catch (Exception e) {
exception.set(e);
} finally {
Closeables.closeQuietly(secondCL);
}
}
};
t.start();
// When the thread started to load the class, close the first ClassLoader
latch.await();
firstCL.close();
t.join();
// There shouldn't be any exception raised from the thread.
Exception ex = exception.get();
if (ex != null) {
throw ex;
}
}
}
}