/**
* 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.server.namenode;
import io.hops.exception.StorageException;
import io.hops.exception.TransactionContextException;
import io.hops.transaction.EntityManager;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.fs.permission.PermissionStatus;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.protocol.DatanodeID;
import org.apache.hadoop.hdfs.server.blockmanagement.BlockCollection;
import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfo;
import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoUnderConstruction;
import org.apache.hadoop.hdfs.server.common.GenerationStamp;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* I-node for closed file.
*/
@InterfaceAudience.Private
public class INodeFile extends INode implements BlockCollection {
/**
* Cast INode to INodeFile.
*/
public static INodeFile valueOf(INode inode, String path) throws IOException {
if (inode == null) {
throw new FileNotFoundException("File does not exist: " + path);
}
if (!(inode instanceof INodeFile)) {
throw new FileNotFoundException("Path is not a file: " + path);
}
return (INodeFile) inode;
}
private int generationStamp = (int) GenerationStamp.FIRST_VALID_STAMP;
private long size;
public INodeFile(PermissionStatus permissions, BlockInfo[] blklist,
short replication, long modificationTime, long atime,
long preferredBlockSize) throws IOException {
super(permissions, modificationTime, atime);
this.setReplicationNoPersistance(replication);
this.setPreferredBlockSizeNoPersistance(preferredBlockSize);
}
public INodeFile(PermissionStatus permissions, long header,
long modificationTime, long atime) throws IOException {
super(permissions, modificationTime, atime);
this.setHeaderNoPersistance(header);
}
//HOP:
public INodeFile(INodeFile other)
throws IOException {
super(other);
setReplicationNoPersistance(other.getBlockReplication());
setPreferredBlockSizeNoPersistance(other.getPreferredBlockSize());
setGenerationStampNoPersistence(other.getGenerationStamp());
setSizeNoPersistence(other.getSize());
setHasBlocksNoPersistance(other.hasBlocks());
setPartitionIdNoPersistance(other.getPartitionId());
setHeaderNoPersistance(other.getHeader());
}
/**
* @return the replication factor of the file.
*/
@Override
public short getBlockReplication() {
return getBlockReplication(header);
}
/**
* @return preferred block size (in bytes) of the file.
*/
@Override
public long getPreferredBlockSize() {
return getPreferredBlockSize(header);
}
/**
* @return the blocks of the file.
*/
@Override
public BlockInfo[] getBlocks()
throws StorageException, TransactionContextException {
List<BlockInfo> blocks = getBlocksOrderedByIndex();
if(blocks == null){
return BlockInfo.EMPTY_ARRAY;
}
BlockInfo[] blks = new BlockInfo[blocks.size()];
return blocks.toArray(blks);
}
/**
* append array of blocks to this.blocks
*/
List<BlockInfo> appendBlocks(INodeFile[] inodes, int totalAddedBlocks /*HOP not used*/)
throws StorageException, TransactionContextException {
List<BlockInfo> oldBlks = new ArrayList<BlockInfo>();
for (INodeFile srcInode : inodes) {
for (BlockInfo block : srcInode.getBlocks()) {
BlockInfo copy = BlockInfo.cloneBlock(block);
oldBlks.add(copy);
addBlock(block);
block.setBlockCollection(this);
}
}
return oldBlks;
}
/**
* add a block to the block list
*/
void addBlock(BlockInfo newblock)
throws StorageException, TransactionContextException {
BlockInfo maxBlk = findMaxBlk();
newblock.setBlockIndex(maxBlk.getBlockIndex() + 1);
}
/**
* Set the block of the file at the given index.
*/
public void setBlock(int idx, BlockInfo blk)
throws StorageException, TransactionContextException {
blk.setBlockIndex(idx);
}
@Override
int collectSubtreeBlocksAndClear(List<Block> v)
throws StorageException, TransactionContextException {
parent = null;
BlockInfo[] blocks = getBlocks();
if (blocks != null && v != null) {
for (BlockInfo blk : blocks) {
blk.setBlockCollection(null);
v.add(blk);
}
}
return 1;
}
@Override
public String getName() throws StorageException, TransactionContextException {
// Get the full path name of this inode.
return getFullPathName();
}
@Override
long[] computeContentSummary(long[] summary)
throws StorageException, TransactionContextException {
summary[0] += computeFileSize(true);
summary[1]++;
summary[3] += diskspaceConsumed();
return summary;
}
/**
* Compute file size.
* May or may not include BlockInfoUnderConstruction.
*/
public long computeFileSize(boolean includesBlockInfoUnderConstruction)
throws StorageException, TransactionContextException {
return computeFileSize(includesBlockInfoUnderConstruction, getBlocks());
}
static long computeFileSize(boolean includesBlockInfoUnderConstruction,
BlockInfo[] blocks) throws StorageException {
if (blocks == null || blocks.length == 0) {
return 0;
}
final int last = blocks.length - 1;
//check if the last block is BlockInfoUnderConstruction
long bytes = 0;
if(blocks[last] instanceof BlockInfoUnderConstruction){
if(includesBlockInfoUnderConstruction){
bytes = blocks[last].getNumBytes();
}
}else{
bytes = blocks[last].getNumBytes();
}
for (int i = 0; i < last; i++) {
bytes += blocks[i].getNumBytes();
}
return bytes;
}
@Override
DirCounts spaceConsumedInTree(DirCounts counts)
throws StorageException, TransactionContextException {
counts.nsCount += 1;
counts.dsCount += diskspaceConsumed();
return counts;
}
long diskspaceConsumed()
throws StorageException, TransactionContextException {
return diskspaceConsumed(getBlocks());
}
long diskspaceConsumed(Block[] blkArr) {
return diskspaceConsumed(blkArr, isUnderConstruction(),
getPreferredBlockSize(), getBlockReplication());
}
static long diskspaceConsumed(Block[] blkArr, boolean underConstruction,
long preferredBlockSize, short blockReplication) {
long size = 0;
if (blkArr == null) {
return 0;
}
for (Block blk : blkArr) {
if (blk != null) {
size += blk.getNumBytes();
}
}
/* If the last block is being written to, use prefferedBlockSize
* rather than the actual block size.
*/
if (blkArr.length > 0 && blkArr[blkArr.length - 1] != null &&
underConstruction) {
size += preferredBlockSize - blkArr[blkArr.length - 1].getNumBytes();
}
return size * blockReplication;
}
/**
* Return the penultimate allocated block for this file.
*/
BlockInfo getPenultimateBlock()
throws StorageException, TransactionContextException {
BlockInfo[] blocks = getBlocks();
if (blocks == null || blocks.length <= 1) {
return null;
}
return blocks[blocks.length - 2];
}
@Override
public BlockInfo getLastBlock() throws IOException, StorageException {
BlockInfo[] blocks = getBlocks();
return blocks == null || blocks.length == 0 ? null :
blocks[blocks.length - 1];
}
@Override
public int numBlocks() throws StorageException, TransactionContextException {
BlockInfo[] blocks = getBlocks();
return blocks == null ? 0 : blocks.length;
}
void setReplication(short replication)
throws StorageException, TransactionContextException {
setReplicationNoPersistance(replication);
save();
}
public INodeFileUnderConstruction convertToUnderConstruction(
String clientName, String clientMachine, DatanodeID clientNode)
throws IOException {
INodeFileUnderConstruction ucfile =
new INodeFileUnderConstruction(this, clientName, clientMachine,
clientNode);
BlockInfo lastBlock = getLastBlock();
BlockInfo penultimateBlock = getPenultimateBlock();
if(lastBlock != null){
ucfile.setLastBlockId(lastBlock.getBlockId());
}
if(penultimateBlock != null){
ucfile.setPenultimateBlockId(penultimateBlock.getBlockId());
}
save(ucfile);
return ucfile;
}
public BlockInfo findMaxBlk()
throws StorageException, TransactionContextException {
BlockInfo maxBlk = (BlockInfo) EntityManager
.find(BlockInfo.Finder.ByMaxBlockIndexForINode, this.getId());
return maxBlk;
}
public int getGenerationStamp() {
return generationStamp;
}
public void setGenerationStampNoPersistence(int generationStamp) {
this.generationStamp = generationStamp;
}
public int nextGenerationStamp()
throws StorageException, TransactionContextException {
generationStamp++;
save();
return generationStamp;
}
public long getSize() {
return size;
}
public void setSizeNoPersistence(long size) {
this.size = size;
}
public void recomputeFileSize() throws StorageException, TransactionContextException {
setSizeNoPersistence(this.computeFileSize(true));
save();
}
protected List<BlockInfo> getBlocksOrderedByIndex()
throws TransactionContextException, StorageException {
if (getId() == INode.NON_EXISTING_ID) {
return null;
}
List<BlockInfo> blocks = (List<BlockInfo>) EntityManager
.findList(BlockInfo.Finder.ByINodeId, id);
if (blocks != null) {
Collections.sort(blocks, BlockInfo.Order.ByBlockIndex);
return blocks;
} else {
return null;
}
}
}