001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.hdfs;
019    
020    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_DEFAULT;
021    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY;
022    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_PROXY_PROVIDER_KEY_PREFIX;
023    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_DEFAULT;
024    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_KEY;
025    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_DEFAULT;
026    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_KEY;
027    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_RETRY_MAX_ATTEMPTS_KEY;
028    import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_RETRY_MAX_ATTEMPTS_DEFAULT;
029    
030    import java.io.IOException;
031    import java.lang.reflect.Constructor;
032    import java.lang.reflect.InvocationHandler;
033    import java.lang.reflect.Proxy;
034    import java.net.InetSocketAddress;
035    import java.net.URI;
036    import java.util.HashMap;
037    import java.util.Map;
038    import java.util.concurrent.TimeUnit;
039    import java.util.concurrent.atomic.AtomicBoolean;
040    
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogFactory;
043    import org.apache.hadoop.conf.Configuration;
044    import org.apache.hadoop.hdfs.DFSClient.Conf;
045    import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException;
046    import org.apache.hadoop.hdfs.protocol.ClientProtocol;
047    import org.apache.hadoop.hdfs.protocol.HdfsConstants;
048    import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB;
049    import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB;
050    import org.apache.hadoop.hdfs.protocolPB.JournalProtocolPB;
051    import org.apache.hadoop.hdfs.protocolPB.JournalProtocolTranslatorPB;
052    import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolPB;
053    import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolTranslatorPB;
054    import org.apache.hadoop.hdfs.server.namenode.ha.AbstractNNFailoverProxyProvider;
055    import org.apache.hadoop.hdfs.server.namenode.ha.WrappedFailoverProxyProvider;
056    import org.apache.hadoop.hdfs.server.namenode.NameNode;
057    import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
058    import org.apache.hadoop.hdfs.server.protocol.JournalProtocol;
059    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocol;
060    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
061    import org.apache.hadoop.io.Text;
062    import org.apache.hadoop.io.retry.DefaultFailoverProxyProvider;
063    import org.apache.hadoop.io.retry.FailoverProxyProvider;
064    import org.apache.hadoop.io.retry.LossyRetryInvocationHandler;
065    import org.apache.hadoop.io.retry.RetryPolicies;
066    import org.apache.hadoop.io.retry.RetryPolicy;
067    import org.apache.hadoop.io.retry.RetryProxy;
068    import org.apache.hadoop.io.retry.RetryUtils;
069    import org.apache.hadoop.ipc.ProtobufRpcEngine;
070    import org.apache.hadoop.ipc.RPC;
071    import org.apache.hadoop.ipc.RemoteException;
072    import org.apache.hadoop.net.NetUtils;
073    import org.apache.hadoop.security.RefreshUserMappingsProtocol;
074    import org.apache.hadoop.security.SecurityUtil;
075    import org.apache.hadoop.security.UserGroupInformation;
076    import org.apache.hadoop.security.authorize.RefreshAuthorizationPolicyProtocol;
077    import org.apache.hadoop.security.protocolPB.RefreshAuthorizationPolicyProtocolClientSideTranslatorPB;
078    import org.apache.hadoop.security.protocolPB.RefreshAuthorizationPolicyProtocolPB;
079    import org.apache.hadoop.security.protocolPB.RefreshUserMappingsProtocolClientSideTranslatorPB;
080    import org.apache.hadoop.security.protocolPB.RefreshUserMappingsProtocolPB;
081    import org.apache.hadoop.ipc.RefreshCallQueueProtocol;
082    import org.apache.hadoop.ipc.protocolPB.RefreshCallQueueProtocolPB;
083    import org.apache.hadoop.ipc.protocolPB.RefreshCallQueueProtocolClientSideTranslatorPB;
084    import org.apache.hadoop.tools.GetUserMappingsProtocol;
085    import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolClientSideTranslatorPB;
086    import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolPB;
087    
088    import com.google.common.annotations.VisibleForTesting;
089    import com.google.common.base.Preconditions;
090    
091    /**
092     * Create proxy objects to communicate with a remote NN. All remote access to an
093     * NN should be funneled through this class. Most of the time you'll want to use
094     * {@link NameNodeProxies#createProxy(Configuration, URI, Class)}, which will
095     * create either an HA- or non-HA-enabled client proxy as appropriate.
096     */
097    public class NameNodeProxies {
098      
099      private static final Log LOG = LogFactory.getLog(NameNodeProxies.class);
100    
101      /**
102       * Wrapper for a client proxy as well as its associated service ID.
103       * This is simply used as a tuple-like return type for
104       * {@link NameNodeProxies#createProxy} and
105       * {@link NameNodeProxies#createNonHAProxy}.
106       */
107      public static class ProxyAndInfo<PROXYTYPE> {
108        private final PROXYTYPE proxy;
109        private final Text dtService;
110        private final InetSocketAddress address;
111        
112        public ProxyAndInfo(PROXYTYPE proxy, Text dtService,
113            InetSocketAddress address) {
114          this.proxy = proxy;
115          this.dtService = dtService;
116          this.address = address;
117        }
118        
119        public PROXYTYPE getProxy() {
120          return proxy;
121        }
122        
123        public Text getDelegationTokenService() {
124          return dtService;
125        }
126    
127        public InetSocketAddress getAddress() {
128          return address;
129        }
130      }
131    
132      /**
133       * Creates the namenode proxy with the passed protocol. This will handle
134       * creation of either HA- or non-HA-enabled proxy objects, depending upon
135       * if the provided URI is a configured logical URI.
136       * 
137       * @param conf the configuration containing the required IPC
138       *        properties, client failover configurations, etc.
139       * @param nameNodeUri the URI pointing either to a specific NameNode
140       *        or to a logical nameservice.
141       * @param xface the IPC interface which should be created
142       * @return an object containing both the proxy and the associated
143       *         delegation token service it corresponds to
144       * @throws IOException if there is an error creating the proxy
145       **/
146      @SuppressWarnings("unchecked")
147      public static <T> ProxyAndInfo<T> createProxy(Configuration conf,
148          URI nameNodeUri, Class<T> xface) throws IOException {
149        return createProxy(conf, nameNodeUri, xface, null);
150      }
151    
152      /**
153       * Creates the namenode proxy with the passed protocol. This will handle
154       * creation of either HA- or non-HA-enabled proxy objects, depending upon
155       * if the provided URI is a configured logical URI.
156       *
157       * @param conf the configuration containing the required IPC
158       *        properties, client failover configurations, etc.
159       * @param nameNodeUri the URI pointing either to a specific NameNode
160       *        or to a logical nameservice.
161       * @param xface the IPC interface which should be created
162       * @param fallbackToSimpleAuth set to true or false during calls to indicate if
163       *   a secure client falls back to simple auth
164       * @return an object containing both the proxy and the associated
165       *         delegation token service it corresponds to
166       * @throws IOException if there is an error creating the proxy
167       **/
168      @SuppressWarnings("unchecked")
169      public static <T> ProxyAndInfo<T> createProxy(Configuration conf,
170          URI nameNodeUri, Class<T> xface, AtomicBoolean fallbackToSimpleAuth)
171          throws IOException {
172        AbstractNNFailoverProxyProvider<T> failoverProxyProvider =
173            createFailoverProxyProvider(conf, nameNodeUri, xface, true,
174              fallbackToSimpleAuth);
175      
176        if (failoverProxyProvider == null) {
177          // Non-HA case
178          return createNonHAProxy(conf, NameNode.getAddress(nameNodeUri), xface,
179              UserGroupInformation.getCurrentUser(), true, fallbackToSimpleAuth);
180        } else {
181          // HA case
182          Conf config = new Conf(conf);
183          T proxy = (T) RetryProxy.create(xface, failoverProxyProvider,
184              RetryPolicies.failoverOnNetworkException(
185                  RetryPolicies.TRY_ONCE_THEN_FAIL, config.maxFailoverAttempts,
186                  config.maxRetryAttempts, config.failoverSleepBaseMillis,
187                  config.failoverSleepMaxMillis));
188    
189          Text dtService;
190          if (failoverProxyProvider.useLogicalURI()) {
191            dtService = HAUtil.buildTokenServiceForLogicalUri(nameNodeUri,
192                HdfsConstants.HDFS_URI_SCHEME);
193          } else {
194            dtService = SecurityUtil.buildTokenService(
195                NameNode.getAddress(nameNodeUri));
196          }
197          return new ProxyAndInfo<T>(proxy, dtService,
198              NameNode.getAddress(nameNodeUri));
199        }
200      }
201      
202      /**
203       * Generate a dummy namenode proxy instance that utilizes our hacked
204       * {@link LossyRetryInvocationHandler}. Proxy instance generated using this
205       * method will proactively drop RPC responses. Currently this method only
206       * support HA setup. null will be returned if the given configuration is not 
207       * for HA.
208       * 
209       * @param config the configuration containing the required IPC
210       *        properties, client failover configurations, etc.
211       * @param nameNodeUri the URI pointing either to a specific NameNode
212       *        or to a logical nameservice.
213       * @param xface the IPC interface which should be created
214       * @param numResponseToDrop The number of responses to drop for each RPC call
215       * @param fallbackToSimpleAuth set to true or false during calls to indicate if
216       *   a secure client falls back to simple auth
217       * @return an object containing both the proxy and the associated
218       *         delegation token service it corresponds to. Will return null of the
219       *         given configuration does not support HA.
220       * @throws IOException if there is an error creating the proxy
221       */
222      @SuppressWarnings("unchecked")
223      public static <T> ProxyAndInfo<T> createProxyWithLossyRetryHandler(
224          Configuration config, URI nameNodeUri, Class<T> xface,
225          int numResponseToDrop, AtomicBoolean fallbackToSimpleAuth)
226          throws IOException {
227        Preconditions.checkArgument(numResponseToDrop > 0);
228        AbstractNNFailoverProxyProvider<T> failoverProxyProvider =
229            createFailoverProxyProvider(config, nameNodeUri, xface, true,
230              fallbackToSimpleAuth);
231    
232        if (failoverProxyProvider != null) { // HA case
233          int delay = config.getInt(
234              DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_KEY,
235              DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_DEFAULT);
236          int maxCap = config.getInt(
237              DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_KEY,
238              DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_DEFAULT);
239          int maxFailoverAttempts = config.getInt(
240              DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY,
241              DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_DEFAULT);
242          int maxRetryAttempts = config.getInt(
243              DFS_CLIENT_RETRY_MAX_ATTEMPTS_KEY,
244              DFS_CLIENT_RETRY_MAX_ATTEMPTS_DEFAULT);
245          InvocationHandler dummyHandler = new LossyRetryInvocationHandler<T>(
246                  numResponseToDrop, failoverProxyProvider,
247                  RetryPolicies.failoverOnNetworkException(
248                      RetryPolicies.TRY_ONCE_THEN_FAIL, maxFailoverAttempts, 
249                      Math.max(numResponseToDrop + 1, maxRetryAttempts), delay, 
250                      maxCap));
251          
252          T proxy = (T) Proxy.newProxyInstance(
253              failoverProxyProvider.getInterface().getClassLoader(),
254              new Class[] { xface }, dummyHandler);
255          Text dtService;
256          if (failoverProxyProvider.useLogicalURI()) {
257            dtService = HAUtil.buildTokenServiceForLogicalUri(nameNodeUri,
258                HdfsConstants.HDFS_URI_SCHEME);
259          } else {
260            dtService = SecurityUtil.buildTokenService(
261                NameNode.getAddress(nameNodeUri));
262          }
263          return new ProxyAndInfo<T>(proxy, dtService,
264              NameNode.getAddress(nameNodeUri));
265        } else {
266          LOG.warn("Currently creating proxy using " +
267                    "LossyRetryInvocationHandler requires NN HA setup");
268          return null;
269        }
270      }
271    
272      /**
273       * Creates an explicitly non-HA-enabled proxy object. Most of the time you
274       * don't want to use this, and should instead use {@link NameNodeProxies#createProxy}.
275       * 
276       * @param conf the configuration object
277       * @param nnAddr address of the remote NN to connect to
278       * @param xface the IPC interface which should be created
279       * @param ugi the user who is making the calls on the proxy object
280       * @param withRetries certain interfaces have a non-standard retry policy
281       * @return an object containing both the proxy and the associated
282       *         delegation token service it corresponds to
283       * @throws IOException
284       */
285      @SuppressWarnings("unchecked")
286      public static <T> ProxyAndInfo<T> createNonHAProxy(
287          Configuration conf, InetSocketAddress nnAddr, Class<T> xface,
288          UserGroupInformation ugi, boolean withRetries) throws IOException {
289        return createNonHAProxy(conf, nnAddr, xface, ugi, withRetries, null);
290      }
291    
292      /**
293       * Creates an explicitly non-HA-enabled proxy object. Most of the time you
294       * don't want to use this, and should instead use {@link NameNodeProxies#createProxy}.
295       *
296       * @param conf the configuration object
297       * @param nnAddr address of the remote NN to connect to
298       * @param xface the IPC interface which should be created
299       * @param ugi the user who is making the calls on the proxy object
300       * @param withRetries certain interfaces have a non-standard retry policy
301       * @param fallbackToSimpleAuth - set to true or false during this method to
302       *   indicate if a secure client falls back to simple auth
303       * @return an object containing both the proxy and the associated
304       *         delegation token service it corresponds to
305       * @throws IOException
306       */
307      @SuppressWarnings("unchecked")
308      public static <T> ProxyAndInfo<T> createNonHAProxy(
309          Configuration conf, InetSocketAddress nnAddr, Class<T> xface,
310          UserGroupInformation ugi, boolean withRetries,
311          AtomicBoolean fallbackToSimpleAuth) throws IOException {
312        Text dtService = SecurityUtil.buildTokenService(nnAddr);
313      
314        T proxy;
315        if (xface == ClientProtocol.class) {
316          proxy = (T) createNNProxyWithClientProtocol(nnAddr, conf, ugi,
317              withRetries, fallbackToSimpleAuth);
318        } else if (xface == JournalProtocol.class) {
319          proxy = (T) createNNProxyWithJournalProtocol(nnAddr, conf, ugi);
320        } else if (xface == NamenodeProtocol.class) {
321          proxy = (T) createNNProxyWithNamenodeProtocol(nnAddr, conf, ugi,
322              withRetries);
323        } else if (xface == GetUserMappingsProtocol.class) {
324          proxy = (T) createNNProxyWithGetUserMappingsProtocol(nnAddr, conf, ugi);
325        } else if (xface == RefreshUserMappingsProtocol.class) {
326          proxy = (T) createNNProxyWithRefreshUserMappingsProtocol(nnAddr, conf, ugi);
327        } else if (xface == RefreshAuthorizationPolicyProtocol.class) {
328          proxy = (T) createNNProxyWithRefreshAuthorizationPolicyProtocol(nnAddr,
329              conf, ugi);
330        } else if (xface == RefreshCallQueueProtocol.class) {
331          proxy = (T) createNNProxyWithRefreshCallQueueProtocol(nnAddr, conf, ugi);
332        } else {
333          String message = "Unsupported protocol found when creating the proxy " +
334              "connection to NameNode: " +
335              ((xface != null) ? xface.getClass().getName() : "null");
336          LOG.error(message);
337          throw new IllegalStateException(message);
338        }
339    
340        return new ProxyAndInfo<T>(proxy, dtService, nnAddr);
341      }
342      
343      private static JournalProtocol createNNProxyWithJournalProtocol(
344          InetSocketAddress address, Configuration conf, UserGroupInformation ugi)
345          throws IOException {
346        JournalProtocolPB proxy = (JournalProtocolPB) createNameNodeProxy(address,
347            conf, ugi, JournalProtocolPB.class);
348        return new JournalProtocolTranslatorPB(proxy);
349      }
350    
351      private static RefreshAuthorizationPolicyProtocol
352          createNNProxyWithRefreshAuthorizationPolicyProtocol(InetSocketAddress address,
353              Configuration conf, UserGroupInformation ugi) throws IOException {
354        RefreshAuthorizationPolicyProtocolPB proxy = (RefreshAuthorizationPolicyProtocolPB)
355            createNameNodeProxy(address, conf, ugi, RefreshAuthorizationPolicyProtocolPB.class);
356        return new RefreshAuthorizationPolicyProtocolClientSideTranslatorPB(proxy);
357      }
358      
359      private static RefreshUserMappingsProtocol
360          createNNProxyWithRefreshUserMappingsProtocol(InetSocketAddress address,
361              Configuration conf, UserGroupInformation ugi) throws IOException {
362        RefreshUserMappingsProtocolPB proxy = (RefreshUserMappingsProtocolPB)
363            createNameNodeProxy(address, conf, ugi, RefreshUserMappingsProtocolPB.class);
364        return new RefreshUserMappingsProtocolClientSideTranslatorPB(proxy);
365      }
366    
367      private static RefreshCallQueueProtocol
368          createNNProxyWithRefreshCallQueueProtocol(InetSocketAddress address,
369              Configuration conf, UserGroupInformation ugi) throws IOException {
370        RefreshCallQueueProtocolPB proxy = (RefreshCallQueueProtocolPB)
371            createNameNodeProxy(address, conf, ugi, RefreshCallQueueProtocolPB.class);
372        return new RefreshCallQueueProtocolClientSideTranslatorPB(proxy);
373      }
374    
375      private static GetUserMappingsProtocol createNNProxyWithGetUserMappingsProtocol(
376          InetSocketAddress address, Configuration conf, UserGroupInformation ugi)
377          throws IOException {
378        GetUserMappingsProtocolPB proxy = (GetUserMappingsProtocolPB)
379            createNameNodeProxy(address, conf, ugi, GetUserMappingsProtocolPB.class);
380        return new GetUserMappingsProtocolClientSideTranslatorPB(proxy);
381      }
382      
383      private static NamenodeProtocol createNNProxyWithNamenodeProtocol(
384          InetSocketAddress address, Configuration conf, UserGroupInformation ugi,
385          boolean withRetries) throws IOException {
386        NamenodeProtocolPB proxy = (NamenodeProtocolPB) createNameNodeProxy(
387            address, conf, ugi, NamenodeProtocolPB.class);
388        if (withRetries) { // create the proxy with retries
389          RetryPolicy timeoutPolicy = RetryPolicies.exponentialBackoffRetry(5, 200,
390                  TimeUnit.MILLISECONDS);
391          Map<String, RetryPolicy> methodNameToPolicyMap
392               = new HashMap<String, RetryPolicy>();
393          methodNameToPolicyMap.put("getBlocks", timeoutPolicy);
394          methodNameToPolicyMap.put("getAccessKeys", timeoutPolicy);
395          NamenodeProtocol translatorProxy =
396              new NamenodeProtocolTranslatorPB(proxy);
397          return (NamenodeProtocol) RetryProxy.create(
398              NamenodeProtocol.class, translatorProxy, methodNameToPolicyMap);
399        } else {
400          return new NamenodeProtocolTranslatorPB(proxy);
401        }
402      }
403      
404      private static ClientProtocol createNNProxyWithClientProtocol(
405          InetSocketAddress address, Configuration conf, UserGroupInformation ugi,
406          boolean withRetries, AtomicBoolean fallbackToSimpleAuth)
407          throws IOException {
408        RPC.setProtocolEngine(conf, ClientNamenodeProtocolPB.class, ProtobufRpcEngine.class);
409    
410        final RetryPolicy defaultPolicy = 
411            RetryUtils.getDefaultRetryPolicy(
412                conf, 
413                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_ENABLED_KEY, 
414                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_ENABLED_DEFAULT, 
415                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_SPEC_KEY,
416                DFSConfigKeys.DFS_CLIENT_RETRY_POLICY_SPEC_DEFAULT,
417                SafeModeException.class);
418        
419        final long version = RPC.getProtocolVersion(ClientNamenodeProtocolPB.class);
420        ClientNamenodeProtocolPB proxy = RPC.getProtocolProxy(
421            ClientNamenodeProtocolPB.class, version, address, ugi, conf,
422            NetUtils.getDefaultSocketFactory(conf),
423            org.apache.hadoop.ipc.Client.getTimeout(conf), defaultPolicy,
424            fallbackToSimpleAuth).getProxy();
425    
426        if (withRetries) { // create the proxy with retries
427    
428          RetryPolicy createPolicy = RetryPolicies
429              .retryUpToMaximumCountWithFixedSleep(5,
430                  HdfsConstants.LEASE_SOFTLIMIT_PERIOD, TimeUnit.MILLISECONDS);
431        
432          Map<Class<? extends Exception>, RetryPolicy> remoteExceptionToPolicyMap 
433                     = new HashMap<Class<? extends Exception>, RetryPolicy>();
434          remoteExceptionToPolicyMap.put(AlreadyBeingCreatedException.class,
435              createPolicy);
436    
437          RetryPolicy methodPolicy = RetryPolicies.retryByRemoteException(
438              defaultPolicy, remoteExceptionToPolicyMap);
439          Map<String, RetryPolicy> methodNameToPolicyMap 
440                     = new HashMap<String, RetryPolicy>();
441        
442          methodNameToPolicyMap.put("create", methodPolicy);
443    
444          ClientProtocol translatorProxy =
445            new ClientNamenodeProtocolTranslatorPB(proxy);
446          return (ClientProtocol) RetryProxy.create(
447              ClientProtocol.class,
448              new DefaultFailoverProxyProvider<ClientProtocol>(
449                  ClientProtocol.class, translatorProxy),
450              methodNameToPolicyMap,
451              defaultPolicy);
452        } else {
453          return new ClientNamenodeProtocolTranslatorPB(proxy);
454        }
455      }
456    
457      private static Object createNameNodeProxy(InetSocketAddress address,
458          Configuration conf, UserGroupInformation ugi, Class<?> xface)
459          throws IOException {
460        RPC.setProtocolEngine(conf, xface, ProtobufRpcEngine.class);
461        Object proxy = RPC.getProxy(xface, RPC.getProtocolVersion(xface), address,
462            ugi, conf, NetUtils.getDefaultSocketFactory(conf));
463        return proxy;
464      }
465    
466      /** Gets the configured Failover proxy provider's class */
467      @VisibleForTesting
468      public static <T> Class<FailoverProxyProvider<T>> getFailoverProxyProviderClass(
469          Configuration conf, URI nameNodeUri) throws IOException {
470        if (nameNodeUri == null) {
471          return null;
472        }
473        String host = nameNodeUri.getHost();
474      
475        String configKey = DFS_CLIENT_FAILOVER_PROXY_PROVIDER_KEY_PREFIX + "."
476            + host;
477        try {
478          @SuppressWarnings("unchecked")
479          Class<FailoverProxyProvider<T>> ret = (Class<FailoverProxyProvider<T>>) conf
480              .getClass(configKey, null, FailoverProxyProvider.class);
481          return ret;
482        } catch (RuntimeException e) {
483          if (e.getCause() instanceof ClassNotFoundException) {
484            throw new IOException("Could not load failover proxy provider class "
485                + conf.get(configKey) + " which is configured for authority "
486                + nameNodeUri, e);
487          } else {
488            throw e;
489          }
490        }
491      }
492    
493      /** Creates the Failover proxy provider instance*/
494      @VisibleForTesting
495      public static <T> AbstractNNFailoverProxyProvider<T> createFailoverProxyProvider(
496          Configuration conf, URI nameNodeUri, Class<T> xface, boolean checkPort,
497          AtomicBoolean fallbackToSimpleAuth) throws IOException {
498        Class<FailoverProxyProvider<T>> failoverProxyProviderClass = null;
499        AbstractNNFailoverProxyProvider<T> providerNN;
500        Preconditions.checkArgument(
501            xface.isAssignableFrom(NamenodeProtocols.class),
502            "Interface %s is not a NameNode protocol", xface);
503        try {
504          // Obtain the class of the proxy provider
505          failoverProxyProviderClass = getFailoverProxyProviderClass(conf,
506              nameNodeUri);
507          if (failoverProxyProviderClass == null) {
508            return null;
509          }
510          // Create a proxy provider instance.
511          Constructor<FailoverProxyProvider<T>> ctor = failoverProxyProviderClass
512              .getConstructor(Configuration.class, URI.class, Class.class);
513          FailoverProxyProvider<T> provider = ctor.newInstance(conf, nameNodeUri,
514              xface);
515    
516          // If the proxy provider is of an old implementation, wrap it.
517          if (!(provider instanceof AbstractNNFailoverProxyProvider)) {
518            providerNN = new WrappedFailoverProxyProvider<T>(provider);
519          } else {
520            providerNN = (AbstractNNFailoverProxyProvider<T>)provider;
521          }
522        } catch (Exception e) {
523          String message = "Couldn't create proxy provider " + failoverProxyProviderClass;
524          if (LOG.isDebugEnabled()) {
525            LOG.debug(message, e);
526          }
527          if (e.getCause() instanceof IOException) {
528            throw (IOException) e.getCause();
529          } else {
530            throw new IOException(message, e);
531          }
532        }
533    
534        // Check the port in the URI, if it is logical.
535        if (checkPort && providerNN.useLogicalURI()) {
536          int port = nameNodeUri.getPort();
537          if (port > 0 && port != NameNode.DEFAULT_PORT) {
538            // Throwing here without any cleanup is fine since we have not
539            // actually created the underlying proxies yet.
540            throw new IOException("Port " + port + " specified in URI "
541                + nameNodeUri + " but host '" + nameNodeUri.getHost()
542                + "' is a logical (HA) namenode"
543                + " and does not use port information.");
544          }
545        }
546        providerNN.setFallbackToSimpleAuth(fallbackToSimpleAuth);
547        return providerNN;
548      }
549    
550    }