001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hdfs.server.blockmanagement;
019
020import org.apache.hadoop.hdfs.protocol.Block;
021import org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BlockUCState;
022import org.apache.hadoop.hdfs.server.namenode.INodeFile;
023import org.apache.hadoop.hdfs.util.LightWeightGSet;
024
025/**
026 * Internal class for block metadata.
027 */
028public class BlockInfo extends Block implements LightWeightGSet.LinkedElement {
029  private INodeFile inode;
030
031  /** For implementing {@link LightWeightGSet.LinkedElement} interface */
032  private LightWeightGSet.LinkedElement nextLinkedElement;
033
034  /**
035   * This array contains triplets of references.
036   * For each i-th datanode the block belongs to
037   * triplets[3*i] is the reference to the DatanodeDescriptor
038   * and triplets[3*i+1] and triplets[3*i+2] are references 
039   * to the previous and the next blocks, respectively, in the 
040   * list of blocks belonging to this data-node.
041   */
042  private Object[] triplets;
043
044  /**
045   * Construct an entry for blocksmap
046   * @param replication the block's replication factor
047   */
048  public BlockInfo(int replication) {
049    this.triplets = new Object[3*replication];
050    this.inode = null;
051  }
052  
053  public BlockInfo(Block blk, int replication) {
054    super(blk);
055    this.triplets = new Object[3*replication];
056    this.inode = null;
057  }
058
059  /**
060   * Copy construction.
061   * This is used to convert BlockInfoUnderConstruction
062   * @param from BlockInfo to copy from.
063   */
064  protected BlockInfo(BlockInfo from) {
065    this(from, from.inode.getReplication());
066    this.inode = from.inode;
067  }
068
069  public INodeFile getINode() {
070    return inode;
071  }
072
073  public void setINode(INodeFile inode) {
074    this.inode = inode;
075  }
076
077  DatanodeDescriptor getDatanode(int index) {
078    assert this.triplets != null : "BlockInfo is not initialized";
079    assert index >= 0 && index*3 < triplets.length : "Index is out of bound";
080    return (DatanodeDescriptor)triplets[index*3];
081  }
082
083  BlockInfo getPrevious(int index) {
084    assert this.triplets != null : "BlockInfo is not initialized";
085    assert index >= 0 && index*3+1 < triplets.length : "Index is out of bound";
086    BlockInfo info = (BlockInfo)triplets[index*3+1];
087    assert info == null || 
088        info.getClass().getName().startsWith(BlockInfo.class.getName()) : 
089              "BlockInfo is expected at " + index*3;
090    return info;
091  }
092
093  BlockInfo getNext(int index) {
094    assert this.triplets != null : "BlockInfo is not initialized";
095    assert index >= 0 && index*3+2 < triplets.length : "Index is out of bound";
096    BlockInfo info = (BlockInfo)triplets[index*3+2];
097    assert info == null || 
098        info.getClass().getName().startsWith(BlockInfo.class.getName()) : 
099              "BlockInfo is expected at " + index*3;
100    return info;
101  }
102
103  void setDatanode(int index, DatanodeDescriptor node) {
104    assert this.triplets != null : "BlockInfo is not initialized";
105    assert index >= 0 && index*3 < triplets.length : "Index is out of bound";
106    triplets[index*3] = node;
107  }
108
109  void setPrevious(int index, BlockInfo to) {
110    assert this.triplets != null : "BlockInfo is not initialized";
111    assert index >= 0 && index*3+1 < triplets.length : "Index is out of bound";
112    triplets[index*3+1] = to;
113  }
114
115  void setNext(int index, BlockInfo to) {
116    assert this.triplets != null : "BlockInfo is not initialized";
117    assert index >= 0 && index*3+2 < triplets.length : "Index is out of bound";
118    triplets[index*3+2] = to;
119  }
120
121  /**
122   * Return the previous block on the block list for the datanode at
123   * position index. Set the previous block on the list to "to".
124   *
125   * @param index - the datanode index
126   * @param to - block to be set to previous on the list of blocks
127   * @return current previous block on the list of blocks
128   */
129  BlockInfo getSetPrevious(int index, BlockInfo to) {
130        assert this.triplets != null : "BlockInfo is not initialized";
131        assert index >= 0 && index*3+1 < triplets.length : "Index is out of bound";
132    BlockInfo info = (BlockInfo)triplets[index*3+1];
133    triplets[index*3+1] = to;
134    return info;
135  }
136
137  /**
138   * Return the next block on the block list for the datanode at
139   * position index. Set the next block on the list to "to".
140   *
141   * @param index - the datanode index
142   * @param to - block to be set to next on the list of blocks
143   *    * @return current next block on the list of blocks
144   */
145  BlockInfo getSetNext(int index, BlockInfo to) {
146        assert this.triplets != null : "BlockInfo is not initialized";
147        assert index >= 0 && index*3+2 < triplets.length : "Index is out of bound";
148    BlockInfo info = (BlockInfo)triplets[index*3+2];
149    triplets[index*3+2] = to;
150    return info;
151  }
152
153  int getCapacity() {
154    assert this.triplets != null : "BlockInfo is not initialized";
155    assert triplets.length % 3 == 0 : "Malformed BlockInfo";
156    return triplets.length / 3;
157  }
158
159  /**
160   * Ensure that there is enough  space to include num more triplets.
161   * @return first free triplet index.
162   */
163  private int ensureCapacity(int num) {
164    assert this.triplets != null : "BlockInfo is not initialized";
165    int last = numNodes();
166    if(triplets.length >= (last+num)*3)
167      return last;
168    /* Not enough space left. Create a new array. Should normally 
169     * happen only when replication is manually increased by the user. */
170    Object[] old = triplets;
171    triplets = new Object[(last+num)*3];
172    System.arraycopy(old, 0, triplets, 0, last*3);
173    return last;
174  }
175
176  /**
177   * Count the number of data-nodes the block belongs to.
178   */
179  int numNodes() {
180    assert this.triplets != null : "BlockInfo is not initialized";
181    assert triplets.length % 3 == 0 : "Malformed BlockInfo";
182    for(int idx = getCapacity()-1; idx >= 0; idx--) {
183      if(getDatanode(idx) != null)
184        return idx+1;
185    }
186    return 0;
187  }
188
189  /**
190   * Add data-node this block belongs to.
191   */
192  public boolean addNode(DatanodeDescriptor node) {
193    if(findDatanode(node) >= 0) // the node is already there
194      return false;
195    // find the last null node
196    int lastNode = ensureCapacity(1);
197    setDatanode(lastNode, node);
198    setNext(lastNode, null);
199    setPrevious(lastNode, null);
200    return true;
201  }
202
203  /**
204   * Remove data-node from the block.
205   */
206  public boolean removeNode(DatanodeDescriptor node) {
207    int dnIndex = findDatanode(node);
208    if(dnIndex < 0) // the node is not found
209      return false;
210    assert getPrevious(dnIndex) == null && getNext(dnIndex) == null : 
211      "Block is still in the list and must be removed first.";
212    // find the last not null node
213    int lastNode = numNodes()-1; 
214    // replace current node triplet by the lastNode one 
215    setDatanode(dnIndex, getDatanode(lastNode));
216    setNext(dnIndex, getNext(lastNode)); 
217    setPrevious(dnIndex, getPrevious(lastNode)); 
218    // set the last triplet to null
219    setDatanode(lastNode, null);
220    setNext(lastNode, null); 
221    setPrevious(lastNode, null); 
222    return true;
223  }
224
225  /**
226   * Find specified DatanodeDescriptor.
227   * @param dn
228   * @return index or -1 if not found.
229   */
230  int findDatanode(DatanodeDescriptor dn) {
231    int len = getCapacity();
232    for(int idx = 0; idx < len; idx++) {
233      DatanodeDescriptor cur = getDatanode(idx);
234      if(cur == dn)
235        return idx;
236      if(cur == null)
237        break;
238    }
239    return -1;
240  }
241
242  /**
243   * Insert this block into the head of the list of blocks 
244   * related to the specified DatanodeDescriptor.
245   * If the head is null then form a new list.
246   * @return current block as the new head of the list.
247   */
248  public BlockInfo listInsert(BlockInfo head, DatanodeDescriptor dn) {
249    int dnIndex = this.findDatanode(dn);
250    assert dnIndex >= 0 : "Data node is not found: current";
251    assert getPrevious(dnIndex) == null && getNext(dnIndex) == null : 
252            "Block is already in the list and cannot be inserted.";
253    this.setPrevious(dnIndex, null);
254    this.setNext(dnIndex, head);
255    if(head != null)
256      head.setPrevious(head.findDatanode(dn), this);
257    return this;
258  }
259
260  /**
261   * Remove this block from the list of blocks 
262   * related to the specified DatanodeDescriptor.
263   * If this block is the head of the list then return the next block as 
264   * the new head.
265   * @return the new head of the list or null if the list becomes
266   * empty after deletion.
267   */
268  public BlockInfo listRemove(BlockInfo head, DatanodeDescriptor dn) {
269    if(head == null)
270      return null;
271    int dnIndex = this.findDatanode(dn);
272    if(dnIndex < 0) // this block is not on the data-node list
273      return head;
274
275    BlockInfo next = this.getNext(dnIndex);
276    BlockInfo prev = this.getPrevious(dnIndex);
277    this.setNext(dnIndex, null);
278    this.setPrevious(dnIndex, null);
279    if(prev != null)
280      prev.setNext(prev.findDatanode(dn), next);
281    if(next != null)
282      next.setPrevious(next.findDatanode(dn), prev);
283    if(this == head)  // removing the head
284      head = next;
285    return head;
286  }
287
288  /**
289   * Remove this block from the list of blocks related to the specified
290   * DatanodeDescriptor. Insert it into the head of the list of blocks.
291   *
292   * @return the new head of the list.
293   */
294  public BlockInfo moveBlockToHead(BlockInfo head, DatanodeDescriptor dn,
295      int curIndex, int headIndex) {
296    if (head == this) {
297      return this;
298    }
299    BlockInfo next = this.getSetNext(curIndex, head);
300    BlockInfo prev = this.getSetPrevious(curIndex, null);
301
302    head.setPrevious(headIndex, this);
303    prev.setNext(prev.findDatanode(dn), next);
304    if (next != null)
305      next.setPrevious(next.findDatanode(dn), prev);
306    return this;
307  }
308
309  /**
310   * BlockInfo represents a block that is not being constructed.
311   * In order to start modifying the block, the BlockInfo should be converted
312   * to {@link BlockInfoUnderConstruction}.
313   * @return {@link BlockUCState#COMPLETE}
314   */
315  public BlockUCState getBlockUCState() {
316    return BlockUCState.COMPLETE;
317  }
318
319  /**
320   * Is this block complete?
321   * 
322   * @return true if the state of the block is {@link BlockUCState#COMPLETE}
323   */
324  public boolean isComplete() {
325    return getBlockUCState().equals(BlockUCState.COMPLETE);
326  }
327
328  /**
329   * Convert a complete block to an under construction block.
330   * 
331   * @return BlockInfoUnderConstruction -  an under construction block.
332   */
333  public BlockInfoUnderConstruction convertToBlockUnderConstruction(
334      BlockUCState s, DatanodeDescriptor[] targets) {
335    if(isComplete()) {
336      return new BlockInfoUnderConstruction(
337          this, getINode().getReplication(), s, targets);
338    }
339    // the block is already under construction
340    BlockInfoUnderConstruction ucBlock = (BlockInfoUnderConstruction)this;
341    ucBlock.setBlockUCState(s);
342    ucBlock.setExpectedLocations(targets);
343    return ucBlock;
344  }
345
346  @Override
347  public int hashCode() {
348    // Super implementation is sufficient
349    return super.hashCode();
350  }
351  
352  @Override
353  public boolean equals(Object obj) {
354    // Sufficient to rely on super's implementation
355    return (this == obj) || super.equals(obj);
356  }
357
358  @Override
359  public LightWeightGSet.LinkedElement getNext() {
360    return nextLinkedElement;
361  }
362
363  @Override
364  public void setNext(LightWeightGSet.LinkedElement next) {
365    this.nextLinkedElement = next;
366  }
367}