/** * 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.metadata.hdfs.entity.LeasePath; 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.BlockInfo; import org.apache.hadoop.hdfs.server.blockmanagement.BlockInfoUnderConstruction; import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeDescriptor; import org.apache.hadoop.hdfs.server.blockmanagement.MutableBlockCollection; import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BlockUCState; import java.io.IOException; import java.util.List; /** * I-node for file being written. */ @InterfaceAudience.Private public class INodeFileUnderConstruction extends INodeFile implements MutableBlockCollection { /** * Cast INode to INodeFileUnderConstruction. */ public static INodeFileUnderConstruction valueOf(INode inode, String path) throws IOException { final INodeFile file = INodeFile.valueOf(inode, path); if (!file.isUnderConstruction()) { throw new IOException("File is not under construction: " + path); } return (INodeFileUnderConstruction) file; } private String clientName; // lease holder private final String clientMachine; private final DatanodeID clientNode; // if client is a cluster node too. private long lastBlockId = -1; private long penultimateBlockId = -1; public INodeFileUnderConstruction(PermissionStatus permissions, short replication, long preferredBlockSize, long modTime, String clientName, String clientMachine, DatanodeID clientNode) throws IOException { super(permissions, null, replication, modTime, modTime, preferredBlockSize); this.clientName = clientName; this.clientMachine = clientMachine; this.clientNode = clientNode; } public INodeFileUnderConstruction(byte[] name, short blockReplication, long modificationTime, long preferredBlockSize, BlockInfo[] blocks, PermissionStatus perm, String clientName, String clientMachine, DatanodeID clientNode, int inodeId, int pid) throws IOException { super(perm, blocks, blockReplication, modificationTime, modificationTime, preferredBlockSize); setLocalNameNoPersistance(name); this.clientName = clientName; this.clientMachine = clientMachine; this.clientNode = clientNode; this.id = inodeId; this.parentId = pid; //throw new UnsupportedOperationException("HOP: This constructor should not be used"); // The only reason it is here that it is called in some FSImage Classes that are not deleted. } //HOP: used instead of INodeFile.convertToUnderConstruction protected INodeFileUnderConstruction(INodeFile file, String clientName, String clientMachine, DatanodeID clientNode) throws IOException { super(file); this.clientName = clientName; this.clientMachine = clientMachine; this.clientNode = clientNode; } public String getClientName() { return clientName; } void setClientName(String clientName) throws StorageException, TransactionContextException { this.clientName = clientName; save(); } public String getClientMachine() { return clientMachine; } public DatanodeID getClientNode() { return clientNode; } /** * Is this inode being constructed? */ @Override public boolean isUnderConstruction() { return true; } // // converts a INodeFileUnderConstruction into a INodeFile // use the modification time as the access time // INodeFile convertToInodeFile() throws IOException { assert allBlocksComplete() : "Can't finalize inode " + this + " since it contains non-complete blocks! Blocks are " + getBlocks(); INodeFile obj = new INodeFile(this); obj.setAccessTime(getModificationTime()); return obj; } /** * @return true if all of the blocks in this file are marked as completed. */ private boolean allBlocksComplete() throws StorageException, TransactionContextException { for (BlockInfo b : getBlocks()) { if (!b.isComplete()) { return false; } } return true; } /** * Remove a block from the block list. This block should be * the last one on the list. */ void removeLastBlock(Block oldblock) throws IOException, StorageException { final BlockInfo[] blocks = getBlocks(); if (blocks == null) { throw new IOException("Trying to delete non-existant block " + oldblock); } int size_1 = blocks.length - 1; if (!blocks[size_1].equals(oldblock)) { throw new IOException("Trying to delete non-last block " + oldblock); } removeBlock(blocks[blocks.length - 1]); } /** * Convert the last block of the file to an under-construction block. * Set its locations. */ @Override public BlockInfoUnderConstruction setLastBlock(BlockInfo lastBlock, DatanodeDescriptor[] targets) throws IOException, StorageException { if (numBlocks() == 0) { throw new IOException("Failed to set last block: File is empty."); } BlockInfoUnderConstruction ucBlock = lastBlock .convertToBlockUnderConstruction(BlockUCState.UNDER_CONSTRUCTION, targets); ucBlock.setBlockCollection(this); setBlock(numBlocks() - 1, ucBlock); return ucBlock; } /** * Update the length for the last block * * @param lastBlockLength * The length of the last block reported from client * @throws IOException */ void updateLengthOfLastBlock(long lastBlockLength) throws IOException, StorageException { BlockInfo lastBlock = this.getLastBlock(); assert (lastBlock != null) : "The last block for path " + this.getFullPathName() + " is null when updating its length"; assert (lastBlock instanceof BlockInfoUnderConstruction) : "The last block for path " + this.getFullPathName() + " is not a BlockInfoUnderConstruction when updating its length"; lastBlock.setNumBytes(lastBlockLength); } public void removeBlock(BlockInfo block) throws StorageException, TransactionContextException { BlockInfo[] blks = getBlocks(); int index = block.getBlockIndex(); block.setBlockCollection(null); if (index != blks.length) { for (int i = index + 1; i < blks.length; i++) { blks[i].setBlockIndex(i - 1); } } } void setLastBlockId(long lastBlockId){ this.lastBlockId = lastBlockId; } void setPenultimateBlockId(long penultimateBlockId){ this.penultimateBlockId = penultimateBlockId; } public void updateLastTwoBlocks(Lease lease) throws TransactionContextException, StorageException { updateLastTwoBlocks(lease, getFullPathName()); } public void updateLastTwoBlocks(Lease lease, String src) throws TransactionContextException, StorageException { LeasePath lp = lease.getLeasePath(src); setLastBlockId(lp.getLastBlockId()); setPenultimateBlockId(lp.getPenultimateBlockId()); } @Override public BlockInfo getLastBlock() throws IOException, StorageException { if(lastBlockId < 0) { return super.getLastBlock(); } return EntityManager.find(BlockInfo.Finder.ByBlockIdAndINodeId, lastBlockId, getId()); } @Override BlockInfo getPenultimateBlock() throws StorageException, TransactionContextException { if(penultimateBlockId < 0) { return super.getPenultimateBlock(); } return EntityManager.find(BlockInfo.Finder.ByBlockIdAndINodeId, penultimateBlockId, getId()); } @Override public BlockInfo getBlock(int index) throws TransactionContextException, StorageException { List<BlockInfo> blocks = getBlocksOrderedByIndex(); if(blocks == null){ return null; } for(BlockInfo blk : blocks){ if(blk.getBlockIndex() == index){ return blk; } } return null; } }