1 /* 2 * Copyright 2016 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 java.net.URI; 21 import java.util.ArrayList; 22 import java.util.Collections; 23 import java.util.List; 24 import java.util.logging.Level; 25 import java.util.logging.Logger; 26 import javax.annotation.Nullable; 27 28 /** 29 * Provider of name resolvers for name agnostic consumption. 30 * 31 * <p>Implementations <em>should not</em> throw. If they do, it may interrupt class loading. If 32 * exceptions may reasonably occur for implementation-specific reasons, implementations should 33 * generally handle the exception gracefully and return {@code false} from {@link #isAvailable()}. 34 */ 35 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4159") 36 public abstract class NameResolverProvider extends NameResolver.Factory { 37 38 private static final Logger logger = Logger.getLogger(NameResolverProvider.class.getName()); 39 40 /** 41 * The port number used in case the target or the underlying naming system doesn't provide a 42 * port number. 43 * 44 * @since 1.0.0 45 */ 46 @SuppressWarnings("unused") // Avoids outside callers accidentally depending on the super class. 47 public static final Attributes.Key<Integer> PARAMS_DEFAULT_PORT = 48 NameResolver.Factory.PARAMS_DEFAULT_PORT; 49 50 @VisibleForTesting 51 static final Iterable<Class<?>> HARDCODED_CLASSES = getHardCodedClasses(); 52 53 private static final List<NameResolverProvider> providers = ServiceProviders.loadAll( 54 NameResolverProvider.class, 55 HARDCODED_CLASSES, 56 NameResolverProvider.class.getClassLoader(), 57 new NameResolverPriorityAccessor()); 58 59 private static final NameResolver.Factory factory = new NameResolverFactory(providers); 60 61 /** 62 * Returns non-{@code null} ClassLoader-wide providers, in preference order. 63 * 64 * @since 1.0.0 65 */ providers()66 public static List<NameResolverProvider> providers() { 67 return providers; 68 } 69 70 /** 71 * @since 1.0.0 72 */ asFactory()73 public static NameResolver.Factory asFactory() { 74 return factory; 75 } 76 77 @VisibleForTesting asFactory(List<NameResolverProvider> providers)78 static NameResolver.Factory asFactory(List<NameResolverProvider> providers) { 79 return new NameResolverFactory(providers); 80 } 81 82 /** 83 * Whether this provider is available for use, taking the current environment into consideration. 84 * If {@code false}, no other methods are safe to be called. 85 * 86 * @since 1.0.0 87 */ isAvailable()88 protected abstract boolean isAvailable(); 89 90 /** 91 * A priority, from 0 to 10 that this provider should be used, taking the current environment into 92 * consideration. 5 should be considered the default, and then tweaked based on environment 93 * detection. A priority of 0 does not imply that the provider wouldn't work; just that it should 94 * be last in line. 95 * 96 * @since 1.0.0 97 */ priority()98 protected abstract int priority(); 99 100 private static final class NameResolverFactory extends NameResolver.Factory { 101 private final List<NameResolverProvider> providers; 102 NameResolverFactory(List<NameResolverProvider> providers)103 NameResolverFactory(List<NameResolverProvider> providers) { 104 this.providers = Collections.unmodifiableList(new ArrayList<>(providers)); 105 } 106 107 @Override 108 @Nullable newNameResolver(URI targetUri, Attributes params)109 public NameResolver newNameResolver(URI targetUri, Attributes params) { 110 checkForProviders(); 111 for (NameResolverProvider provider : providers) { 112 NameResolver resolver = provider.newNameResolver(targetUri, params); 113 if (resolver != null) { 114 return resolver; 115 } 116 } 117 return null; 118 } 119 120 @Override getDefaultScheme()121 public String getDefaultScheme() { 122 checkForProviders(); 123 return providers.get(0).getDefaultScheme(); 124 } 125 checkForProviders()126 private void checkForProviders() { 127 if (providers.isEmpty()) { 128 String msg = "No NameResolverProviders found via ServiceLoader, including for DNS. " 129 + "This is probably due to a broken build. If using ProGuard, check your configuration"; 130 throw new RuntimeException(msg); 131 } 132 } 133 } 134 135 @VisibleForTesting getHardCodedClasses()136 static final List<Class<?>> getHardCodedClasses() { 137 // Class.forName(String) is used to remove the need for ProGuard configuration. Note that 138 // ProGuard does not detect usages of Class.forName(String, boolean, ClassLoader): 139 // https://sourceforge.net/p/proguard/bugs/418/ 140 try { 141 return Collections.<Class<?>>singletonList( 142 Class.forName("io.grpc.internal.DnsNameResolverProvider")); 143 } catch (ClassNotFoundException e) { 144 logger.log(Level.FINE, "Unable to find DNS NameResolver", e); 145 } 146 return Collections.emptyList(); 147 } 148 149 private static final class NameResolverPriorityAccessor 150 implements ServiceProviders.PriorityAccessor<NameResolverProvider> { 151 NameResolverPriorityAccessor()152 NameResolverPriorityAccessor() {} 153 154 @Override isAvailable(NameResolverProvider provider)155 public boolean isAvailable(NameResolverProvider provider) { 156 return provider.isAvailable(); 157 } 158 159 @Override getPriority(NameResolverProvider provider)160 public int getPriority(NameResolverProvider provider) { 161 return provider.priority(); 162 } 163 } 164 } 165