/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hdfs;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.net.InetAddresses;
import io.hops.leader_election.node.ActiveNode;
import io.hops.leader_election.node.SortedActiveNodeList;
import io.hops.metadata.hdfs.entity.EncodingPolicy;
import io.hops.metadata.hdfs.entity.EncodingStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.BlockStorageLocation;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
import org.apache.hadoop.fs.ContentSummary;
import org.apache.hadoop.fs.CreateFlag;
import org.apache.hadoop.fs.FileAlreadyExistsException;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FsServerDefaults;
import org.apache.hadoop.fs.FsStatus;
import org.apache.hadoop.fs.HdfsBlockLocation;
import org.apache.hadoop.fs.InvalidPathException;
import org.apache.hadoop.fs.MD5MD5CRC32CastagnoliFileChecksum;
import org.apache.hadoop.fs.MD5MD5CRC32FileChecksum;
import org.apache.hadoop.fs.MD5MD5CRC32GzipFileChecksum;
import org.apache.hadoop.fs.Options;
import org.apache.hadoop.fs.Options.ChecksumOpt;
import org.apache.hadoop.fs.ParentNotDirectoryException;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.UnresolvedLinkException;
import org.apache.hadoop.fs.VolumeId;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.hdfs.NamenodeSelector.NamenodeHandle;
import org.apache.hadoop.hdfs.client.HdfsDataInputStream;
import org.apache.hadoop.hdfs.client.HdfsDataOutputStream;
import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException;
import org.apache.hadoop.hdfs.protocol.ClientProtocol;
import org.apache.hadoop.hdfs.protocol.CorruptFileBlocks;
import org.apache.hadoop.hdfs.protocol.DSQuotaExceededException;
import org.apache.hadoop.hdfs.protocol.DatanodeID;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
import org.apache.hadoop.hdfs.protocol.DirectoryListing;
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
import org.apache.hadoop.hdfs.protocol.HdfsBlocksMetadata;
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
import org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction;
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
import org.apache.hadoop.hdfs.protocol.NSQuotaExceededException;
import org.apache.hadoop.hdfs.protocol.UnresolvedPathException;
import org.apache.hadoop.hdfs.protocol.datatransfer.DataTransferEncryptor;
import org.apache.hadoop.hdfs.protocol.datatransfer.IOStreamPair;
import org.apache.hadoop.hdfs.protocol.datatransfer.Op;
import org.apache.hadoop.hdfs.protocol.datatransfer.ReplaceDatanodeOnFailure;
import org.apache.hadoop.hdfs.protocol.datatransfer.Sender;
import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.BlockOpResponseProto;
import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.OpBlockChecksumResponseProto;
import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.Status;
import org.apache.hadoop.hdfs.protocolPB.PBHelper;
import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier;
import org.apache.hadoop.hdfs.security.token.block.DataEncryptionKey;
import org.apache.hadoop.hdfs.security.token.block.InvalidBlockTokenException;
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
import org.apache.hadoop.hdfs.server.common.HdfsServerConstants;
import org.apache.hadoop.hdfs.server.namenode.NameNode;
import org.apache.hadoop.hdfs.server.namenode.NotReplicatedYetException;
import org.apache.hadoop.hdfs.server.namenode.SafeModeException;
import org.apache.hadoop.io.DataOutputBuffer;
import org.apache.hadoop.io.EnumSetWritable;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.MD5Hash;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.ipc.Client;
import org.apache.hadoop.ipc.RemoteException;
import org.apache.hadoop.net.DNS;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.security.token.SecretManager.InvalidToken;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.security.token.TokenRenewer;
import org.apache.hadoop.util.DataChecksum;
import org.apache.hadoop.util.DataChecksum.Type;
import org.apache.hadoop.util.Progressable;
import org.apache.hadoop.util.Time;
import javax.net.SocketFactory;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_SIZE_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_SIZE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_BLOCK_WRITE_RETRIES_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_BLOCK_WRITE_RETRIES_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_CACHED_CONN_RETRY_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_CACHED_CONN_RETRY_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_MAX_BLOCK_ACQUIRE_FAILURES_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_MAX_BLOCK_ACQUIRE_FAILURES_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_READ_PREFETCH_SIZE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_RETRY_WINDOW_BASE;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_SOCKET_CACHE_CAPACITY_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_SOCKET_CACHE_CAPACITY_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_SOCKET_CACHE_EXPIRY_MSEC_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_SOCKET_CACHE_EXPIRY_MSEC_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_SOCKET_TIMEOUT_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_USE_DN_HOSTNAME;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_USE_DN_HOSTNAME_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_USE_LEGACY_BLOCKREADER;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_USE_LEGACY_BLOCKREADER_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_WRITE_PACKET_SIZE_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_CLIENT_WRITE_PACKET_SIZE_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_REPLICATION_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_REPLICATION_KEY;
/**
* *****************************************************
* DFSClient can connect to a Hadoop Filesystem and
* perform basic file tasks. It uses the ClientProtocol
* to communicate with a NameNode daemon, and connects
* directly to DataNodes to read/write block data.
* <p/>
* Hadoop DFS users should obtain an instance of
* DistributedFileSystem, which uses DFSClient to handle
* filesystem tasks.
* <p/>
* ******************************************************
*/
@InterfaceAudience.Private
public class DFSClient implements java.io.Closeable {
public static final Log LOG = LogFactory.getLog(DFSClient.class);
public static final long SERVER_DEFAULTS_VALIDITY_PERIOD = 60 * 60 * 1000L;
// 1 hour
static final int TCP_WINDOW_SIZE = 128 * 1024; // 128 KB
final NamenodeSelector namenodeSelector;
final int MAX_RPC_RETRIES;
/* The service used for delegation tokens */
private Text dtService;
final UserGroupInformation ugi;
volatile boolean clientRunning = true;
volatile long lastLeaseRenewal;
private volatile FsServerDefaults serverDefaults;
private volatile long serverDefaultsLastUpdate;
final String clientName;
Configuration conf;
SocketFactory socketFactory;
final ReplaceDatanodeOnFailure dtpReplaceDatanodeOnFailure;
final FileSystem.Statistics stats;
final int hdfsTimeout; // timeout value for a DFS operation.
private final String authority;
final SocketCache socketCache;
final Conf dfsClientConf;
private Random r = new Random();
private SocketAddress[] localInterfaceAddrs;
private DataEncryptionKey encryptionKey;
/**
* DFSClient configuration
*/
static class Conf {
final int maxFailoverAttempts;
final int failoverSleepBaseMillis;
final int failoverSleepMaxMillis;
final int maxBlockAcquireFailures;
final int confTime;
final int ioBufferSize;
final ChecksumOpt defaultChecksumOpt;
final int writePacketSize;
final int socketTimeout;
final int socketCacheCapacity;
final long socketCacheExpiry;
/**
* Wait time window (in msec) if BlockMissingException is caught
*/
final int timeWindow;
final int nCachedConnRetry;
final int nBlockWriteRetry;
final int nBlockWriteLocateFollowingRetry;
final long defaultBlockSize;
final long prefetchSize;
final short defaultReplication;
final String taskId;
final FsPermission uMask;
final boolean useLegacyBlockReader;
final boolean connectToDnViaHostname;
final boolean getHdfsBlocksMetadataEnabled;
final int getFileBlockStorageLocationsNumThreads;
final int getFileBlockStorageLocationsTimeout;
final int dfsClientInitialWaitOnRetry;
Conf(Configuration conf) {
maxFailoverAttempts = conf.getInt(DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_KEY,
DFS_CLIENT_FAILOVER_MAX_ATTEMPTS_DEFAULT);
failoverSleepBaseMillis =
conf.getInt(DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_KEY,
DFS_CLIENT_FAILOVER_SLEEPTIME_BASE_DEFAULT);
failoverSleepMaxMillis =
conf.getInt(DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_KEY,
DFS_CLIENT_FAILOVER_SLEEPTIME_MAX_DEFAULT);
maxBlockAcquireFailures =
conf.getInt(DFS_CLIENT_MAX_BLOCK_ACQUIRE_FAILURES_KEY,
DFS_CLIENT_MAX_BLOCK_ACQUIRE_FAILURES_DEFAULT);
confTime = conf.getInt(DFS_DATANODE_SOCKET_WRITE_TIMEOUT_KEY,
HdfsServerConstants.WRITE_TIMEOUT);
ioBufferSize =
conf.getInt(CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_KEY,
CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_DEFAULT);
defaultChecksumOpt = getChecksumOptFromConf(conf);
socketTimeout = conf.getInt(DFS_CLIENT_SOCKET_TIMEOUT_KEY,
HdfsServerConstants.READ_TIMEOUT);
/** dfs.write.packet.size is an internal config variable */
writePacketSize = conf.getInt(DFS_CLIENT_WRITE_PACKET_SIZE_KEY,
DFS_CLIENT_WRITE_PACKET_SIZE_DEFAULT);
defaultBlockSize =
conf.getLongBytes(DFS_BLOCK_SIZE_KEY, DFS_BLOCK_SIZE_DEFAULT);
defaultReplication =
(short) conf.getInt(DFS_REPLICATION_KEY, DFS_REPLICATION_DEFAULT);
String machineName = "";
try {
machineName = InetAddress.getLocalHost().getHostName() + "_";
} catch (UnknownHostException e) {
}
taskId = conf.get("mapreduce.task.attempt.id", machineName+"NONMAPREDUCE");
socketCacheCapacity = conf.getInt(DFS_CLIENT_SOCKET_CACHE_CAPACITY_KEY,
DFS_CLIENT_SOCKET_CACHE_CAPACITY_DEFAULT);
socketCacheExpiry = conf.getLong(DFS_CLIENT_SOCKET_CACHE_EXPIRY_MSEC_KEY,
DFS_CLIENT_SOCKET_CACHE_EXPIRY_MSEC_DEFAULT);
prefetchSize = conf.getLong(DFS_CLIENT_READ_PREFETCH_SIZE_KEY,
10 * defaultBlockSize);
timeWindow = conf.getInt(DFS_CLIENT_RETRY_WINDOW_BASE, 3000);
nCachedConnRetry = conf.getInt(DFS_CLIENT_CACHED_CONN_RETRY_KEY,
DFS_CLIENT_CACHED_CONN_RETRY_DEFAULT);
nBlockWriteRetry = conf.getInt(DFS_CLIENT_BLOCK_WRITE_RETRIES_KEY,
DFS_CLIENT_BLOCK_WRITE_RETRIES_DEFAULT);
nBlockWriteLocateFollowingRetry =
conf.getInt(DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_KEY,
DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_DEFAULT);
uMask = FsPermission.getUMask(conf);
useLegacyBlockReader = conf.getBoolean(DFS_CLIENT_USE_LEGACY_BLOCKREADER,
DFS_CLIENT_USE_LEGACY_BLOCKREADER_DEFAULT);
connectToDnViaHostname = conf.getBoolean(DFS_CLIENT_USE_DN_HOSTNAME,
DFS_CLIENT_USE_DN_HOSTNAME_DEFAULT);
getHdfsBlocksMetadataEnabled =
conf.getBoolean(DFSConfigKeys.DFS_HDFS_BLOCKS_METADATA_ENABLED,
DFSConfigKeys.DFS_HDFS_BLOCKS_METADATA_ENABLED_DEFAULT);
getFileBlockStorageLocationsNumThreads = conf.getInt(
DFSConfigKeys.DFS_CLIENT_FILE_BLOCK_STORAGE_LOCATIONS_NUM_THREADS,
DFSConfigKeys.DFS_CLIENT_FILE_BLOCK_STORAGE_LOCATIONS_NUM_THREADS_DEFAULT);
getFileBlockStorageLocationsTimeout = conf.getInt(
DFSConfigKeys.DFS_CLIENT_FILE_BLOCK_STORAGE_LOCATIONS_TIMEOUT,
DFSConfigKeys.DFS_CLIENT_FILE_BLOCK_STORAGE_LOCATIONS_TIMEOUT_DEFAULT);
dfsClientInitialWaitOnRetry =
conf.getInt(DFSConfigKeys.DFS_CLIENT_INITIAL_WAIT_ON_RETRY_IN_MS_KEY,
DFSConfigKeys.DFS_CLIENT_INITIAL_WAIT_ON_RETRY_IN_MS_DEFAULT);
}
private DataChecksum.Type getChecksumType(Configuration conf) {
final String checksum = conf.get(DFSConfigKeys.DFS_CHECKSUM_TYPE_KEY,
DFSConfigKeys.DFS_CHECKSUM_TYPE_DEFAULT);
try {
return DataChecksum.Type.valueOf(checksum);
} catch (IllegalArgumentException iae) {
LOG.warn("Bad checksum type: " + checksum + ". Using default " +
DFSConfigKeys.DFS_CHECKSUM_TYPE_DEFAULT);
return DataChecksum.Type
.valueOf(DFSConfigKeys.DFS_CHECKSUM_TYPE_DEFAULT);
}
}
// Construct a checksum option from conf
private ChecksumOpt getChecksumOptFromConf(Configuration conf) {
DataChecksum.Type type = getChecksumType(conf);
int bytesPerChecksum = conf.getInt(DFS_BYTES_PER_CHECKSUM_KEY,
DFS_BYTES_PER_CHECKSUM_DEFAULT);
return new ChecksumOpt(type, bytesPerChecksum);
}
// create a DataChecksum with the default option.
private DataChecksum createChecksum() throws IOException {
return createChecksum(null);
}
private DataChecksum createChecksum(ChecksumOpt userOpt)
throws IOException {
// Fill in any missing field with the default.
ChecksumOpt myOpt =
ChecksumOpt.processChecksumOpt(defaultChecksumOpt, userOpt);
DataChecksum dataChecksum = DataChecksum
.newDataChecksum(myOpt.getChecksumType(),
myOpt.getBytesPerChecksum());
if (dataChecksum == null) {
throw new IOException("Invalid checksum type specified: " +
myOpt.getChecksumType().name());
}
return dataChecksum;
}
}
Conf getConf() {
return dfsClientConf;
}
/**
* A map from file names to {@link DFSOutputStream} objects
* that are currently being written by this client.
* Note that a file can only be written by a single client.
*/
private final Map<String, DFSOutputStream> filesBeingWritten =
new HashMap<String, DFSOutputStream>();
private boolean shortCircuitLocalReads;
/**
* Same as this(NameNode.getAddress(conf), conf);
*
* @see #DFSClient(InetSocketAddress, Configuration)
* @deprecated Deprecated at 0.21
*/
@Deprecated
public DFSClient(Configuration conf) throws IOException {
this(NameNode.getAddress(conf), conf);
}
public DFSClient(InetSocketAddress address, Configuration conf)
throws IOException {
this(NameNode.getUri(address), conf);
}
/**
* Same as this(nameNodeUri, conf, null);
*
* @see #DFSClient(URI, Configuration, FileSystem.Statistics)
*/
public DFSClient(URI nameNodeUri, Configuration conf) throws IOException {
this(nameNodeUri, conf, null);
}
/**
* Same as this(nameNodeUri, null, conf, stats);
*
* @see #DFSClient(URI, ClientProtocol, Configuration, FileSystem.Statistics)
*/
public DFSClient(URI nameNodeUri, Configuration conf,
FileSystem.Statistics stats) throws IOException {
this(nameNodeUri, null, conf, stats);
}
/**
* Create a new DFSClient connected to the given nameNodeUri or rpcNamenode.
* Exactly one of nameNodeUri or rpcNamenode must be null.
*/
DFSClient(URI nameNodeUri, ClientProtocol rpcNamenode, Configuration conf,
FileSystem.Statistics stats) throws IOException {
// Copy only the required DFSClient configuration
this.dfsClientConf = new Conf(conf);
this.conf = conf;
this.stats = stats;
this.socketFactory = NetUtils.getSocketFactory(conf, ClientProtocol.class);
this.dtpReplaceDatanodeOnFailure = ReplaceDatanodeOnFailure.get(conf);
// The hdfsTimeout is currently the same as the ipc timeout
this.hdfsTimeout = Client.getTimeout(conf);
this.ugi = UserGroupInformation.getCurrentUser();
this.authority = nameNodeUri == null ? "null" : nameNodeUri.getAuthority();
this.clientName = "DFSClient_" + dfsClientConf.taskId + "_" +
DFSUtil.getRandom().nextInt() + "_" + Thread.currentThread().getId();
if (rpcNamenode != null) {
// This case is used for testing.
Preconditions.checkArgument(nameNodeUri == null);
namenodeSelector = new NamenodeSelector(conf, rpcNamenode);
dtService = null;
} else {
Preconditions.checkArgument(nameNodeUri != null, "null URI");
NameNodeProxies.ProxyAndInfo<ClientProtocol> proxyInfo =
NameNodeProxies.createProxy(conf, nameNodeUri, ClientProtocol.class);
this.dtService = proxyInfo.getDelegationTokenService();
namenodeSelector = new NamenodeSelector(conf, nameNodeUri);
}
// read directly from the block file if configured.
this.shortCircuitLocalReads =
conf.getBoolean(DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_KEY,
DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_DEFAULT);
if (LOG.isDebugEnabled()) {
LOG.debug("Short circuit read is " + shortCircuitLocalReads);
}
String localInterfaces[] =
conf.getTrimmedStrings(DFSConfigKeys.DFS_CLIENT_LOCAL_INTERFACES);
localInterfaceAddrs = getLocalInterfaceAddrs(localInterfaces);
if (LOG.isDebugEnabled() && 0 != localInterfaces.length) {
LOG.debug("Using local interfaces [" +
Joiner.on(',').join(localInterfaces) + "] with addresses [" +
Joiner.on(',').join(localInterfaceAddrs) + "]");
}
this.socketCache = SocketCache
.getInstance(dfsClientConf.socketCacheCapacity,
dfsClientConf.socketCacheExpiry);
this.MAX_RPC_RETRIES =
conf.getInt(DFSConfigKeys.DFS_CLIENT_RETRIES_ON_FAILURE_KEY,
DFSConfigKeys.DFS_CLIENT_RETRIES_ON_FAILURE_DEFAULT);
}
/**
* Return the socket addresses to use with each configured
* local interface. Local interfaces may be specified by IP
* address, IP address range using CIDR notation, interface
* name (e.g. eth0) or sub-interface name (e.g. eth0:0).
* The socket addresses consist of the IPs for the interfaces
* and the ephemeral port (port 0). If an IP, IP range, or
* interface name matches an interface with sub-interfaces
* only the IP of the interface is used. Sub-interfaces can
* be used by specifying them explicitly (by IP or name).
*
* @return SocketAddresses for the configured local interfaces,
* or an empty array if none are configured
* @throws UnknownHostException
* if a given interface name is invalid
*/
private static SocketAddress[] getLocalInterfaceAddrs(String interfaceNames[])
throws UnknownHostException {
List<SocketAddress> localAddrs = new ArrayList<SocketAddress>();
for (String interfaceName : interfaceNames) {
if (InetAddresses.isInetAddress(interfaceName)) {
localAddrs.add(new InetSocketAddress(interfaceName, 0));
} else if (NetUtils.isValidSubnet(interfaceName)) {
for (InetAddress addr : NetUtils.getIPs(interfaceName, false)) {
localAddrs.add(new InetSocketAddress(addr, 0));
}
} else {
for (String ip : DNS.getIPs(interfaceName, false)) {
localAddrs.add(new InetSocketAddress(ip, 0));
}
}
}
return localAddrs.toArray(new SocketAddress[localAddrs.size()]);
}
/**
* Select one of the configured local interfaces at random. We use a random
* interface because other policies like round-robin are less effective
* given that we cache connections to datanodes.
*
* @return one of the local interface addresses at random, or null if no
* local interfaces are configured
*/
SocketAddress getRandomLocalInterfaceAddr() {
if (localInterfaceAddrs.length == 0) {
return null;
}
final int idx = r.nextInt(localInterfaceAddrs.length);
final SocketAddress addr = localInterfaceAddrs[idx];
if (LOG.isDebugEnabled()) {
LOG.debug("Using local interface " + addr);
}
return addr;
}
/**
* Return the number of times the client should go back to the namenode
* to retrieve block locations when reading.
*/
int getMaxBlockAcquireFailures() {
return dfsClientConf.maxBlockAcquireFailures;
}
/**
* Return the timeout that clients should use when writing to datanodes.
*
* @param numNodes
* the number of nodes in the pipeline.
*/
int getDatanodeWriteTimeout(int numNodes) {
return (dfsClientConf.confTime > 0) ? (dfsClientConf.confTime +
HdfsServerConstants.WRITE_TIMEOUT_EXTENSION * numNodes) : 0;
}
int getDatanodeReadTimeout(int numNodes) {
return dfsClientConf.socketTimeout > 0 ?
(HdfsServerConstants.READ_TIMEOUT_EXTENSION * numNodes +
dfsClientConf.socketTimeout) : 0;
}
int getHdfsTimeout() {
return hdfsTimeout;
}
String getClientName() {
return clientName;
}
/**
* @return whether the client should use hostnames instead of IPs
* when connecting to DataNodes
*/
boolean connectToDnViaHostname() {
return dfsClientConf.connectToDnViaHostname;
}
void checkOpen() throws IOException {
if (!clientRunning) {
IOException result = new IOException("Filesystem closed");
throw result;
}
}
/**
* Return the lease renewer instance. The renewer thread won't start
* until the first output stream is created. The same instance will
* be returned until all output streams are closed.
*/
public LeaseRenewer getLeaseRenewer() throws IOException {
return LeaseRenewer.getInstance(authority, ugi, this);
}
/**
* Get a lease and start automatic renewal
*/
private void beginFileLease(final String src, final DFSOutputStream out)
throws IOException {
getLeaseRenewer().put(src, out, this);
}
/**
* Stop renewal of lease for the file.
*/
void endFileLease(final String src) throws IOException {
getLeaseRenewer().closeFile(src, this);
}
/**
* Put a file. Only called from LeaseRenewer, where proper locking is
* enforced to consistently update its local dfsclients array and
* client's filesBeingWritten map.
*/
void putFileBeingWritten(final String src, final DFSOutputStream out) {
synchronized (filesBeingWritten) {
filesBeingWritten.put(src, out);
// update the last lease renewal time only when there was no
// writes. once there is one write stream open, the lease renewer
// thread keeps it updated well with in anyone's expiration time.
if (lastLeaseRenewal == 0) {
updateLastLeaseRenewal();
}
}
}
/**
* Remove a file. Only called from LeaseRenewer.
*/
void removeFileBeingWritten(final String src) {
synchronized (filesBeingWritten) {
filesBeingWritten.remove(src);
if (filesBeingWritten.isEmpty()) {
lastLeaseRenewal = 0;
}
}
}
/**
* Is file-being-written map empty?
*/
boolean isFilesBeingWrittenEmpty() {
synchronized (filesBeingWritten) {
return filesBeingWritten.isEmpty();
}
}
/**
* @return true if the client is running
*/
boolean isClientRunning() {
return clientRunning;
}
long getLastLeaseRenewal() {
return lastLeaseRenewal;
}
void updateLastLeaseRenewal() {
synchronized (filesBeingWritten) {
if (filesBeingWritten.isEmpty()) {
return;
}
lastLeaseRenewal = Time.now();
}
}
/**
* Renew leases.
*
* @return true if lease was renewed. May return false if this
* client has been closed or has no files open.
*/
boolean renewLease() throws IOException {
if (clientRunning && !isFilesBeingWrittenEmpty()) {
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.renewLease(clientName);
return null;
}
};
doClientActionWithRetry(handler, "renewLease");
updateLastLeaseRenewal();
return true;
} catch (IOException e) {
// Abort if the lease has already expired.
final long elapsed = Time.now() - getLastLeaseRenewal();
if (elapsed > HdfsConstants.LEASE_HARDLIMIT_PERIOD) {
LOG.warn("Failed to renew lease for " + clientName + " for " +
(elapsed / 1000) + " seconds (>= soft-limit =" +
(HdfsConstants.LEASE_HARDLIMIT_PERIOD / 1000) + " seconds.) " +
"Closing all files being written ...", e);
closeAllFilesBeingWritten(true);
} else {
// Let the lease renewer handle it and retry.
throw e;
}
}
}
return false;
}
/**
* Close connections the Namenode.
*/
void closeConnectionToNamenode() {
namenodeSelector.close();
}
/**
* Abort and release resources held. Ignore all errors.
*/
void abort() {
clientRunning = false;
closeAllFilesBeingWritten(true);
try {
// remove reference to this client and stop the renewer,
// if there is no more clients under the renewer.
getLeaseRenewer().closeClient(this);
} catch (IOException ioe) {
LOG.info("Exception occurred while aborting the client " + ioe);
}
closeConnectionToNamenode();
}
/**
* Close/abort all files being written.
*/
private void closeAllFilesBeingWritten(final boolean abort) {
for (; ; ) {
final String src;
final DFSOutputStream out;
synchronized (filesBeingWritten) {
if (filesBeingWritten.isEmpty()) {
return;
}
src = filesBeingWritten.keySet().iterator().next();
out = filesBeingWritten.remove(src);
}
if (out != null) {
try {
if (abort) {
out.abort();
} else {
out.close();
}
} catch (IOException ie) {
LOG.error("Failed to " + (abort ? "abort" : "close") + " file " + src,
ie);
}
}
}
}
/**
* Close the file system, abandoning all of the leases and files being
* created and close connections to the namenode.
*/
@Override
public synchronized void close() throws IOException {
if (clientRunning) {
closeAllFilesBeingWritten(false);
clientRunning = false;
getLeaseRenewer().closeClient(this);
// close connections to the namenode
closeConnectionToNamenode();
}
}
/**
* Get the default block size for this cluster
*
* @return the default block size in bytes
*/
public long getDefaultBlockSize() {
return dfsClientConf.defaultBlockSize;
}
/**
* @see ClientProtocol#getPreferredBlockSize(String)
*/
public long getBlockSize(final String f) throws IOException {
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getPreferredBlockSize(f);
}
};
return (Long) doClientActionWithRetry(handler, "getBlockSize");
} catch (IOException ie) {
LOG.warn("Problem getting block size", ie);
throw ie;
}
}
/**
* Get server default values for a number of configuration params.
*
* @see ClientProtocol#getServerDefaults()
*/
public FsServerDefaults getServerDefaults() throws IOException {
long now = Time.now();
if (now - serverDefaultsLastUpdate > SERVER_DEFAULTS_VALIDITY_PERIOD) {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getServerDefaults();
}
};
serverDefaults = (FsServerDefaults) doClientActionWithRetry(handler,
"getServerDefaults");
serverDefaultsLastUpdate = now;
}
return serverDefaults;
}
/**
* Get a canonical token service name for this client's tokens. Null should
* be returned if the client is not using tokens.
*
* @return the token service for the client
*/
@InterfaceAudience.LimitedPrivate({"HDFS"})
public String getCanonicalServiceName() {
return (dtService != null) ? dtService.toString() : null;
}
/**
* @see ClientProtocol#getDelegationToken(Text)
*/
public Token<DelegationTokenIdentifier> getDelegationToken(final Text renewer)
throws IOException {
assert dtService != null;
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getDelegationToken(renewer);
}
};
Token<DelegationTokenIdentifier> token =
(Token<DelegationTokenIdentifier>) doClientActionWithRetry(handler,
"getDelegationToken");
token.setService(this.dtService);
LOG.info("Created " + DelegationTokenIdentifier.stringifyToken(token));
return token;
}
/**
* Renew a delegation token
*
* @param token
* the token to renew
* @return the new expiration time
* @throws InvalidToken
* @throws IOException
* @deprecated Use Token.renew instead.
*/
public long renewDelegationToken(Token<DelegationTokenIdentifier> token)
throws InvalidToken, IOException {
LOG.info("Renewing " + DelegationTokenIdentifier.stringifyToken(token));
try {
return token.renew(conf);
} catch (InterruptedException ie) {
throw new RuntimeException("caught interrupted", ie);
} catch (RemoteException re) {
throw re.unwrapRemoteException(InvalidToken.class,
AccessControlException.class);
}
}
/**
* Get {@link BlockReader} for short circuited local reads.
*/
static BlockReader getLocalBlockReader(Configuration conf, String src,
ExtendedBlock blk, Token<BlockTokenIdentifier> accessToken,
DatanodeInfo chosenNode, int socketTimeout, long offsetIntoBlock,
boolean connectToDnViaHostname) throws InvalidToken, IOException {
try {
return BlockReaderLocal
.newBlockReader(conf, src, blk, accessToken, chosenNode,
socketTimeout, offsetIntoBlock,
blk.getNumBytes() - offsetIntoBlock, connectToDnViaHostname);
} catch (RemoteException re) {
throw re.unwrapRemoteException(InvalidToken.class,
AccessControlException.class);
}
}
private static Map<String, Boolean> localAddrMap =
Collections.synchronizedMap(new HashMap<String, Boolean>());
private static boolean isLocalAddress(InetSocketAddress targetAddr) {
InetAddress addr = targetAddr.getAddress();
Boolean cached = localAddrMap.get(addr.getHostAddress());
if (cached != null) {
if (LOG.isTraceEnabled()) {
LOG.trace("Address " + targetAddr +
(cached ? " is local" : " is not local"));
}
return cached;
}
// Check if the address is any local or loop back
boolean local = addr.isAnyLocalAddress() || addr.isLoopbackAddress();
// Check if the address is defined on any interface
if (!local) {
try {
local = NetworkInterface.getByInetAddress(addr) != null;
} catch (SocketException e) {
local = false;
}
}
if (LOG.isTraceEnabled()) {
LOG.trace("Address " + targetAddr +
(local ? " is local" : " is not local"));
}
localAddrMap.put(addr.getHostAddress(), local);
return local;
}
/**
* Should the block access token be refetched on an exception
*
* @param ex
* Exception received
* @param targetAddr
* Target datanode address from where exception was received
* @return true if block access token has expired or invalid and it should be
* refetched
*/
private static boolean tokenRefetchNeeded(IOException ex,
InetSocketAddress targetAddr) {
/*
* Get a new access token and retry. Retry is needed in 2 cases. 1) When
* both NN and DN re-started while DFSClient holding a cached access token.
* 2) In the case that NN fails to update its access key at pre-set interval
* (by a wide margin) and subsequently restarts. In this case, DN
* re-registers itself with NN and receives a new access key, but DN will
* delete the old access key from its memory since it's considered expired
* based on the estimated expiration date.
*/
if (ex instanceof InvalidBlockTokenException ||
ex instanceof InvalidToken) {
LOG.info(
"Access token was invalid when connecting to " + targetAddr + " : " +
ex);
return true;
}
return false;
}
/**
* Cancel a delegation token
*
* @param token
* the token to cancel
* @throws InvalidToken
* @throws IOException
* @deprecated Use Token.cancel instead.
*/
public void cancelDelegationToken(Token<DelegationTokenIdentifier> token)
throws InvalidToken, IOException {
LOG.info("Cancelling " + DelegationTokenIdentifier.stringifyToken(token));
try {
token.cancel(conf);
} catch (InterruptedException ie) {
throw new RuntimeException("caught interrupted", ie);
} catch (RemoteException re) {
throw re.unwrapRemoteException(InvalidToken.class,
AccessControlException.class);
}
}
@InterfaceAudience.Private
public static class Renewer extends TokenRenewer {
static {
//Ensure that HDFS Configuration files are loaded before trying to use
// the renewer.
HdfsConfiguration.init();
}
@Override
public boolean handleKind(Text kind) {
return DelegationTokenIdentifier.HDFS_DELEGATION_KIND.equals(kind);
}
@SuppressWarnings("unchecked")
@Override
public long renew(Token<?> token, Configuration conf) throws IOException {
Token<DelegationTokenIdentifier> delToken =
(Token<DelegationTokenIdentifier>) token;
ClientProtocol nn = getNNProxy(delToken, conf);
try {
return nn.renewDelegationToken(delToken);
} catch (RemoteException re) {
throw re.unwrapRemoteException(InvalidToken.class,
AccessControlException.class);
}
}
@SuppressWarnings("unchecked")
@Override
public void cancel(Token<?> token, Configuration conf) throws IOException {
Token<DelegationTokenIdentifier> delToken =
(Token<DelegationTokenIdentifier>) token;
LOG.info(
"Cancelling " + DelegationTokenIdentifier.stringifyToken(delToken));
ClientProtocol nn = getNNProxy(delToken, conf);
try {
nn.cancelDelegationToken(delToken);
} catch (RemoteException re) {
throw re.unwrapRemoteException(InvalidToken.class,
AccessControlException.class);
}
}
private static ClientProtocol getNNProxy(
Token<DelegationTokenIdentifier> token, Configuration conf)
throws IOException {
URI uri = DFSUtil.getServiceUriFromToken(token);
NameNodeProxies.ProxyAndInfo<ClientProtocol> info =
NameNodeProxies.createProxy(conf, uri, ClientProtocol.class);
assert info.getDelegationTokenService().equals(token.getService()) :
"Returned service '" + info.getDelegationTokenService().toString() +
"' doesn't match expected service '" +
token.getService().toString() + "'";
return info.getProxy();
}
@Override
public boolean isManaged(Token<?> token) throws IOException {
return true;
}
}
/**
* Report corrupt blocks that were discovered by the client.
*
* @see ClientProtocol#reportBadBlocks(LocatedBlock[])
*/
public void reportBadBlocks(final LocatedBlock[] blocks) throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.reportBadBlocks(blocks);
return null;
}
};
doClientActionWithRetry(handler, "reportBadBlocks");
}
public short getDefaultReplication() {
return dfsClientConf.defaultReplication;
}
/*
* This is just a wrapper around callGetBlockLocations, but non-static so that
* we can stub it out for tests.
*/
@VisibleForTesting
public LocatedBlocks getLocatedBlocks(final String src, final long start,
final long length) throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return callGetBlockLocations(namenode, src, start, length);
}
};
return (LocatedBlocks) doClientActionWithRetry(handler, "getLocatedBlocks");
}
/**
* @see ClientProtocol#getBlockLocations(String, long, long)
*/
static LocatedBlocks callGetBlockLocations(ClientProtocol namenode,
String src, long start, long length) throws IOException {
try {
return namenode.getBlockLocations(src, start, length);
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, UnresolvedPathException.class);
}
}
/**
* @see ClientProtocol#getMissingBlockLocations(String)
*/
public LocatedBlocks getMissingLocatedBlocks(final String src)
throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return callGetMissingBlockLocations(namenode, src);
}
};
return (LocatedBlocks) doClientActionWithRetry(handler,
"getMissingLocatedBlocks");
}
static LocatedBlocks callGetMissingBlockLocations(ClientProtocol namenode,
String filePath) throws IOException {
try {
return namenode.getMissingBlockLocations(filePath);
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, UnresolvedPathException.class);
}
}
/**
* @see ClientProtocol#addBlockChecksum(String, int, long)
*/
public void addBlockChecksum(final String src, final int blockIndex,
final long checksum) throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode) throws IOException {
try {
namenode.addBlockChecksum(src, blockIndex, checksum);
} catch (RemoteException re) {
throw re.unwrapRemoteException();
}
return null;
}
};
doClientActionWithRetry(handler, "addBlockChecksum");
}
/**
* @see ClientProtocol#getBlockChecksum(String, int)
*/
public long getBlockChecksum(final String filePath, final int blockIndex)
throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode) throws IOException {
try {
return namenode.getBlockChecksum(filePath, blockIndex);
} catch (RemoteException e) {
throw e.unwrapRemoteException();
}
}
};
return (Long) doClientActionWithRetry(handler, "getBlockChecksum");
}
/**
* Recover a file's lease
*
* @param src
* a file's path
* @return true if the file is already closed
* @throws IOException
*/
boolean recoverLease(final String src) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.recoverLease(src, clientName);
}
};
return (Boolean) doClientActionWithRetry(handler, "recoverLease");
} catch (RemoteException re) {
throw re.unwrapRemoteException(FileNotFoundException.class,
AccessControlException.class);
}
}
/**
* Get block location info about file
* <p/>
* getBlockLocations() returns a list of hostnames that store
* data for a specific file region. It returns a set of hostnames
* for every block within the indicated region.
* <p/>
* This function is very useful when writing code that considers
* data-placement when performing operations. For example, the
* MapReduce system tries to schedule tasks on the same machines
* as the data-block the task processes.
*/
public BlockLocation[] getBlockLocations(String src, long start, long length)
throws IOException, UnresolvedLinkException {
LocatedBlocks blocks = getLocatedBlocks(src, start, length);
BlockLocation[] locations = DFSUtil.locatedBlocks2Locations(blocks);
HdfsBlockLocation[] hdfsLocations = new HdfsBlockLocation[locations.length];
for (int i = 0; i < locations.length; i++) {
hdfsLocations[i] = new HdfsBlockLocation(locations[i], blocks.get(i));
}
return hdfsLocations;
}
/**
* Get block location information about a list of {@link HdfsBlockLocation}.
* Used by {@link DistributedFileSystem#getFileBlockStorageLocations(List)}
* to
* get {@link BlockStorageLocation}s for blocks returned by
* {@link DistributedFileSystem#getFileBlockLocations(org.apache.hadoop.fs.FileStatus,
* long, long)}
* .
* <p/>
* This is done by making a round of RPCs to the associated datanodes, asking
* the volume of each block replica. The returned array of
* {@link BlockStorageLocation} expose this information as a
* {@link VolumeId}.
*
* @param blockLocations
* target blocks on which to query volume location information
* @return volumeBlockLocations original block array augmented with additional
* volume location information for each replica.
*/
public BlockStorageLocation[] getBlockStorageLocations(
List<BlockLocation> blockLocations)
throws IOException, UnsupportedOperationException,
InvalidBlockTokenException {
if (!getConf().getHdfsBlocksMetadataEnabled) {
throw new UnsupportedOperationException("Datanode-side support for " +
"getVolumeBlockLocations() must also be enabled in the client " +
"configuration.");
}
// Downcast blockLocations and fetch out required LocatedBlock(s)
List<LocatedBlock> blocks = new ArrayList<LocatedBlock>();
for (BlockLocation loc : blockLocations) {
if (!(loc instanceof HdfsBlockLocation)) {
throw new ClassCastException("DFSClient#getVolumeBlockLocations " +
"expected to be passed HdfsBlockLocations");
}
HdfsBlockLocation hdfsLoc = (HdfsBlockLocation) loc;
blocks.add(hdfsLoc.getLocatedBlock());
}
// Re-group the LocatedBlocks to be grouped by datanodes, with the values
// a list of the LocatedBlocks on the datanode.
Map<DatanodeInfo, List<LocatedBlock>> datanodeBlocks =
new LinkedHashMap<DatanodeInfo, List<LocatedBlock>>();
for (LocatedBlock b : blocks) {
for (DatanodeInfo info : b.getLocations()) {
if (!datanodeBlocks.containsKey(info)) {
datanodeBlocks.put(info, new ArrayList<LocatedBlock>());
}
List<LocatedBlock> l = datanodeBlocks.get(info);
l.add(b);
}
}
// Make RPCs to the datanodes to get volume locations for its replicas
List<HdfsBlocksMetadata> metadatas = BlockStorageLocationUtil
.queryDatanodesForHdfsBlocksMetadata(conf, datanodeBlocks,
getConf().getFileBlockStorageLocationsNumThreads,
getConf().getFileBlockStorageLocationsTimeout,
getConf().connectToDnViaHostname);
// Regroup the returned VolumeId metadata to again be grouped by
// LocatedBlock rather than by datanode
Map<LocatedBlock, List<VolumeId>> blockVolumeIds = BlockStorageLocationUtil
.associateVolumeIdsWithBlocks(blocks, datanodeBlocks, metadatas);
// Combine original BlockLocations with new VolumeId information
BlockStorageLocation[] volumeBlockLocations = BlockStorageLocationUtil
.convertToVolumeBlockLocations(blocks, blockVolumeIds);
return volumeBlockLocations;
}
public DFSInputStream open(String src)
throws IOException, UnresolvedLinkException {
return open(src, dfsClientConf.ioBufferSize, true, null);
}
/**
* Create an input stream that obtains a nodelist from the
* namenode, and then reads from all the right places. Creates
* inner subclass of InputStream that does the right out-of-band
* work.
*
* @deprecated Use {@link #open(String, int, boolean)} instead.
*/
@Deprecated
public DFSInputStream open(String src, int buffersize, boolean verifyChecksum,
FileSystem.Statistics stats) throws IOException, UnresolvedLinkException {
return open(src, buffersize, verifyChecksum);
}
/**
* Create an input stream that obtains a nodelist from the
* namenode, and then reads from all the right places. Creates
* inner subclass of InputStream that does the right out-of-band
* work.
*/
public DFSInputStream open(String src, int buffersize, boolean verifyChecksum)
throws IOException, UnresolvedLinkException {
checkOpen();
// Get block info from namenode
return new DFSInputStream(this, src, buffersize, verifyChecksum);
}
/**
* Get the namenode associated with this DFSClient object
*
* @return the namenode associated with this DFSClient object
*/
public ClientProtocol getNamenode() throws IOException {
return namenodeSelector.getNextNamenode().getRPCHandle();
}
/**
* Call {@link #create(String, boolean, short, long, Progressable)} with
* default <code>replication</code> and <code>blockSize<code> and null <code>
* progress</code>.
*/
public OutputStream create(String src, boolean overwrite) throws IOException {
return create(src, overwrite, dfsClientConf.defaultReplication,
dfsClientConf.defaultBlockSize, null);
}
/**
* Call {@link #create(String, boolean, short, long, Progressable)} with
* default <code>replication</code> and <code>blockSize<code>.
*/
public OutputStream create(String src, boolean overwrite,
Progressable progress) throws IOException {
return create(src, overwrite, dfsClientConf.defaultReplication,
dfsClientConf.defaultBlockSize, progress);
}
/**
* Call {@link #create(String, boolean, short, long, Progressable)} with
* null <code>progress</code>.
*/
public OutputStream create(String src, boolean overwrite, short replication,
long blockSize) throws IOException {
return create(src, overwrite, replication, blockSize, null);
}
/**
* Call {@link #create(String, boolean, short, long, Progressable, int)}
* with default bufferSize.
*/
public OutputStream create(String src, boolean overwrite, short replication,
long blockSize, Progressable progress) throws IOException {
return create(src, overwrite, replication, blockSize, progress,
dfsClientConf.ioBufferSize);
}
/**
* Call {@link #create(String, FsPermission, EnumSet, short, long,
* Progressable, int, ChecksumOpt)} with default <code>permission</code>
* {@link FsPermission#getFileDefault()}.
*
* @param src
* File name
* @param overwrite
* overwrite an existing file if true
* @param replication
* replication factor for the file
* @param blockSize
* maximum block size
* @param progress
* interface for reporting client progress
* @param buffersize
* underlying buffersize
* @return output stream
*/
public OutputStream create(String src, boolean overwrite, short replication,
long blockSize, Progressable progress, int buffersize)
throws IOException {
return create(src, FsPermission.getFileDefault(),
overwrite ? EnumSet.of(CreateFlag.CREATE, CreateFlag.OVERWRITE) :
EnumSet.of(CreateFlag.CREATE), replication, blockSize, progress,
buffersize, null);
}
/**
* Call {@link #create(String, FsPermission, EnumSet, boolean, short,
* long, Progressable, int, ChecksumOpt)} with <code>createParent</code>
* set to true.
*/
public DFSOutputStream create(String src, FsPermission permission,
EnumSet<CreateFlag> flag, short replication, long blockSize,
Progressable progress, int buffersize, ChecksumOpt checksumOpt)
throws IOException {
return create(src, permission, flag, true, replication, blockSize, progress,
buffersize, checksumOpt);
}
/**
* Call {@link #create(String, FsPermission, EnumSet, boolean, short,
* long, Progressable, int, ChecksumOpt)} with <code>createParent</code>
* set to true.
*/
public DFSOutputStream create(String src, FsPermission permission,
EnumSet<CreateFlag> flag, short replication, long blockSize,
Progressable progress, int buffersize, ChecksumOpt checksumOpt,
EncodingPolicy policy) throws IOException {
return create(src, permission, flag, true, replication, blockSize, progress,
buffersize, checksumOpt, policy);
}
/**
* Create a new dfs file with the specified block replication
* with write-progress reporting and return an output stream for writing
* into the file.
*
* @param src
* File name
* @param permission
* The permission of the directory being created.
* If null, use default permission {@link FsPermission#getFileDefault()}
* @param flag
* indicates create a new file or create/overwrite an
* existing file or append to an existing file
* @param createParent
* create missing parent directory if true
* @param replication
* block replication
* @param blockSize
* maximum block size
* @param progress
* interface for reporting client progress
* @param buffersize
* underlying buffer size
* @param checksumOpt
* checksum options
* @return output stream
* @see ClientProtocol#create(String, FsPermission, String, EnumSetWritable,
* boolean, short, long) for detailed description of exceptions thrown
*/
public DFSOutputStream create(String src, FsPermission permission,
EnumSet<CreateFlag> flag, boolean createParent, short replication,
long blockSize, Progressable progress, int buffersize,
ChecksumOpt checksumOpt) throws IOException {
return create(src, permission, flag, createParent, replication, blockSize,
progress, buffersize, checksumOpt, null);
}
/**
* Create a new dfs file with the specified block replication
* with write-progress reporting and return an output stream for writing
* into the file.
*
* @param src
* File name
* @param permission
* The permission of the directory being created.
* If null, use default permission {@link FsPermission#getFileDefault()}
* @param flag
* indicates create a new file or create/overwrite an
* existing file or append to an existing file
* @param createParent
* create missing parent directory if true
* @param replication
* block replication
* @param blockSize
* maximum block size
* @param progress
* interface for reporting client progress
* @param buffersize
* underlying buffer size
* @param checksumOpt
* checksum options
* @return output stream
* @see ClientProtocol#create(String, FsPermission, String, EnumSetWritable,
* boolean, short, long) for detailed description of exceptions thrown
*/
public DFSOutputStream create(String src, FsPermission permission,
EnumSet<CreateFlag> flag, boolean createParent, short replication,
long blockSize, Progressable progress, int buffersize,
ChecksumOpt checksumOpt, EncodingPolicy policy) throws IOException {
checkOpen();
if (permission == null) {
permission = FsPermission.getFileDefault();
}
FsPermission masked = permission.applyUMask(dfsClientConf.uMask);
if (LOG.isDebugEnabled()) {
LOG.debug(src + ": masked=" + masked);
}
final DFSOutputStream result = DFSOutputStream
.newStreamForCreate(this, src, masked, flag, createParent, replication,
blockSize, progress, buffersize,
dfsClientConf.createChecksum(checksumOpt), policy);
beginFileLease(src, result);
return result;
}
public DFSOutputStream sendBlock(String src, LocatedBlock block,
Progressable progress, ChecksumOpt checksumOpt) throws IOException {
checkOpen();
final DFSOutputStream result = DFSOutputStream
.newStreamForSingleBlock(this, src, dfsClientConf.ioBufferSize,
progress, block, dfsClientConf.createChecksum(checksumOpt));
return result;
}
/**
* Append to an existing file if {@link CreateFlag#APPEND} is present
*/
private DFSOutputStream primitiveAppend(String src, EnumSet<CreateFlag> flag,
int buffersize, Progressable progress) throws IOException {
if (flag.contains(CreateFlag.APPEND)) {
HdfsFileStatus stat = getFileInfo(src);
if (stat == null) { // No file to append to
// New file needs to be created if create option is present
if (!flag.contains(CreateFlag.CREATE)) {
throw new FileNotFoundException(
"failed to append to non-existent file " + src + " on client " +
clientName);
}
return null;
}
return callAppend(stat, src, buffersize, progress);
}
return null;
}
/**
* Same as {{@link #create(String, FsPermission, EnumSet, short, long,
* Progressable, int, ChecksumOpt)} except that the permission
* is absolute (ie has already been masked with umask.
*/
public DFSOutputStream primitiveCreate(String src, FsPermission absPermission,
EnumSet<CreateFlag> flag, boolean createParent, short replication,
long blockSize, Progressable progress, int buffersize,
ChecksumOpt checksumOpt) throws IOException, UnresolvedLinkException {
checkOpen();
CreateFlag.validate(flag);
DFSOutputStream result = primitiveAppend(src, flag, buffersize, progress);
if (result == null) {
DataChecksum checksum = dfsClientConf.createChecksum(checksumOpt);
result = DFSOutputStream
.newStreamForCreate(this, src, absPermission, flag, createParent,
replication, blockSize, progress, buffersize, checksum);
}
beginFileLease(src, result);
return result;
}
/**
* Creates a symbolic link.
*
* @see ClientProtocol#createSymlink(String, String, FsPermission, boolean)
*/
public void createSymlink(final String target, final String link,
final boolean createParent) throws IOException {
try {
final FsPermission dirPerm =
FsPermission.getDefault().applyUMask(dfsClientConf.uMask);
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.createSymlink(target, link, dirPerm, createParent);
return null;
}
};
doClientActionWithRetry(handler, "createSymlink");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileAlreadyExistsException.class, FileNotFoundException.class,
ParentNotDirectoryException.class, NSQuotaExceededException.class,
DSQuotaExceededException.class, UnresolvedPathException.class);
}
}
/**
* Resolve the *first* symlink, if any, in the path.
*
* @see ClientProtocol#getLinkTarget(String)
*/
public String getLinkTarget(final String path) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getLinkTarget(path);
}
};
return (String) doClientActionWithRetry(handler, "getLinkTarget");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class);
}
}
/**
* Method to get stream returned by append call
*/
private DFSOutputStream callAppend(HdfsFileStatus stat, final String src,
int buffersize, Progressable progress) throws IOException {
LocatedBlock lastBlock = null;
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.append(src, clientName);
}
};
lastBlock = (LocatedBlock) doClientActionWithRetry(handler, "callAppend");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, SafeModeException.class,
DSQuotaExceededException.class, UnsupportedOperationException.class,
UnresolvedPathException.class);
}
return DFSOutputStream
.newStreamForAppend(this, src, buffersize, progress, lastBlock, stat,
dfsClientConf.createChecksum());
}
/**
* Append to an existing HDFS file.
*
* @param src
* file name
* @param buffersize
* buffer size
* @param progress
* for reporting write-progress; null is acceptable.
* @param statistics
* file system statistics; null is acceptable.
* @return an output stream for writing into the file
* @see ClientProtocol#append(String, String)
*/
public HdfsDataOutputStream append(final String src, final int buffersize,
final Progressable progress, final FileSystem.Statistics statistics)
throws IOException {
final DFSOutputStream out = append(src, buffersize, progress);
return new HdfsDataOutputStream(out, statistics, out.getInitialLen());
}
private DFSOutputStream append(String src, int buffersize,
Progressable progress) throws IOException {
checkOpen();
HdfsFileStatus stat = getFileInfo(src);
if (stat == null) { // No file found
throw new FileNotFoundException(
"failed to append to non-existent file " + src + " on client " +
clientName);
}
final DFSOutputStream result = callAppend(stat, src, buffersize, progress);
beginFileLease(src, result);
return result;
}
/**
* Set replication for an existing file.
*
* @param src
* file name
* @param replication
* @see ClientProtocol#setReplication(String, short)
*/
public boolean setReplication(final String src, final short replication)
throws IOException {
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.setReplication(src, replication);
}
};
return (Boolean) doClientActionWithRetry(handler, "setReplication");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, SafeModeException.class,
DSQuotaExceededException.class, UnresolvedPathException.class);
}
}
public void setMetaEnabled(final String src, final boolean metaEnabled)
throws IOException {
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.setMetaEnabled(src, metaEnabled);
return null;
}
};
doClientActionWithRetry(handler, "setMetaEnabled");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, SafeModeException.class,
UnresolvedPathException.class);
}
}
/**
* Rename file or directory.
*
* @see ClientProtocol#rename(String, String)
* @deprecated Use {@link #rename(String, String, Options.Rename...)} instead.
*/
@Deprecated
public boolean rename(final String src, final String dst) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.rename(src, dst);
}
};
return (Boolean) doClientActionWithRetry(handler, "rename");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
NSQuotaExceededException.class, DSQuotaExceededException.class,
UnresolvedPathException.class);
}
}
/**
* Move blocks from src to trg and delete src
* See {@link ClientProtocol#concat(String, String[])}.
*/
public void concat(final String trg, final String[] srcs) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.concat(trg, srcs);
return null;
}
};
doClientActionWithRetry(handler, "concat");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
UnresolvedPathException.class);
}
}
/**
* Rename file or directory.
*
* @see ClientProtocol#rename2(String, String, Options.Rename...)
*/
public void rename(final String src, final String dst,
final Options.Rename... options) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.rename2(src, dst, options);
return null;
}
};
doClientActionWithRetry(handler, "rename");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
DSQuotaExceededException.class, FileAlreadyExistsException.class,
FileNotFoundException.class, ParentNotDirectoryException.class,
SafeModeException.class, NSQuotaExceededException.class,
UnresolvedPathException.class);
}
}
/**
* Delete file or directory.
* See {@link ClientProtocol#delete(String, boolean)}.
*/
@Deprecated
public boolean delete(final String src) throws IOException {
checkOpen();
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.delete(src, true);
}
};
return (Boolean) doClientActionWithRetry(handler, "delete");
}
/**
* delete file or directory.
* delete contents of the directory if non empty and recursive
* set to true
*
* @see ClientProtocol#delete(String, boolean)
*/
public boolean delete(final String src, final boolean recursive)
throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.delete(src, recursive);
}
};
return (Boolean) doClientActionWithRetry(handler, "delete");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, SafeModeException.class,
UnresolvedPathException.class);
}
}
/**
* Implemented using getFileInfo(src)
*/
public boolean exists(String src) throws IOException {
checkOpen();
return getFileInfo(src) != null;
}
/**
* Get a partial listing of the indicated directory
* No block locations need to be fetched
*/
public DirectoryListing listPaths(String src, byte[] startAfter)
throws IOException {
return listPaths(src, startAfter, false);
}
/**
* Get a partial listing of the indicated directory
* <p/>
* Recommend to use HdfsFileStatus.EMPTY_NAME as startAfter
* if the application wants to fetch a listing starting from
* the first entry in the directory
*
* @see ClientProtocol#getListing(String, byte[], boolean)
*/
public DirectoryListing listPaths(final String src, final byte[] startAfter,
final boolean needLocation) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getListing(src, startAfter, needLocation);
}
};
return (DirectoryListing) doClientActionWithRetry(handler, "listPaths");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, UnresolvedPathException.class);
}
}
/**
* Get the file info for a specific file or directory.
*
* @param src
* The string representation of the path to the file
* @return object containing information regarding the file
* or null if file not found
* @see ClientProtocol#getFileInfo(String) for description of exceptions
*/
public HdfsFileStatus getFileInfo(final String src) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getFileInfo(src);
}
};
return (HdfsFileStatus) doClientActionWithRetry(handler, "getFileInfo");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, UnresolvedPathException.class);
}
}
/**
* Get the file info for a specific file or directory. If src
* refers to a symlink then the FileStatus of the link is returned.
*
* @param src
* path to a file or directory.
* <p/>
* For description of exceptions thrown
* @see ClientProtocol#getFileLinkInfo(String)
*/
public HdfsFileStatus getFileLinkInfo(final String src) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getFileLinkInfo(src);
}
};
return (HdfsFileStatus) doClientActionWithRetry(handler,
"getFileLinkInfo");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
UnresolvedPathException.class);
}
}
/**
* Get the checksum of a file.
*
* @param src
* The file path
* @return The checksum
* @see DistributedFileSystem#getFileChecksum(Path)
*/
public MD5MD5CRC32FileChecksum getFileChecksum(String src)
throws IOException {
checkOpen();
return getFileChecksum(src, clientName, /*HOPnamenode*/
namenodeSelector.getNextNamenode().getRPCHandle(), socketFactory,
dfsClientConf.socketTimeout, getDataEncryptionKey(),
dfsClientConf.connectToDnViaHostname);
}
@InterfaceAudience.Private
public void clearDataEncryptionKey() {
LOG.debug("Clearing encryption key");
synchronized (this) {
encryptionKey = null;
}
}
/**
* @return true if data sent between this client and DNs should be encrypted,
* false otherwise.
* @throws IOException
* in the event of error communicating with the NN
*/
boolean shouldEncryptData() throws IOException {
FsServerDefaults d = getServerDefaults();
return d == null ? false : d.getEncryptDataTransfer();
}
@InterfaceAudience.Private
public DataEncryptionKey getDataEncryptionKey() throws IOException {
if (shouldEncryptData()) {
synchronized (this) {
if (encryptionKey == null || encryptionKey.expiryDate < Time.now()) {
LOG.debug("Getting new encryption token from NN");
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getDataEncryptionKey();
}
};
encryptionKey = (DataEncryptionKey) doClientActionWithRetry(handler,
"getDataEncryptionKey");
}
return encryptionKey;
}
} else {
return null;
}
}
/**
* Get the checksum of a file.
*
* @param src
* The file path
* @param clientName
* the name of the client requesting the checksum.
* @param namenode
* the RPC proxy for the namenode
* @param socketFactory
* to create sockets to connect to DNs
* @param socketTimeout
* timeout to use when connecting and waiting for a response
* @param encryptionKey
* the key needed to communicate with DNs in this cluster
* @param connectToDnViaHostname
* {@see #connectToDnViaHostname()}
* @return The checksum
*/
static MD5MD5CRC32FileChecksum getFileChecksum(String src, String clientName,
ClientProtocol namenode, SocketFactory socketFactory, int socketTimeout,
DataEncryptionKey encryptionKey, boolean connectToDnViaHostname)
throws IOException {
//get all block locations
LocatedBlocks blockLocations =
callGetBlockLocations(namenode, src, 0, Long.MAX_VALUE);
if (null == blockLocations) {
throw new FileNotFoundException("File does not exist: " + src);
}
List<LocatedBlock> locatedblocks = blockLocations.getLocatedBlocks();
final DataOutputBuffer md5out = new DataOutputBuffer();
int bytesPerCRC = -1;
DataChecksum.Type crcType = DataChecksum.Type.DEFAULT;
long crcPerBlock = 0;
boolean refetchBlocks = false;
int lastRetriedIndex = -1;
//get block checksum for each block
for (int i = 0; i < locatedblocks.size(); i++) {
if (refetchBlocks) { // refetch to get fresh tokens
blockLocations =
callGetBlockLocations(namenode, src, 0, Long.MAX_VALUE);
if (null == blockLocations) {
throw new FileNotFoundException("File does not exist: " + src);
}
locatedblocks = blockLocations.getLocatedBlocks();
refetchBlocks = false;
}
LocatedBlock lb = locatedblocks.get(i);
final ExtendedBlock block = lb.getBlock();
final DatanodeInfo[] datanodes = lb.getLocations();
//try each datanode location of the block
final int timeout = 3000 * datanodes.length + socketTimeout;
boolean done = false;
for (int j = 0; !done && j < datanodes.length; j++) {
DataOutputStream out = null;
DataInputStream in = null;
try {
//connect to a datanode
IOStreamPair pair =
connectToDN(socketFactory, connectToDnViaHostname, encryptionKey,
datanodes[j], timeout);
out = new DataOutputStream(new BufferedOutputStream(pair.out,
HdfsConstants.SMALL_BUFFER_SIZE));
in = new DataInputStream(pair.in);
if (LOG.isDebugEnabled()) {
LOG.debug("write to " + datanodes[j] + ": " + Op.BLOCK_CHECKSUM +
", block=" + block);
}
// get block MD5
new Sender(out).blockChecksum(block, lb.getBlockToken());
final BlockOpResponseProto reply =
BlockOpResponseProto.parseFrom(PBHelper.vintPrefixed(in));
if (reply.getStatus() != Status.SUCCESS) {
if (reply.getStatus() == Status.ERROR_ACCESS_TOKEN) {
throw new InvalidBlockTokenException();
} else {
throw new IOException(
"Bad response " + reply + " for block " + block +
" from datanode " + datanodes[j]);
}
}
OpBlockChecksumResponseProto checksumData =
reply.getChecksumResponse();
//read byte-per-checksum
final int bpc = checksumData.getBytesPerCrc();
if (i == 0) { //first block
bytesPerCRC = bpc;
} else if (bpc != bytesPerCRC) {
throw new IOException("Byte-per-checksum not matched: bpc=" + bpc +
" but bytesPerCRC=" + bytesPerCRC);
}
//read crc-per-block
final long cpb = checksumData.getCrcPerBlock();
if (locatedblocks.size() > 1 && i == 0) {
crcPerBlock = cpb;
}
//read md5
final MD5Hash md5 = new MD5Hash(checksumData.getMd5().toByteArray());
md5.write(md5out);
// read crc-type
final DataChecksum.Type ct;
if (checksumData.hasCrcType()) {
ct = PBHelper.convert(checksumData.getCrcType());
} else {
LOG.debug("Retrieving checksum from an earlier-version DataNode: " +
"inferring checksum by reading first byte");
ct = inferChecksumTypeByReading(clientName, socketFactory,
socketTimeout, lb, datanodes[j], encryptionKey,
connectToDnViaHostname);
}
if (i == 0) { // first block
crcType = ct;
} else if (crcType != DataChecksum.Type.MIXED && crcType != ct) {
// if crc types are mixed in a file
crcType = DataChecksum.Type.MIXED;
}
done = true;
if (LOG.isDebugEnabled()) {
if (i == 0) {
LOG.debug("set bytesPerCRC=" + bytesPerCRC + ", crcPerBlock=" +
crcPerBlock);
}
LOG.debug("got reply from " + datanodes[j] + ": md5=" + md5);
}
} catch (InvalidBlockTokenException ibte) {
if (i > lastRetriedIndex) {
if (LOG.isDebugEnabled()) {
LOG.debug(
"Got access token error in response to OP_BLOCK_CHECKSUM " +
"for file " + src + " for block " + block +
" from datanode " + datanodes[j] +
". Will retry the block once.");
}
lastRetriedIndex = i;
done = true; // actually it's not done; but we'll retry
i--; // repeat at i-th block
refetchBlocks = true;
break;
}
} catch (IOException ie) {
LOG.warn("src=" + src + ", datanodes[" + j + "]=" + datanodes[j], ie);
} finally {
IOUtils.closeStream(in);
IOUtils.closeStream(out);
}
}
if (!done) {
throw new IOException("Fail to get block MD5 for " + block);
}
}
//compute file MD5
final MD5Hash fileMD5 = MD5Hash.digest(md5out.getData());
switch (crcType) {
case CRC32:
return new MD5MD5CRC32GzipFileChecksum(bytesPerCRC, crcPerBlock,
fileMD5);
case CRC32C:
return new MD5MD5CRC32CastagnoliFileChecksum(bytesPerCRC, crcPerBlock,
fileMD5);
default:
// If there is no block allocated for the file,
// return one with the magic entry that matches what previous
// hdfs versions return.
if (locatedblocks.size() == 0) {
return new MD5MD5CRC32GzipFileChecksum(0, 0, fileMD5);
}
// we should never get here since the validity was checked
// when getCrcType() was called above.
return null;
}
}
/**
* Connect to the given datanode's datantrasfer port, and return
* the resulting IOStreamPair. This includes encryption wrapping, etc.
*/
private static IOStreamPair connectToDN(SocketFactory socketFactory,
boolean connectToDnViaHostname, DataEncryptionKey encryptionKey,
DatanodeInfo dn, int timeout) throws IOException {
boolean success = false;
Socket sock = null;
try {
sock = socketFactory.createSocket();
String dnAddr = dn.getXferAddr(connectToDnViaHostname);
if (LOG.isDebugEnabled()) {
LOG.debug("Connecting to datanode " + dnAddr);
}
NetUtils.connect(sock, NetUtils.createSocketAddr(dnAddr), timeout);
sock.setSoTimeout(timeout);
OutputStream unbufOut = NetUtils.getOutputStream(sock);
InputStream unbufIn = NetUtils.getInputStream(sock);
IOStreamPair ret;
if (encryptionKey != null) {
ret = DataTransferEncryptor
.getEncryptedStreams(unbufOut, unbufIn, encryptionKey);
} else {
ret = new IOStreamPair(unbufIn, unbufOut);
}
success = true;
return ret;
} finally {
if (!success) {
IOUtils.closeSocket(sock);
}
}
}
/**
* Infer the checksum type for a replica by sending an OP_READ_BLOCK
* for the first byte of that replica. This is used for compatibility
* with older HDFS versions which did not include the checksum type in
* OpBlockChecksumResponseProto.
*
* @param lb
* the located block
* @param clientName
* the name of the DFSClient requesting the checksum
* @param dn
* the connected datanode
* @return the inferred checksum type
* @throws IOException
* if an error occurs
*/
private static Type inferChecksumTypeByReading(String clientName,
SocketFactory socketFactory, int socketTimeout, LocatedBlock lb,
DatanodeInfo dn, DataEncryptionKey encryptionKey,
boolean connectToDnViaHostname) throws IOException {
IOStreamPair pair =
connectToDN(socketFactory, connectToDnViaHostname, encryptionKey, dn,
socketTimeout);
try {
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(pair.out, HdfsConstants.SMALL_BUFFER_SIZE));
DataInputStream in = new DataInputStream(pair.in);
new Sender(out)
.readBlock(lb.getBlock(), lb.getBlockToken(), clientName, 0, 1, true);
final BlockOpResponseProto reply =
BlockOpResponseProto.parseFrom(PBHelper.vintPrefixed(in));
if (reply.getStatus() != Status.SUCCESS) {
if (reply.getStatus() == Status.ERROR_ACCESS_TOKEN) {
throw new InvalidBlockTokenException();
} else {
throw new IOException(
"Bad response " + reply + " trying to read " + lb.getBlock() +
" from datanode " + dn);
}
}
return PBHelper
.convert(reply.getReadOpChecksumInfo().getChecksum().getType());
} finally {
IOUtils.cleanup(null, pair.in, pair.out);
}
}
/**
* Set permissions to a file or directory.
*
* @param src
* path name.
* @param permission
* @see ClientProtocol#setPermission(String, FsPermission)
*/
public void setPermission(final String src, final FsPermission permission)
throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.setPermission(src, permission);
return null;
}
};
doClientActionWithRetry(handler, "setPermission");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, SafeModeException.class,
UnresolvedPathException.class);
}
}
/**
* Set file or directory owner.
*
* @param src
* path name.
* @param username
* user id.
* @param groupname
* user group.
* @see ClientProtocol#setOwner(String, String, String)
*/
public void setOwner(final String src, final String username,
final String groupname) throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.setOwner(src, username, groupname);
return null;
}
};
doClientActionWithRetry(handler, "setOwner");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, SafeModeException.class,
UnresolvedPathException.class);
}
}
/**
* @see ClientProtocol#getStats()
*/
public FsStatus getDiskStatus() throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getStats();
}
};
long rawNums[] = (long[]) doClientActionWithRetry(handler, "getDiskStatus");
return new FsStatus(rawNums[0], rawNums[1], rawNums[2]);
}
/**
* Returns count of blocks with no good replicas left. Normally should be
* zero.
*
* @throws IOException
*/
public long getMissingBlocksCount() throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getStats()[ClientProtocol.GET_STATS_MISSING_BLOCKS_IDX];
}
};
return (Long) doClientActionWithRetry(handler, "getMissingBlocksCount");
}
/**
* Returns count of blocks with one of more replica missing.
*
* @throws IOException
*/
public long getUnderReplicatedBlocksCount() throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode
.getStats()[ClientProtocol.GET_STATS_UNDER_REPLICATED_IDX];
}
};
return (Long) doClientActionWithRetry(handler,
"getUnderReplicatedBlocksCount");
}
/**
* Returns count of blocks with at least one replica marked corrupt.
*
* @throws IOException
*/
public long getCorruptBlocksCount() throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getStats()[ClientProtocol.GET_STATS_CORRUPT_BLOCKS_IDX];
}
};
return (Long) doClientActionWithRetry(handler, "getCorruptBlocksCount");
}
/**
* @return a list in which each entry describes a corrupt file/block
* @throws IOException
*/
public CorruptFileBlocks listCorruptFileBlocks(final String path,
final String cookie) throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.listCorruptFileBlocks(path, cookie);
}
};
return (CorruptFileBlocks) doClientActionWithRetry(handler,
"listCorruptFileBlocks");
}
public DatanodeInfo[] datanodeReport(final DatanodeReportType type)
throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getDatanodeReport(type);
}
};
return (DatanodeInfo[]) doClientActionWithRetry(handler, "datanodeReport");
}
/**
* Enter, leave or get safe mode.
*
* @see ClientProtocol#setSafeMode(HdfsConstants.SafeModeAction, boolean)
*/
public boolean setSafeMode(SafeModeAction action) throws IOException {
return setSafeMode(action, false);
}
/**
* Enter, leave or get safe mode.
*
* @param action
* One of SafeModeAction.GET, SafeModeAction.ENTER and
* SafeModeActiob.LEAVE
* @param isChecked
* If true, then check only active namenode's safemode status, else
* check first namenode's status.
* @see ClientProtocol#setSafeMode(HdfsConstants.SafeModeAction, boolean)
*/
public boolean setSafeMode(final SafeModeAction action,
final boolean isChecked) throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.setSafeMode(action, isChecked);
}
};
return (Boolean) doClientActionWithRetry(handler, "setSafeMode");
}
/**
* Refresh the hosts and exclude files. (Rereads them.)
* See {@link ClientProtocol#refreshNodes()}
* for more details.
*
* @see ClientProtocol#refreshNodes()
*/
public void refreshNodes() throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.refreshNodes();
return null;
}
};
doClientActionWithRetry(handler, "refreshNodes");
}
/**
* Requests the namenode to tell all datanodes to use a new, non-persistent
* bandwidth value for dfs.balance.bandwidthPerSec.
* See {@link ClientProtocol#setBalancerBandwidth(long)}
* for more details.
*
* @see ClientProtocol#setBalancerBandwidth(long)
*/
public void setBalancerBandwidth(final long bandwidth) throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.setBalancerBandwidth(bandwidth);
return null;
}
};
doClientActionWithRetry(handler, "setBalancerBandwidth");
}
/**
*/
@Deprecated
public boolean mkdirs(String src) throws IOException {
return mkdirs(src, null, true);
}
/**
* Create a directory (or hierarchy of directories) with the given
* name and permission.
*
* @param src
* The path of the directory being created
* @param permission
* The permission of the directory being created.
* If permission == null, use {@link FsPermission#getDefault()}.
* @param createParent
* create missing parent directory if true
* @return True if the operation success.
* @see ClientProtocol#mkdirs(String, FsPermission, boolean)
*/
public boolean mkdirs(String src, FsPermission permission,
boolean createParent) throws IOException {
if (permission == null) {
permission = FsPermission.getDefault();
}
FsPermission masked = permission.applyUMask(dfsClientConf.uMask);
return primitiveMkdir(src, masked, createParent);
}
/**
* Same {{@link #mkdirs(String, FsPermission, boolean)} except
* that the permissions has already been masked against umask.
*/
public boolean primitiveMkdir(String src, FsPermission absPermission)
throws IOException {
return primitiveMkdir(src, absPermission, true);
}
/**
* Same {{@link #mkdirs(String, FsPermission, boolean)} except
* that the permissions has already been masked against umask.
*/
public boolean primitiveMkdir(final String src, FsPermission absPermission,
final boolean createParent) throws IOException {
checkOpen();
final FsPermission finalPermission;
if (absPermission == null) {
finalPermission =
FsPermission.getDefault().applyUMask(dfsClientConf.uMask);
} else {
finalPermission = absPermission;
}
if (LOG.isDebugEnabled()) {
LOG.debug(src + ": masked=" + finalPermission);
}
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.mkdirs(src, finalPermission, createParent);
}
};
return (Boolean) doClientActionWithRetry(handler, "primitiveMkdir");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
InvalidPathException.class, FileAlreadyExistsException.class,
FileNotFoundException.class, ParentNotDirectoryException.class,
SafeModeException.class, NSQuotaExceededException.class,
DSQuotaExceededException.class, UnresolvedPathException.class);
}
}
/**
* Get {@link ContentSummary} rooted at the specified directory.
*
* @param src
* The string representation of the path
* @see ClientProtocol#getContentSummary(String)
*/
ContentSummary getContentSummary(final String src) throws IOException {
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getContentSummary(src);
}
};
return (ContentSummary) doClientActionWithRetry(handler,
"getContentSummary");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, UnresolvedPathException.class);
}
}
/**
* Sets or resets quotas for a directory.
*
* @see ClientProtocol#setQuota(String, long, long)
*/
void setQuota(final String src, final long namespaceQuota,
final long diskspaceQuota) throws IOException {
// sanity check
if ((namespaceQuota <= 0 &&
namespaceQuota != HdfsConstants.QUOTA_DONT_SET &&
namespaceQuota != HdfsConstants.QUOTA_RESET) || (diskspaceQuota <= 0 &&
diskspaceQuota != HdfsConstants.QUOTA_DONT_SET &&
diskspaceQuota != HdfsConstants.QUOTA_RESET)) {
throw new IllegalArgumentException("Invalid values for quota : " +
namespaceQuota + " and " +
diskspaceQuota);
}
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.setQuota(src, namespaceQuota, diskspaceQuota);
return null;
}
};
doClientActionOnLeader(handler, "setQuota");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, NSQuotaExceededException.class,
DSQuotaExceededException.class, UnresolvedPathException.class);
}
}
/**
* set the modification and access time of a file
*
* @see ClientProtocol#setTimes(String, long, long)
*/
public void setTimes(final String src, final long mtime, final long atime)
throws IOException {
checkOpen();
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.setTimes(src, mtime, atime);
return null;
}
};
doClientActionWithRetry(handler, "setTimes");
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, UnresolvedPathException.class);
}
}
/**
* @deprecated use {@link HdfsDataInputStream} instead.
*/
@Deprecated
public static class DFSDataInputStream extends HdfsDataInputStream {
public DFSDataInputStream(DFSInputStream in) throws IOException {
super(in);
}
}
boolean shouldTryShortCircuitRead(InetSocketAddress targetAddr) {
return shortCircuitLocalReads && isLocalAddress(targetAddr);
}
void reportChecksumFailure(String file, ExtendedBlock blk, DatanodeInfo dn) {
DatanodeInfo[] dnArr = {dn};
LocatedBlock[] lblocks = {new LocatedBlock(blk, dnArr)};
reportChecksumFailure(file, lblocks);
}
// just reports checksum failure and ignores any exception during the report.
void reportChecksumFailure(String file, LocatedBlock lblocks[]) {
try {
reportBadBlocks(lblocks);
} catch (IOException ie) {
LOG.info("Found corruption while reading " + file +
". Error repairing corrupt blocks. Bad blocks remain.", ie);
}
}
@Override
public String toString() {
return getClass().getSimpleName() + "[clientName=" + clientName + ", ugi=" +
ugi + "]";
}
void disableShortCircuit() {
shortCircuitLocalReads = false;
}
/**
* Action Handler to encapsualte the client requests to the namenode.
*/
private interface ClientActionHandler {
Object doAction(ClientProtocol namenode)
throws RemoteException, IOException;
}
/**
* Return the next namenode to be used by the client. It also make sure
* that this namenode doesn't belong to the blacklisted namenodes. A
* namenode is blacklisted if it doesn't respond to a request
* for @link{MAX_RPC_RETRIES} times.
*/
private interface NameNodeFetcher {
public NamenodeHandle getNextNameNode(List<ActiveNode> blackList)
throws IOException;
}
/**
* Default NameNodeFetcher which returns a namenode from the active
* namenode list. It delegates the process of selecting nextNamenode to the
* NameNodeSelector according to the specified policy in the selector.
* @see NamenodeSelector#getNextNamenode()
*/
private final NameNodeFetcher defaultNameNodeFetcher = new NameNodeFetcher() {
public NamenodeHandle getNextNameNode(List<ActiveNode> blackList)
throws IOException {
NamenodeSelector.NamenodeHandle handle = null;
for (int i = 0; i < 10; i++) {
handle = namenodeSelector.getNextNamenode();
if (!blackList.contains(handle.getNamenode())) {
return handle;
}
}
return handle;
}
};
/**
* Returns the leader namenode if it's not blacklisted, otherwise it will
* return null.
*/
private final NameNodeFetcher leaderNameNodeFetcher = new NameNodeFetcher() {
public NamenodeHandle getNextNameNode(List<ActiveNode> blackList)
throws IOException {
NamenodeHandle leader = namenodeSelector.getLeadingNameNode();
if (blackList.contains(leader)) {
return null;
}
return leader;
}
};
/**
* A client request encapsulated in @link{ClientActionHandler} run on the
* leader namenode. If the client failed to connect to the namenode, it will
* keep retying for @link{MAX_RPC_RETRIES} untill either it succeeds or fails.
* @param handler
* encapsualted client request
* @param callerID
* requested operation
* @return Object result of the operation is any, otherwise return null
* @throws RemoteException
* @throws IOException
*/
private Object doClientActionOnLeader(ClientActionHandler handler,
String callerID) throws RemoteException, IOException {
return doClientActionWithRetry(handler, callerID, leaderNameNodeFetcher);
}
/**
* A client request encapsulated in @link{ClientActionHandler} run on a
* namenode. If the client failed to connect to the namenode, it will
* keep retying for @link{MAX_RPC_RETRIES} untill either it succeeds or fails.
* @param handler
* encapsualted client request
* @param callerID
* requested operation
* @return Object result of the operation is any, otherwise return null
* @throws RemoteException
* @throws IOException
*/
private Object doClientActionWithRetry(ClientActionHandler handler,
String callerID) throws RemoteException, IOException {
return doClientActionWithRetry(handler, callerID, defaultNameNodeFetcher);
}
private static AtomicLong fnID = new AtomicLong(); // for debugging purpose
/**
* A client request encapsulated in @link{ClientActionHandler} will run on
* a namenode specified by the nameNodeFetcher. If the client failed to
* connect to the namenode, it will keep retying for @link{MAX_RPC_RETRIES}
* times untill either it succeeds or fails. If the request failed, the
* namenode will be blacklisted. @link{NamenodeSector} will handle the
* revocation of the blacklisted namenodes.
* @param handler
* encapsualted client request
* @param callerID
* requested operation
* @param nameNodeFetcher
* fetcher used to get namenode to be used for the request
* @return Object result of the operation is any, otherwise return null
* @throws RemoteException
* @throws IOException
*/
private Object doClientActionWithRetry(ClientActionHandler handler,
String callerID, NameNodeFetcher nameNodeFetcher)
throws RemoteException, IOException {
callerID = callerID.toUpperCase();
long thisFnID = fnID.incrementAndGet();
//When a RPC call to NN fails then the client will put the NamenodeSelector.java
//will put the NN address in blacklist and send an RPC to NN to get a fresh list of NNs.
//After obtaining a fresh list from server, the NamenodeSector will wipe the NN balacklist.
//It is quite possible that the refesh list of NNs may again contain the descriptors
//for dead Namenodes (depends on the convergence rate of Leader Election).
//To avoid contacting a dead node a list of black listed namenodes is also maintained on the
//client side to avoid contacting dead NNs
List<ActiveNode> blackListedNamenodes = new ArrayList<ActiveNode>();
IOException exception = null;
NamenodeSelector.NamenodeHandle handle = null;
int waitTime = dfsClientConf.dfsClientInitialWaitOnRetry;
for (int i = 0; i <= MAX_RPC_RETRIES;
i++) { // min value of MAX_RPC_RETRIES is 0
try {
handle = nameNodeFetcher.getNextNameNode(blackListedNamenodes);
LOG.debug(thisFnID + ") " + callerID + " sending RPC to " +
handle.getNamenode() + " tries left (" + (MAX_RPC_RETRIES - i) +
")");
Object obj = handler.doAction(handle.getRPCHandle());
//no exception
return obj;
} catch (IOException e) {
exception = e;
if (ExceptionCheck.isLocalConnectException(e)) {
//black list the namenode
//so that it is not used again
if (handle != null) {
LOG.debug(thisFnID + ") " + callerID + " RPC failed. NN used was " +
handle.getNamenode() + ", retries left (" +
(MAX_RPC_RETRIES - (i)) + ")", e);
namenodeSelector.blackListNamenode(handle);
blackListedNamenodes.add(handle.getNamenode());
} else {
LOG.debug(thisFnID + ") " + callerID +
" RPC failed. NN was NULL, retries left (" +
(MAX_RPC_RETRIES - (i)) + ")", e);
}
try {
LOG.debug(thisFnID + ") RPC failed. Sleep " + waitTime);
Thread.sleep(waitTime);
waitTime *= 2;
} catch (InterruptedException ex) {
}
continue;
} else {
break;
}
}
}
LOG.warn(thisFnID + ") " + callerID + " RPC failed", exception);
throw exception; // Did not return so RPC failed
}
/**
* Ping the name node to see if there is a connection
* -- A connection won't exist if it gives an IOException of type
* ConnectException or SocketTimeout
*/
public boolean pingNamenode() {
try {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode) throws IOException {
namenode.ping();
return null;
}
};
doClientActionWithRetry(handler, "pingNamenode");
} catch (Exception ex) {
ex.printStackTrace();
LOG.warn("Ping to Namenode " + ex);
return false;
}
// There is a connection
return true;
}
public SortedActiveNodeList getActiveNodes() throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getActiveNamenodesForClient();
}
};
return (SortedActiveNodeList) doClientActionWithRetry(handler,
"getActiveNodes");
}
public LocatedBlock getAdditionalDatanode(final String src,
final ExtendedBlock blk, final DatanodeInfo[] existings,
final DatanodeInfo[] excludes, final int numAdditionalNodes,
final String clientName)
throws AccessControlException, FileNotFoundException, SafeModeException,
UnresolvedLinkException, IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.getAdditionalDatanode(src, blk, existings, excludes,
numAdditionalNodes, clientName);
}
};
return (LocatedBlock) doClientActionWithRetry(handler,
"getAdditionalDatanode");
}
public LocatedBlock updateBlockForPipeline(final ExtendedBlock block,
final String clientName) throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.updateBlockForPipeline(block, clientName);
}
};
return (LocatedBlock) doClientActionWithRetry(handler,
"updateBlockForPipeline");
}
public void updatePipeline(final String clientName,
final ExtendedBlock oldBlock, final ExtendedBlock newBlock,
final DatanodeID[] newNodes) throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.updatePipeline(clientName, oldBlock, newBlock, newNodes);
return null;
}
};
doClientActionWithRetry(handler, "updatePipeline");
}
public void abandonBlock(final ExtendedBlock b, final String src,
final String holder) throws AccessControlException, FileNotFoundException,
UnresolvedLinkException, IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.abandonBlock(b, src, holder);
return null;
}
};
doClientActionWithRetry(handler, "abandonBlock");
}
public LocatedBlock addBlock(final String src, final String clientName,
final ExtendedBlock previous, final DatanodeInfo[] excludeNodes)
throws AccessControlException, FileNotFoundException,
NotReplicatedYetException, SafeModeException, UnresolvedLinkException,
IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.addBlock(src, clientName, previous, excludeNodes);
}
};
return (LocatedBlock) doClientActionWithRetry(handler, "addBlock");
}
public void create(final String src, final FsPermission masked,
final String clientName, final EnumSetWritable<CreateFlag> flag,
final boolean createParent, final short replication, final long blockSize)
throws AccessControlException, AlreadyBeingCreatedException,
DSQuotaExceededException, FileAlreadyExistsException,
FileNotFoundException, NSQuotaExceededException,
ParentNotDirectoryException, SafeModeException, UnresolvedLinkException,
IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode
.create(src, masked, clientName, flag, createParent, replication,
blockSize);
return null;
}
};
doClientActionWithRetry(handler, "create");
}
public void create(final String src, final FsPermission masked,
final String clientName, final EnumSetWritable<CreateFlag> flag,
final boolean createParent, final short replication, final long blockSize,
final EncodingPolicy policy)
throws AccessControlException, AlreadyBeingCreatedException,
DSQuotaExceededException, FileAlreadyExistsException,
FileNotFoundException, NSQuotaExceededException,
ParentNotDirectoryException, SafeModeException, UnresolvedLinkException,
IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode
.create(src, masked, clientName, flag, createParent, replication,
blockSize, policy);
return null;
}
};
doClientActionWithRetry(handler, "create");
}
public void fsync(final String src, final String client,
final long lastBlockLength)
throws AccessControlException, FileNotFoundException,
UnresolvedLinkException, IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.fsync(src, client, lastBlockLength);
return null;
}
};
doClientActionWithRetry(handler, "fsync");
}
public boolean complete(final String src, final String clientName,
final ExtendedBlock last)
throws AccessControlException, FileNotFoundException, SafeModeException,
UnresolvedLinkException, IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return namenode.complete(src, clientName, last);
}
};
return (Boolean) doClientActionWithRetry(handler, "complete");
}
public EncodingStatus getEncodingStatus(final String filePath)
throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode) throws IOException {
try {
return namenode.getEncodingStatus(filePath);
} catch (RemoteException e) {
throw e.unwrapRemoteException();
}
}
};
return (EncodingStatus) doClientActionWithRetry(handler, "EncodingStatus");
}
/**
* @see org.apache.hadoop.hdfs.protocol.ClientProtocol#encodeFile
*/
public void encodeFile(final String filePath, final EncodingPolicy policy)
throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode) throws IOException {
namenode.encodeFile(filePath, policy);
return null;
}
};
doClientActionWithRetry(handler, "encodeFile");
}
/**
* @see org.apache.hadoop.hdfs.protocol.ClientProtocol#revokeEncoding
*/
public void revokeEncoding(final String filePath, final short replication)
throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode) throws IOException {
namenode.revokeEncoding(filePath, replication);
return null;
}
};
doClientActionWithRetry(handler, "revokeEncoding");
}
/**
* @see org.apache.hadoop.hdfs.protocol.ClientProtocol#getRepairedBlockLocations
*/
public LocatedBlock getRepairedBlockLocations(final String sourcePath,
final String parityPath, final LocatedBlock block, final boolean isParity)
throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
return callGetRepairedBlockLocations(namenode, sourcePath, parityPath,
block, isParity);
}
};
return (LocatedBlock) doClientActionWithRetry(handler,
"getMissingLocatedBlocks");
}
static LocatedBlock callGetRepairedBlockLocations(ClientProtocol namenode,
String sourcePath, String parityPath, LocatedBlock block,
boolean isParity) throws IOException {
try {
return namenode
.getRepairedBlockLocations(sourcePath, parityPath, block, isParity);
} catch (RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class, UnresolvedPathException.class);
}
}
public void changeConf(final List<String> props, final List<String> newVals)
throws IOException {
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.changeConf(props, newVals);
return null;
}
};
doClientActionToAll(handler, "changeConf");
}
public void flushCache(final String userName, final String groupName)
throws IOException{
ClientActionHandler handler = new ClientActionHandler() {
@Override
public Object doAction(ClientProtocol namenode)
throws RemoteException, IOException {
namenode.flushCache(userName, groupName);
return null;
}
};
doClientActionToAll(handler, "flushCache");
}
/**
* Send the client request to all namenodes in the cluster
* @param handler
* encapsulated client request
* @param callerID
* requested operation
* @return Object result of the operation if any, otherwise return null
* @throws RemoteException
* @throws IOException
*/
private Object doClientActionToAll(ClientActionHandler handler,
String callerID) throws RemoteException, IOException {
callerID = callerID.toUpperCase();
long thisFnID = fnID.incrementAndGet();
Exception exception = null;
boolean success = false;
for (NamenodeSelector.NamenodeHandle handle : namenodeSelector
.getAllNameNode()) {
try {
LOG.debug(thisFnID + ") " + callerID + " sending RPC to " +
handle.getNamenode());
Object obj = handler.doAction(handle.getRPCHandle());
success = true;
//no exception
return obj;
} catch (Exception e) {
exception = e;
if (ExceptionCheck.isLocalConnectException(e)) {
//black list the namenode
//so that it is not used again
if (handle != null) {
LOG.warn(thisFnID + ") " + callerID + " RPC faild. NN used was " +
handle.getNamenode() + ", Exception " + e);
namenodeSelector.blackListNamenode(handle);
} else {
LOG.warn(thisFnID + ") " + callerID +
" RPC faild. NN was NULL, Exception " + e);
}
continue;
} else {
break;
}
}
}
if (!success) {
//print the fn call trace to figure out with RPC failed
for (int j = 0; j < Thread.currentThread().getStackTrace().length; j++) {
LOG.debug(thisFnID + ") " + callerID + " Failed RPC Trace, " +
Thread.currentThread().getStackTrace()[j]);
}
LOG.warn(thisFnID + ") " + callerID + " Exception was " + exception);
exception.printStackTrace();
if (exception != null) {
if (exception instanceof RemoteException) {
throw (RemoteException) exception;
} else {
throw (IOException) exception;
}
}
}
return null;
}
public int getNameNodesCount() throws IOException {
return namenodeSelector.getNameNodesCount();
}
}