1 /* 2 * Copyright 2020 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 com.google.common.annotations.VisibleForTesting; 20 import com.google.common.base.Preconditions; 21 import java.util.ArrayList; 22 import java.util.Collections; 23 import java.util.Comparator; 24 import java.util.LinkedHashSet; 25 import java.util.List; 26 import java.util.logging.Logger; 27 import javax.annotation.concurrent.GuardedBy; 28 import javax.annotation.concurrent.ThreadSafe; 29 30 /** 31 * Registry of {@link ServerProvider}s. The {@link #getDefaultRegistry default instance} loads 32 * providers at runtime through the Java service provider mechanism. 33 */ 34 @Internal 35 @ThreadSafe 36 public final class ServerRegistry { 37 private static final Logger logger = Logger.getLogger(ServerRegistry.class.getName()); 38 private static ServerRegistry instance; 39 40 @GuardedBy("this") 41 private final LinkedHashSet<ServerProvider> allProviders = new LinkedHashSet<>(); 42 /** Immutable, sorted version of {@code allProviders}. Is replaced instead of mutating. */ 43 @GuardedBy("this") 44 private List<ServerProvider> effectiveProviders = Collections.emptyList(); 45 46 /** 47 * Register a provider. 48 * 49 * <p>If the provider's {@link ServerProvider#isAvailable isAvailable()} returns 50 * {@code false}, this method will throw {@link IllegalArgumentException}. 51 * 52 * <p>Providers will be used in priority order. In case of ties, providers are used in 53 * registration order. 54 */ register(ServerProvider provider)55 public synchronized void register(ServerProvider provider) { 56 addProvider(provider); 57 refreshProviders(); 58 } 59 addProvider(ServerProvider provider)60 private synchronized void addProvider(ServerProvider provider) { 61 Preconditions.checkArgument(provider.isAvailable(), "isAvailable() returned false"); 62 allProviders.add(provider); 63 } 64 65 /** 66 * Deregisters a provider. No-op if the provider is not in the registry. 67 * 68 * @param provider the provider that was added to the register via {@link #register}. 69 */ deregister(ServerProvider provider)70 public synchronized void deregister(ServerProvider provider) { 71 allProviders.remove(provider); 72 refreshProviders(); 73 } 74 refreshProviders()75 private synchronized void refreshProviders() { 76 List<ServerProvider> providers = new ArrayList<>(allProviders); 77 // Sort descending based on priority. 78 // sort() must be stable, as we prefer first-registered providers 79 Collections.sort(providers, Collections.reverseOrder(new Comparator<ServerProvider>() { 80 @Override 81 public int compare(ServerProvider o1, ServerProvider o2) { 82 return o1.priority() - o2.priority(); 83 } 84 })); 85 effectiveProviders = Collections.unmodifiableList(providers); 86 } 87 88 /** 89 * Returns the default registry that loads providers via the Java service loader mechanism. 90 */ getDefaultRegistry()91 public static synchronized ServerRegistry getDefaultRegistry() { 92 if (instance == null) { 93 List<ServerProvider> providerList = ServiceProviders.loadAll( 94 ServerProvider.class, 95 Collections.<Class<?>>emptyList(), 96 ServerProvider.class.getClassLoader(), 97 new ServerPriorityAccessor()); 98 instance = new ServerRegistry(); 99 for (ServerProvider provider : providerList) { 100 logger.fine("Service loader found " + provider); 101 instance.addProvider(provider); 102 } 103 instance.refreshProviders(); 104 } 105 return instance; 106 } 107 108 /** 109 * Returns effective providers, in priority order. 110 */ 111 @VisibleForTesting providers()112 synchronized List<ServerProvider> providers() { 113 return effectiveProviders; 114 } 115 116 // For emulating ServerProvider.provider() provider()117 ServerProvider provider() { 118 List<ServerProvider> providers = providers(); 119 return providers.isEmpty() ? null : providers.get(0); 120 } 121 newServerBuilderForPort(int port, ServerCredentials creds)122 ServerBuilder<?> newServerBuilderForPort(int port, ServerCredentials creds) { 123 List<ServerProvider> providers = providers(); 124 if (providers.isEmpty()) { 125 throw new ProviderNotFoundException("No functional server found. " 126 + "Try adding a dependency on the grpc-netty or grpc-netty-shaded artifact"); 127 } 128 StringBuilder error = new StringBuilder(); 129 for (ServerProvider provider : providers()) { 130 ServerProvider.NewServerBuilderResult result 131 = provider.newServerBuilderForPort(port, creds); 132 if (result.getServerBuilder() != null) { 133 return result.getServerBuilder(); 134 } 135 error.append("; "); 136 error.append(provider.getClass().getName()); 137 error.append(": "); 138 error.append(result.getError()); 139 } 140 throw new ProviderNotFoundException(error.substring(2)); 141 } 142 143 private static final class ServerPriorityAccessor 144 implements ServiceProviders.PriorityAccessor<ServerProvider> { 145 @Override isAvailable(ServerProvider provider)146 public boolean isAvailable(ServerProvider provider) { 147 return provider.isAvailable(); 148 } 149 150 @Override getPriority(ServerProvider provider)151 public int getPriority(ServerProvider provider) { 152 return provider.priority(); 153 } 154 } 155 156 /** Thrown when no suitable {@link ServerProvider} objects can be found. */ 157 public static final class ProviderNotFoundException extends RuntimeException { 158 private static final long serialVersionUID = 1; 159 ProviderNotFoundException(String msg)160 public ProviderNotFoundException(String msg) { 161 super(msg); 162 } 163 } 164 } 165