/*
* Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved.
*
* 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 com.hazelcast.util;
import com.hazelcast.core.HazelcastException;
import com.hazelcast.internal.serialization.PortableHook;
import com.hazelcast.nio.serialization.ClassDefinition;
import com.hazelcast.nio.serialization.PortableFactory;
import com.hazelcast.nio.serialization.Serializer;
import com.hazelcast.nio.serialization.SerializerHook;
import com.hazelcast.spi.impl.SpiPortableHook;
import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.HazelcastTestSupport;
import com.hazelcast.test.annotation.ParallelTest;
import com.hazelcast.test.annotation.QuickTest;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static com.hazelcast.test.TestCollectionUtils.setOf;
import static java.util.Collections.singleton;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelTest.class})
public class ServiceLoaderTest extends HazelcastTestSupport {
@Test
public void testSkipHooksWithImplementingTheExpectedInterfaceButLoadedByDifferentClassloader() {
Class<?> otherInterface = newInterface(PortableHook.class.getName());
ClassLoader otherClassloader = otherInterface.getClassLoader();
Class<?> otherHook = newClassImplementingInterface("com.hazelcast.internal.serialization.DifferentHook",
otherInterface, otherClassloader);
ServiceLoader.ServiceDefinition definition = new ServiceLoader.ServiceDefinition(otherHook.getName(), otherClassloader);
Set<ServiceLoader.ServiceDefinition> definitions = singleton(definition);
ServiceLoader.ClassIterator<PortableHook> iterator = new ServiceLoader.ClassIterator<PortableHook>(definitions, PortableHook.class);
assertFalse(iterator.hasNext());
}
@Test(expected = ClassCastException.class)
public void testFailFastWhenHookDoesNotImplementExpectedInteface() {
Class<?> otherInterface = newInterface("com.hazelcast.internal.serialization.DifferentInterface");
ClassLoader otherClassloader = otherInterface.getClassLoader();
Class<?> otherHook = newClassImplementingInterface("com.hazelcast.internal.serialization.DifferentHook",
otherInterface, otherClassloader);
ServiceLoader.ServiceDefinition definition = new ServiceLoader.ServiceDefinition(otherHook.getName(), otherClassloader);
Set<ServiceLoader.ServiceDefinition> definitions = singleton(definition);
ServiceLoader.ClassIterator<PortableHook> iterator = new ServiceLoader.ClassIterator<PortableHook>(definitions, PortableHook.class);
iterator.hasNext();
}
@Test
public void testSkipUnknownClassesStartingFromHazelcastPackage() {
ServiceLoader.ServiceDefinition definition = new ServiceLoader.ServiceDefinition("com.hazelcast.DoesNotExist", getClass().getClassLoader());
Set<ServiceLoader.ServiceDefinition> definitions = singleton(definition);
ServiceLoader.ClassIterator<PortableHook> iterator = new ServiceLoader.ClassIterator<PortableHook>(definitions, PortableHook.class);
assertFalse(iterator.hasNext());
}
@Test(expected = HazelcastException.class)
public void testFailFastOnUnknownClassesFromNonHazelcastPackage() {
ServiceLoader.ServiceDefinition definition = new ServiceLoader.ServiceDefinition("non.a.hazelcast.DoesNotExist", getClass().getClassLoader());
Set<ServiceLoader.ServiceDefinition> definitions = singleton(definition);
ServiceLoader.ClassIterator<PortableHook> iterator = new ServiceLoader.ClassIterator<PortableHook>(definitions, PortableHook.class);
assertFalse(iterator.hasNext());
}
@Test
public void testSkipHookLoadedByDifferentClassloader() {
Class<?> otherInterface = newInterface(PortableHook.class.getName());
ClassLoader otherClassloader = otherInterface.getClassLoader();
Class<?> otherHook = newClassImplementingInterface("com.hazelcast.internal.serialization.DifferentHook",
otherInterface, otherClassloader);
//otherHook is loaded by other classloader -> it should be skipped
ServiceLoader.ServiceDefinition definition1 = new ServiceLoader.ServiceDefinition(otherHook.getName(), otherClassloader);
//this hook should be loaded
ServiceLoader.ServiceDefinition definition2 = new ServiceLoader.ServiceDefinition(SpiPortableHook.class.getName(), SpiPortableHook.class.getClassLoader());
Set<ServiceLoader.ServiceDefinition> definitions = setOf(definition1, definition2);
ServiceLoader.ClassIterator<PortableHook> iterator = new ServiceLoader.ClassIterator<PortableHook>(definitions, PortableHook.class);
assertTrue(iterator.hasNext());
Class<PortableHook> hook = iterator.next();
assertEquals(SpiPortableHook.class, hook);
assertFalse(iterator.hasNext());
}
@Test
public void testPrivatePortableHook() {
String hookName = DummyPrivatePortableHook.class.getName();
ClassLoader classLoader = DummyPrivatePortableHook.class.getClassLoader();
ServiceLoader.ServiceDefinition definition = new ServiceLoader.ServiceDefinition(hookName, classLoader);
ServiceLoader.ClassIterator<PortableHook> classIterator = new ServiceLoader.ClassIterator<PortableHook>(singleton(definition), PortableHook.class);
ServiceLoader.NewInstanceIterator<PortableHook> instanceIterator = new ServiceLoader.NewInstanceIterator<PortableHook>(classIterator);
assertTrue(instanceIterator.hasNext());
DummyPrivatePortableHook hook = (DummyPrivatePortableHook) instanceIterator.next();
assertNotNull(hook);
}
@Test
public void testPrivateSerializerHook() {
String hookName = DummyPrivateSerializerHook.class.getName();
ClassLoader classLoader = DummyPrivateSerializerHook.class.getClassLoader();
ServiceLoader.ServiceDefinition definition = new ServiceLoader.ServiceDefinition(hookName, classLoader);
ServiceLoader.ClassIterator<SerializerHook> classIterator = new ServiceLoader.ClassIterator<SerializerHook>(singleton(definition), SerializerHook.class);
ServiceLoader.NewInstanceIterator<SerializerHook> instanceIterator = new ServiceLoader.NewInstanceIterator<SerializerHook>(classIterator);
assertTrue(instanceIterator.hasNext());
DummyPrivateSerializerHook hook = (DummyPrivateSerializerHook) instanceIterator.next();
assertNotNull(hook);
}
private Class<?> newClassImplementingInterface(String classname, Class<?> iface, ClassLoader classLoader) {
DynamicType.Unloaded<?> otherHookTypeDefinition = new ByteBuddy()
.subclass(iface)
.name(classname)
.make();
return otherHookTypeDefinition.load(classLoader).getLoaded();
}
private Class<?> newInterface(String name) {
DynamicType.Unloaded<?> otherInterfaceTypeDefinition = new ByteBuddy()
.makeInterface()
.name(name)
.make();
return otherInterfaceTypeDefinition.load(null).getLoaded();
}
@Test
public void testConstructor() {
assertUtilityConstructor(ServiceLoader.class);
}
@Test
public void selectingSimpleSingleClassLoader() {
List<ClassLoader> classLoaders = ServiceLoader.selectClassLoaders(null);
assertEquals(1, classLoaders.size());
}
@Test
public void selectingSimpleGivenClassLoader() {
List<ClassLoader> classLoaders = ServiceLoader.selectClassLoaders(new URLClassLoader(new URL[0]));
assertEquals(2, classLoaders.size());
}
@Test
public void selectingSimpleDifferentThreadContextClassLoader() {
Thread currentThread = Thread.currentThread();
ClassLoader tccl = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(new URLClassLoader(new URL[0]));
List<ClassLoader> classLoaders = ServiceLoader.selectClassLoaders(null);
currentThread.setContextClassLoader(tccl);
assertEquals(2, classLoaders.size());
}
@Test
public void selectingTcclAndGivenClassLoader() {
Thread currentThread = Thread.currentThread();
ClassLoader tccl = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(new URLClassLoader(new URL[0]));
List<ClassLoader> classLoaders = ServiceLoader.selectClassLoaders(new URLClassLoader(new URL[0]));
currentThread.setContextClassLoader(tccl);
assertEquals(3, classLoaders.size());
}
@Test
public void selectingSameTcclAndGivenClassLoader() {
ClassLoader same = new URLClassLoader(new URL[0]);
Thread currentThread = Thread.currentThread();
ClassLoader tccl = currentThread.getContextClassLoader();
currentThread.setContextClassLoader(same);
List<ClassLoader> classLoaders = ServiceLoader.selectClassLoaders(same);
currentThread.setContextClassLoader(tccl);
assertEquals(2, classLoaders.size());
}
@Test
public void loadServicesSingleClassLoader() throws Exception {
Class<ServiceLoaderTestInterface> type = ServiceLoaderTestInterface.class;
String factoryId = "com.hazelcast.ServiceLoaderTestInterface";
Set<ServiceLoaderTestInterface> implementations = new HashSet<ServiceLoaderTestInterface>();
Iterator<ServiceLoaderTestInterface> iterator = ServiceLoader.iterator(type, factoryId, null);
while (iterator.hasNext()) {
implementations.add(iterator.next());
}
assertEquals(1, implementations.size());
}
@Test
public void loadServicesSimpleGivenClassLoader() throws Exception {
Class<ServiceLoaderTestInterface> type = ServiceLoaderTestInterface.class;
String factoryId = "com.hazelcast.ServiceLoaderTestInterface";
ClassLoader given = new URLClassLoader(new URL[0]);
Set<ServiceLoaderTestInterface> implementations = new HashSet<ServiceLoaderTestInterface>();
Iterator<ServiceLoaderTestInterface> iterator = ServiceLoader.iterator(type, factoryId, given);
while (iterator.hasNext()) {
implementations.add(iterator.next());
}
assertEquals(1, implementations.size());
}
@Test
public void loadServicesSimpleDifferentThreadContextClassLoader() throws Exception {
Class<ServiceLoaderTestInterface> type = ServiceLoaderTestInterface.class;
String factoryId = "com.hazelcast.ServiceLoaderTestInterface";
Thread current = Thread.currentThread();
ClassLoader tccl = current.getContextClassLoader();
current.setContextClassLoader(new URLClassLoader(new URL[0]));
Set<ServiceLoaderTestInterface> implementations = new HashSet<ServiceLoaderTestInterface>();
Iterator<ServiceLoaderTestInterface> iterator = ServiceLoader.iterator(type, factoryId, null);
while (iterator.hasNext()) {
implementations.add(iterator.next());
}
current.setContextClassLoader(tccl);
assertEquals(1, implementations.size());
}
@Test
public void loadServicesTcclAndGivenClassLoader() throws Exception {
Class<ServiceLoaderTestInterface> type = ServiceLoaderTestInterface.class;
String factoryId = "com.hazelcast.ServiceLoaderTestInterface";
ClassLoader given = new URLClassLoader(new URL[0]);
Thread current = Thread.currentThread();
ClassLoader tccl = current.getContextClassLoader();
current.setContextClassLoader(new URLClassLoader(new URL[0]));
Set<ServiceLoaderTestInterface> implementations = new HashSet<ServiceLoaderTestInterface>();
Iterator<ServiceLoaderTestInterface> iterator = ServiceLoader.iterator(type, factoryId, given);
while (iterator.hasNext()) {
implementations.add(iterator.next());
}
current.setContextClassLoader(tccl);
assertEquals(1, implementations.size());
}
@Test
public void loadServicesSameTcclAndGivenClassLoader() throws Exception {
Class<ServiceLoaderTestInterface> type = ServiceLoaderTestInterface.class;
String factoryId = "com.hazelcast.ServiceLoaderTestInterface";
ClassLoader same = new URLClassLoader(new URL[0]);
Thread current = Thread.currentThread();
ClassLoader tccl = current.getContextClassLoader();
current.setContextClassLoader(same);
Set<ServiceLoaderTestInterface> implementations = new HashSet<ServiceLoaderTestInterface>();
Iterator<ServiceLoaderTestInterface> iterator = ServiceLoader.iterator(type, factoryId, same);
while (iterator.hasNext()) {
implementations.add(iterator.next());
}
current.setContextClassLoader(tccl);
assertEquals(1, implementations.size());
}
@Test
public void loadServicesWithSpaceInURL() throws Exception {
Class<ServiceLoaderSpacesTestInterface> type = ServiceLoaderSpacesTestInterface.class;
String factoryId = "com.hazelcast.ServiceLoaderSpacesTestInterface";
URL url = new URL(ClassLoader.getSystemResource("test with spaces").toExternalForm().replace("%20", " ") + "/");
ClassLoader given = new URLClassLoader(new URL[]{url});
Set<ServiceLoaderSpacesTestInterface> implementations = new HashSet<ServiceLoaderSpacesTestInterface>();
Iterator<ServiceLoaderSpacesTestInterface> iterator = ServiceLoader.iterator(type, factoryId, given);
while (iterator.hasNext()) {
implementations.add(iterator.next());
}
assertEquals(1, implementations.size());
}
public interface ServiceLoaderTestInterface {
}
public static class ServiceLoaderTestInterfaceImpl implements ServiceLoaderTestInterface {
}
public interface ServiceLoaderSpacesTestInterface {
}
public static class ServiceLoaderSpacesTestInterfaceImpl implements ServiceLoaderSpacesTestInterface {
}
private static class DummyPrivatePortableHook implements PortableHook {
@Override
public int getFactoryId() {
return 0;
}
@Override
public PortableFactory createFactory() {
return null;
}
@Override
public Collection<ClassDefinition> getBuiltinDefinitions() {
return null;
}
}
private static class DummyPrivateSerializerHook implements SerializerHook {
@Override
public Class getSerializationType() {
return null;
}
@Override
public Serializer createSerializer() {
return null;
}
@Override
public boolean isOverwritable() {
return false;
}
}
}