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