• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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