1 /* 2 * Copyright 2018 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc; 18 19 import static com.google.common.truth.Truth.assertWithMessage; 20 21 import com.google.common.collect.Iterators; 22 import java.lang.reflect.Constructor; 23 import java.lang.reflect.Method; 24 import java.util.HashSet; 25 import java.util.Iterator; 26 import java.util.Set; 27 import java.util.regex.Pattern; 28 29 final class ServiceProvidersTestUtil { 30 /** 31 * Creates an iterator from the callable class via reflection, and checks that all expected 32 * classes were loaded. 33 * 34 * <p>{@code callableClassName} is a {@code Callable<Iterable<Class<?>>} rather than the iterable 35 * class name itself so that the iterable class can be non-public. 36 * 37 * <p>We accept class names as input so that we can test against classes not in the 38 * testing class path. 39 */ testHardcodedClasses( String callableClassName, ClassLoader cl, Set<String> hardcodedClassNames)40 static void testHardcodedClasses( 41 String callableClassName, 42 ClassLoader cl, 43 Set<String> hardcodedClassNames) throws Exception { 44 final Set<String> notLoaded = new HashSet<>(hardcodedClassNames); 45 cl = new ClassLoader(cl) { 46 @Override 47 public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { 48 if (notLoaded.remove(name)) { 49 throw new ClassNotFoundException(); 50 } else { 51 return super.loadClass(name, resolve); 52 } 53 } 54 }; 55 cl = new StaticTestingClassLoader(cl, Pattern.compile("io\\.grpc\\.[^.]*")); 56 // Some classes fall back to the context class loader. 57 // Ensure that the context class loader is not an accidental backdoor. 58 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 59 try { 60 Thread.currentThread().setContextClassLoader(cl); 61 Object[] results = Iterators.toArray( 62 invokeIteratorCallable(callableClassName, cl), Object.class); 63 assertWithMessage("The Iterable loaded a class that was not in hardcodedClassNames") 64 .that(results).isEmpty(); 65 assertWithMessage( 66 "The Iterable did not attempt to load some classes from hardcodedClassNames") 67 .that(notLoaded).isEmpty(); 68 } finally { 69 Thread.currentThread().setContextClassLoader(ccl); 70 } 71 } 72 invokeIteratorCallable( String callableClassName, ClassLoader cl)73 private static Iterator<?> invokeIteratorCallable( 74 String callableClassName, ClassLoader cl) throws Exception { 75 Class<?> klass = Class.forName(callableClassName, true, cl); 76 Constructor<?> ctor = klass.getDeclaredConstructor(); 77 Object instance = ctor.newInstance(); 78 Method callMethod = klass.getMethod("call"); 79 return (Iterator<?>) callMethod.invoke(instance); 80 } 81 } 82