1 /* 2 * Copyright 2015 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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.assertSame; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 26 import com.google.common.collect.ImmutableList; 27 import io.grpc.InternalServiceProviders.PriorityAccessor; 28 import java.util.Collections; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.ServiceConfigurationError; 32 import org.junit.Test; 33 import org.junit.runner.RunWith; 34 import org.junit.runners.JUnit4; 35 36 /** Unit tests for {@link ServiceProviders}. */ 37 @RunWith(JUnit4.class) 38 public class ServiceProvidersTest { 39 private static final List<Class<?>> NO_HARDCODED = Collections.emptyList(); 40 private static final PriorityAccessor<ServiceProvidersTestAbstractProvider> ACCESSOR = 41 new PriorityAccessor<ServiceProvidersTestAbstractProvider>() { 42 @Override 43 public boolean isAvailable(ServiceProvidersTestAbstractProvider provider) { 44 return provider.isAvailable(); 45 } 46 47 @Override 48 public int getPriority(ServiceProvidersTestAbstractProvider provider) { 49 return provider.priority(); 50 } 51 }; 52 private final String serviceFile = 53 "META-INF/services/io.grpc.ServiceProvidersTestAbstractProvider"; 54 55 @Test contextClassLoaderProvider()56 public void contextClassLoaderProvider() { 57 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 58 try { 59 ClassLoader cl = new ReplacingClassLoader( 60 getClass().getClassLoader(), 61 serviceFile, 62 "io/grpc/ServiceProvidersTestAbstractProvider-multipleProvider.txt"); 63 64 // test that the context classloader is used as fallback 65 ClassLoader rcll = new ReplacingClassLoader( 66 getClass().getClassLoader(), 67 serviceFile, 68 "io/grpc/ServiceProvidersTestAbstractProvider-empty.txt"); 69 Thread.currentThread().setContextClassLoader(rcll); 70 assertEquals( 71 Available7Provider.class, 72 ServiceProviders.load( 73 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass()); 74 } finally { 75 Thread.currentThread().setContextClassLoader(ccl); 76 } 77 } 78 79 @Test noProvider()80 public void noProvider() { 81 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 82 try { 83 ClassLoader cl = new ReplacingClassLoader( 84 getClass().getClassLoader(), 85 serviceFile, 86 "io/grpc/ServiceProvidersTestAbstractProvider-doesNotExist.txt"); 87 Thread.currentThread().setContextClassLoader(cl); 88 assertNull(ServiceProviders.load( 89 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR)); 90 } finally { 91 Thread.currentThread().setContextClassLoader(ccl); 92 } 93 } 94 95 @Test multipleProvider()96 public void multipleProvider() throws Exception { 97 ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, 98 "io/grpc/ServiceProvidersTestAbstractProvider-multipleProvider.txt"); 99 assertSame( 100 Available7Provider.class, 101 ServiceProviders.load( 102 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass()); 103 104 List<ServiceProvidersTestAbstractProvider> providers = ServiceProviders.loadAll( 105 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); 106 assertEquals(3, providers.size()); 107 assertEquals(Available7Provider.class, providers.get(0).getClass()); 108 assertEquals(Available5Provider.class, providers.get(1).getClass()); 109 assertEquals(Available0Provider.class, providers.get(2).getClass()); 110 } 111 112 @Test unavailableProvider()113 public void unavailableProvider() { 114 // tries to load Available7 and UnavailableProvider, which has priority 10 115 ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, 116 "io/grpc/ServiceProvidersTestAbstractProvider-unavailableProvider.txt"); 117 assertEquals( 118 Available7Provider.class, 119 ServiceProviders.load( 120 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass()); 121 } 122 123 @Test unknownClassProvider()124 public void unknownClassProvider() { 125 ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, 126 "io/grpc/ServiceProvidersTestAbstractProvider-unknownClassProvider.txt"); 127 try { 128 ServiceProviders.load( 129 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); 130 fail("Exception expected"); 131 } catch (ServiceConfigurationError e) { 132 // noop 133 } 134 } 135 136 @Test exceptionSurfacedToCaller_failAtInit()137 public void exceptionSurfacedToCaller_failAtInit() { 138 ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, 139 "io/grpc/ServiceProvidersTestAbstractProvider-failAtInitProvider.txt"); 140 try { 141 // Even though there is a working provider, if any providers fail then we should fail 142 // completely to avoid returning something unexpected. 143 ServiceProviders.load( 144 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); 145 fail("Expected exception"); 146 } catch (ServiceConfigurationError expected) { 147 // noop 148 } 149 } 150 151 @Test exceptionSurfacedToCaller_failAtPriority()152 public void exceptionSurfacedToCaller_failAtPriority() { 153 ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, 154 "io/grpc/ServiceProvidersTestAbstractProvider-failAtPriorityProvider.txt"); 155 try { 156 // The exception should be surfaced to the caller 157 ServiceProviders.load( 158 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); 159 fail("Expected exception"); 160 } catch (FailAtPriorityProvider.PriorityException expected) { 161 // noop 162 } 163 } 164 165 @Test exceptionSurfacedToCaller_failAtAvailable()166 public void exceptionSurfacedToCaller_failAtAvailable() { 167 ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, 168 "io/grpc/ServiceProvidersTestAbstractProvider-failAtAvailableProvider.txt"); 169 try { 170 // The exception should be surfaced to the caller 171 ServiceProviders.load( 172 ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); 173 fail("Expected exception"); 174 } catch (FailAtAvailableProvider.AvailableException expected) { 175 // noop 176 } 177 } 178 179 @Test getCandidatesViaHardCoded_multipleProvider()180 public void getCandidatesViaHardCoded_multipleProvider() throws Exception { 181 Iterator<ServiceProvidersTestAbstractProvider> candidates = 182 ServiceProviders.getCandidatesViaHardCoded( 183 ServiceProvidersTestAbstractProvider.class, 184 ImmutableList.<Class<?>>of( 185 Available7Provider.class, 186 Available0Provider.class)) 187 .iterator(); 188 assertEquals(Available7Provider.class, candidates.next().getClass()); 189 assertEquals(Available0Provider.class, candidates.next().getClass()); 190 assertFalse(candidates.hasNext()); 191 } 192 193 @Test getCandidatesViaHardCoded_failAtInit()194 public void getCandidatesViaHardCoded_failAtInit() throws Exception { 195 try { 196 ServiceProviders.getCandidatesViaHardCoded( 197 ServiceProvidersTestAbstractProvider.class, 198 Collections.<Class<?>>singletonList(FailAtInitProvider.class)); 199 fail("Expected exception"); 200 } catch (ServiceConfigurationError expected) { 201 // noop 202 } 203 } 204 205 @Test getCandidatesViaHardCoded_failAtInit_moreCandidates()206 public void getCandidatesViaHardCoded_failAtInit_moreCandidates() throws Exception { 207 try { 208 ServiceProviders.getCandidatesViaHardCoded( 209 ServiceProvidersTestAbstractProvider.class, 210 ImmutableList.<Class<?>>of(FailAtInitProvider.class, Available0Provider.class)); 211 fail("Expected exception"); 212 } catch (ServiceConfigurationError expected) { 213 // noop 214 } 215 } 216 217 @Test getCandidatesViaHardCoded_throwsErrorOnMisconfiguration()218 public void getCandidatesViaHardCoded_throwsErrorOnMisconfiguration() throws Exception { 219 class PrivateClass extends BaseProvider { 220 private PrivateClass() { 221 super(true, 5); 222 } 223 } 224 225 try { 226 ServiceProviders.getCandidatesViaHardCoded( 227 ServiceProvidersTestAbstractProvider.class, 228 Collections.<Class<?>>singletonList(PrivateClass.class)); 229 fail("Expected exception"); 230 } catch (ServiceConfigurationError expected) { 231 assertTrue("Expected NoSuchMethodException cause: " + expected.getCause(), 232 expected.getCause() instanceof NoSuchMethodException); 233 } 234 } 235 236 @Test getCandidatesViaHardCoded_skipsWrongClassType()237 public void getCandidatesViaHardCoded_skipsWrongClassType() throws Exception { 238 class RandomClass {} 239 240 Iterable<ServiceProvidersTestAbstractProvider> candidates = 241 ServiceProviders.getCandidatesViaHardCoded( 242 ServiceProvidersTestAbstractProvider.class, 243 Collections.<Class<?>>singletonList(RandomClass.class)); 244 assertFalse(candidates.iterator().hasNext()); 245 } 246 247 private static class BaseProvider extends ServiceProvidersTestAbstractProvider { 248 private final boolean isAvailable; 249 private final int priority; 250 BaseProvider(boolean isAvailable, int priority)251 public BaseProvider(boolean isAvailable, int priority) { 252 this.isAvailable = isAvailable; 253 this.priority = priority; 254 } 255 256 @Override isAvailable()257 public boolean isAvailable() { 258 return isAvailable; 259 } 260 261 @Override priority()262 public int priority() { 263 return priority; 264 } 265 } 266 267 public static final class Available0Provider extends BaseProvider { Available0Provider()268 public Available0Provider() { 269 super(true, 0); 270 } 271 } 272 273 public static final class Available5Provider extends BaseProvider { Available5Provider()274 public Available5Provider() { 275 super(true, 5); 276 } 277 } 278 279 public static final class Available7Provider extends BaseProvider { Available7Provider()280 public Available7Provider() { 281 super(true, 7); 282 } 283 } 284 285 public static final class UnavailableProvider extends BaseProvider { UnavailableProvider()286 public UnavailableProvider() { 287 super(false, 10); 288 } 289 } 290 291 public static final class FailAtInitProvider extends ServiceProvidersTestAbstractProvider { FailAtInitProvider()292 public FailAtInitProvider() { 293 throw new RuntimeException("intentionally broken"); 294 } 295 296 @Override isAvailable()297 public boolean isAvailable() { 298 return true; 299 } 300 301 @Override priority()302 public int priority() { 303 return 0; 304 } 305 } 306 307 public static final class FailAtPriorityProvider extends ServiceProvidersTestAbstractProvider { 308 @Override isAvailable()309 public boolean isAvailable() { 310 return true; 311 } 312 313 @Override priority()314 public int priority() { 315 throw new PriorityException(); 316 } 317 318 public static final class PriorityException extends RuntimeException {} 319 } 320 321 public static final class FailAtAvailableProvider extends ServiceProvidersTestAbstractProvider { 322 @Override isAvailable()323 public boolean isAvailable() { 324 throw new AvailableException(); 325 } 326 327 @Override priority()328 public int priority() { 329 return 0; 330 } 331 332 public static final class AvailableException extends RuntimeException {} 333 } 334 } 335