/**
* Copyright 2015 StreamSets Inc.
*
* Licensed under 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 com.streamsets.pipeline;
import com.google.common.collect.ImmutableList;
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.TreeSet;
public class TestSDCClassloader {
@Test
@SuppressWarnings("unchecked")
public void testServices() throws Exception {
SystemPackage systemPackage = new SystemPackage(Arrays.asList("org.apache.hadoop.fs."));
Assert.assertTrue(systemPackage.isSystem(ClassLoaderUtil.SERVICES_PREFIX +
"org.apache.hadoop.fs.FileSystem"));
}
private static class CallStoringURLClassLoader extends SDCClassLoader {
final List<String> calls = new ArrayList<>();
public CallStoringURLClassLoader(ClassLoader parent, String systemClasses, String appClasses) {
super("test", "somecl", Arrays.<URL>asList(), parent,
new String[0], new SystemPackage(ClassLoaderUtil.getTrimmedStrings(systemClasses)),
new ApplicationPackage(new TreeSet<String>(Arrays.asList(ClassLoaderUtil.getTrimmedStrings(appClasses)))),
false, false, false);
}
@Override
public URL findResource(String name) {
calls.add("findResource " + name);
return super.findResource(name);
}
@Override
public Enumeration<URL> findResources(String name) throws IOException {
calls.add("findResources " + name);
return super.findResources(name);
}
@Override
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
calls.add("loadClass " + name);
return super.loadClass(name, resolve);
}
}
@Test
@SuppressWarnings("unchecked")
public void testClassLoaderOrder() throws Exception {
CallStoringURLClassLoader parent = new CallStoringURLClassLoader(new URLClassLoader(new URL[0]), "", "");
CallStoringURLClassLoader stage = new CallStoringURLClassLoader(parent, "sys.", "app.");
// sys should be delegated to the parent
doLoadClass(Arrays.asList("loadClass sys.Dummy"), stage, "sys.Dummy");
// other should be delegated to the parent
doLoadClass(Arrays.asList("loadClass other.Dummy"), stage, "other.Dummy");
// app should be delegated to the parent
doLoadClass(Arrays.<String>asList(), stage, "app.Dummy");
doGetResource(Arrays.<String>asList("findResource /META-INF/services/sys.Dummy",
"findResource META-INF/services/sys.Dummy"), stage, ClassLoaderUtil.SERVICES_PREFIX + "sys.Dummy");
doGetResource(Arrays.<String>asList("findResource /META-INF/services/other.Dummy",
"findResource META-INF/services/other.Dummy"), stage, ClassLoaderUtil.SERVICES_PREFIX + "other.Dummy");
doGetResource(Arrays.<String>asList(), stage, ClassLoaderUtil.SERVICES_PREFIX + "app.Dummy");
doGetResources(Arrays.<String>asList("findResources /META-INF/services/sys.Dummy"),
stage, ClassLoaderUtil.SERVICES_PREFIX + "sys.Dummy");
doGetResources(Arrays.<String>asList("findResources /META-INF/services/other.Dummy"),
stage, ClassLoaderUtil.SERVICES_PREFIX + "other.Dummy");
doGetResources(Arrays.<String>asList(), stage, ClassLoaderUtil.SERVICES_PREFIX + "app.Dummy");
doGetResourceAsStream(Arrays.<String>asList("findResource /META-INF/services/sys.Dummy"), stage,
ClassLoaderUtil.SERVICES_PREFIX + "sys.Dummy");
doGetResourceAsStream(Arrays.<String>asList("findResource /META-INF/services/other.Dummy"), stage,
ClassLoaderUtil.SERVICES_PREFIX + "other.Dummy");
doGetResourceAsStream(Arrays.<String>asList(), stage, ClassLoaderUtil.SERVICES_PREFIX +
"app.Dummy");
doGetResource(Arrays.<String>asList("findResource sys.Dummy"), stage, "sys.Dummy");
doGetResource(Arrays.<String>asList("findResource other.Dummy"), stage, "other.Dummy");
doGetResource(Arrays.<String>asList(), stage, "app.Dummy");
doGetResources(Arrays.<String>asList("findResources sys.Dummy"), stage, "sys.Dummy");
doGetResources(Arrays.<String>asList("findResources other.Dummy"), stage, "other.Dummy");
doGetResources(Arrays.<String>asList(), stage, "app.Dummy");
doGetResourceAsStream(Arrays.<String>asList(
"findResource sys.Dummy"), stage, "sys.Dummy");
doGetResourceAsStream(Arrays.<String>asList(
"findResource other.Dummy"), stage, "other.Dummy");
doGetResourceAsStream(Arrays.<String>asList(), stage, "app.Dummy");
}
private static void doGetResourceAsStream(List<String> expectedCallsToParent,
SDCClassLoader stage, String name) throws Exception {
List<String> actualCallsToParent = ((CallStoringURLClassLoader) stage.getParent()).calls;
Assert.assertNull(stage.getResourceAsStream(name));
Assert.assertEquals(expectedCallsToParent, actualCallsToParent);
actualCallsToParent.clear();
}
private static void doGetResources(List<String> expectedCallsToParent,
SDCClassLoader stage, String name) throws Exception {
List<String> actualCallsToParent = ((CallStoringURLClassLoader) stage.getParent()).calls;
Enumeration<URL> enumeration = stage.getResources(name);
Assert.assertFalse(enumeration.hasMoreElements());
Assert.assertEquals(expectedCallsToParent, actualCallsToParent);
actualCallsToParent.clear();
}
private static void doGetResource(List<String> expectedCallsToParent,
SDCClassLoader stage, String name) {
List<String> actualCallsToParent = ((CallStoringURLClassLoader) stage.getParent()).calls;
Assert.assertNull(stage.getResource(name));
Assert.assertEquals(expectedCallsToParent, actualCallsToParent);
actualCallsToParent.clear();
}
private static void doLoadClass(List<String> expectedCallsToParent,
SDCClassLoader stage, String name) {
List<String> actualCallsToParent = ((CallStoringURLClassLoader) stage.getParent()).calls;
try {
stage.loadClass(name);
Assert.fail("expected ClassNotFoundException");
} catch (ClassNotFoundException ex) {
// expected
}
Assert.assertEquals(expectedCallsToParent, actualCallsToParent);
actualCallsToParent.clear();
}
// we are expecting a ClassFormatError because the class file is invalid
@Test(expected = ClassFormatError.class)
public void testDuplicateStageClassLoader() throws Exception {
File dir = TestBlackListURLClassLoader.getBaseDir();
SDCClassLoader cl = SDCClassLoader.getStageClassLoader("foo",
"bar",
Arrays.asList(dir.toURI().toURL(), new File(dir, "bar.jar").toURI().toURL()),
getClass().getClassLoader()
);
Assert.assertFalse(cl.isPrivate());
cl = cl.duplicateStageClassLoader();
Assert.assertTrue(cl.isPrivate());
cl.loadClass("x.y.Dummy");
}
@Test
@SuppressWarnings("unchecked")
public void testBringStageLibAndProtoLibsToFront() throws Exception {
List<URL> urls = ImmutableList.of(new URL("file:///tmp/bar-1.jar"));
Assert.assertEquals(urls, SDCClassLoader.bringStageAndProtoLibsToFront("bar", urls));
urls = ImmutableList.of(new URL("file:///tmp/foo-protolib-1.jar"), new URL("file:///tmp/bar-1.jar"));
List<URL> expected = ImmutableList.of(new URL("file:///tmp/bar-1.jar"), new URL("file:///tmp/foo-protolib-1.jar"));
Assert.assertEquals(expected, SDCClassLoader.bringStageAndProtoLibsToFront("bar", urls));
urls = ImmutableList.of(new URL("file:///tmp/bar-1.jar"), new URL("file:///tmp/foo-protolib-1.jar"));
Assert.assertEquals(urls, SDCClassLoader.bringStageAndProtoLibsToFront("bar", urls));
urls = ImmutableList.of(
new URL("file:///tmp/foo-1.jar"),
new URL("file:///tmp/bar-1.jar"),
new URL("file:///tmp/foo-protolib-1.jar")
);
expected = ImmutableList.of(
new URL("file:///tmp/bar-1.jar"),
new URL("file:///tmp/foo-protolib-1.jar"),
new URL("file:///tmp/foo-1.jar")
);
Assert.assertEquals(expected, SDCClassLoader.bringStageAndProtoLibsToFront("bar", urls));
}
@Test(expected = ExceptionInInitializerError.class)
public void testBringMultipleStageLibsToFront() throws Exception {
List<URL> urls = ImmutableList.of(new URL("file:///tmp/bar-1.jar"), new URL("file:///tmp/bar-X-1.jar"));
Assert.assertEquals(urls, SDCClassLoader.bringStageAndProtoLibsToFront("bar", urls));
}
}