| /* |
| * Copyright 2015 The gRPC Authors |
| * |
| * 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 io.grpc; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import com.google.common.collect.ImmutableList; |
| import io.grpc.InternalServiceProviders.PriorityAccessor; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ServiceConfigurationError; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Unit tests for {@link ServiceProviders}. */ |
| @RunWith(JUnit4.class) |
| public class ServiceProvidersTest { |
| private static final List<Class<?>> NO_HARDCODED = Collections.emptyList(); |
| private static final PriorityAccessor<ServiceProvidersTestAbstractProvider> ACCESSOR = |
| new PriorityAccessor<ServiceProvidersTestAbstractProvider>() { |
| @Override |
| public boolean isAvailable(ServiceProvidersTestAbstractProvider provider) { |
| return provider.isAvailable(); |
| } |
| |
| @Override |
| public int getPriority(ServiceProvidersTestAbstractProvider provider) { |
| return provider.priority(); |
| } |
| }; |
| private final String serviceFile = |
| "META-INF/services/io.grpc.ServiceProvidersTestAbstractProvider"; |
| |
| @Test |
| public void contextClassLoaderProvider() { |
| ClassLoader ccl = Thread.currentThread().getContextClassLoader(); |
| try { |
| ClassLoader cl = new ReplacingClassLoader( |
| getClass().getClassLoader(), |
| serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-multipleProvider.txt"); |
| |
| // test that the context classloader is used as fallback |
| ClassLoader rcll = new ReplacingClassLoader( |
| getClass().getClassLoader(), |
| serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-empty.txt"); |
| Thread.currentThread().setContextClassLoader(rcll); |
| assertEquals( |
| Available7Provider.class, |
| ServiceProviders.load( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass()); |
| } finally { |
| Thread.currentThread().setContextClassLoader(ccl); |
| } |
| } |
| |
| @Test |
| public void noProvider() { |
| ClassLoader ccl = Thread.currentThread().getContextClassLoader(); |
| try { |
| ClassLoader cl = new ReplacingClassLoader( |
| getClass().getClassLoader(), |
| serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-doesNotExist.txt"); |
| Thread.currentThread().setContextClassLoader(cl); |
| assertNull(ServiceProviders.load( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR)); |
| } finally { |
| Thread.currentThread().setContextClassLoader(ccl); |
| } |
| } |
| |
| @Test |
| public void multipleProvider() throws Exception { |
| ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-multipleProvider.txt"); |
| assertSame( |
| Available7Provider.class, |
| ServiceProviders.load( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass()); |
| |
| List<ServiceProvidersTestAbstractProvider> providers = ServiceProviders.loadAll( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); |
| assertEquals(3, providers.size()); |
| assertEquals(Available7Provider.class, providers.get(0).getClass()); |
| assertEquals(Available5Provider.class, providers.get(1).getClass()); |
| assertEquals(Available0Provider.class, providers.get(2).getClass()); |
| } |
| |
| @Test |
| public void unavailableProvider() { |
| // tries to load Available7 and UnavailableProvider, which has priority 10 |
| ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-unavailableProvider.txt"); |
| assertEquals( |
| Available7Provider.class, |
| ServiceProviders.load( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass()); |
| } |
| |
| @Test |
| public void unknownClassProvider() { |
| ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-unknownClassProvider.txt"); |
| try { |
| ServiceProviders.load( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); |
| fail("Exception expected"); |
| } catch (ServiceConfigurationError e) { |
| // noop |
| } |
| } |
| |
| @Test |
| public void exceptionSurfacedToCaller_failAtInit() { |
| ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-failAtInitProvider.txt"); |
| try { |
| // Even though there is a working provider, if any providers fail then we should fail |
| // completely to avoid returning something unexpected. |
| ServiceProviders.load( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); |
| fail("Expected exception"); |
| } catch (ServiceConfigurationError expected) { |
| // noop |
| } |
| } |
| |
| @Test |
| public void exceptionSurfacedToCaller_failAtPriority() { |
| ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-failAtPriorityProvider.txt"); |
| try { |
| // The exception should be surfaced to the caller |
| ServiceProviders.load( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); |
| fail("Expected exception"); |
| } catch (FailAtPriorityProvider.PriorityException expected) { |
| // noop |
| } |
| } |
| |
| @Test |
| public void exceptionSurfacedToCaller_failAtAvailable() { |
| ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile, |
| "io/grpc/ServiceProvidersTestAbstractProvider-failAtAvailableProvider.txt"); |
| try { |
| // The exception should be surfaced to the caller |
| ServiceProviders.load( |
| ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR); |
| fail("Expected exception"); |
| } catch (FailAtAvailableProvider.AvailableException expected) { |
| // noop |
| } |
| } |
| |
| @Test |
| public void getCandidatesViaHardCoded_multipleProvider() throws Exception { |
| Iterator<ServiceProvidersTestAbstractProvider> candidates = |
| ServiceProviders.getCandidatesViaHardCoded( |
| ServiceProvidersTestAbstractProvider.class, |
| ImmutableList.<Class<?>>of( |
| Available7Provider.class, |
| Available0Provider.class)) |
| .iterator(); |
| assertEquals(Available7Provider.class, candidates.next().getClass()); |
| assertEquals(Available0Provider.class, candidates.next().getClass()); |
| assertFalse(candidates.hasNext()); |
| } |
| |
| @Test |
| public void getCandidatesViaHardCoded_failAtInit() throws Exception { |
| try { |
| ServiceProviders.getCandidatesViaHardCoded( |
| ServiceProvidersTestAbstractProvider.class, |
| Collections.<Class<?>>singletonList(FailAtInitProvider.class)); |
| fail("Expected exception"); |
| } catch (ServiceConfigurationError expected) { |
| // noop |
| } |
| } |
| |
| @Test |
| public void getCandidatesViaHardCoded_failAtInit_moreCandidates() throws Exception { |
| try { |
| ServiceProviders.getCandidatesViaHardCoded( |
| ServiceProvidersTestAbstractProvider.class, |
| ImmutableList.<Class<?>>of(FailAtInitProvider.class, Available0Provider.class)); |
| fail("Expected exception"); |
| } catch (ServiceConfigurationError expected) { |
| // noop |
| } |
| } |
| |
| @Test |
| public void getCandidatesViaHardCoded_throwsErrorOnMisconfiguration() throws Exception { |
| class PrivateClass extends BaseProvider { |
| private PrivateClass() { |
| super(true, 5); |
| } |
| } |
| |
| try { |
| ServiceProviders.getCandidatesViaHardCoded( |
| ServiceProvidersTestAbstractProvider.class, |
| Collections.<Class<?>>singletonList(PrivateClass.class)); |
| fail("Expected exception"); |
| } catch (ServiceConfigurationError expected) { |
| assertTrue("Expected NoSuchMethodException cause: " + expected.getCause(), |
| expected.getCause() instanceof NoSuchMethodException); |
| } |
| } |
| |
| @Test |
| public void getCandidatesViaHardCoded_skipsWrongClassType() throws Exception { |
| class RandomClass {} |
| |
| Iterable<ServiceProvidersTestAbstractProvider> candidates = |
| ServiceProviders.getCandidatesViaHardCoded( |
| ServiceProvidersTestAbstractProvider.class, |
| Collections.<Class<?>>singletonList(RandomClass.class)); |
| assertFalse(candidates.iterator().hasNext()); |
| } |
| |
| private static class BaseProvider extends ServiceProvidersTestAbstractProvider { |
| private final boolean isAvailable; |
| private final int priority; |
| |
| public BaseProvider(boolean isAvailable, int priority) { |
| this.isAvailable = isAvailable; |
| this.priority = priority; |
| } |
| |
| @Override |
| public boolean isAvailable() { |
| return isAvailable; |
| } |
| |
| @Override |
| public int priority() { |
| return priority; |
| } |
| } |
| |
| public static final class Available0Provider extends BaseProvider { |
| public Available0Provider() { |
| super(true, 0); |
| } |
| } |
| |
| public static final class Available5Provider extends BaseProvider { |
| public Available5Provider() { |
| super(true, 5); |
| } |
| } |
| |
| public static final class Available7Provider extends BaseProvider { |
| public Available7Provider() { |
| super(true, 7); |
| } |
| } |
| |
| public static final class UnavailableProvider extends BaseProvider { |
| public UnavailableProvider() { |
| super(false, 10); |
| } |
| } |
| |
| public static final class FailAtInitProvider extends ServiceProvidersTestAbstractProvider { |
| public FailAtInitProvider() { |
| throw new RuntimeException("intentionally broken"); |
| } |
| |
| @Override |
| public boolean isAvailable() { |
| return true; |
| } |
| |
| @Override |
| public int priority() { |
| return 0; |
| } |
| } |
| |
| public static final class FailAtPriorityProvider extends ServiceProvidersTestAbstractProvider { |
| @Override |
| public boolean isAvailable() { |
| return true; |
| } |
| |
| @Override |
| public int priority() { |
| throw new PriorityException(); |
| } |
| |
| public static final class PriorityException extends RuntimeException {} |
| } |
| |
| public static final class FailAtAvailableProvider extends ServiceProvidersTestAbstractProvider { |
| @Override |
| public boolean isAvailable() { |
| throw new AvailableException(); |
| } |
| |
| @Override |
| public int priority() { |
| return 0; |
| } |
| |
| public static final class AvailableException extends RuntimeException {} |
| } |
| } |