• 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.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