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.net.SocketAddress; 22 import java.net.URI; 23 import java.net.URISyntaxException; 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.Collection; 27 import java.util.Collections; 28 import java.util.Comparator; 29 import java.util.LinkedHashSet; 30 import java.util.List; 31 import java.util.logging.Level; 32 import java.util.logging.Logger; 33 import javax.annotation.concurrent.GuardedBy; 34 import javax.annotation.concurrent.ThreadSafe; 35 36 /** 37 * Registry of {@link ManagedChannelProvider}s. The {@link #getDefaultRegistry default instance} 38 * loads providers at runtime through the Java service provider mechanism. 39 * 40 * @since 1.32.0 41 */ 42 @Internal 43 @ThreadSafe 44 public final class ManagedChannelRegistry { 45 private static final Logger logger = Logger.getLogger(ManagedChannelRegistry.class.getName()); 46 private static ManagedChannelRegistry instance; 47 48 @GuardedBy("this") 49 private final LinkedHashSet<ManagedChannelProvider> allProviders = new LinkedHashSet<>(); 50 /** Immutable, sorted version of {@code allProviders}. Is replaced instead of mutating. */ 51 @GuardedBy("this") 52 private List<ManagedChannelProvider> effectiveProviders = Collections.emptyList(); 53 54 /** 55 * Register a provider. 56 * 57 * <p>If the provider's {@link ManagedChannelProvider#isAvailable isAvailable()} returns 58 * {@code false}, this method will throw {@link IllegalArgumentException}. 59 * 60 * <p>Providers will be used in priority order. In case of ties, providers are used in 61 * registration order. 62 */ register(ManagedChannelProvider provider)63 public synchronized void register(ManagedChannelProvider provider) { 64 addProvider(provider); 65 refreshProviders(); 66 } 67 addProvider(ManagedChannelProvider provider)68 private synchronized void addProvider(ManagedChannelProvider provider) { 69 Preconditions.checkArgument(provider.isAvailable(), "isAvailable() returned false"); 70 allProviders.add(provider); 71 } 72 73 /** 74 * Deregisters a provider. No-op if the provider is not in the registry. 75 * 76 * @param provider the provider that was added to the register via {@link #register}. 77 */ deregister(ManagedChannelProvider provider)78 public synchronized void deregister(ManagedChannelProvider provider) { 79 allProviders.remove(provider); 80 refreshProviders(); 81 } 82 refreshProviders()83 private synchronized void refreshProviders() { 84 List<ManagedChannelProvider> providers = new ArrayList<>(allProviders); 85 // Sort descending based on priority. 86 // sort() must be stable, as we prefer first-registered providers 87 Collections.sort(providers, Collections.reverseOrder(new Comparator<ManagedChannelProvider>() { 88 @Override 89 public int compare(ManagedChannelProvider o1, ManagedChannelProvider o2) { 90 return o1.priority() - o2.priority(); 91 } 92 })); 93 effectiveProviders = Collections.unmodifiableList(providers); 94 } 95 96 /** 97 * Returns the default registry that loads providers via the Java service loader mechanism. 98 */ getDefaultRegistry()99 public static synchronized ManagedChannelRegistry getDefaultRegistry() { 100 if (instance == null) { 101 List<ManagedChannelProvider> providerList = ServiceProviders.loadAll( 102 ManagedChannelProvider.class, 103 getHardCodedClasses(), 104 ManagedChannelProvider.class.getClassLoader(), 105 new ManagedChannelPriorityAccessor()); 106 instance = new ManagedChannelRegistry(); 107 for (ManagedChannelProvider provider : providerList) { 108 logger.fine("Service loader found " + provider); 109 instance.addProvider(provider); 110 } 111 instance.refreshProviders(); 112 } 113 return instance; 114 } 115 116 /** 117 * Returns effective providers, in priority order. 118 */ 119 @VisibleForTesting providers()120 synchronized List<ManagedChannelProvider> providers() { 121 return effectiveProviders; 122 } 123 124 // For emulating ManagedChannelProvider.provider() provider()125 ManagedChannelProvider provider() { 126 List<ManagedChannelProvider> providers = providers(); 127 return providers.isEmpty() ? null : providers.get(0); 128 } 129 130 @VisibleForTesting getHardCodedClasses()131 static List<Class<?>> getHardCodedClasses() { 132 // Class.forName(String) is used to remove the need for ProGuard configuration. Note that 133 // ProGuard does not detect usages of Class.forName(String, boolean, ClassLoader): 134 // https://sourceforge.net/p/proguard/bugs/418/ 135 List<Class<?>> list = new ArrayList<>(); 136 try { 137 list.add(Class.forName("io.grpc.okhttp.OkHttpChannelProvider")); 138 } catch (ClassNotFoundException e) { 139 logger.log(Level.FINE, "Unable to find OkHttpChannelProvider", e); 140 } 141 try { 142 list.add(Class.forName("io.grpc.netty.NettyChannelProvider")); 143 } catch (ClassNotFoundException e) { 144 logger.log(Level.FINE, "Unable to find NettyChannelProvider", e); 145 } 146 try { 147 list.add(Class.forName("io.grpc.netty.UdsNettyChannelProvider")); 148 } catch (ClassNotFoundException e) { 149 logger.log(Level.FINE, "Unable to find UdsNettyChannelProvider", e); 150 } 151 return Collections.unmodifiableList(list); 152 } 153 newChannelBuilder(String target, ChannelCredentials creds)154 ManagedChannelBuilder<?> newChannelBuilder(String target, ChannelCredentials creds) { 155 return newChannelBuilder(NameResolverRegistry.getDefaultRegistry(), target, creds); 156 } 157 158 @VisibleForTesting newChannelBuilder(NameResolverRegistry nameResolverRegistry, String target, ChannelCredentials creds)159 ManagedChannelBuilder<?> newChannelBuilder(NameResolverRegistry nameResolverRegistry, 160 String target, ChannelCredentials creds) { 161 NameResolverProvider nameResolverProvider = null; 162 try { 163 URI uri = new URI(target); 164 nameResolverProvider = nameResolverRegistry.providers().get(uri.getScheme()); 165 } catch (URISyntaxException ignore) { 166 // bad URI found, just ignore and continue 167 } 168 if (nameResolverProvider == null) { 169 nameResolverProvider = nameResolverRegistry.providers().get( 170 nameResolverRegistry.asFactory().getDefaultScheme()); 171 } 172 Collection<Class<? extends SocketAddress>> nameResolverSocketAddressTypes 173 = (nameResolverProvider != null) 174 ? nameResolverProvider.getProducedSocketAddressTypes() : 175 Collections.emptySet(); 176 177 List<ManagedChannelProvider> providers = providers(); 178 if (providers.isEmpty()) { 179 throw new ProviderNotFoundException("No functional channel service provider found. " 180 + "Try adding a dependency on the grpc-okhttp, grpc-netty, or grpc-netty-shaded " 181 + "artifact"); 182 } 183 StringBuilder error = new StringBuilder(); 184 for (ManagedChannelProvider provider : providers()) { 185 Collection<Class<? extends SocketAddress>> channelProviderSocketAddressTypes 186 = provider.getSupportedSocketAddressTypes(); 187 if (!channelProviderSocketAddressTypes.containsAll(nameResolverSocketAddressTypes)) { 188 error.append("; "); 189 error.append(provider.getClass().getName()); 190 error.append(": does not support 1 or more of "); 191 error.append(Arrays.toString(nameResolverSocketAddressTypes.toArray())); 192 continue; 193 } 194 ManagedChannelProvider.NewChannelBuilderResult result 195 = provider.newChannelBuilder(target, creds); 196 if (result.getChannelBuilder() != null) { 197 return result.getChannelBuilder(); 198 } 199 error.append("; "); 200 error.append(provider.getClass().getName()); 201 error.append(": "); 202 error.append(result.getError()); 203 } 204 throw new ProviderNotFoundException(error.substring(2)); 205 } 206 207 private static final class ManagedChannelPriorityAccessor 208 implements ServiceProviders.PriorityAccessor<ManagedChannelProvider> { 209 @Override isAvailable(ManagedChannelProvider provider)210 public boolean isAvailable(ManagedChannelProvider provider) { 211 return provider.isAvailable(); 212 } 213 214 @Override getPriority(ManagedChannelProvider provider)215 public int getPriority(ManagedChannelProvider provider) { 216 return provider.priority(); 217 } 218 } 219 220 /** Thrown when no suitable {@link ManagedChannelProvider} objects can be found. */ 221 public static final class ProviderNotFoundException extends RuntimeException { 222 private static final long serialVersionUID = 1; 223 ProviderNotFoundException(String msg)224 public ProviderNotFoundException(String msg) { 225 super(msg); 226 } 227 } 228 } 229