/*
* 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.test;
import com.google.common.collect.Sets;
import org.reflections.Configuration;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.adapters.JavaReflectionAdapter;
import org.reflections.scanners.AbstractScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
/**
* Initialize once org.reflections library to avoid duplicate work scanning the classpath from individual tests.
*/
public class ReflectionsHelper {
public static final Reflections REFLECTIONS;
static {
// obtain all classpath URLs with com.hazelcast package classes
Collection<URL> comHazelcastPackageURLs = ClasspathHelper.forPackage("com.hazelcast");
// exclude hazelcast test artifacts from package URLs
for (Iterator<URL> iterator = comHazelcastPackageURLs.iterator(); iterator.hasNext(); ) {
URL url = iterator.next();
// detect hazelcast-VERSION-tests.jar & $SOMEPATH/hazelcast/target/test-classes/ and exclude it from classpath
// also exclude hazelcast-license-extractor artifact
if (url.toString().contains("-tests.jar") || url.toString().contains("target/test-classes") ||
url.toString().contains("hazelcast-license-extractor")) {
iterator.remove();
}
}
HierarchyTraversingSubtypesScanner subtypesScanner = new HierarchyTraversingSubtypesScanner();
subtypesScanner.setResultFilter(new FilterBuilder().exclude("java\\.lang\\.(Object|Enum)")
.exclude("com\\.hazelcast\\.com\\.eclipsesource.*"));
REFLECTIONS = new ReflectionsTransitive(new ConfigurationBuilder().addUrls(comHazelcastPackageURLs)
.addScanners(subtypesScanner, new TypeAnnotationsScanner())
.setMetadataAdapter(new JavaReflectionAdapter()));
}
// Overrides default implementation of getSubTypesOf to obtain also transitive subtypes of given class
public static class ReflectionsTransitive extends Reflections {
public ReflectionsTransitive(Configuration configuration) {
super(configuration);
}
@Override
public <T> Set<Class<? extends T>> getSubTypesOf(Class<T> type) {
return Sets.newHashSet(ReflectionUtils.<T>forNames(
store.getAll(
HierarchyTraversingSubtypesScanner.class.getSimpleName(),
Arrays.asList(type.getName())),
configuration.getClassLoaders()));
}
}
public static class HierarchyTraversingSubtypesScanner extends AbstractScanner{
/**
* creates new HierarchyTraversingSubtypesScanner. will exclude direct Object subtypes
*/
public HierarchyTraversingSubtypesScanner() {
this(true); //exclude direct Object subtypes by default
}
/**
* creates new HierarchyTraversingSubtypesScanner.
* @param excludeObjectClass if false, include direct {@link Object} subtypes in results.
*/
public HierarchyTraversingSubtypesScanner(boolean excludeObjectClass) {
if (excludeObjectClass) {
filterResultsBy(new FilterBuilder().exclude(Object.class.getName())); //exclude direct Object subtypes
}
}
// depending on Reflections configuration, cls is either a regular Class or a javassist ClassFile
@SuppressWarnings({"unchecked"})
public void scan(final Object cls) {
String className = getMetadataAdapter().getClassName(cls);
for (String anInterface : (List<String>) getMetadataAdapter().getInterfacesNames(cls)) {
if (acceptResult(anInterface)) {
getStore().put(anInterface, className);
}
}
// apart from this class' direct supertype and directly declared interfaces, also scan the class
// hierarchy up until Object class
Class superKlass = ((Class)cls).getSuperclass();
while (superKlass != null) {
scanClassAndInterfaces(superKlass, className);
superKlass = superKlass.getSuperclass();
}
}
private void scanClassAndInterfaces(Class klass, String className) {
if (acceptResult(klass.getName())) {
getStore().put(klass.getName(), className);
for (String anInterface : (List<String>) getMetadataAdapter().getInterfacesNames(klass)) {
if (acceptResult(anInterface)) {
getStore().put(anInterface, className);
}
}
}
}
}
}