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.AlreadyBeingCreatedException;
046import org.apache.hadoop.hdfs.protocol.ClientProtocol;
047import org.apache.hadoop.hdfs.protocol.HdfsConstants;
048import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolPB;
049import org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB;
050import org.apache.hadoop.hdfs.protocolPB.JournalProtocolPB;
051import org.apache.hadoop.hdfs.protocolPB.JournalProtocolTranslatorPB;
052import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolPB;
053import org.apache.hadoop.hdfs.protocolPB.NamenodeProtocolTranslatorPB;
054import org.apache.hadoop.hdfs.server.namenode.ha.AbstractNNFailoverProxyProvider;
055import org.apache.hadoop.hdfs.server.namenode.ha.WrappedFailoverProxyProvider;
056import org.apache.hadoop.hdfs.server.namenode.NameNode;
057import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
058import org.apache.hadoop.hdfs.server.protocol.JournalProtocol;
059import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocol;
060import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
061import org.apache.hadoop.io.Text;
062import org.apache.hadoop.io.retry.DefaultFailoverProxyProvider;
063import org.apache.hadoop.io.retry.FailoverProxyProvider;
064import org.apache.hadoop.io.retry.LossyRetryInvocationHandler;
065import org.apache.hadoop.io.retry.RetryPolicies;
066import org.apache.hadoop.io.retry.RetryPolicy;
067import org.apache.hadoop.io.retry.RetryProxy;
068import org.apache.hadoop.io.retry.RetryUtils;
069import org.apache.hadoop.ipc.ProtobufRpcEngine;
070import org.apache.hadoop.ipc.RPC;
071import org.apache.hadoop.ipc.RemoteException;
072import org.apache.hadoop.net.NetUtils;
073import org.apache.hadoop.security.RefreshUserMappingsProtocol;
074import org.apache.hadoop.security.SecurityUtil;
075import org.apache.hadoop.security.UserGroupInformation;
076import org.apache.hadoop.security.authorize.RefreshAuthorizationPolicyProtocol;
077import org.apache.hadoop.security.protocolPB.RefreshAuthorizationPolicyProtocolClientSideTranslatorPB;
078import org.apache.hadoop.security.protocolPB.RefreshAuthorizationPolicyProtocolPB;
079import org.apache.hadoop.security.protocolPB.RefreshUserMappingsProtocolClientSideTranslatorPB;
080import org.apache.hadoop.security.protocolPB.RefreshUserMappingsProtocolPB;
081import org.apache.hadoop.ipc.RefreshCallQueueProtocol;
082import org.apache.hadoop.ipc.protocolPB.RefreshCallQueueProtocolPB;
083import org.apache.hadoop.ipc.protocolPB.RefreshCallQueueProtocolClientSideTranslatorPB;
084import org.apache.hadoop.tools.GetUserMappingsProtocol;
085import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolClientSideTranslatorPB;
086import org.apache.hadoop.tools.protocolPB.GetUserMappingsProtocolPB;
087
088import com.google.common.annotations.VisibleForTesting;
089import 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 */
097public 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}