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.namenode;
019
020import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_ADD;
021import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_ADD_BLOCK;
022import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_ADD_CACHE_DIRECTIVE;
023import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_ADD_CACHE_POOL;
024import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_ALLOCATE_BLOCK_ID;
025import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_ALLOW_SNAPSHOT;
026import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_CANCEL_DELEGATION_TOKEN;
027import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_CLEAR_NS_QUOTA;
028import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_CLOSE;
029import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_CONCAT_DELETE;
030import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_CREATE_SNAPSHOT;
031import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_DELETE;
032import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_DELETE_SNAPSHOT;
033import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_DISALLOW_SNAPSHOT;
034import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_END_LOG_SEGMENT;
035import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_GET_DELEGATION_TOKEN;
036import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_INVALID;
037import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_MKDIR;
038import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_MODIFY_CACHE_DIRECTIVE;
039import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_MODIFY_CACHE_POOL;
040import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_REASSIGN_LEASE;
041import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_REMOVE_CACHE_DIRECTIVE;
042import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_REMOVE_CACHE_POOL;
043import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_RENAME;
044import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_RENAME_OLD;
045import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_RENAME_SNAPSHOT;
046import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_RENEW_DELEGATION_TOKEN;
047import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SET_ACL;
048import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_ROLLING_UPGRADE_FINALIZE;
049import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_ROLLING_UPGRADE_START;
050import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SET_GENSTAMP_V1;
051import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SET_GENSTAMP_V2;
052import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SET_NS_QUOTA;
053import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SET_OWNER;
054import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SET_PERMISSIONS;
055import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SET_QUOTA;
056import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SET_REPLICATION;
057import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_START_LOG_SEGMENT;
058import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_SYMLINK;
059import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_TIMES;
060import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_UPDATE_BLOCKS;
061import static org.apache.hadoop.hdfs.server.namenode.FSEditLogOpCodes.OP_UPDATE_MASTER_KEY;
062
063import java.io.DataInput;
064import java.io.DataInputStream;
065import java.io.DataOutput;
066import java.io.DataOutputStream;
067import java.io.EOFException;
068import java.io.IOException;
069import java.util.ArrayList;
070import java.util.Arrays;
071import java.util.EnumMap;
072import java.util.List;
073import java.util.zip.CheckedInputStream;
074import java.util.zip.Checksum;
075
076import org.apache.commons.codec.DecoderException;
077import org.apache.commons.codec.binary.Hex;
078import org.apache.hadoop.classification.InterfaceAudience;
079import org.apache.hadoop.classification.InterfaceStability;
080import org.apache.hadoop.fs.ChecksumException;
081import org.apache.hadoop.fs.Options.Rename;
082import org.apache.hadoop.fs.permission.AclEntry;
083import org.apache.hadoop.fs.permission.AclEntryScope;
084import org.apache.hadoop.fs.permission.AclEntryType;
085import org.apache.hadoop.fs.permission.FsAction;
086import org.apache.hadoop.fs.permission.FsPermission;
087import org.apache.hadoop.fs.permission.PermissionStatus;
088import org.apache.hadoop.hdfs.DFSConfigKeys;
089import org.apache.hadoop.hdfs.DeprecatedUTF8;
090import org.apache.hadoop.hdfs.protocol.Block;
091import org.apache.hadoop.hdfs.protocol.CacheDirectiveInfo;
092import org.apache.hadoop.hdfs.protocol.CachePoolInfo;
093import org.apache.hadoop.hdfs.protocol.ClientProtocol;
094import org.apache.hadoop.hdfs.protocol.HdfsConstants;
095import org.apache.hadoop.hdfs.protocol.LayoutVersion;
096import org.apache.hadoop.hdfs.protocol.LayoutVersion.Feature;
097import org.apache.hadoop.hdfs.protocol.proto.AclProtos.AclEditLogProto;
098import org.apache.hadoop.hdfs.protocolPB.PBHelper;
099import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
100import org.apache.hadoop.hdfs.util.XMLUtils;
101import org.apache.hadoop.hdfs.util.XMLUtils.InvalidXmlException;
102import org.apache.hadoop.hdfs.util.XMLUtils.Stanza;
103import org.apache.hadoop.io.ArrayWritable;
104import org.apache.hadoop.io.BytesWritable;
105import org.apache.hadoop.io.DataOutputBuffer;
106import org.apache.hadoop.io.IOUtils;
107import org.apache.hadoop.io.Text;
108import org.apache.hadoop.io.Writable;
109import org.apache.hadoop.io.WritableFactories;
110import org.apache.hadoop.io.WritableFactory;
111import org.apache.hadoop.ipc.ClientId;
112import org.apache.hadoop.ipc.RpcConstants;
113import org.apache.hadoop.security.token.delegation.DelegationKey;
114import org.apache.hadoop.util.PureJavaCrc32;
115import org.xml.sax.ContentHandler;
116import org.xml.sax.SAXException;
117import org.xml.sax.helpers.AttributesImpl;
118
119import com.google.common.annotations.VisibleForTesting;
120import com.google.common.base.Joiner;
121import com.google.common.base.Preconditions;
122import com.google.common.collect.ImmutableMap;
123import com.google.common.collect.Lists;
124
125/**
126 * Helper classes for reading the ops from an InputStream.
127 * All ops derive from FSEditLogOp and are only
128 * instantiated from Reader#readOp()
129 */
130@InterfaceAudience.Private
131@InterfaceStability.Unstable
132public abstract class FSEditLogOp {
133  public final FSEditLogOpCodes opCode;
134  long txid = HdfsConstants.INVALID_TXID;
135  byte[] rpcClientId = RpcConstants.DUMMY_CLIENT_ID;
136  int rpcCallId = RpcConstants.INVALID_CALL_ID;
137
138  final public static class OpInstanceCache {
139    private final EnumMap<FSEditLogOpCodes, FSEditLogOp> inst =
140        new EnumMap<FSEditLogOpCodes, FSEditLogOp>(FSEditLogOpCodes.class);
141    
142    public OpInstanceCache() {
143      inst.put(OP_ADD, new AddOp());
144      inst.put(OP_CLOSE, new CloseOp());
145      inst.put(OP_SET_REPLICATION, new SetReplicationOp());
146      inst.put(OP_CONCAT_DELETE, new ConcatDeleteOp());
147      inst.put(OP_RENAME_OLD, new RenameOldOp());
148      inst.put(OP_DELETE, new DeleteOp());
149      inst.put(OP_MKDIR, new MkdirOp());
150      inst.put(OP_SET_GENSTAMP_V1, new SetGenstampV1Op());
151      inst.put(OP_SET_PERMISSIONS, new SetPermissionsOp());
152      inst.put(OP_SET_OWNER, new SetOwnerOp());
153      inst.put(OP_SET_NS_QUOTA, new SetNSQuotaOp());
154      inst.put(OP_CLEAR_NS_QUOTA, new ClearNSQuotaOp());
155      inst.put(OP_SET_QUOTA, new SetQuotaOp());
156      inst.put(OP_TIMES, new TimesOp());
157      inst.put(OP_SYMLINK, new SymlinkOp());
158      inst.put(OP_RENAME, new RenameOp());
159      inst.put(OP_REASSIGN_LEASE, new ReassignLeaseOp());
160      inst.put(OP_GET_DELEGATION_TOKEN, new GetDelegationTokenOp());
161      inst.put(OP_RENEW_DELEGATION_TOKEN, new RenewDelegationTokenOp());
162      inst.put(OP_CANCEL_DELEGATION_TOKEN, new CancelDelegationTokenOp());
163      inst.put(OP_UPDATE_MASTER_KEY, new UpdateMasterKeyOp());
164      inst.put(OP_START_LOG_SEGMENT, new LogSegmentOp(OP_START_LOG_SEGMENT));
165      inst.put(OP_END_LOG_SEGMENT, new LogSegmentOp(OP_END_LOG_SEGMENT));
166      inst.put(OP_UPDATE_BLOCKS, new UpdateBlocksOp());
167
168      inst.put(OP_ALLOW_SNAPSHOT, new AllowSnapshotOp());
169      inst.put(OP_DISALLOW_SNAPSHOT, new DisallowSnapshotOp());
170      inst.put(OP_CREATE_SNAPSHOT, new CreateSnapshotOp());
171      inst.put(OP_DELETE_SNAPSHOT, new DeleteSnapshotOp());
172      inst.put(OP_RENAME_SNAPSHOT, new RenameSnapshotOp());
173      inst.put(OP_SET_GENSTAMP_V2, new SetGenstampV2Op());
174      inst.put(OP_ALLOCATE_BLOCK_ID, new AllocateBlockIdOp());
175      inst.put(OP_ADD_BLOCK, new AddBlockOp());
176      inst.put(OP_ADD_CACHE_DIRECTIVE,
177          new AddCacheDirectiveInfoOp());
178      inst.put(OP_MODIFY_CACHE_DIRECTIVE,
179          new ModifyCacheDirectiveInfoOp());
180      inst.put(OP_REMOVE_CACHE_DIRECTIVE,
181          new RemoveCacheDirectiveInfoOp());
182      inst.put(OP_ADD_CACHE_POOL, new AddCachePoolOp());
183      inst.put(OP_MODIFY_CACHE_POOL, new ModifyCachePoolOp());
184      inst.put(OP_REMOVE_CACHE_POOL, new RemoveCachePoolOp());
185
186      inst.put(OP_SET_ACL, new SetAclOp());
187      inst.put(OP_ROLLING_UPGRADE_START, new RollingUpgradeOp(
188          OP_ROLLING_UPGRADE_START, "start"));
189      inst.put(OP_ROLLING_UPGRADE_FINALIZE, new RollingUpgradeOp(
190          OP_ROLLING_UPGRADE_FINALIZE, "finalize"));
191    }
192    
193    public FSEditLogOp get(FSEditLogOpCodes opcode) {
194      return inst.get(opcode);
195    }
196  }
197
198  private static ImmutableMap<String, FsAction> fsActionMap() {
199    ImmutableMap.Builder<String, FsAction> b = ImmutableMap.builder();
200    for (FsAction v : FsAction.values())
201      b.put(v.SYMBOL, v);
202    return b.build();
203  }
204
205  private static final ImmutableMap<String, FsAction> FSACTION_SYMBOL_MAP
206    = fsActionMap();
207
208  /**
209   * Constructor for an EditLog Op. EditLog ops cannot be constructed
210   * directly, but only through Reader#readOp.
211   */
212  @VisibleForTesting
213  protected FSEditLogOp(FSEditLogOpCodes opCode) {
214    this.opCode = opCode;
215  }
216
217  public long getTransactionId() {
218    Preconditions.checkState(txid != HdfsConstants.INVALID_TXID);
219    return txid;
220  }
221
222  public String getTransactionIdStr() {
223    return (txid == HdfsConstants.INVALID_TXID) ? "(none)" : "" + txid;
224  }
225  
226  public boolean hasTransactionId() {
227    return (txid != HdfsConstants.INVALID_TXID);
228  }
229
230  public void setTransactionId(long txid) {
231    this.txid = txid;
232  }
233  
234  public boolean hasRpcIds() {
235    return rpcClientId != RpcConstants.DUMMY_CLIENT_ID
236        && rpcCallId != RpcConstants.INVALID_CALL_ID;
237  }
238  
239  /** this has to be called after calling {@link #hasRpcIds()} */
240  public byte[] getClientId() {
241    Preconditions.checkState(rpcClientId != RpcConstants.DUMMY_CLIENT_ID);
242    return rpcClientId;
243  }
244  
245  public void setRpcClientId(byte[] clientId) {
246    this.rpcClientId = clientId;
247  }
248  
249  /** this has to be called after calling {@link #hasRpcIds()} */
250  public int getCallId() {
251    Preconditions.checkState(rpcCallId != RpcConstants.INVALID_CALL_ID);
252    return rpcCallId;
253  }
254  
255  public void setRpcCallId(int callId) {
256    this.rpcCallId = callId;
257  }
258
259  abstract void readFields(DataInputStream in, int logVersion)
260      throws IOException;
261
262  public abstract void writeFields(DataOutputStream out)
263      throws IOException;
264
265  static interface BlockListUpdatingOp {
266    Block[] getBlocks();
267    String getPath();
268    boolean shouldCompleteLastBlock();
269  }
270  
271  private static void writeRpcIds(final byte[] clientId, final int callId,
272      DataOutputStream out) throws IOException {
273    FSImageSerialization.writeBytes(clientId, out);
274    FSImageSerialization.writeInt(callId, out);
275  }
276  
277  void readRpcIds(DataInputStream in, int logVersion)
278      throws IOException {
279    if (NameNodeLayoutVersion.supports(
280        LayoutVersion.Feature.EDITLOG_SUPPORT_RETRYCACHE, logVersion)) {
281      this.rpcClientId = FSImageSerialization.readBytes(in);
282      this.rpcCallId = FSImageSerialization.readInt(in);
283    }
284  }
285  
286  void readRpcIdsFromXml(Stanza st) {
287    this.rpcClientId = st.hasChildren("RPC_CLIENTID") ? 
288        ClientId.toBytes(st.getValue("RPC_CLIENTID"))
289        : RpcConstants.DUMMY_CLIENT_ID;
290    this.rpcCallId = st.hasChildren("RPC_CALLID") ? 
291        Integer.valueOf(st.getValue("RPC_CALLID"))
292        : RpcConstants.INVALID_CALL_ID;
293  }
294  
295  private static void appendRpcIdsToString(final StringBuilder builder,
296      final byte[] clientId, final int callId) {
297    builder.append(", RpcClientId=");
298    builder.append(ClientId.toString(clientId));
299    builder.append(", RpcCallId=");
300    builder.append(callId);
301  }
302  
303  private static void appendRpcIdsToXml(ContentHandler contentHandler,
304      final byte[] clientId, final int callId) throws SAXException {
305    XMLUtils.addSaxString(contentHandler, "RPC_CLIENTID",
306        ClientId.toString(clientId));
307    XMLUtils.addSaxString(contentHandler, "RPC_CALLID", 
308        Integer.valueOf(callId).toString());
309  }
310
311  private static final class AclEditLogUtil {
312    private static final int ACL_EDITLOG_ENTRY_HAS_NAME_OFFSET = 6;
313    private static final int ACL_EDITLOG_ENTRY_TYPE_OFFSET = 3;
314    private static final int ACL_EDITLOG_ENTRY_SCOPE_OFFSET = 5;
315    private static final int ACL_EDITLOG_PERM_MASK = 7;
316    private static final int ACL_EDITLOG_ENTRY_TYPE_MASK = 3;
317    private static final int ACL_EDITLOG_ENTRY_SCOPE_MASK = 1;
318
319    private static final FsAction[] FSACTION_VALUES = FsAction.values();
320    private static final AclEntryScope[] ACL_ENTRY_SCOPE_VALUES = AclEntryScope
321        .values();
322    private static final AclEntryType[] ACL_ENTRY_TYPE_VALUES = AclEntryType
323        .values();
324
325    private static List<AclEntry> read(DataInputStream in, int logVersion)
326        throws IOException {
327      if (!NameNodeLayoutVersion.supports(Feature.EXTENDED_ACL, logVersion)) {
328        return null;
329      }
330
331      int size = in.readInt();
332      if (size == 0) {
333        return null;
334      }
335
336      List<AclEntry> aclEntries = Lists.newArrayListWithCapacity(size);
337      for (int i = 0; i < size; ++i) {
338        int v = in.read();
339        int p = v & ACL_EDITLOG_PERM_MASK;
340        int t = (v >> ACL_EDITLOG_ENTRY_TYPE_OFFSET)
341            & ACL_EDITLOG_ENTRY_TYPE_MASK;
342        int s = (v >> ACL_EDITLOG_ENTRY_SCOPE_OFFSET)
343            & ACL_EDITLOG_ENTRY_SCOPE_MASK;
344        boolean hasName = ((v >> ACL_EDITLOG_ENTRY_HAS_NAME_OFFSET) & 1) == 1;
345        String name = hasName ? FSImageSerialization.readString(in) : null;
346        aclEntries.add(new AclEntry.Builder().setName(name)
347            .setPermission(FSACTION_VALUES[p])
348            .setScope(ACL_ENTRY_SCOPE_VALUES[s])
349            .setType(ACL_ENTRY_TYPE_VALUES[t]).build());
350      }
351
352      return aclEntries;
353    }
354
355    private static void write(List<AclEntry> aclEntries, DataOutputStream out)
356        throws IOException {
357      if (aclEntries == null) {
358        out.writeInt(0);
359        return;
360      }
361
362      out.writeInt(aclEntries.size());
363      for (AclEntry e : aclEntries) {
364        boolean hasName = e.getName() != null;
365        int v = (e.getScope().ordinal() << ACL_EDITLOG_ENTRY_SCOPE_OFFSET)
366            | (e.getType().ordinal() << ACL_EDITLOG_ENTRY_TYPE_OFFSET)
367            | e.getPermission().ordinal();
368
369        if (hasName) {
370          v |= 1 << ACL_EDITLOG_ENTRY_HAS_NAME_OFFSET;
371        }
372        out.write(v);
373        if (hasName) {
374          FSImageSerialization.writeString(e.getName(), out);
375        }
376      }
377    }
378  }
379
380  @SuppressWarnings("unchecked")
381  static abstract class AddCloseOp extends FSEditLogOp implements BlockListUpdatingOp {
382    int length;
383    long inodeId;
384    String path;
385    short replication;
386    long mtime;
387    long atime;
388    long blockSize;
389    Block[] blocks;
390    PermissionStatus permissions;
391    List<AclEntry> aclEntries;
392    String clientName;
393    String clientMachine;
394    
395    private AddCloseOp(FSEditLogOpCodes opCode) {
396      super(opCode);
397      assert(opCode == OP_ADD || opCode == OP_CLOSE);
398    }
399    
400    <T extends AddCloseOp> T setInodeId(long inodeId) {
401      this.inodeId = inodeId;
402      return (T)this;
403    }
404
405    <T extends AddCloseOp> T setPath(String path) {
406      this.path = path;
407      return (T)this;
408    }
409    
410    @Override
411    public String getPath() {
412      return path;
413    }
414
415    <T extends AddCloseOp> T setReplication(short replication) {
416      this.replication = replication;
417      return (T)this;
418    }
419
420    <T extends AddCloseOp> T setModificationTime(long mtime) {
421      this.mtime = mtime;
422      return (T)this;
423    }
424
425    <T extends AddCloseOp> T setAccessTime(long atime) {
426      this.atime = atime;
427      return (T)this;
428    }
429
430    <T extends AddCloseOp> T setBlockSize(long blockSize) {
431      this.blockSize = blockSize;
432      return (T)this;
433    }
434
435    <T extends AddCloseOp> T setBlocks(Block[] blocks) {
436      if (blocks.length > MAX_BLOCKS) {
437        throw new RuntimeException("Can't have more than " + MAX_BLOCKS +
438            " in an AddCloseOp.");
439      }
440      this.blocks = blocks;
441      return (T)this;
442    }
443    
444    @Override
445    public Block[] getBlocks() {
446      return blocks;
447    }
448
449    <T extends AddCloseOp> T setPermissionStatus(PermissionStatus permissions) {
450      this.permissions = permissions;
451      return (T)this;
452    }
453
454    <T extends AddCloseOp> T setAclEntries(List<AclEntry> aclEntries) {
455      this.aclEntries = aclEntries;
456      return (T)this;
457    }
458
459    <T extends AddCloseOp> T setClientName(String clientName) {
460      this.clientName = clientName;
461      return (T)this;
462    }
463
464    <T extends AddCloseOp> T setClientMachine(String clientMachine) {
465      this.clientMachine = clientMachine;
466      return (T)this;
467    }
468
469    @Override
470    public void writeFields(DataOutputStream out) throws IOException {
471      FSImageSerialization.writeLong(inodeId, out);
472      FSImageSerialization.writeString(path, out);
473      FSImageSerialization.writeShort(replication, out);
474      FSImageSerialization.writeLong(mtime, out);
475      FSImageSerialization.writeLong(atime, out);
476      FSImageSerialization.writeLong(blockSize, out);
477      new ArrayWritable(Block.class, blocks).write(out);
478      permissions.write(out);
479
480      if (this.opCode == OP_ADD) {
481        AclEditLogUtil.write(aclEntries, out);
482        FSImageSerialization.writeString(clientName,out);
483        FSImageSerialization.writeString(clientMachine,out);
484        // write clientId and callId
485        writeRpcIds(rpcClientId, rpcCallId, out);
486      }
487    }
488
489    @Override
490    void readFields(DataInputStream in, int logVersion)
491        throws IOException {
492      if (!NameNodeLayoutVersion.supports(
493          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
494        this.length = in.readInt();
495      }
496      if (NameNodeLayoutVersion.supports(
497          LayoutVersion.Feature.ADD_INODE_ID, logVersion)) {
498        this.inodeId = in.readLong();
499      } else {
500        // The inodeId should be updated when this editLogOp is applied
501        this.inodeId = INodeId.GRANDFATHER_INODE_ID;
502      }
503      if ((-17 < logVersion && length != 4) ||
504          (logVersion <= -17 && length != 5 && !NameNodeLayoutVersion.supports(
505              LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion))) {
506        throw new IOException("Incorrect data format."  +
507                              " logVersion is " + logVersion +
508                              " but writables.length is " +
509                              length + ". ");
510      }
511      this.path = FSImageSerialization.readString(in);
512
513      if (NameNodeLayoutVersion.supports(
514          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
515        this.replication = FSImageSerialization.readShort(in);
516        this.mtime = FSImageSerialization.readLong(in);
517      } else {
518        this.replication = readShort(in);
519        this.mtime = readLong(in);
520      }
521
522      if (NameNodeLayoutVersion.supports(
523          LayoutVersion.Feature.FILE_ACCESS_TIME, logVersion)) {
524        if (NameNodeLayoutVersion.supports(
525            LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
526          this.atime = FSImageSerialization.readLong(in);
527        } else {
528          this.atime = readLong(in);
529        }
530      } else {
531        this.atime = 0;
532      }
533
534      if (NameNodeLayoutVersion.supports(
535          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
536        this.blockSize = FSImageSerialization.readLong(in);
537      } else {
538        this.blockSize = readLong(in);
539      }
540
541      this.blocks = readBlocks(in, logVersion);
542      this.permissions = PermissionStatus.read(in);
543
544      // clientname, clientMachine and block locations of last block.
545      if (this.opCode == OP_ADD) {
546        aclEntries = AclEditLogUtil.read(in, logVersion);
547        this.clientName = FSImageSerialization.readString(in);
548        this.clientMachine = FSImageSerialization.readString(in);
549        // read clientId and callId
550        readRpcIds(in, logVersion);
551      } else {
552        this.clientName = "";
553        this.clientMachine = "";
554      }
555    }
556
557    static final public int MAX_BLOCKS = 1024 * 1024 * 64;
558    
559    private static Block[] readBlocks(
560        DataInputStream in,
561        int logVersion) throws IOException {
562      int numBlocks = in.readInt();
563      if (numBlocks < 0) {
564        throw new IOException("invalid negative number of blocks");
565      } else if (numBlocks > MAX_BLOCKS) {
566        throw new IOException("invalid number of blocks: " + numBlocks +
567            ".  The maximum number of blocks per file is " + MAX_BLOCKS);
568      }
569      Block[] blocks = new Block[numBlocks];
570      for (int i = 0; i < numBlocks; i++) {
571        Block blk = new Block();
572        blk.readFields(in);
573        blocks[i] = blk;
574      }
575      return blocks;
576    }
577
578    public String stringifyMembers() {
579      StringBuilder builder = new StringBuilder();
580      builder.append("[length=");
581      builder.append(length);
582      builder.append(", inodeId=");
583      builder.append(inodeId);
584      builder.append(", path=");
585      builder.append(path);
586      builder.append(", replication=");
587      builder.append(replication);
588      builder.append(", mtime=");
589      builder.append(mtime);
590      builder.append(", atime=");
591      builder.append(atime);
592      builder.append(", blockSize=");
593      builder.append(blockSize);
594      builder.append(", blocks=");
595      builder.append(Arrays.toString(blocks));
596      builder.append(", permissions=");
597      builder.append(permissions);
598      builder.append(", aclEntries=");
599      builder.append(aclEntries);
600      builder.append(", clientName=");
601      builder.append(clientName);
602      builder.append(", clientMachine=");
603      builder.append(clientMachine);
604      if (this.opCode == OP_ADD) {
605        appendRpcIdsToString(builder, rpcClientId, rpcCallId);
606      }
607      builder.append(", opCode=");
608      builder.append(opCode);
609      builder.append(", txid=");
610      builder.append(txid);
611      builder.append("]");
612      return builder.toString();
613    }
614    
615    @Override
616    protected void toXml(ContentHandler contentHandler) throws SAXException {
617      XMLUtils.addSaxString(contentHandler, "LENGTH",
618          Integer.valueOf(length).toString());
619      XMLUtils.addSaxString(contentHandler, "INODEID",
620          Long.valueOf(inodeId).toString());
621      XMLUtils.addSaxString(contentHandler, "PATH", path);
622      XMLUtils.addSaxString(contentHandler, "REPLICATION",
623          Short.valueOf(replication).toString());
624      XMLUtils.addSaxString(contentHandler, "MTIME",
625          Long.valueOf(mtime).toString());
626      XMLUtils.addSaxString(contentHandler, "ATIME",
627          Long.valueOf(atime).toString());
628      XMLUtils.addSaxString(contentHandler, "BLOCKSIZE",
629          Long.valueOf(blockSize).toString());
630      XMLUtils.addSaxString(contentHandler, "CLIENT_NAME", clientName);
631      XMLUtils.addSaxString(contentHandler, "CLIENT_MACHINE", clientMachine);
632      for (Block b : blocks) {
633        FSEditLogOp.blockToXml(contentHandler, b);
634      }
635      FSEditLogOp.permissionStatusToXml(contentHandler, permissions);
636      if (this.opCode == OP_ADD) {
637        if (aclEntries != null) {
638          appendAclEntriesToXml(contentHandler, aclEntries);
639        }
640        appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
641      }
642    }
643
644    @Override 
645    void fromXml(Stanza st) throws InvalidXmlException {
646      this.length = Integer.valueOf(st.getValue("LENGTH"));
647      this.inodeId = Long.valueOf(st.getValue("INODEID"));
648      this.path = st.getValue("PATH");
649      this.replication = Short.valueOf(st.getValue("REPLICATION"));
650      this.mtime = Long.valueOf(st.getValue("MTIME"));
651      this.atime = Long.valueOf(st.getValue("ATIME"));
652      this.blockSize = Long.valueOf(st.getValue("BLOCKSIZE"));
653      this.clientName = st.getValue("CLIENT_NAME");
654      this.clientMachine = st.getValue("CLIENT_MACHINE");
655      if (st.hasChildren("BLOCK")) {
656        List<Stanza> blocks = st.getChildren("BLOCK");
657        this.blocks = new Block[blocks.size()];
658        for (int i = 0; i < blocks.size(); i++) {
659          this.blocks[i] = FSEditLogOp.blockFromXml(blocks.get(i));
660        }
661      } else {
662        this.blocks = new Block[0];
663      }
664      this.permissions = permissionStatusFromXml(st);
665      aclEntries = readAclEntriesFromXml(st);
666      readRpcIdsFromXml(st);
667    }
668  }
669
670  /**
671   * {@literal @AtMostOnce} for {@link ClientProtocol#startFile} and
672   * {@link ClientProtocol#appendFile}
673   */
674  static class AddOp extends AddCloseOp {
675    private AddOp() {
676      super(OP_ADD);
677    }
678
679    static AddOp getInstance(OpInstanceCache cache) {
680      return (AddOp)cache.get(OP_ADD);
681    }
682
683    @Override
684    public boolean shouldCompleteLastBlock() {
685      return false;
686    }
687
688    @Override
689    public String toString() {
690      StringBuilder builder = new StringBuilder();
691      builder.append("AddOp ");
692      builder.append(stringifyMembers());
693      return builder.toString();
694    }
695  }
696
697  /**
698   * Although {@link ClientProtocol#appendFile} may also log a close op, we do
699   * not need to record the rpc ids here since a successful appendFile op will
700   * finally log an AddOp.
701   */
702  static class CloseOp extends AddCloseOp {
703    private CloseOp() {
704      super(OP_CLOSE);
705    }
706
707    static CloseOp getInstance(OpInstanceCache cache) {
708      return (CloseOp)cache.get(OP_CLOSE);
709    }
710
711    @Override
712    public boolean shouldCompleteLastBlock() {
713      return true;
714    }
715
716    @Override
717    public String toString() {
718      StringBuilder builder = new StringBuilder();
719      builder.append("CloseOp ");
720      builder.append(stringifyMembers());
721      return builder.toString();
722    }
723  }
724  
725  static class AddBlockOp extends FSEditLogOp {
726    private String path;
727    private Block penultimateBlock;
728    private Block lastBlock;
729    
730    private AddBlockOp() {
731      super(OP_ADD_BLOCK);
732    }
733    
734    static AddBlockOp getInstance(OpInstanceCache cache) {
735      return (AddBlockOp) cache.get(OP_ADD_BLOCK);
736    }
737    
738    AddBlockOp setPath(String path) {
739      this.path = path;
740      return this;
741    }
742    
743    public String getPath() {
744      return path;
745    }
746
747    AddBlockOp setPenultimateBlock(Block pBlock) {
748      this.penultimateBlock = pBlock;
749      return this;
750    }
751    
752    Block getPenultimateBlock() {
753      return penultimateBlock;
754    }
755    
756    AddBlockOp setLastBlock(Block lastBlock) {
757      this.lastBlock = lastBlock;
758      return this;
759    }
760    
761    Block getLastBlock() {
762      return lastBlock;
763    }
764
765    @Override
766    public void writeFields(DataOutputStream out) throws IOException {
767      FSImageSerialization.writeString(path, out);
768      int size = penultimateBlock != null ? 2 : 1;
769      Block[] blocks = new Block[size];
770      if (penultimateBlock != null) {
771        blocks[0] = penultimateBlock;
772      }
773      blocks[size - 1] = lastBlock;
774      FSImageSerialization.writeCompactBlockArray(blocks, out);
775      // clientId and callId
776      writeRpcIds(rpcClientId, rpcCallId, out);
777    }
778    
779    @Override
780    void readFields(DataInputStream in, int logVersion) throws IOException {
781      path = FSImageSerialization.readString(in);
782      Block[] blocks = FSImageSerialization.readCompactBlockArray(in,
783          logVersion);
784      Preconditions.checkState(blocks.length == 2 || blocks.length == 1);
785      penultimateBlock = blocks.length == 1 ? null : blocks[0];
786      lastBlock = blocks[blocks.length - 1];
787      readRpcIds(in, logVersion);
788    }
789
790    @Override
791    public String toString() {
792      StringBuilder sb = new StringBuilder();
793      sb.append("AddBlockOp [path=")
794        .append(path)
795        .append(", penultimateBlock=")
796        .append(penultimateBlock == null ? "NULL" : penultimateBlock)
797        .append(", lastBlock=")
798        .append(lastBlock);
799      appendRpcIdsToString(sb, rpcClientId, rpcCallId);
800      sb.append("]");
801      return sb.toString();
802    }
803    
804    @Override
805    protected void toXml(ContentHandler contentHandler) throws SAXException {
806      XMLUtils.addSaxString(contentHandler, "PATH", path);
807      if (penultimateBlock != null) {
808        FSEditLogOp.blockToXml(contentHandler, penultimateBlock);
809      }
810      FSEditLogOp.blockToXml(contentHandler, lastBlock);
811      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
812    }
813    
814    @Override 
815    void fromXml(Stanza st) throws InvalidXmlException {
816      this.path = st.getValue("PATH");
817      List<Stanza> blocks = st.getChildren("BLOCK");
818      int size = blocks.size();
819      Preconditions.checkState(size == 1 || size == 2);
820      this.penultimateBlock = size == 2 ? 
821          FSEditLogOp.blockFromXml(blocks.get(0)) : null;
822      this.lastBlock = FSEditLogOp.blockFromXml(blocks.get(size - 1));
823      readRpcIdsFromXml(st);
824    }
825  }
826  
827  /**
828   * {@literal @AtMostOnce} for {@link ClientProtocol#updatePipeline}, but 
829   * {@literal @Idempotent} for some other ops.
830   */
831  static class UpdateBlocksOp extends FSEditLogOp implements BlockListUpdatingOp {
832    String path;
833    Block[] blocks;
834    
835    private UpdateBlocksOp() {
836      super(OP_UPDATE_BLOCKS);
837    }
838    
839    static UpdateBlocksOp getInstance(OpInstanceCache cache) {
840      return (UpdateBlocksOp)cache.get(OP_UPDATE_BLOCKS);
841    }
842    
843    UpdateBlocksOp setPath(String path) {
844      this.path = path;
845      return this;
846    }
847    
848    @Override
849    public String getPath() {
850      return path;
851    }
852
853    UpdateBlocksOp setBlocks(Block[] blocks) {
854      this.blocks = blocks;
855      return this;
856    }
857    
858    @Override
859    public Block[] getBlocks() {
860      return blocks;
861    }
862
863    @Override
864    public
865    void writeFields(DataOutputStream out) throws IOException {
866      FSImageSerialization.writeString(path, out);
867      FSImageSerialization.writeCompactBlockArray(blocks, out);
868      // clientId and callId
869      writeRpcIds(rpcClientId, rpcCallId, out);
870    }
871    
872    @Override
873    void readFields(DataInputStream in, int logVersion) throws IOException {
874      path = FSImageSerialization.readString(in);
875      this.blocks = FSImageSerialization.readCompactBlockArray(
876          in, logVersion);
877      readRpcIds(in, logVersion);
878    }
879
880    @Override
881    public boolean shouldCompleteLastBlock() {
882      return false;
883    }
884
885    @Override
886    public String toString() {
887      StringBuilder sb = new StringBuilder();
888      sb.append("UpdateBlocksOp [path=")
889        .append(path)
890        .append(", blocks=")
891        .append(Arrays.toString(blocks));
892      appendRpcIdsToString(sb, rpcClientId, rpcCallId);
893      sb.append("]");
894      return sb.toString();
895    }
896    
897    @Override
898    protected void toXml(ContentHandler contentHandler) throws SAXException {
899      XMLUtils.addSaxString(contentHandler, "PATH", path);
900      for (Block b : blocks) {
901        FSEditLogOp.blockToXml(contentHandler, b);
902      }
903      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
904    }
905    
906    @Override void fromXml(Stanza st) throws InvalidXmlException {
907      this.path = st.getValue("PATH");
908      List<Stanza> blocks = st.getChildren("BLOCK");
909      this.blocks = new Block[blocks.size()];
910      for (int i = 0; i < blocks.size(); i++) {
911        this.blocks[i] = FSEditLogOp.blockFromXml(blocks.get(i));
912      }
913      readRpcIdsFromXml(st);
914    }
915  }
916
917  /** {@literal @Idempotent} for {@link ClientProtocol#setReplication} */
918  static class SetReplicationOp extends FSEditLogOp {
919    String path;
920    short replication;
921
922    private SetReplicationOp() {
923      super(OP_SET_REPLICATION);
924    }
925
926    static SetReplicationOp getInstance(OpInstanceCache cache) {
927      return (SetReplicationOp)cache.get(OP_SET_REPLICATION);
928    }
929
930    SetReplicationOp setPath(String path) {
931      this.path = path;
932      return this;
933    }
934
935    SetReplicationOp setReplication(short replication) {
936      this.replication = replication;
937      return this;
938    }
939
940    @Override
941    public 
942    void writeFields(DataOutputStream out) throws IOException {
943      FSImageSerialization.writeString(path, out);
944      FSImageSerialization.writeShort(replication, out);
945    }
946    
947    @Override
948    void readFields(DataInputStream in, int logVersion)
949        throws IOException {
950      this.path = FSImageSerialization.readString(in);
951      if (NameNodeLayoutVersion.supports(
952          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
953        this.replication = FSImageSerialization.readShort(in);
954      } else {
955        this.replication = readShort(in);
956      }
957    }
958
959    @Override
960    public String toString() {
961      StringBuilder builder = new StringBuilder();
962      builder.append("SetReplicationOp [path=");
963      builder.append(path);
964      builder.append(", replication=");
965      builder.append(replication);
966      builder.append(", opCode=");
967      builder.append(opCode);
968      builder.append(", txid=");
969      builder.append(txid);
970      builder.append("]");
971      return builder.toString();
972    }
973    
974    @Override
975    protected void toXml(ContentHandler contentHandler) throws SAXException {
976      XMLUtils.addSaxString(contentHandler, "PATH", path);
977      XMLUtils.addSaxString(contentHandler, "REPLICATION",
978          Short.valueOf(replication).toString());
979    }
980    
981    @Override void fromXml(Stanza st) throws InvalidXmlException {
982      this.path = st.getValue("PATH");
983      this.replication = Short.valueOf(st.getValue("REPLICATION"));
984    }
985  }
986
987  /** {@literal @AtMostOnce} for {@link ClientProtocol#concat} */
988  static class ConcatDeleteOp extends FSEditLogOp {
989    int length;
990    String trg;
991    String[] srcs;
992    long timestamp;
993    final static public int MAX_CONCAT_SRC = 1024 * 1024;
994
995    private ConcatDeleteOp() {
996      super(OP_CONCAT_DELETE);
997    }
998
999    static ConcatDeleteOp getInstance(OpInstanceCache cache) {
1000      return (ConcatDeleteOp)cache.get(OP_CONCAT_DELETE);
1001    }
1002
1003    ConcatDeleteOp setTarget(String trg) {
1004      this.trg = trg;
1005      return this;
1006    }
1007
1008    ConcatDeleteOp setSources(String[] srcs) {
1009      if (srcs.length > MAX_CONCAT_SRC) {
1010        throw new RuntimeException("ConcatDeleteOp can only have " +
1011            MAX_CONCAT_SRC + " sources at most.");
1012      }
1013      this.srcs = srcs;
1014
1015      return this;
1016    }
1017
1018    ConcatDeleteOp setTimestamp(long timestamp) {
1019      this.timestamp = timestamp;
1020      return this;
1021    }
1022
1023    @Override
1024    public void writeFields(DataOutputStream out) throws IOException {
1025      FSImageSerialization.writeString(trg, out);
1026            
1027      DeprecatedUTF8 info[] = new DeprecatedUTF8[srcs.length];
1028      int idx = 0;
1029      for(int i=0; i<srcs.length; i++) {
1030        info[idx++] = new DeprecatedUTF8(srcs[i]);
1031      }
1032      new ArrayWritable(DeprecatedUTF8.class, info).write(out);
1033
1034      FSImageSerialization.writeLong(timestamp, out);
1035      
1036      // rpc ids
1037      writeRpcIds(rpcClientId, rpcCallId, out);
1038    }
1039
1040    @Override
1041    void readFields(DataInputStream in, int logVersion)
1042        throws IOException {
1043      if (!NameNodeLayoutVersion.supports(
1044          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1045        this.length = in.readInt();
1046        if (length < 3) { // trg, srcs.., timestamp
1047          throw new IOException("Incorrect data format " +
1048              "for ConcatDeleteOp.");
1049        }
1050      }
1051      this.trg = FSImageSerialization.readString(in);
1052      int srcSize = 0;
1053      if (NameNodeLayoutVersion.supports(
1054          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1055        srcSize = in.readInt();
1056      } else {
1057        srcSize = this.length - 1 - 1; // trg and timestamp
1058      }
1059      if (srcSize < 0) {
1060          throw new IOException("Incorrect data format. "
1061              + "ConcatDeleteOp cannot have a negative number of data " +
1062              " sources.");
1063      } else if (srcSize > MAX_CONCAT_SRC) {
1064          throw new IOException("Incorrect data format. "
1065              + "ConcatDeleteOp can have at most " + MAX_CONCAT_SRC +
1066              " sources, but we tried to have " + (length - 3) + " sources.");
1067      }
1068      this.srcs = new String [srcSize];
1069      for(int i=0; i<srcSize;i++) {
1070        srcs[i]= FSImageSerialization.readString(in);
1071      }
1072      
1073      if (NameNodeLayoutVersion.supports(
1074          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1075        this.timestamp = FSImageSerialization.readLong(in);
1076      } else {
1077        this.timestamp = readLong(in);
1078      }
1079      // read RPC ids if necessary
1080      readRpcIds(in, logVersion);
1081    }
1082
1083    @Override
1084    public String toString() {
1085      StringBuilder builder = new StringBuilder();
1086      builder.append("ConcatDeleteOp [length=");
1087      builder.append(length);
1088      builder.append(", trg=");
1089      builder.append(trg);
1090      builder.append(", srcs=");
1091      builder.append(Arrays.toString(srcs));
1092      builder.append(", timestamp=");
1093      builder.append(timestamp);
1094      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
1095      builder.append(", opCode=");
1096      builder.append(opCode);
1097      builder.append(", txid=");
1098      builder.append(txid);
1099      builder.append("]");
1100      return builder.toString();
1101    }
1102    
1103    @Override
1104    protected void toXml(ContentHandler contentHandler) throws SAXException {
1105      XMLUtils.addSaxString(contentHandler, "LENGTH",
1106          Integer.valueOf(length).toString());
1107      XMLUtils.addSaxString(contentHandler, "TRG", trg);
1108      XMLUtils.addSaxString(contentHandler, "TIMESTAMP",
1109          Long.valueOf(timestamp).toString());
1110      contentHandler.startElement("", "", "SOURCES", new AttributesImpl());
1111      for (int i = 0; i < srcs.length; ++i) {
1112        XMLUtils.addSaxString(contentHandler,
1113            "SOURCE" + (i + 1), srcs[i]);
1114      }
1115      contentHandler.endElement("", "", "SOURCES");
1116      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
1117    }
1118    
1119    @Override void fromXml(Stanza st) throws InvalidXmlException {
1120      this.length = Integer.valueOf(st.getValue("LENGTH"));
1121      this.trg = st.getValue("TRG");
1122      this.timestamp = Long.valueOf(st.getValue("TIMESTAMP"));
1123      List<Stanza> sources = st.getChildren("SOURCES");
1124      int i = 0;
1125      while (true) {
1126        if (!sources.get(0).hasChildren("SOURCE" + (i + 1)))
1127          break;
1128        i++;
1129      }
1130      srcs = new String[i];
1131      for (i = 0; i < srcs.length; i++) {
1132        srcs[i] = sources.get(0).getValue("SOURCE" + (i + 1));
1133      }
1134      readRpcIdsFromXml(st);
1135    }
1136  }
1137
1138  /** {@literal @AtMostOnce} for {@link ClientProtocol#rename} */
1139  static class RenameOldOp extends FSEditLogOp {
1140    int length;
1141    String src;
1142    String dst;
1143    long timestamp;
1144
1145    private RenameOldOp() {
1146      super(OP_RENAME_OLD);
1147    }
1148
1149    static RenameOldOp getInstance(OpInstanceCache cache) {
1150      return (RenameOldOp)cache.get(OP_RENAME_OLD);
1151    }
1152
1153    RenameOldOp setSource(String src) {
1154      this.src = src;
1155      return this;
1156    }
1157
1158    RenameOldOp setDestination(String dst) {
1159      this.dst = dst;
1160      return this;
1161    }
1162
1163    RenameOldOp setTimestamp(long timestamp) {
1164      this.timestamp = timestamp;
1165      return this;
1166    }
1167
1168    @Override
1169    public 
1170    void writeFields(DataOutputStream out) throws IOException {
1171      FSImageSerialization.writeString(src, out);
1172      FSImageSerialization.writeString(dst, out);
1173      FSImageSerialization.writeLong(timestamp, out);
1174      writeRpcIds(rpcClientId, rpcCallId, out);
1175    }
1176
1177    @Override
1178    void readFields(DataInputStream in, int logVersion)
1179        throws IOException {
1180      if (!NameNodeLayoutVersion.supports(
1181          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1182        this.length = in.readInt();
1183        if (this.length != 3) {
1184          throw new IOException("Incorrect data format. "
1185              + "Old rename operation.");
1186        }
1187      }
1188      this.src = FSImageSerialization.readString(in);
1189      this.dst = FSImageSerialization.readString(in);
1190      if (NameNodeLayoutVersion.supports(
1191          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1192        this.timestamp = FSImageSerialization.readLong(in);
1193      } else {
1194        this.timestamp = readLong(in);
1195      }
1196      
1197      // read RPC ids if necessary
1198      readRpcIds(in, logVersion);
1199    }
1200
1201    @Override
1202    public String toString() {
1203      StringBuilder builder = new StringBuilder();
1204      builder.append("RenameOldOp [length=");
1205      builder.append(length);
1206      builder.append(", src=");
1207      builder.append(src);
1208      builder.append(", dst=");
1209      builder.append(dst);
1210      builder.append(", timestamp=");
1211      builder.append(timestamp);
1212      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
1213      builder.append(", opCode=");
1214      builder.append(opCode);
1215      builder.append(", txid=");
1216      builder.append(txid);
1217      builder.append("]");
1218      return builder.toString();
1219    }
1220    
1221    @Override
1222    protected void toXml(ContentHandler contentHandler) throws SAXException {
1223      XMLUtils.addSaxString(contentHandler, "LENGTH",
1224          Integer.valueOf(length).toString());
1225      XMLUtils.addSaxString(contentHandler, "SRC", src);
1226      XMLUtils.addSaxString(contentHandler, "DST", dst);
1227      XMLUtils.addSaxString(contentHandler, "TIMESTAMP",
1228          Long.valueOf(timestamp).toString());
1229      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
1230    }
1231    
1232    @Override 
1233    void fromXml(Stanza st) throws InvalidXmlException {
1234      this.length = Integer.valueOf(st.getValue("LENGTH"));
1235      this.src = st.getValue("SRC");
1236      this.dst = st.getValue("DST");
1237      this.timestamp = Long.valueOf(st.getValue("TIMESTAMP"));
1238      
1239      readRpcIdsFromXml(st);
1240    }
1241  }
1242
1243  /** {@literal @AtMostOnce} for {@link ClientProtocol#delete} */
1244  static class DeleteOp extends FSEditLogOp {
1245    int length;
1246    String path;
1247    long timestamp;
1248
1249    private DeleteOp() {
1250      super(OP_DELETE);
1251    }
1252
1253    static DeleteOp getInstance(OpInstanceCache cache) {
1254      return (DeleteOp)cache.get(OP_DELETE);
1255    }
1256
1257    DeleteOp setPath(String path) {
1258      this.path = path;
1259      return this;
1260    }
1261
1262    DeleteOp setTimestamp(long timestamp) {
1263      this.timestamp = timestamp;
1264      return this;
1265    }
1266
1267    @Override
1268    public 
1269    void writeFields(DataOutputStream out) throws IOException {
1270      FSImageSerialization.writeString(path, out);
1271      FSImageSerialization.writeLong(timestamp, out);
1272      writeRpcIds(rpcClientId, rpcCallId, out);
1273    }
1274
1275    @Override
1276    void readFields(DataInputStream in, int logVersion)
1277        throws IOException {
1278      if (!NameNodeLayoutVersion.supports(
1279          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1280        this.length = in.readInt();
1281        if (this.length != 2) {
1282          throw new IOException("Incorrect data format. " + "delete operation.");
1283        }
1284      }
1285      this.path = FSImageSerialization.readString(in);
1286      if (NameNodeLayoutVersion.supports(
1287          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1288        this.timestamp = FSImageSerialization.readLong(in);
1289      } else {
1290        this.timestamp = readLong(in);
1291      }
1292      // read RPC ids if necessary
1293      readRpcIds(in, logVersion);
1294    }
1295
1296    @Override
1297    public String toString() {
1298      StringBuilder builder = new StringBuilder();
1299      builder.append("DeleteOp [length=");
1300      builder.append(length);
1301      builder.append(", path=");
1302      builder.append(path);
1303      builder.append(", timestamp=");
1304      builder.append(timestamp);
1305      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
1306      builder.append(", opCode=");
1307      builder.append(opCode);
1308      builder.append(", txid=");
1309      builder.append(txid);
1310      builder.append("]");
1311      return builder.toString();
1312    }
1313    
1314    @Override
1315    protected void toXml(ContentHandler contentHandler) throws SAXException {
1316      XMLUtils.addSaxString(contentHandler, "LENGTH",
1317          Integer.valueOf(length).toString());
1318      XMLUtils.addSaxString(contentHandler, "PATH", path);
1319      XMLUtils.addSaxString(contentHandler, "TIMESTAMP",
1320          Long.valueOf(timestamp).toString());
1321      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
1322    }
1323    
1324    @Override void fromXml(Stanza st) throws InvalidXmlException {
1325      this.length = Integer.valueOf(st.getValue("LENGTH"));
1326      this.path = st.getValue("PATH");
1327      this.timestamp = Long.valueOf(st.getValue("TIMESTAMP"));
1328      
1329      readRpcIdsFromXml(st);
1330    }
1331  }
1332
1333  /** {@literal @Idempotent} for {@link ClientProtocol#mkdirs} */
1334  static class MkdirOp extends FSEditLogOp {
1335    int length;
1336    long inodeId;
1337    String path;
1338    long timestamp;
1339    PermissionStatus permissions;
1340    List<AclEntry> aclEntries;
1341
1342    private MkdirOp() {
1343      super(OP_MKDIR);
1344    }
1345    
1346    static MkdirOp getInstance(OpInstanceCache cache) {
1347      return (MkdirOp)cache.get(OP_MKDIR);
1348    }
1349
1350    MkdirOp setInodeId(long inodeId) {
1351      this.inodeId = inodeId;
1352      return this;
1353    }
1354    
1355    MkdirOp setPath(String path) {
1356      this.path = path;
1357      return this;
1358    }
1359
1360    MkdirOp setTimestamp(long timestamp) {
1361      this.timestamp = timestamp;
1362      return this;
1363    }
1364
1365    MkdirOp setPermissionStatus(PermissionStatus permissions) {
1366      this.permissions = permissions;
1367      return this;
1368    }
1369
1370    MkdirOp setAclEntries(List<AclEntry> aclEntries) {
1371      this.aclEntries = aclEntries;
1372      return this;
1373    }
1374
1375    @Override
1376    public 
1377    void writeFields(DataOutputStream out) throws IOException {
1378      FSImageSerialization.writeLong(inodeId, out);
1379      FSImageSerialization.writeString(path, out);
1380      FSImageSerialization.writeLong(timestamp, out); // mtime
1381      FSImageSerialization.writeLong(timestamp, out); // atime, unused at this
1382      permissions.write(out);
1383      AclEditLogUtil.write(aclEntries, out);
1384    }
1385    
1386    @Override
1387    void readFields(DataInputStream in, int logVersion) throws IOException {
1388      if (!NameNodeLayoutVersion.supports(
1389          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1390        this.length = in.readInt();
1391      }
1392      if (-17 < logVersion && length != 2 ||
1393          logVersion <= -17 && length != 3
1394          && !NameNodeLayoutVersion.supports(
1395              LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1396        throw new IOException("Incorrect data format. Mkdir operation.");
1397      }
1398      if (NameNodeLayoutVersion.supports(
1399          LayoutVersion.Feature.ADD_INODE_ID, logVersion)) {
1400        this.inodeId = FSImageSerialization.readLong(in);
1401      } else {
1402        // This id should be updated when this editLogOp is applied
1403        this.inodeId = INodeId.GRANDFATHER_INODE_ID;
1404      }
1405      this.path = FSImageSerialization.readString(in);
1406      if (NameNodeLayoutVersion.supports(
1407          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1408        this.timestamp = FSImageSerialization.readLong(in);
1409      } else {
1410        this.timestamp = readLong(in);
1411      }
1412
1413      // The disk format stores atimes for directories as well.
1414      // However, currently this is not being updated/used because of
1415      // performance reasons.
1416      if (NameNodeLayoutVersion.supports(
1417          LayoutVersion.Feature.FILE_ACCESS_TIME, logVersion)) {
1418        if (NameNodeLayoutVersion.supports(
1419            LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
1420          FSImageSerialization.readLong(in);
1421        } else {
1422          readLong(in);
1423        }
1424      }
1425
1426      this.permissions = PermissionStatus.read(in);
1427      aclEntries = AclEditLogUtil.read(in, logVersion);
1428    }
1429
1430    @Override
1431    public String toString() {
1432      StringBuilder builder = new StringBuilder();
1433      builder.append("MkdirOp [length=");
1434      builder.append(length);
1435      builder.append(", inodeId=");
1436      builder.append(inodeId);
1437      builder.append(", path=");
1438      builder.append(path);
1439      builder.append(", timestamp=");
1440      builder.append(timestamp);
1441      builder.append(", permissions=");
1442      builder.append(permissions);
1443      builder.append(", aclEntries=");
1444      builder.append(aclEntries);
1445      builder.append(", opCode=");
1446      builder.append(opCode);
1447      builder.append(", txid=");
1448      builder.append(txid);
1449      builder.append("]");
1450      return builder.toString();
1451    }
1452
1453    @Override
1454    protected void toXml(ContentHandler contentHandler) throws SAXException {
1455      XMLUtils.addSaxString(contentHandler, "LENGTH",
1456          Integer.valueOf(length).toString());
1457      XMLUtils.addSaxString(contentHandler, "INODEID",
1458          Long.valueOf(inodeId).toString());
1459      XMLUtils.addSaxString(contentHandler, "PATH", path);
1460      XMLUtils.addSaxString(contentHandler, "TIMESTAMP",
1461          Long.valueOf(timestamp).toString());
1462      FSEditLogOp.permissionStatusToXml(contentHandler, permissions);
1463      if (aclEntries != null) {
1464        appendAclEntriesToXml(contentHandler, aclEntries);
1465      }
1466    }
1467    
1468    @Override void fromXml(Stanza st) throws InvalidXmlException {
1469      this.length = Integer.valueOf(st.getValue("LENGTH"));
1470      this.inodeId = Long.valueOf(st.getValue("INODEID"));
1471      this.path = st.getValue("PATH");
1472      this.timestamp = Long.valueOf(st.getValue("TIMESTAMP"));
1473      this.permissions = permissionStatusFromXml(st);
1474      aclEntries = readAclEntriesFromXml(st);
1475    }
1476  }
1477
1478  /**
1479   * The corresponding operations are either {@literal @Idempotent} (
1480   * {@link ClientProtocol#updateBlockForPipeline},
1481   * {@link ClientProtocol#recoverLease}, {@link ClientProtocol#addBlock}) or
1482   * already bound with other editlog op which records rpc ids (
1483   * {@link ClientProtocol#startFile}). Thus no need to record rpc ids here.
1484   */
1485  static class SetGenstampV1Op extends FSEditLogOp {
1486    long genStampV1;
1487
1488    private SetGenstampV1Op() {
1489      super(OP_SET_GENSTAMP_V1);
1490    }
1491
1492    static SetGenstampV1Op getInstance(OpInstanceCache cache) {
1493      return (SetGenstampV1Op)cache.get(OP_SET_GENSTAMP_V1);
1494    }
1495
1496    SetGenstampV1Op setGenerationStamp(long genStamp) {
1497      this.genStampV1 = genStamp;
1498      return this;
1499    }
1500
1501    @Override
1502    public
1503    void writeFields(DataOutputStream out) throws IOException {
1504      FSImageSerialization.writeLong(genStampV1, out);
1505    }
1506
1507    @Override
1508    void readFields(DataInputStream in, int logVersion)
1509        throws IOException {
1510      this.genStampV1 = FSImageSerialization.readLong(in);
1511    }
1512
1513    @Override
1514    public String toString() {
1515      StringBuilder builder = new StringBuilder();
1516      builder.append("SetGenstampOp [GenStamp=");
1517      builder.append(genStampV1);
1518      builder.append(", opCode=");
1519      builder.append(opCode);
1520      builder.append(", txid=");
1521      builder.append(txid);
1522      builder.append("]");
1523      return builder.toString();
1524    }
1525
1526    @Override
1527    protected void toXml(ContentHandler contentHandler) throws SAXException {
1528      XMLUtils.addSaxString(contentHandler, "GENSTAMP",
1529                            Long.valueOf(genStampV1).toString());
1530    }
1531
1532    @Override void fromXml(Stanza st) throws InvalidXmlException {
1533      this.genStampV1 = Long.valueOf(st.getValue("GENSTAMP"));
1534    }
1535  }
1536
1537  /** Similar with {@link SetGenstampV1Op} */
1538  static class SetGenstampV2Op extends FSEditLogOp {
1539    long genStampV2;
1540
1541    private SetGenstampV2Op() {
1542      super(OP_SET_GENSTAMP_V2);
1543    }
1544
1545    static SetGenstampV2Op getInstance(OpInstanceCache cache) {
1546      return (SetGenstampV2Op)cache.get(OP_SET_GENSTAMP_V2);
1547    }
1548
1549    SetGenstampV2Op setGenerationStamp(long genStamp) {
1550      this.genStampV2 = genStamp;
1551      return this;
1552    }
1553
1554    @Override
1555    public
1556    void writeFields(DataOutputStream out) throws IOException {
1557      FSImageSerialization.writeLong(genStampV2, out);
1558    }
1559
1560    @Override
1561    void readFields(DataInputStream in, int logVersion)
1562        throws IOException {
1563      this.genStampV2 = FSImageSerialization.readLong(in);
1564    }
1565
1566    @Override
1567    public String toString() {
1568      StringBuilder builder = new StringBuilder();
1569      builder.append("SetGenstampV2Op [GenStampV2=");
1570      builder.append(genStampV2);
1571      builder.append(", opCode=");
1572      builder.append(opCode);
1573      builder.append(", txid=");
1574      builder.append(txid);
1575      builder.append("]");
1576      return builder.toString();
1577    }
1578
1579    @Override
1580    protected void toXml(ContentHandler contentHandler) throws SAXException {
1581      XMLUtils.addSaxString(contentHandler, "GENSTAMPV2",
1582                            Long.valueOf(genStampV2).toString());
1583    }
1584
1585    @Override void fromXml(Stanza st) throws InvalidXmlException {
1586      this.genStampV2 = Long.valueOf(st.getValue("GENSTAMPV2"));
1587    }
1588  }
1589
1590  /** {@literal @Idempotent} for {@link ClientProtocol#addBlock} */
1591  static class AllocateBlockIdOp extends FSEditLogOp {
1592    long blockId;
1593
1594    private AllocateBlockIdOp() {
1595      super(OP_ALLOCATE_BLOCK_ID);
1596    }
1597
1598    static AllocateBlockIdOp getInstance(OpInstanceCache cache) {
1599      return (AllocateBlockIdOp)cache.get(OP_ALLOCATE_BLOCK_ID);
1600    }
1601
1602    AllocateBlockIdOp setBlockId(long blockId) {
1603      this.blockId = blockId;
1604      return this;
1605    }
1606
1607    @Override
1608    public
1609    void writeFields(DataOutputStream out) throws IOException {
1610      FSImageSerialization.writeLong(blockId, out);
1611    }
1612
1613    @Override
1614    void readFields(DataInputStream in, int logVersion)
1615        throws IOException {
1616      this.blockId = FSImageSerialization.readLong(in);
1617    }
1618
1619    @Override
1620    public String toString() {
1621      StringBuilder builder = new StringBuilder();
1622      builder.append("AllocateBlockIdOp [blockId=");
1623      builder.append(blockId);
1624      builder.append(", opCode=");
1625      builder.append(opCode);
1626      builder.append(", txid=");
1627      builder.append(txid);
1628      builder.append("]");
1629      return builder.toString();
1630    }
1631
1632    @Override
1633    protected void toXml(ContentHandler contentHandler) throws SAXException {
1634      XMLUtils.addSaxString(contentHandler, "BLOCK_ID",
1635                            Long.valueOf(blockId).toString());
1636    }
1637
1638    @Override void fromXml(Stanza st) throws InvalidXmlException {
1639      this.blockId = Long.valueOf(st.getValue("BLOCK_ID"));
1640    }
1641  }
1642
1643  /** {@literal @Idempotent} for {@link ClientProtocol#setPermission} */
1644  static class SetPermissionsOp extends FSEditLogOp {
1645    String src;
1646    FsPermission permissions;
1647
1648    private SetPermissionsOp() {
1649      super(OP_SET_PERMISSIONS);
1650    }
1651
1652    static SetPermissionsOp getInstance(OpInstanceCache cache) {
1653      return (SetPermissionsOp)cache.get(OP_SET_PERMISSIONS);
1654    }
1655
1656    SetPermissionsOp setSource(String src) {
1657      this.src = src;
1658      return this;
1659    }
1660
1661    SetPermissionsOp setPermissions(FsPermission permissions) {
1662      this.permissions = permissions;
1663      return this;
1664    }
1665
1666    @Override
1667    public 
1668    void writeFields(DataOutputStream out) throws IOException {
1669      FSImageSerialization.writeString(src, out);
1670      permissions.write(out);
1671     }
1672 
1673    @Override
1674    void readFields(DataInputStream in, int logVersion)
1675        throws IOException {
1676      this.src = FSImageSerialization.readString(in);
1677      this.permissions = FsPermission.read(in);
1678    }
1679
1680    @Override
1681    public String toString() {
1682      StringBuilder builder = new StringBuilder();
1683      builder.append("SetPermissionsOp [src=");
1684      builder.append(src);
1685      builder.append(", permissions=");
1686      builder.append(permissions);
1687      builder.append(", opCode=");
1688      builder.append(opCode);
1689      builder.append(", txid=");
1690      builder.append(txid);
1691      builder.append("]");
1692      return builder.toString();
1693    }
1694    
1695    @Override
1696    protected void toXml(ContentHandler contentHandler) throws SAXException {
1697      XMLUtils.addSaxString(contentHandler, "SRC", src);
1698      XMLUtils.addSaxString(contentHandler, "MODE",
1699          Short.valueOf(permissions.toShort()).toString());
1700    }
1701    
1702    @Override void fromXml(Stanza st) throws InvalidXmlException {
1703      this.src = st.getValue("SRC");
1704      this.permissions = new FsPermission(
1705          Short.valueOf(st.getValue("MODE")));
1706    }
1707  }
1708
1709  /** {@literal @Idempotent} for {@link ClientProtocol#setOwner} */
1710  static class SetOwnerOp extends FSEditLogOp {
1711    String src;
1712    String username;
1713    String groupname;
1714
1715    private SetOwnerOp() {
1716      super(OP_SET_OWNER);
1717    }
1718
1719    static SetOwnerOp getInstance(OpInstanceCache cache) {
1720      return (SetOwnerOp)cache.get(OP_SET_OWNER);
1721    }
1722
1723    SetOwnerOp setSource(String src) {
1724      this.src = src;
1725      return this;
1726    }
1727
1728    SetOwnerOp setUser(String username) {
1729      this.username = username;
1730      return this;
1731    }
1732
1733    SetOwnerOp setGroup(String groupname) {
1734      this.groupname = groupname;
1735      return this;
1736    }
1737
1738    @Override
1739    public 
1740    void writeFields(DataOutputStream out) throws IOException {
1741      FSImageSerialization.writeString(src, out);
1742      FSImageSerialization.writeString(username == null ? "" : username, out);
1743      FSImageSerialization.writeString(groupname == null ? "" : groupname, out);
1744    }
1745
1746    @Override
1747    void readFields(DataInputStream in, int logVersion)
1748        throws IOException {
1749      this.src = FSImageSerialization.readString(in);
1750      this.username = FSImageSerialization.readString_EmptyAsNull(in);
1751      this.groupname = FSImageSerialization.readString_EmptyAsNull(in);
1752    }
1753
1754    @Override
1755    public String toString() {
1756      StringBuilder builder = new StringBuilder();
1757      builder.append("SetOwnerOp [src=");
1758      builder.append(src);
1759      builder.append(", username=");
1760      builder.append(username);
1761      builder.append(", groupname=");
1762      builder.append(groupname);
1763      builder.append(", opCode=");
1764      builder.append(opCode);
1765      builder.append(", txid=");
1766      builder.append(txid);
1767      builder.append("]");
1768      return builder.toString();
1769    }
1770    
1771    @Override
1772    protected void toXml(ContentHandler contentHandler) throws SAXException {
1773      XMLUtils.addSaxString(contentHandler, "SRC", src);
1774      if (username != null) {
1775        XMLUtils.addSaxString(contentHandler, "USERNAME", username);
1776      }
1777      if (groupname != null) {
1778        XMLUtils.addSaxString(contentHandler, "GROUPNAME", groupname);
1779      }
1780    }
1781    
1782    @Override void fromXml(Stanza st) throws InvalidXmlException {
1783      this.src = st.getValue("SRC");
1784      this.username = (st.hasChildren("USERNAME")) ? 
1785          st.getValue("USERNAME") : null;
1786      this.groupname = (st.hasChildren("GROUPNAME")) ? 
1787          st.getValue("GROUPNAME") : null;
1788    }
1789  }
1790  
1791  static class SetNSQuotaOp extends FSEditLogOp {
1792    String src;
1793    long nsQuota;
1794
1795    private SetNSQuotaOp() {
1796      super(OP_SET_NS_QUOTA);
1797    }
1798
1799    static SetNSQuotaOp getInstance(OpInstanceCache cache) {
1800      return (SetNSQuotaOp)cache.get(OP_SET_NS_QUOTA);
1801    }
1802
1803    @Override
1804    public 
1805    void writeFields(DataOutputStream out) throws IOException {
1806      throw new IOException("Deprecated");      
1807    }
1808
1809    @Override
1810    void readFields(DataInputStream in, int logVersion)
1811        throws IOException {
1812      this.src = FSImageSerialization.readString(in);
1813      this.nsQuota = FSImageSerialization.readLong(in);
1814    }
1815
1816    @Override
1817    public String toString() {
1818      StringBuilder builder = new StringBuilder();
1819      builder.append("SetNSQuotaOp [src=");
1820      builder.append(src);
1821      builder.append(", nsQuota=");
1822      builder.append(nsQuota);
1823      builder.append(", opCode=");
1824      builder.append(opCode);
1825      builder.append(", txid=");
1826      builder.append(txid);
1827      builder.append("]");
1828      return builder.toString();
1829    }
1830    
1831    @Override
1832    protected void toXml(ContentHandler contentHandler) throws SAXException {
1833      XMLUtils.addSaxString(contentHandler, "SRC", src);
1834      XMLUtils.addSaxString(contentHandler, "NSQUOTA",
1835          Long.valueOf(nsQuota).toString());
1836    }
1837    
1838    @Override void fromXml(Stanza st) throws InvalidXmlException {
1839      this.src = st.getValue("SRC");
1840      this.nsQuota = Long.valueOf(st.getValue("NSQUOTA"));
1841    }
1842  }
1843
1844  static class ClearNSQuotaOp extends FSEditLogOp {
1845    String src;
1846
1847    private ClearNSQuotaOp() {
1848      super(OP_CLEAR_NS_QUOTA);
1849    }
1850
1851    static ClearNSQuotaOp getInstance(OpInstanceCache cache) {
1852      return (ClearNSQuotaOp)cache.get(OP_CLEAR_NS_QUOTA);
1853    }
1854
1855    @Override
1856    public 
1857    void writeFields(DataOutputStream out) throws IOException {
1858      throw new IOException("Deprecated");      
1859    }
1860
1861    @Override
1862    void readFields(DataInputStream in, int logVersion)
1863        throws IOException {
1864      this.src = FSImageSerialization.readString(in);
1865    }
1866
1867    @Override
1868    public String toString() {
1869      StringBuilder builder = new StringBuilder();
1870      builder.append("ClearNSQuotaOp [src=");
1871      builder.append(src);
1872      builder.append(", opCode=");
1873      builder.append(opCode);
1874      builder.append(", txid=");
1875      builder.append(txid);
1876      builder.append("]");
1877      return builder.toString();
1878    }
1879    
1880    @Override
1881    protected void toXml(ContentHandler contentHandler) throws SAXException {
1882      XMLUtils.addSaxString(contentHandler, "SRC", src);
1883    }
1884    
1885    @Override void fromXml(Stanza st) throws InvalidXmlException {
1886      this.src = st.getValue("SRC");
1887    }
1888  }
1889
1890  /** {@literal @Idempotent} for {@link ClientProtocol#setQuota} */
1891  static class SetQuotaOp extends FSEditLogOp {
1892    String src;
1893    long nsQuota;
1894    long dsQuota;
1895
1896    private SetQuotaOp() {
1897      super(OP_SET_QUOTA);
1898    }
1899
1900    static SetQuotaOp getInstance(OpInstanceCache cache) {
1901      return (SetQuotaOp)cache.get(OP_SET_QUOTA);
1902    }
1903
1904    SetQuotaOp setSource(String src) {
1905      this.src = src;
1906      return this;
1907    }
1908
1909    SetQuotaOp setNSQuota(long nsQuota) {
1910      this.nsQuota = nsQuota;
1911      return this;
1912    }
1913
1914    SetQuotaOp setDSQuota(long dsQuota) {
1915      this.dsQuota = dsQuota;
1916      return this;
1917    }
1918
1919    @Override
1920    public 
1921    void writeFields(DataOutputStream out) throws IOException {
1922      FSImageSerialization.writeString(src, out);
1923      FSImageSerialization.writeLong(nsQuota, out);
1924      FSImageSerialization.writeLong(dsQuota, out);
1925    }
1926
1927    @Override
1928    void readFields(DataInputStream in, int logVersion)
1929        throws IOException {
1930      this.src = FSImageSerialization.readString(in);
1931      this.nsQuota = FSImageSerialization.readLong(in);
1932      this.dsQuota = FSImageSerialization.readLong(in);
1933    }
1934
1935    @Override
1936    public String toString() {
1937      StringBuilder builder = new StringBuilder();
1938      builder.append("SetQuotaOp [src=");
1939      builder.append(src);
1940      builder.append(", nsQuota=");
1941      builder.append(nsQuota);
1942      builder.append(", dsQuota=");
1943      builder.append(dsQuota);
1944      builder.append(", opCode=");
1945      builder.append(opCode);
1946      builder.append(", txid=");
1947      builder.append(txid);
1948      builder.append("]");
1949      return builder.toString();
1950    }
1951    
1952    @Override
1953    protected void toXml(ContentHandler contentHandler) throws SAXException {
1954      XMLUtils.addSaxString(contentHandler, "SRC", src);
1955      XMLUtils.addSaxString(contentHandler, "NSQUOTA",
1956          Long.valueOf(nsQuota).toString());
1957      XMLUtils.addSaxString(contentHandler, "DSQUOTA",
1958          Long.valueOf(dsQuota).toString());
1959    }
1960    
1961    @Override void fromXml(Stanza st) throws InvalidXmlException {
1962      this.src = st.getValue("SRC");
1963      this.nsQuota = Long.valueOf(st.getValue("NSQUOTA"));
1964      this.dsQuota = Long.valueOf(st.getValue("DSQUOTA"));
1965    }
1966  }
1967
1968  /** {@literal @Idempotent} for {@link ClientProtocol#setTimes} */
1969  static class TimesOp extends FSEditLogOp {
1970    int length;
1971    String path;
1972    long mtime;
1973    long atime;
1974
1975    private TimesOp() {
1976      super(OP_TIMES);
1977    }
1978
1979    static TimesOp getInstance(OpInstanceCache cache) {
1980      return (TimesOp)cache.get(OP_TIMES);
1981    }
1982
1983    TimesOp setPath(String path) {
1984      this.path = path;
1985      return this;
1986    }
1987
1988    TimesOp setModificationTime(long mtime) {
1989      this.mtime = mtime;
1990      return this;
1991    }
1992
1993    TimesOp setAccessTime(long atime) {
1994      this.atime = atime;
1995      return this;
1996    }
1997
1998    @Override
1999    public 
2000    void writeFields(DataOutputStream out) throws IOException {
2001      FSImageSerialization.writeString(path, out);
2002      FSImageSerialization.writeLong(mtime, out);
2003      FSImageSerialization.writeLong(atime, out);
2004    }
2005
2006    @Override
2007    void readFields(DataInputStream in, int logVersion)
2008        throws IOException {
2009      if (!NameNodeLayoutVersion.supports(
2010          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
2011        this.length = in.readInt();
2012        if (length != 3) {
2013          throw new IOException("Incorrect data format. " + "times operation.");
2014        }
2015      }
2016      this.path = FSImageSerialization.readString(in);
2017
2018      if (NameNodeLayoutVersion.supports(
2019          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
2020        this.mtime = FSImageSerialization.readLong(in);
2021        this.atime = FSImageSerialization.readLong(in);
2022      } else {
2023        this.mtime = readLong(in);
2024        this.atime = readLong(in);
2025      }
2026    }
2027
2028    @Override
2029    public String toString() {
2030      StringBuilder builder = new StringBuilder();
2031      builder.append("TimesOp [length=");
2032      builder.append(length);
2033      builder.append(", path=");
2034      builder.append(path);
2035      builder.append(", mtime=");
2036      builder.append(mtime);
2037      builder.append(", atime=");
2038      builder.append(atime);
2039      builder.append(", opCode=");
2040      builder.append(opCode);
2041      builder.append(", txid=");
2042      builder.append(txid);
2043      builder.append("]");
2044      return builder.toString();
2045    }
2046    
2047    @Override
2048    protected void toXml(ContentHandler contentHandler) throws SAXException {
2049      XMLUtils.addSaxString(contentHandler, "LENGTH",
2050          Integer.valueOf(length).toString());
2051      XMLUtils.addSaxString(contentHandler, "PATH", path);
2052      XMLUtils.addSaxString(contentHandler, "MTIME",
2053          Long.valueOf(mtime).toString());
2054      XMLUtils.addSaxString(contentHandler, "ATIME",
2055          Long.valueOf(atime).toString());
2056    }
2057    
2058    @Override void fromXml(Stanza st) throws InvalidXmlException {
2059      this.length = Integer.valueOf(st.getValue("LENGTH"));
2060      this.path = st.getValue("PATH");
2061      this.mtime = Long.valueOf(st.getValue("MTIME"));
2062      this.atime = Long.valueOf(st.getValue("ATIME"));
2063    }
2064  }
2065
2066  /** {@literal @AtMostOnce} for {@link ClientProtocol#createSymlink} */
2067  static class SymlinkOp extends FSEditLogOp {
2068    int length;
2069    long inodeId;
2070    String path;
2071    String value;
2072    long mtime;
2073    long atime;
2074    PermissionStatus permissionStatus;
2075
2076    private SymlinkOp() {
2077      super(OP_SYMLINK);
2078    }
2079
2080    static SymlinkOp getInstance(OpInstanceCache cache) {
2081      return (SymlinkOp)cache.get(OP_SYMLINK);
2082    }
2083
2084    SymlinkOp setId(long inodeId) {
2085      this.inodeId = inodeId;
2086      return this;
2087    }
2088    
2089    SymlinkOp setPath(String path) {
2090      this.path = path;
2091      return this;
2092    }
2093
2094    SymlinkOp setValue(String value) {
2095      this.value = value;
2096      return this;
2097    }
2098
2099    SymlinkOp setModificationTime(long mtime) {
2100      this.mtime = mtime;
2101      return this;
2102    }
2103
2104    SymlinkOp setAccessTime(long atime) {
2105      this.atime = atime;
2106      return this;
2107    }
2108
2109    SymlinkOp setPermissionStatus(PermissionStatus permissionStatus) {
2110      this.permissionStatus = permissionStatus;
2111      return this;
2112    }
2113
2114    @Override
2115    public void writeFields(DataOutputStream out) throws IOException {
2116      FSImageSerialization.writeLong(inodeId, out);      
2117      FSImageSerialization.writeString(path, out);
2118      FSImageSerialization.writeString(value, out);
2119      FSImageSerialization.writeLong(mtime, out);
2120      FSImageSerialization.writeLong(atime, out);
2121      permissionStatus.write(out);
2122      writeRpcIds(rpcClientId, rpcCallId, out);
2123    }
2124
2125    @Override
2126    void readFields(DataInputStream in, int logVersion)
2127        throws IOException {
2128      if (!NameNodeLayoutVersion.supports(
2129          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
2130        this.length = in.readInt();
2131        if (this.length != 4) {
2132          throw new IOException("Incorrect data format. "
2133              + "symlink operation.");
2134        }
2135      }
2136      if (NameNodeLayoutVersion.supports(
2137          LayoutVersion.Feature.ADD_INODE_ID, logVersion)) {
2138        this.inodeId = FSImageSerialization.readLong(in);
2139      } else {
2140        // This id should be updated when the editLogOp is applied
2141        this.inodeId = INodeId.GRANDFATHER_INODE_ID;
2142      }
2143      this.path = FSImageSerialization.readString(in);
2144      this.value = FSImageSerialization.readString(in);
2145
2146      if (NameNodeLayoutVersion.supports(
2147          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
2148        this.mtime = FSImageSerialization.readLong(in);
2149        this.atime = FSImageSerialization.readLong(in);
2150      } else {
2151        this.mtime = readLong(in);
2152        this.atime = readLong(in);
2153      }
2154      this.permissionStatus = PermissionStatus.read(in);
2155      
2156      // read RPC ids if necessary
2157      readRpcIds(in, logVersion);
2158    }
2159
2160    @Override
2161    public String toString() {
2162      StringBuilder builder = new StringBuilder();
2163      builder.append("SymlinkOp [length=");
2164      builder.append(length);
2165      builder.append(", inodeId=");
2166      builder.append(inodeId);
2167      builder.append(", path=");
2168      builder.append(path);
2169      builder.append(", value=");
2170      builder.append(value);
2171      builder.append(", mtime=");
2172      builder.append(mtime);
2173      builder.append(", atime=");
2174      builder.append(atime);
2175      builder.append(", permissionStatus=");
2176      builder.append(permissionStatus);
2177      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
2178      builder.append(", opCode=");
2179      builder.append(opCode);
2180      builder.append(", txid=");
2181      builder.append(txid);
2182      builder.append("]");
2183      return builder.toString();
2184    }
2185    
2186    @Override
2187    protected void toXml(ContentHandler contentHandler) throws SAXException {
2188      XMLUtils.addSaxString(contentHandler, "LENGTH",
2189          Integer.valueOf(length).toString());
2190      XMLUtils.addSaxString(contentHandler, "INODEID",
2191          Long.valueOf(inodeId).toString());
2192      XMLUtils.addSaxString(contentHandler, "PATH", path);
2193      XMLUtils.addSaxString(contentHandler, "VALUE", value);
2194      XMLUtils.addSaxString(contentHandler, "MTIME",
2195          Long.valueOf(mtime).toString());
2196      XMLUtils.addSaxString(contentHandler, "ATIME",
2197          Long.valueOf(atime).toString());
2198      FSEditLogOp.permissionStatusToXml(contentHandler, permissionStatus);
2199      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
2200    }
2201
2202    @Override 
2203    void fromXml(Stanza st) throws InvalidXmlException {
2204      this.length = Integer.valueOf(st.getValue("LENGTH"));
2205      this.inodeId = Long.valueOf(st.getValue("INODEID"));
2206      this.path = st.getValue("PATH");
2207      this.value = st.getValue("VALUE");
2208      this.mtime = Long.valueOf(st.getValue("MTIME"));
2209      this.atime = Long.valueOf(st.getValue("ATIME"));
2210      this.permissionStatus = permissionStatusFromXml(st);
2211      
2212      readRpcIdsFromXml(st);
2213    }
2214  }
2215
2216  /** {@literal @AtMostOnce} for {@link ClientProtocol#rename2} */
2217  static class RenameOp extends FSEditLogOp {
2218    int length;
2219    String src;
2220    String dst;
2221    long timestamp;
2222    Rename[] options;
2223
2224    private RenameOp() {
2225      super(OP_RENAME);
2226    }
2227
2228    static RenameOp getInstance(OpInstanceCache cache) {
2229      return (RenameOp)cache.get(OP_RENAME);
2230    }
2231
2232    RenameOp setSource(String src) {
2233      this.src = src;
2234      return this;
2235    }
2236
2237    RenameOp setDestination(String dst) {
2238      this.dst = dst;
2239      return this;
2240    }
2241    
2242    RenameOp setTimestamp(long timestamp) {
2243      this.timestamp = timestamp;
2244      return this;
2245    }
2246    
2247    RenameOp setOptions(Rename[] options) {
2248      this.options = options;
2249      return this;
2250    }
2251
2252    @Override
2253    public 
2254    void writeFields(DataOutputStream out) throws IOException {
2255      FSImageSerialization.writeString(src, out);
2256      FSImageSerialization.writeString(dst, out);
2257      FSImageSerialization.writeLong(timestamp, out);
2258      toBytesWritable(options).write(out);
2259      writeRpcIds(rpcClientId, rpcCallId, out);
2260    }
2261
2262    @Override
2263    void readFields(DataInputStream in, int logVersion)
2264        throws IOException {
2265      if (!NameNodeLayoutVersion.supports(
2266          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
2267        this.length = in.readInt();
2268        if (this.length != 3) {
2269          throw new IOException("Incorrect data format. " + "Rename operation.");
2270        }
2271      }
2272      this.src = FSImageSerialization.readString(in);
2273      this.dst = FSImageSerialization.readString(in);
2274
2275      if (NameNodeLayoutVersion.supports(
2276          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
2277        this.timestamp = FSImageSerialization.readLong(in);
2278      } else {
2279        this.timestamp = readLong(in);
2280      }
2281      this.options = readRenameOptions(in);
2282      
2283      // read RPC ids if necessary
2284      readRpcIds(in, logVersion);
2285    }
2286
2287    private static Rename[] readRenameOptions(DataInputStream in) throws IOException {
2288      BytesWritable writable = new BytesWritable();
2289      writable.readFields(in);
2290
2291      byte[] bytes = writable.getBytes();
2292      Rename[] options = new Rename[bytes.length];
2293
2294      for (int i = 0; i < bytes.length; i++) {
2295        options[i] = Rename.valueOf(bytes[i]);
2296      }
2297      return options;
2298    }
2299
2300    static BytesWritable toBytesWritable(Rename... options) {
2301      byte[] bytes = new byte[options.length];
2302      for (int i = 0; i < options.length; i++) {
2303        bytes[i] = options[i].value();
2304      }
2305      return new BytesWritable(bytes);
2306    }
2307
2308    @Override
2309    public String toString() {
2310      StringBuilder builder = new StringBuilder();
2311      builder.append("RenameOp [length=");
2312      builder.append(length);
2313      builder.append(", src=");
2314      builder.append(src);
2315      builder.append(", dst=");
2316      builder.append(dst);
2317      builder.append(", timestamp=");
2318      builder.append(timestamp);
2319      builder.append(", options=");
2320      builder.append(Arrays.toString(options));
2321      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
2322      builder.append(", opCode=");
2323      builder.append(opCode);
2324      builder.append(", txid=");
2325      builder.append(txid);
2326      builder.append("]");
2327      return builder.toString();
2328    }
2329    
2330    @Override
2331    protected void toXml(ContentHandler contentHandler) throws SAXException {
2332      XMLUtils.addSaxString(contentHandler, "LENGTH",
2333          Integer.valueOf(length).toString());
2334      XMLUtils.addSaxString(contentHandler, "SRC", src);
2335      XMLUtils.addSaxString(contentHandler, "DST", dst);
2336      XMLUtils.addSaxString(contentHandler, "TIMESTAMP",
2337          Long.valueOf(timestamp).toString());
2338      StringBuilder bld = new StringBuilder();
2339      String prefix = "";
2340      for (Rename r : options) {
2341        bld.append(prefix).append(r.toString());
2342        prefix = "|";
2343      }
2344      XMLUtils.addSaxString(contentHandler, "OPTIONS", bld.toString());
2345      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
2346    }
2347    
2348    @Override void fromXml(Stanza st) throws InvalidXmlException {
2349      this.length = Integer.valueOf(st.getValue("LENGTH"));
2350      this.src = st.getValue("SRC");
2351      this.dst = st.getValue("DST");
2352      this.timestamp = Long.valueOf(st.getValue("TIMESTAMP"));
2353      String opts = st.getValue("OPTIONS");
2354      String o[] = opts.split("\\|");
2355      this.options = new Rename[o.length];
2356      for (int i = 0; i < o.length; i++) {
2357        if (o[i].equals(""))
2358          continue;
2359        try {
2360          this.options[i] = Rename.valueOf(o[i]);
2361        } finally {
2362          if (this.options[i] == null) {
2363            System.err.println("error parsing Rename value: \"" + o[i] + "\"");
2364          }
2365        }
2366      }
2367      readRpcIdsFromXml(st);
2368    }
2369  }
2370 
2371  /**
2372   * {@literal @Idempotent} for {@link ClientProtocol#recoverLease}. In the
2373   * meanwhile, startFile and appendFile both have their own corresponding
2374   * editlog op.
2375   */
2376  static class ReassignLeaseOp extends FSEditLogOp {
2377    String leaseHolder;
2378    String path;
2379    String newHolder;
2380
2381    private ReassignLeaseOp() {
2382      super(OP_REASSIGN_LEASE);
2383    }
2384
2385    static ReassignLeaseOp getInstance(OpInstanceCache cache) {
2386      return (ReassignLeaseOp)cache.get(OP_REASSIGN_LEASE);
2387    }
2388
2389    ReassignLeaseOp setLeaseHolder(String leaseHolder) {
2390      this.leaseHolder = leaseHolder;
2391      return this;
2392    }
2393
2394    ReassignLeaseOp setPath(String path) {
2395      this.path = path;
2396      return this;
2397    }
2398
2399    ReassignLeaseOp setNewHolder(String newHolder) {
2400      this.newHolder = newHolder;
2401      return this;
2402    }
2403
2404    @Override
2405    public 
2406    void writeFields(DataOutputStream out) throws IOException {
2407      FSImageSerialization.writeString(leaseHolder, out);
2408      FSImageSerialization.writeString(path, out);
2409      FSImageSerialization.writeString(newHolder, out);
2410    }
2411
2412    @Override
2413    void readFields(DataInputStream in, int logVersion)
2414        throws IOException {
2415      this.leaseHolder = FSImageSerialization.readString(in);
2416      this.path = FSImageSerialization.readString(in);
2417      this.newHolder = FSImageSerialization.readString(in);
2418    }
2419
2420    @Override
2421    public String toString() {
2422      StringBuilder builder = new StringBuilder();
2423      builder.append("ReassignLeaseOp [leaseHolder=");
2424      builder.append(leaseHolder);
2425      builder.append(", path=");
2426      builder.append(path);
2427      builder.append(", newHolder=");
2428      builder.append(newHolder);
2429      builder.append(", opCode=");
2430      builder.append(opCode);
2431      builder.append(", txid=");
2432      builder.append(txid);
2433      builder.append("]");
2434      return builder.toString();
2435    }
2436    
2437    @Override
2438    protected void toXml(ContentHandler contentHandler) throws SAXException {
2439      XMLUtils.addSaxString(contentHandler, "LEASEHOLDER", leaseHolder);
2440      XMLUtils.addSaxString(contentHandler, "PATH", path);
2441      XMLUtils.addSaxString(contentHandler, "NEWHOLDER", newHolder);
2442    }
2443    
2444    @Override void fromXml(Stanza st) throws InvalidXmlException {
2445      this.leaseHolder = st.getValue("LEASEHOLDER");
2446      this.path = st.getValue("PATH");
2447      this.newHolder = st.getValue("NEWHOLDER");
2448    }
2449  }
2450
2451  /** {@literal @Idempotent} for {@link ClientProtocol#getDelegationToken} */
2452  static class GetDelegationTokenOp extends FSEditLogOp {
2453    DelegationTokenIdentifier token;
2454    long expiryTime;
2455
2456    private GetDelegationTokenOp() {
2457      super(OP_GET_DELEGATION_TOKEN);
2458    }
2459
2460    static GetDelegationTokenOp getInstance(OpInstanceCache cache) {
2461      return (GetDelegationTokenOp)cache.get(OP_GET_DELEGATION_TOKEN);
2462    }
2463
2464    GetDelegationTokenOp setDelegationTokenIdentifier(
2465        DelegationTokenIdentifier token) {
2466      this.token = token;
2467      return this;
2468    }
2469
2470    GetDelegationTokenOp setExpiryTime(long expiryTime) {
2471      this.expiryTime = expiryTime;
2472      return this;
2473    }
2474
2475    @Override
2476    public 
2477    void writeFields(DataOutputStream out) throws IOException {
2478      token.write(out);
2479      FSImageSerialization.writeLong(expiryTime, out);
2480    }
2481
2482    @Override
2483    void readFields(DataInputStream in, int logVersion)
2484        throws IOException {
2485      this.token = new DelegationTokenIdentifier();
2486      this.token.readFields(in);
2487      if (NameNodeLayoutVersion.supports(
2488          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
2489        this.expiryTime = FSImageSerialization.readLong(in);
2490      } else {
2491        this.expiryTime = readLong(in);
2492      }
2493    }
2494
2495    @Override
2496    public String toString() {
2497      StringBuilder builder = new StringBuilder();
2498      builder.append("GetDelegationTokenOp [token=");
2499      builder.append(token);
2500      builder.append(", expiryTime=");
2501      builder.append(expiryTime);
2502      builder.append(", opCode=");
2503      builder.append(opCode);
2504      builder.append(", txid=");
2505      builder.append(txid);
2506      builder.append("]");
2507      return builder.toString();
2508    }
2509    
2510    @Override
2511    protected void toXml(ContentHandler contentHandler) throws SAXException {
2512      FSEditLogOp.delegationTokenToXml(contentHandler, token);
2513      XMLUtils.addSaxString(contentHandler, "EXPIRY_TIME",
2514          Long.valueOf(expiryTime).toString());
2515    }
2516    
2517    @Override void fromXml(Stanza st) throws InvalidXmlException {
2518      this.token = delegationTokenFromXml(st.getChildren(
2519          "DELEGATION_TOKEN_IDENTIFIER").get(0));
2520      this.expiryTime = Long.valueOf(st.getValue("EXPIRY_TIME"));
2521    }
2522  }
2523
2524  /** {@literal @Idempotent} for {@link ClientProtocol#renewDelegationToken} */
2525  static class RenewDelegationTokenOp extends FSEditLogOp {
2526    DelegationTokenIdentifier token;
2527    long expiryTime;
2528
2529    private RenewDelegationTokenOp() {
2530      super(OP_RENEW_DELEGATION_TOKEN);
2531    }
2532
2533    static RenewDelegationTokenOp getInstance(OpInstanceCache cache) {
2534      return (RenewDelegationTokenOp)cache.get(OP_RENEW_DELEGATION_TOKEN);
2535    }
2536
2537    RenewDelegationTokenOp setDelegationTokenIdentifier(
2538        DelegationTokenIdentifier token) {
2539      this.token = token;
2540      return this;
2541    }
2542
2543    RenewDelegationTokenOp setExpiryTime(long expiryTime) {
2544      this.expiryTime = expiryTime;
2545      return this;
2546    }
2547
2548    @Override
2549    public 
2550    void writeFields(DataOutputStream out) throws IOException {
2551      token.write(out);
2552      FSImageSerialization.writeLong(expiryTime, out);
2553    }
2554
2555    @Override
2556    void readFields(DataInputStream in, int logVersion)
2557        throws IOException {
2558      this.token = new DelegationTokenIdentifier();
2559      this.token.readFields(in);
2560      if (NameNodeLayoutVersion.supports(
2561          LayoutVersion.Feature.EDITLOG_OP_OPTIMIZATION, logVersion)) {
2562        this.expiryTime = FSImageSerialization.readLong(in);
2563      } else {
2564        this.expiryTime = readLong(in);
2565      }
2566    }
2567
2568    @Override
2569    public String toString() {
2570      StringBuilder builder = new StringBuilder();
2571      builder.append("RenewDelegationTokenOp [token=");
2572      builder.append(token);
2573      builder.append(", expiryTime=");
2574      builder.append(expiryTime);
2575      builder.append(", opCode=");
2576      builder.append(opCode);
2577      builder.append(", txid=");
2578      builder.append(txid);
2579      builder.append("]");
2580      return builder.toString();
2581    }
2582    
2583    @Override
2584    protected void toXml(ContentHandler contentHandler) throws SAXException {
2585      FSEditLogOp.delegationTokenToXml(contentHandler, token);
2586      XMLUtils.addSaxString(contentHandler, "EXPIRY_TIME",
2587          Long.valueOf(expiryTime).toString());
2588    }
2589    
2590    @Override void fromXml(Stanza st) throws InvalidXmlException {
2591      this.token = delegationTokenFromXml(st.getChildren(
2592          "DELEGATION_TOKEN_IDENTIFIER").get(0));
2593      this.expiryTime = Long.valueOf(st.getValue("EXPIRY_TIME"));
2594    }
2595  }
2596
2597  /** {@literal @Idempotent} for {@link ClientProtocol#cancelDelegationToken} */
2598  static class CancelDelegationTokenOp extends FSEditLogOp {
2599    DelegationTokenIdentifier token;
2600
2601    private CancelDelegationTokenOp() {
2602      super(OP_CANCEL_DELEGATION_TOKEN);
2603    }
2604
2605    static CancelDelegationTokenOp getInstance(OpInstanceCache cache) {
2606      return (CancelDelegationTokenOp)cache.get(OP_CANCEL_DELEGATION_TOKEN);
2607    }
2608
2609    CancelDelegationTokenOp setDelegationTokenIdentifier(
2610        DelegationTokenIdentifier token) {
2611      this.token = token;
2612      return this;
2613    }
2614
2615    @Override
2616    public 
2617    void writeFields(DataOutputStream out) throws IOException {
2618      token.write(out);
2619    }
2620
2621    @Override
2622    void readFields(DataInputStream in, int logVersion)
2623        throws IOException {
2624      this.token = new DelegationTokenIdentifier();
2625      this.token.readFields(in);
2626    }
2627
2628    @Override
2629    public String toString() {
2630      StringBuilder builder = new StringBuilder();
2631      builder.append("CancelDelegationTokenOp [token=");
2632      builder.append(token);
2633      builder.append(", opCode=");
2634      builder.append(opCode);
2635      builder.append(", txid=");
2636      builder.append(txid);
2637      builder.append("]");
2638      return builder.toString();
2639    }
2640    
2641    @Override
2642    protected void toXml(ContentHandler contentHandler) throws SAXException {
2643      FSEditLogOp.delegationTokenToXml(contentHandler, token);
2644    }
2645    
2646    @Override void fromXml(Stanza st) throws InvalidXmlException {
2647      this.token = delegationTokenFromXml(st.getChildren(
2648          "DELEGATION_TOKEN_IDENTIFIER").get(0));
2649    }
2650  }
2651
2652  static class UpdateMasterKeyOp extends FSEditLogOp {
2653    DelegationKey key;
2654
2655    private UpdateMasterKeyOp() {
2656      super(OP_UPDATE_MASTER_KEY);
2657    }
2658
2659    static UpdateMasterKeyOp getInstance(OpInstanceCache cache) {
2660      return (UpdateMasterKeyOp)cache.get(OP_UPDATE_MASTER_KEY);
2661    }
2662
2663    UpdateMasterKeyOp setDelegationKey(DelegationKey key) {
2664      this.key = key;
2665      return this;
2666    }
2667    
2668    @Override
2669    public 
2670    void writeFields(DataOutputStream out) throws IOException {
2671      key.write(out);
2672    }
2673
2674    @Override
2675    void readFields(DataInputStream in, int logVersion)
2676        throws IOException {
2677      this.key = new DelegationKey();
2678      this.key.readFields(in);
2679    }
2680
2681    @Override
2682    public String toString() {
2683      StringBuilder builder = new StringBuilder();
2684      builder.append("UpdateMasterKeyOp [key=");
2685      builder.append(key);
2686      builder.append(", opCode=");
2687      builder.append(opCode);
2688      builder.append(", txid=");
2689      builder.append(txid);
2690      builder.append("]");
2691      return builder.toString();
2692    }
2693    
2694    @Override
2695    protected void toXml(ContentHandler contentHandler) throws SAXException {
2696      FSEditLogOp.delegationKeyToXml(contentHandler, key);
2697    }
2698    
2699    @Override void fromXml(Stanza st) throws InvalidXmlException {
2700      this.key = delegationKeyFromXml(st.getChildren(
2701          "DELEGATION_KEY").get(0));
2702    }
2703  }
2704  
2705  static class LogSegmentOp extends FSEditLogOp {
2706    private LogSegmentOp(FSEditLogOpCodes code) {
2707      super(code);
2708      assert code == OP_START_LOG_SEGMENT ||
2709             code == OP_END_LOG_SEGMENT : "Bad op: " + code;
2710    }
2711
2712    static LogSegmentOp getInstance(OpInstanceCache cache,
2713        FSEditLogOpCodes code) {
2714      return (LogSegmentOp)cache.get(code);
2715    }
2716
2717    @Override
2718    public void readFields(DataInputStream in, int logVersion)
2719        throws IOException {
2720      // no data stored in these ops yet
2721    }
2722
2723    @Override
2724    public
2725    void writeFields(DataOutputStream out) throws IOException {
2726      // no data stored
2727    }
2728
2729    @Override
2730    public String toString() {
2731      StringBuilder builder = new StringBuilder();
2732      builder.append("LogSegmentOp [opCode=");
2733      builder.append(opCode);
2734      builder.append(", txid=");
2735      builder.append(txid);
2736      builder.append("]");
2737      return builder.toString();
2738    }
2739
2740    @Override
2741    protected void toXml(ContentHandler contentHandler) throws SAXException {
2742      // no data stored
2743    }
2744    
2745    @Override void fromXml(Stanza st) throws InvalidXmlException {
2746      // do nothing
2747    }
2748  }
2749
2750  static class InvalidOp extends FSEditLogOp {
2751    private InvalidOp() {
2752      super(OP_INVALID);
2753    }
2754
2755    static InvalidOp getInstance(OpInstanceCache cache) {
2756      return (InvalidOp)cache.get(OP_INVALID);
2757    }
2758
2759    @Override
2760    public 
2761    void writeFields(DataOutputStream out) throws IOException {
2762    }
2763    
2764    @Override
2765    void readFields(DataInputStream in, int logVersion)
2766        throws IOException {
2767      // nothing to read
2768    }
2769
2770    @Override
2771    public String toString() {
2772      StringBuilder builder = new StringBuilder();
2773      builder.append("InvalidOp [opCode=");
2774      builder.append(opCode);
2775      builder.append(", txid=");
2776      builder.append(txid);
2777      builder.append("]");
2778      return builder.toString();
2779    }
2780    @Override
2781    protected void toXml(ContentHandler contentHandler) throws SAXException {
2782      // no data stored
2783    }
2784    
2785    @Override void fromXml(Stanza st) throws InvalidXmlException {
2786      // do nothing
2787    }
2788  }
2789
2790  /**
2791   * Operation corresponding to creating a snapshot.
2792   * {@literal @AtMostOnce} for {@link ClientProtocol#createSnapshot}.
2793   */
2794  static class CreateSnapshotOp extends FSEditLogOp {
2795    String snapshotRoot;
2796    String snapshotName;
2797    
2798    public CreateSnapshotOp() {
2799      super(OP_CREATE_SNAPSHOT);
2800    }
2801    
2802    static CreateSnapshotOp getInstance(OpInstanceCache cache) {
2803      return (CreateSnapshotOp)cache.get(OP_CREATE_SNAPSHOT);
2804    }
2805    
2806    CreateSnapshotOp setSnapshotName(String snapName) {
2807      this.snapshotName = snapName;
2808      return this;
2809    }
2810
2811    public CreateSnapshotOp setSnapshotRoot(String snapRoot) {
2812      snapshotRoot = snapRoot;
2813      return this;
2814    }
2815    
2816    @Override
2817    void readFields(DataInputStream in, int logVersion) throws IOException {
2818      snapshotRoot = FSImageSerialization.readString(in);
2819      snapshotName = FSImageSerialization.readString(in);
2820      
2821      // read RPC ids if necessary
2822      readRpcIds(in, logVersion);
2823    }
2824
2825    @Override
2826    public void writeFields(DataOutputStream out) throws IOException {
2827      FSImageSerialization.writeString(snapshotRoot, out);
2828      FSImageSerialization.writeString(snapshotName, out);
2829      writeRpcIds(rpcClientId, rpcCallId, out);
2830    }
2831
2832    @Override
2833    protected void toXml(ContentHandler contentHandler) throws SAXException {
2834      XMLUtils.addSaxString(contentHandler, "SNAPSHOTROOT", snapshotRoot);
2835      XMLUtils.addSaxString(contentHandler, "SNAPSHOTNAME", snapshotName);
2836      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
2837    }
2838
2839    @Override
2840    void fromXml(Stanza st) throws InvalidXmlException {
2841      snapshotRoot = st.getValue("SNAPSHOTROOT");
2842      snapshotName = st.getValue("SNAPSHOTNAME");
2843      
2844      readRpcIdsFromXml(st);
2845    }
2846    
2847    @Override
2848    public String toString() {
2849      StringBuilder builder = new StringBuilder();
2850      builder.append("CreateSnapshotOp [snapshotRoot=");
2851      builder.append(snapshotRoot);
2852      builder.append(", snapshotName=");
2853      builder.append(snapshotName);
2854      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
2855      builder.append("]");
2856      return builder.toString();
2857    }
2858  }
2859  
2860  /**
2861   * Operation corresponding to delete a snapshot.
2862   * {@literal @AtMostOnce} for {@link ClientProtocol#deleteSnapshot}.
2863   */
2864  static class DeleteSnapshotOp extends FSEditLogOp {
2865    String snapshotRoot;
2866    String snapshotName;
2867    
2868    DeleteSnapshotOp() {
2869      super(OP_DELETE_SNAPSHOT);
2870    }
2871    
2872    static DeleteSnapshotOp getInstance(OpInstanceCache cache) {
2873      return (DeleteSnapshotOp)cache.get(OP_DELETE_SNAPSHOT);
2874    }
2875    
2876    DeleteSnapshotOp setSnapshotName(String snapName) {
2877      this.snapshotName = snapName;
2878      return this;
2879    }
2880
2881    DeleteSnapshotOp setSnapshotRoot(String snapRoot) {
2882      snapshotRoot = snapRoot;
2883      return this;
2884    }
2885    
2886    @Override
2887    void readFields(DataInputStream in, int logVersion) throws IOException {
2888      snapshotRoot = FSImageSerialization.readString(in);
2889      snapshotName = FSImageSerialization.readString(in);
2890      
2891      // read RPC ids if necessary
2892      readRpcIds(in, logVersion);
2893    }
2894
2895    @Override
2896    public void writeFields(DataOutputStream out) throws IOException {
2897      FSImageSerialization.writeString(snapshotRoot, out);
2898      FSImageSerialization.writeString(snapshotName, out);
2899      writeRpcIds(rpcClientId, rpcCallId, out);
2900    }
2901
2902    @Override
2903    protected void toXml(ContentHandler contentHandler) throws SAXException {
2904      XMLUtils.addSaxString(contentHandler, "SNAPSHOTROOT", snapshotRoot);
2905      XMLUtils.addSaxString(contentHandler, "SNAPSHOTNAME", snapshotName);
2906      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
2907    }
2908
2909    @Override
2910    void fromXml(Stanza st) throws InvalidXmlException {
2911      snapshotRoot = st.getValue("SNAPSHOTROOT");
2912      snapshotName = st.getValue("SNAPSHOTNAME");
2913      
2914      readRpcIdsFromXml(st);
2915    }
2916    
2917    @Override
2918    public String toString() {
2919      StringBuilder builder = new StringBuilder();
2920      builder.append("DeleteSnapshotOp [snapshotRoot=");
2921      builder.append(snapshotRoot);
2922      builder.append(", snapshotName=");
2923      builder.append(snapshotName);
2924      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
2925      builder.append("]");
2926      return builder.toString();
2927    }
2928  }
2929  
2930  /**
2931   * Operation corresponding to rename a snapshot.
2932   * {@literal @AtMostOnce} for {@link ClientProtocol#renameSnapshot}.
2933   */
2934  static class RenameSnapshotOp extends FSEditLogOp {
2935    String snapshotRoot;
2936    String snapshotOldName;
2937    String snapshotNewName;
2938    
2939    RenameSnapshotOp() {
2940      super(OP_RENAME_SNAPSHOT);
2941    }
2942    
2943    static RenameSnapshotOp getInstance(OpInstanceCache cache) {
2944      return (RenameSnapshotOp) cache.get(OP_RENAME_SNAPSHOT);
2945    }
2946    
2947    RenameSnapshotOp setSnapshotOldName(String snapshotOldName) {
2948      this.snapshotOldName = snapshotOldName;
2949      return this;
2950    }
2951
2952    RenameSnapshotOp setSnapshotNewName(String snapshotNewName) {
2953      this.snapshotNewName = snapshotNewName;
2954      return this;
2955    }
2956    
2957    RenameSnapshotOp setSnapshotRoot(String snapshotRoot) {
2958      this.snapshotRoot = snapshotRoot;
2959      return this;
2960    }
2961    
2962    @Override
2963    void readFields(DataInputStream in, int logVersion) throws IOException {
2964      snapshotRoot = FSImageSerialization.readString(in);
2965      snapshotOldName = FSImageSerialization.readString(in);
2966      snapshotNewName = FSImageSerialization.readString(in);
2967      
2968      // read RPC ids if necessary
2969      readRpcIds(in, logVersion);
2970    }
2971
2972    @Override
2973    public void writeFields(DataOutputStream out) throws IOException {
2974      FSImageSerialization.writeString(snapshotRoot, out);
2975      FSImageSerialization.writeString(snapshotOldName, out);
2976      FSImageSerialization.writeString(snapshotNewName, out);
2977      
2978      writeRpcIds(rpcClientId, rpcCallId, out);
2979    }
2980
2981    @Override
2982    protected void toXml(ContentHandler contentHandler) throws SAXException {
2983      XMLUtils.addSaxString(contentHandler, "SNAPSHOTROOT", snapshotRoot);
2984      XMLUtils.addSaxString(contentHandler, "SNAPSHOTOLDNAME", snapshotOldName);
2985      XMLUtils.addSaxString(contentHandler, "SNAPSHOTNEWNAME", snapshotNewName);
2986      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
2987    }
2988
2989    @Override
2990    void fromXml(Stanza st) throws InvalidXmlException {
2991      snapshotRoot = st.getValue("SNAPSHOTROOT");
2992      snapshotOldName = st.getValue("SNAPSHOTOLDNAME");
2993      snapshotNewName = st.getValue("SNAPSHOTNEWNAME");
2994      
2995      readRpcIdsFromXml(st);
2996    }
2997    
2998    @Override
2999    public String toString() {
3000      StringBuilder builder = new StringBuilder();
3001      builder.append("RenameSnapshotOp [snapshotRoot=");
3002      builder.append(snapshotRoot);
3003      builder.append(", snapshotOldName=");
3004      builder.append(snapshotOldName);
3005      builder.append(", snapshotNewName=");
3006      builder.append(snapshotNewName);
3007      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
3008      builder.append("]");
3009      return builder.toString();
3010    }
3011  }
3012
3013  /**
3014   * Operation corresponding to allow creating snapshot on a directory
3015   */
3016  static class AllowSnapshotOp extends FSEditLogOp { // @Idempotent
3017    String snapshotRoot;
3018
3019    public AllowSnapshotOp() {
3020      super(OP_ALLOW_SNAPSHOT);
3021    }
3022
3023    public AllowSnapshotOp(String snapRoot) {
3024      super(OP_ALLOW_SNAPSHOT);
3025      snapshotRoot = snapRoot;
3026    }
3027
3028    static AllowSnapshotOp getInstance(OpInstanceCache cache) {
3029      return (AllowSnapshotOp) cache.get(OP_ALLOW_SNAPSHOT);
3030    }
3031
3032    public AllowSnapshotOp setSnapshotRoot(String snapRoot) {
3033      snapshotRoot = snapRoot;
3034      return this;
3035    }
3036
3037    @Override
3038    void readFields(DataInputStream in, int logVersion) throws IOException {
3039      snapshotRoot = FSImageSerialization.readString(in);
3040    }
3041
3042    @Override
3043    public void writeFields(DataOutputStream out) throws IOException {
3044      FSImageSerialization.writeString(snapshotRoot, out);
3045    }
3046
3047    @Override
3048    protected void toXml(ContentHandler contentHandler) throws SAXException {
3049      XMLUtils.addSaxString(contentHandler, "SNAPSHOTROOT", snapshotRoot);
3050    }
3051
3052    @Override
3053    void fromXml(Stanza st) throws InvalidXmlException {
3054      snapshotRoot = st.getValue("SNAPSHOTROOT");
3055    }
3056
3057    @Override
3058    public String toString() {
3059      StringBuilder builder = new StringBuilder();
3060      builder.append("AllowSnapshotOp [snapshotRoot=");
3061      builder.append(snapshotRoot);
3062      builder.append("]");
3063      return builder.toString();
3064    }
3065  }
3066
3067  /**
3068   * Operation corresponding to disallow creating snapshot on a directory
3069   */
3070  static class DisallowSnapshotOp extends FSEditLogOp { // @Idempotent
3071    String snapshotRoot;
3072
3073    public DisallowSnapshotOp() {
3074      super(OP_DISALLOW_SNAPSHOT);
3075    }
3076
3077    public DisallowSnapshotOp(String snapRoot) {
3078      super(OP_DISALLOW_SNAPSHOT);
3079      snapshotRoot = snapRoot;
3080    }
3081
3082    static DisallowSnapshotOp getInstance(OpInstanceCache cache) {
3083      return (DisallowSnapshotOp) cache.get(OP_DISALLOW_SNAPSHOT);
3084    }
3085
3086    public DisallowSnapshotOp setSnapshotRoot(String snapRoot) {
3087      snapshotRoot = snapRoot;
3088      return this;
3089    }
3090
3091    @Override
3092    void readFields(DataInputStream in, int logVersion) throws IOException {
3093      snapshotRoot = FSImageSerialization.readString(in);
3094    }
3095
3096    @Override
3097    public void writeFields(DataOutputStream out) throws IOException {
3098      FSImageSerialization.writeString(snapshotRoot, out);
3099    }
3100
3101    @Override
3102    protected void toXml(ContentHandler contentHandler) throws SAXException {
3103      XMLUtils.addSaxString(contentHandler, "SNAPSHOTROOT", snapshotRoot);
3104    }
3105
3106    @Override
3107    void fromXml(Stanza st) throws InvalidXmlException {
3108      snapshotRoot = st.getValue("SNAPSHOTROOT");
3109    }
3110
3111    @Override
3112    public String toString() {
3113      StringBuilder builder = new StringBuilder();
3114      builder.append("DisallowSnapshotOp [snapshotRoot=");
3115      builder.append(snapshotRoot);
3116      builder.append("]");
3117      return builder.toString();
3118    }
3119  }
3120
3121  /**
3122   * {@literal @AtMostOnce} for
3123   * {@link ClientProtocol#addCacheDirective}
3124   */
3125  static class AddCacheDirectiveInfoOp extends FSEditLogOp {
3126    CacheDirectiveInfo directive;
3127
3128    public AddCacheDirectiveInfoOp() {
3129      super(OP_ADD_CACHE_DIRECTIVE);
3130    }
3131
3132    static AddCacheDirectiveInfoOp getInstance(OpInstanceCache cache) {
3133      return (AddCacheDirectiveInfoOp) cache
3134          .get(OP_ADD_CACHE_DIRECTIVE);
3135    }
3136
3137    public AddCacheDirectiveInfoOp setDirective(
3138        CacheDirectiveInfo directive) {
3139      this.directive = directive;
3140      assert(directive.getId() != null);
3141      assert(directive.getPath() != null);
3142      assert(directive.getReplication() != null);
3143      assert(directive.getPool() != null);
3144      assert(directive.getExpiration() != null);
3145      return this;
3146    }
3147
3148    @Override
3149    void readFields(DataInputStream in, int logVersion) throws IOException {
3150      directive = FSImageSerialization.readCacheDirectiveInfo(in);
3151      readRpcIds(in, logVersion);
3152    }
3153
3154    @Override
3155    public void writeFields(DataOutputStream out) throws IOException {
3156      FSImageSerialization.writeCacheDirectiveInfo(out, directive);
3157      writeRpcIds(rpcClientId, rpcCallId, out);
3158    }
3159
3160    @Override
3161    protected void toXml(ContentHandler contentHandler) throws SAXException {
3162      FSImageSerialization.writeCacheDirectiveInfo(contentHandler, directive);
3163      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
3164    }
3165
3166    @Override
3167    void fromXml(Stanza st) throws InvalidXmlException {
3168      directive = FSImageSerialization.readCacheDirectiveInfo(st);
3169      readRpcIdsFromXml(st);
3170    }
3171
3172    @Override
3173    public String toString() {
3174      StringBuilder builder = new StringBuilder();
3175      builder.append("AddCacheDirectiveInfo [");
3176      builder.append("id=" + directive.getId() + ",");
3177      builder.append("path=" + directive.getPath().toUri().getPath() + ",");
3178      builder.append("replication=" + directive.getReplication() + ",");
3179      builder.append("pool=" + directive.getPool() + ",");
3180      builder.append("expiration=" + directive.getExpiration().getMillis());
3181      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
3182      builder.append("]");
3183      return builder.toString();
3184    }
3185  }
3186
3187  /**
3188   * {@literal @AtMostOnce} for
3189   * {@link ClientProtocol#modifyCacheDirective}
3190   */
3191  static class ModifyCacheDirectiveInfoOp extends FSEditLogOp {
3192    CacheDirectiveInfo directive;
3193
3194    public ModifyCacheDirectiveInfoOp() {
3195      super(OP_MODIFY_CACHE_DIRECTIVE);
3196    }
3197
3198    static ModifyCacheDirectiveInfoOp getInstance(OpInstanceCache cache) {
3199      return (ModifyCacheDirectiveInfoOp) cache
3200          .get(OP_MODIFY_CACHE_DIRECTIVE);
3201    }
3202
3203    public ModifyCacheDirectiveInfoOp setDirective(
3204        CacheDirectiveInfo directive) {
3205      this.directive = directive;
3206      assert(directive.getId() != null);
3207      return this;
3208    }
3209
3210    @Override
3211    void readFields(DataInputStream in, int logVersion) throws IOException {
3212      this.directive = FSImageSerialization.readCacheDirectiveInfo(in);
3213      readRpcIds(in, logVersion);
3214    }
3215
3216    @Override
3217    public void writeFields(DataOutputStream out) throws IOException {
3218      FSImageSerialization.writeCacheDirectiveInfo(out, directive);
3219      writeRpcIds(rpcClientId, rpcCallId, out);
3220    }
3221
3222    @Override
3223    protected void toXml(ContentHandler contentHandler) throws SAXException {
3224      FSImageSerialization.writeCacheDirectiveInfo(contentHandler, directive);
3225      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
3226    }
3227
3228    @Override
3229    void fromXml(Stanza st) throws InvalidXmlException {
3230      this.directive = FSImageSerialization.readCacheDirectiveInfo(st);
3231      readRpcIdsFromXml(st);
3232    }
3233
3234    @Override
3235    public String toString() {
3236      StringBuilder builder = new StringBuilder();
3237      builder.append("ModifyCacheDirectiveInfoOp[");
3238      builder.append("id=").append(directive.getId());
3239      if (directive.getPath() != null) {
3240        builder.append(",").append("path=").append(directive.getPath());
3241      }
3242      if (directive.getReplication() != null) {
3243        builder.append(",").append("replication=").
3244            append(directive.getReplication());
3245      }
3246      if (directive.getPool() != null) {
3247        builder.append(",").append("pool=").append(directive.getPool());
3248      }
3249      if (directive.getExpiration() != null) {
3250        builder.append(",").append("expiration=").
3251            append(directive.getExpiration().getMillis());
3252      }
3253      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
3254      builder.append("]");
3255      return builder.toString();
3256    }
3257  }
3258
3259  /**
3260   * {@literal @AtMostOnce} for
3261   * {@link ClientProtocol#removeCacheDirective}
3262   */
3263  static class RemoveCacheDirectiveInfoOp extends FSEditLogOp {
3264    long id;
3265
3266    public RemoveCacheDirectiveInfoOp() {
3267      super(OP_REMOVE_CACHE_DIRECTIVE);
3268    }
3269
3270    static RemoveCacheDirectiveInfoOp getInstance(OpInstanceCache cache) {
3271      return (RemoveCacheDirectiveInfoOp) cache
3272          .get(OP_REMOVE_CACHE_DIRECTIVE);
3273    }
3274
3275    public RemoveCacheDirectiveInfoOp setId(long id) {
3276      this.id = id;
3277      return this;
3278    }
3279
3280    @Override
3281    void readFields(DataInputStream in, int logVersion) throws IOException {
3282      this.id = FSImageSerialization.readLong(in);
3283      readRpcIds(in, logVersion);
3284    }
3285
3286    @Override
3287    public void writeFields(DataOutputStream out) throws IOException {
3288      FSImageSerialization.writeLong(id, out);
3289      writeRpcIds(rpcClientId, rpcCallId, out);
3290    }
3291
3292    @Override
3293    protected void toXml(ContentHandler contentHandler) throws SAXException {
3294      XMLUtils.addSaxString(contentHandler, "ID", Long.toString(id));
3295      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
3296    }
3297
3298    @Override
3299    void fromXml(Stanza st) throws InvalidXmlException {
3300      this.id = Long.parseLong(st.getValue("ID"));
3301      readRpcIdsFromXml(st);
3302    }
3303
3304    @Override
3305    public String toString() {
3306      StringBuilder builder = new StringBuilder();
3307      builder.append("RemoveCacheDirectiveInfo [");
3308      builder.append("id=" + Long.toString(id));
3309      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
3310      builder.append("]");
3311      return builder.toString();
3312    }
3313  }
3314
3315  /** {@literal @AtMostOnce} for {@link ClientProtocol#addCachePool} */
3316  static class AddCachePoolOp extends FSEditLogOp {
3317    CachePoolInfo info;
3318
3319    public AddCachePoolOp() {
3320      super(OP_ADD_CACHE_POOL);
3321    }
3322
3323    static AddCachePoolOp getInstance(OpInstanceCache cache) {
3324      return (AddCachePoolOp) cache.get(OP_ADD_CACHE_POOL);
3325    }
3326
3327    public AddCachePoolOp setPool(CachePoolInfo info) {
3328      this.info = info;
3329      assert(info.getPoolName() != null);
3330      assert(info.getOwnerName() != null);
3331      assert(info.getGroupName() != null);
3332      assert(info.getMode() != null);
3333      assert(info.getLimit() != null);
3334      return this;
3335    }
3336
3337    @Override
3338    void readFields(DataInputStream in, int logVersion) throws IOException {
3339      info = FSImageSerialization.readCachePoolInfo(in);
3340      readRpcIds(in, logVersion);
3341    }
3342
3343    @Override
3344    public void writeFields(DataOutputStream out) throws IOException {
3345      FSImageSerialization.writeCachePoolInfo(out, info);
3346      writeRpcIds(rpcClientId, rpcCallId, out);
3347    }
3348
3349    @Override
3350    protected void toXml(ContentHandler contentHandler) throws SAXException {
3351      FSImageSerialization.writeCachePoolInfo(contentHandler, info);
3352      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
3353    }
3354
3355    @Override
3356    void fromXml(Stanza st) throws InvalidXmlException {
3357      this.info = FSImageSerialization.readCachePoolInfo(st);
3358      readRpcIdsFromXml(st);
3359    }
3360
3361    @Override
3362    public String toString() {
3363      StringBuilder builder = new StringBuilder();
3364      builder.append("AddCachePoolOp [");
3365      builder.append("poolName=" + info.getPoolName() + ",");
3366      builder.append("ownerName=" + info.getOwnerName() + ",");
3367      builder.append("groupName=" + info.getGroupName() + ",");
3368      builder.append("mode=" + Short.toString(info.getMode().toShort()) + ",");
3369      builder.append("limit=" + Long.toString(info.getLimit()));
3370      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
3371      builder.append("]");
3372      return builder.toString();
3373    }
3374  }
3375
3376  /** {@literal @AtMostOnce} for {@link ClientProtocol#modifyCachePool} */
3377  static class ModifyCachePoolOp extends FSEditLogOp {
3378    CachePoolInfo info;
3379
3380    public ModifyCachePoolOp() {
3381      super(OP_MODIFY_CACHE_POOL);
3382    }
3383
3384    static ModifyCachePoolOp getInstance(OpInstanceCache cache) {
3385      return (ModifyCachePoolOp) cache.get(OP_MODIFY_CACHE_POOL);
3386    }
3387
3388    public ModifyCachePoolOp setInfo(CachePoolInfo info) {
3389      this.info = info;
3390      return this;
3391    }
3392
3393    @Override
3394    void readFields(DataInputStream in, int logVersion) throws IOException {
3395      info = FSImageSerialization.readCachePoolInfo(in);
3396      readRpcIds(in, logVersion);
3397    }
3398
3399    @Override
3400    public void writeFields(DataOutputStream out) throws IOException {
3401      FSImageSerialization.writeCachePoolInfo(out, info);
3402      writeRpcIds(rpcClientId, rpcCallId, out);
3403    }
3404
3405    @Override
3406    protected void toXml(ContentHandler contentHandler) throws SAXException {
3407      FSImageSerialization.writeCachePoolInfo(contentHandler, info);
3408      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
3409    }
3410
3411    @Override
3412    void fromXml(Stanza st) throws InvalidXmlException {
3413      this.info = FSImageSerialization.readCachePoolInfo(st);
3414      readRpcIdsFromXml(st);
3415    }
3416
3417    @Override
3418    public String toString() {
3419      StringBuilder builder = new StringBuilder();
3420      builder.append("ModifyCachePoolOp [");
3421      ArrayList<String> fields = new ArrayList<String>(5);
3422      if (info.getPoolName() != null) {
3423        fields.add("poolName=" + info.getPoolName());
3424      }
3425      if (info.getOwnerName() != null) {
3426        fields.add("ownerName=" + info.getOwnerName());
3427      }
3428      if (info.getGroupName() != null) {
3429        fields.add("groupName=" + info.getGroupName());
3430      }
3431      if (info.getMode() != null) {
3432        fields.add("mode=" + info.getMode().toString());
3433      }
3434      if (info.getLimit() != null) {
3435        fields.add("limit=" + info.getLimit());
3436      }
3437      builder.append(Joiner.on(",").join(fields));
3438      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
3439      builder.append("]");
3440      return builder.toString();
3441    }
3442  }
3443
3444  /** {@literal @AtMostOnce} for {@link ClientProtocol#removeCachePool} */
3445  static class RemoveCachePoolOp extends FSEditLogOp {
3446    String poolName;
3447
3448    public RemoveCachePoolOp() {
3449      super(OP_REMOVE_CACHE_POOL);
3450    }
3451
3452    static RemoveCachePoolOp getInstance(OpInstanceCache cache) {
3453      return (RemoveCachePoolOp) cache.get(OP_REMOVE_CACHE_POOL);
3454    }
3455
3456    public RemoveCachePoolOp setPoolName(String poolName) {
3457      this.poolName = poolName;
3458      return this;
3459    }
3460
3461    @Override
3462    void readFields(DataInputStream in, int logVersion) throws IOException {
3463      poolName = FSImageSerialization.readString(in);
3464      readRpcIds(in, logVersion);
3465    }
3466
3467    @Override
3468    public void writeFields(DataOutputStream out) throws IOException {
3469      FSImageSerialization.writeString(poolName, out);
3470      writeRpcIds(rpcClientId, rpcCallId, out);
3471    }
3472
3473    @Override
3474    protected void toXml(ContentHandler contentHandler) throws SAXException {
3475      XMLUtils.addSaxString(contentHandler, "POOLNAME", poolName);
3476      appendRpcIdsToXml(contentHandler, rpcClientId, rpcCallId);
3477    }
3478
3479    @Override
3480    void fromXml(Stanza st) throws InvalidXmlException {
3481      this.poolName = st.getValue("POOLNAME");
3482      readRpcIdsFromXml(st);
3483    }
3484
3485    @Override
3486    public String toString() {
3487      StringBuilder builder = new StringBuilder();
3488      builder.append("RemoveCachePoolOp [");
3489      builder.append("poolName=" + poolName);
3490      appendRpcIdsToString(builder, rpcClientId, rpcCallId);
3491      builder.append("]");
3492      return builder.toString();
3493    }
3494  }
3495
3496  static class SetAclOp extends FSEditLogOp {
3497    List<AclEntry> aclEntries = Lists.newArrayList();
3498    String src;
3499
3500    private SetAclOp() {
3501      super(OP_SET_ACL);
3502    }
3503
3504    static SetAclOp getInstance() {
3505      return new SetAclOp();
3506    }
3507
3508    @Override
3509    void readFields(DataInputStream in, int logVersion) throws IOException {
3510      AclEditLogProto p = AclEditLogProto.parseDelimitedFrom((DataInputStream)in);
3511      if (p == null) {
3512        throw new IOException("Failed to read fields from SetAclOp");
3513      }
3514      src = p.getSrc();
3515      aclEntries = PBHelper.convertAclEntry(p.getEntriesList());
3516    }
3517
3518    @Override
3519    public void writeFields(DataOutputStream out) throws IOException {
3520      AclEditLogProto.Builder b = AclEditLogProto.newBuilder();
3521      if (src != null)
3522        b.setSrc(src);
3523      b.addAllEntries(PBHelper.convertAclEntryProto(aclEntries));
3524      b.build().writeDelimitedTo(out);
3525    }
3526
3527    @Override
3528    protected void toXml(ContentHandler contentHandler) throws SAXException {
3529      XMLUtils.addSaxString(contentHandler, "SRC", src);
3530      appendAclEntriesToXml(contentHandler, aclEntries);
3531    }
3532
3533    @Override
3534    void fromXml(Stanza st) throws InvalidXmlException {
3535      src = st.getValue("SRC");
3536      aclEntries = readAclEntriesFromXml(st);
3537      if (aclEntries == null) {
3538        aclEntries = Lists.newArrayList();
3539      }
3540    }
3541  }
3542
3543  static private short readShort(DataInputStream in) throws IOException {
3544    return Short.parseShort(FSImageSerialization.readString(in));
3545  }
3546
3547  static private long readLong(DataInputStream in) throws IOException {
3548    return Long.parseLong(FSImageSerialization.readString(in));
3549  }
3550
3551  /**
3552   * A class to read in blocks stored in the old format. The only two
3553   * fields in the block were blockid and length.
3554   */
3555  static class BlockTwo implements Writable {
3556    long blkid;
3557    long len;
3558
3559    static {                                      // register a ctor
3560      WritableFactories.setFactory
3561        (BlockTwo.class,
3562         new WritableFactory() {
3563           @Override
3564           public Writable newInstance() { return new BlockTwo(); }
3565         });
3566    }
3567
3568
3569    BlockTwo() {
3570      blkid = 0;
3571      len = 0;
3572    }
3573    /////////////////////////////////////
3574    // Writable
3575    /////////////////////////////////////
3576    @Override
3577    public void write(DataOutput out) throws IOException {
3578      out.writeLong(blkid);
3579      out.writeLong(len);
3580    }
3581
3582    @Override
3583    public void readFields(DataInput in) throws IOException {
3584      this.blkid = in.readLong();
3585      this.len = in.readLong();
3586    }
3587  }
3588  /**
3589   * Operation corresponding to upgrade
3590   */
3591  static class RollingUpgradeOp extends FSEditLogOp { // @Idempotent
3592    private final String name;
3593    private long time;
3594
3595    public RollingUpgradeOp(FSEditLogOpCodes code, String name) {
3596      super(code);
3597      this.name = name.toUpperCase();
3598    }
3599
3600    static RollingUpgradeOp getStartInstance(OpInstanceCache cache) {
3601      return (RollingUpgradeOp) cache.get(OP_ROLLING_UPGRADE_START);
3602    }
3603
3604    static RollingUpgradeOp getFinalizeInstance(OpInstanceCache cache) {
3605      return (RollingUpgradeOp) cache.get(OP_ROLLING_UPGRADE_FINALIZE);
3606    }
3607
3608    long getTime() {
3609      return time;
3610    }
3611
3612    void setTime(long time) {
3613      this.time = time;
3614    }
3615
3616    @Override
3617    void readFields(DataInputStream in, int logVersion) throws IOException {
3618      time = in.readLong();
3619    }
3620
3621    @Override
3622    public void writeFields(DataOutputStream out) throws IOException {
3623      FSImageSerialization.writeLong(time, out);
3624    }
3625
3626    @Override
3627    protected void toXml(ContentHandler contentHandler) throws SAXException {
3628      XMLUtils.addSaxString(contentHandler, name + "TIME",
3629          Long.valueOf(time).toString());
3630    }
3631
3632    @Override
3633    void fromXml(Stanza st) throws InvalidXmlException {
3634      this.time = Long.valueOf(st.getValue(name + "TIME"));
3635    }
3636
3637    @Override
3638    public String toString() {
3639      return new StringBuilder().append("RollingUpgradeOp [").append(name)
3640          .append(", time=").append(time).append("]").toString();
3641    }
3642    
3643    static class RollbackException extends IOException {
3644      private static final long serialVersionUID = 1L;
3645    }
3646  }
3647
3648  /**
3649   * Class for writing editlog ops
3650   */
3651  public static class Writer {
3652    private final DataOutputBuffer buf;
3653    private final Checksum checksum;
3654
3655    public Writer(DataOutputBuffer out) {
3656      this.buf = out;
3657      this.checksum = new PureJavaCrc32();
3658    }
3659
3660    /**
3661     * Write an operation to the output stream
3662     * 
3663     * @param op The operation to write
3664     * @throws IOException if an error occurs during writing.
3665     */
3666    public void writeOp(FSEditLogOp op) throws IOException {
3667      int start = buf.getLength();
3668      // write the op code first to make padding and terminator verification
3669      // work
3670      buf.writeByte(op.opCode.getOpCode());
3671      buf.writeInt(0); // write 0 for the length first
3672      buf.writeLong(op.txid);
3673      op.writeFields(buf);
3674      int end = buf.getLength();
3675      
3676      // write the length back: content of the op + 4 bytes checksum - op_code
3677      int length = end - start - 1;
3678      buf.writeInt(length, start + 1);
3679
3680      checksum.reset();
3681      checksum.update(buf.getData(), start, end-start);
3682      int sum = (int)checksum.getValue();
3683      buf.writeInt(sum);
3684    }
3685  }
3686
3687  /**
3688   * Class for reading editlog ops from a stream
3689   */
3690  public static class Reader {
3691    private final DataInputStream in;
3692    private final StreamLimiter limiter;
3693    private final int logVersion;
3694    private final Checksum checksum;
3695    private final OpInstanceCache cache;
3696    private int maxOpSize;
3697    private final boolean supportEditLogLength;
3698
3699    /**
3700     * Construct the reader
3701     * @param in The stream to read from.
3702     * @param logVersion The version of the data coming from the stream.
3703     */
3704    public Reader(DataInputStream in, StreamLimiter limiter, int logVersion) {
3705      this.logVersion = logVersion;
3706      if (NameNodeLayoutVersion.supports(
3707          LayoutVersion.Feature.EDITS_CHESKUM, logVersion)) {
3708        this.checksum = new PureJavaCrc32();
3709      } else {
3710        this.checksum = null;
3711      }
3712      // It is possible that the logVersion is actually a future layoutversion
3713      // during the rolling upgrade (e.g., the NN gets upgraded first). We
3714      // assume future layout will also support length of editlog op.
3715      this.supportEditLogLength = NameNodeLayoutVersion.supports(
3716          NameNodeLayoutVersion.Feature.EDITLOG_LENGTH, logVersion)
3717          || logVersion < NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION;
3718
3719      if (this.checksum != null) {
3720        this.in = new DataInputStream(
3721            new CheckedInputStream(in, this.checksum));
3722      } else {
3723        this.in = in;
3724      }
3725      this.limiter = limiter;
3726      this.cache = new OpInstanceCache();
3727      this.maxOpSize = DFSConfigKeys.DFS_NAMENODE_MAX_OP_SIZE_DEFAULT;
3728    }
3729
3730    public void setMaxOpSize(int maxOpSize) {
3731      this.maxOpSize = maxOpSize;
3732    }
3733
3734    /**
3735     * Read an operation from the input stream.
3736     * 
3737     * Note that the objects returned from this method may be re-used by future
3738     * calls to the same method.
3739     * 
3740     * @param skipBrokenEdits    If true, attempt to skip over damaged parts of
3741     * the input stream, rather than throwing an IOException
3742     * @return the operation read from the stream, or null at the end of the 
3743     *         file
3744     * @throws IOException on error.  This function should only throw an
3745     *         exception when skipBrokenEdits is false.
3746     */
3747    public FSEditLogOp readOp(boolean skipBrokenEdits) throws IOException {
3748      while (true) {
3749        try {
3750          return decodeOp();
3751        } catch (IOException e) {
3752          in.reset();
3753          if (!skipBrokenEdits) {
3754            throw e;
3755          }
3756        } catch (RuntimeException e) {
3757          // FSEditLogOp#decodeOp is not supposed to throw RuntimeException.
3758          // However, we handle it here for recovery mode, just to be more
3759          // robust.
3760          in.reset();
3761          if (!skipBrokenEdits) {
3762            throw e;
3763          }
3764        } catch (Throwable e) {
3765          in.reset();
3766          if (!skipBrokenEdits) {
3767            throw new IOException("got unexpected exception " +
3768                e.getMessage(), e);
3769          }
3770        }
3771        // Move ahead one byte and re-try the decode process.
3772        if (in.skip(1) < 1) {
3773          return null;
3774        }
3775      }
3776    }
3777
3778    private void verifyTerminator() throws IOException {
3779      /** The end of the edit log should contain only 0x00 or 0xff bytes.
3780       * If it contains other bytes, the log itself may be corrupt.
3781       * It is important to check this; if we don't, a stray OP_INVALID byte 
3782       * could make us stop reading the edit log halfway through, and we'd never
3783       * know that we had lost data.
3784       */
3785      byte[] buf = new byte[4096];
3786      limiter.clearLimit();
3787      int numRead = -1, idx = 0;
3788      while (true) {
3789        try {
3790          numRead = -1;
3791          idx = 0;
3792          numRead = in.read(buf);
3793          if (numRead == -1) {
3794            return;
3795          }
3796          while (idx < numRead) {
3797            if ((buf[idx] != (byte)0) && (buf[idx] != (byte)-1)) {
3798              throw new IOException("Read extra bytes after " +
3799                "the terminator!");
3800            }
3801            idx++;
3802          }
3803        } finally {
3804          // After reading each group of bytes, we reposition the mark one
3805          // byte before the next group.  Similarly, if there is an error, we
3806          // want to reposition the mark one byte before the error
3807          if (numRead != -1) { 
3808            in.reset();
3809            IOUtils.skipFully(in, idx);
3810            in.mark(buf.length + 1);
3811            IOUtils.skipFully(in, 1);
3812          }
3813        }
3814      }
3815    }
3816
3817    /**
3818     * Read an opcode from the input stream.
3819     *
3820     * @return   the opcode, or null on EOF.
3821     *
3822     * If an exception is thrown, the stream's mark will be set to the first
3823     * problematic byte.  This usually means the beginning of the opcode.
3824     */
3825    private FSEditLogOp decodeOp() throws IOException {
3826      limiter.setLimit(maxOpSize);
3827      in.mark(maxOpSize);
3828
3829      if (checksum != null) {
3830        checksum.reset();
3831      }
3832
3833      byte opCodeByte;
3834      try {
3835        opCodeByte = in.readByte();
3836      } catch (EOFException eof) {
3837        // EOF at an opcode boundary is expected.
3838        return null;
3839      }
3840
3841      FSEditLogOpCodes opCode = FSEditLogOpCodes.fromByte(opCodeByte);
3842      if (opCode == OP_INVALID) {
3843        verifyTerminator();
3844        return null;
3845      }
3846
3847      FSEditLogOp op = cache.get(opCode);
3848      if (op == null) {
3849        throw new IOException("Read invalid opcode " + opCode);
3850      }
3851
3852      if (supportEditLogLength) {
3853        in.readInt();
3854      }
3855
3856      if (NameNodeLayoutVersion.supports(
3857          LayoutVersion.Feature.STORED_TXIDS, logVersion)) {
3858        // Read the txid
3859        op.setTransactionId(in.readLong());
3860      } else {
3861        op.setTransactionId(HdfsConstants.INVALID_TXID);
3862      }
3863
3864      op.readFields(in, logVersion);
3865
3866      validateChecksum(in, checksum, op.txid);
3867      return op;
3868    }
3869
3870    /**
3871     * Similar with decodeOp(), but instead of doing the real decoding, we skip
3872     * the content of the op if the length of the editlog is supported.
3873     * @return the last txid of the segment, or INVALID_TXID on exception
3874     */
3875    public long scanOp() throws IOException {
3876      if (supportEditLogLength) {
3877        limiter.setLimit(maxOpSize);
3878        in.mark(maxOpSize);
3879
3880        final byte opCodeByte;
3881        try {
3882          opCodeByte = in.readByte(); // op code
3883        } catch (EOFException e) {
3884          return HdfsConstants.INVALID_TXID;
3885        }
3886
3887        FSEditLogOpCodes opCode = FSEditLogOpCodes.fromByte(opCodeByte);
3888        if (opCode == OP_INVALID) {
3889          verifyTerminator();
3890          return HdfsConstants.INVALID_TXID;
3891        }
3892
3893        int length = in.readInt(); // read the length of the op
3894        long txid = in.readLong(); // read the txid
3895
3896        // skip the remaining content
3897        IOUtils.skipFully(in, length - 8); 
3898        // TODO: do we want to verify checksum for JN? For now we don't.
3899        return txid;
3900      } else {
3901        FSEditLogOp op = decodeOp();
3902        return op == null ? HdfsConstants.INVALID_TXID : op.getTransactionId();
3903      }
3904    }
3905
3906    /**
3907     * Validate a transaction's checksum
3908     */
3909    private void validateChecksum(DataInputStream in,
3910                                  Checksum checksum,
3911                                  long txid)
3912        throws IOException {
3913      if (checksum != null) {
3914        int calculatedChecksum = (int)checksum.getValue();
3915        int readChecksum = in.readInt(); // read in checksum
3916        if (readChecksum != calculatedChecksum) {
3917          throw new ChecksumException(
3918              "Transaction is corrupt. Calculated checksum is " +
3919              calculatedChecksum + " but read checksum " + readChecksum, txid);
3920        }
3921      }
3922    }
3923  }
3924
3925  public void outputToXml(ContentHandler contentHandler) throws SAXException {
3926    contentHandler.startElement("", "", "RECORD", new AttributesImpl());
3927    XMLUtils.addSaxString(contentHandler, "OPCODE", opCode.toString());
3928    contentHandler.startElement("", "", "DATA", new AttributesImpl());
3929    XMLUtils.addSaxString(contentHandler, "TXID", "" + txid);
3930    toXml(contentHandler);
3931    contentHandler.endElement("", "", "DATA");
3932    contentHandler.endElement("", "", "RECORD");
3933  }
3934
3935  protected abstract void toXml(ContentHandler contentHandler)
3936      throws SAXException;
3937  
3938  abstract void fromXml(Stanza st) throws InvalidXmlException;
3939  
3940  public void decodeXml(Stanza st) throws InvalidXmlException {
3941    this.txid = Long.valueOf(st.getValue("TXID"));
3942    fromXml(st);
3943  }
3944  
3945  public static void blockToXml(ContentHandler contentHandler, Block block) 
3946      throws SAXException {
3947    contentHandler.startElement("", "", "BLOCK", new AttributesImpl());
3948    XMLUtils.addSaxString(contentHandler, "BLOCK_ID",
3949        Long.valueOf(block.getBlockId()).toString());
3950    XMLUtils.addSaxString(contentHandler, "NUM_BYTES",
3951        Long.valueOf(block.getNumBytes()).toString());
3952    XMLUtils.addSaxString(contentHandler, "GENSTAMP",
3953        Long.valueOf(block.getGenerationStamp()).toString());
3954    contentHandler.endElement("", "", "BLOCK");
3955  }
3956
3957  public static Block blockFromXml(Stanza st)
3958      throws InvalidXmlException {
3959    long blockId = Long.valueOf(st.getValue("BLOCK_ID"));
3960    long numBytes = Long.valueOf(st.getValue("NUM_BYTES"));
3961    long generationStamp = Long.valueOf(st.getValue("GENSTAMP"));
3962    return new Block(blockId, numBytes, generationStamp);
3963  }
3964
3965  public static void delegationTokenToXml(ContentHandler contentHandler,
3966      DelegationTokenIdentifier token) throws SAXException {
3967    contentHandler.startElement("", "", "DELEGATION_TOKEN_IDENTIFIER", new AttributesImpl());
3968    XMLUtils.addSaxString(contentHandler, "KIND", token.getKind().toString());
3969    XMLUtils.addSaxString(contentHandler, "SEQUENCE_NUMBER",
3970        Integer.valueOf(token.getSequenceNumber()).toString());
3971    XMLUtils.addSaxString(contentHandler, "OWNER",
3972        token.getOwner().toString());
3973    XMLUtils.addSaxString(contentHandler, "RENEWER",
3974        token.getRenewer().toString());
3975    XMLUtils.addSaxString(contentHandler, "REALUSER",
3976        token.getRealUser().toString());
3977    XMLUtils.addSaxString(contentHandler, "ISSUE_DATE",
3978        Long.valueOf(token.getIssueDate()).toString());
3979    XMLUtils.addSaxString(contentHandler, "MAX_DATE",
3980        Long.valueOf(token.getMaxDate()).toString());
3981    XMLUtils.addSaxString(contentHandler, "MASTER_KEY_ID",
3982        Integer.valueOf(token.getMasterKeyId()).toString());
3983    contentHandler.endElement("", "", "DELEGATION_TOKEN_IDENTIFIER");
3984  }
3985
3986  public static DelegationTokenIdentifier delegationTokenFromXml(Stanza st)
3987      throws InvalidXmlException {
3988    String kind = st.getValue("KIND");
3989    if (!kind.equals(DelegationTokenIdentifier.
3990        HDFS_DELEGATION_KIND.toString())) {
3991      throw new InvalidXmlException("can't understand " +
3992        "DelegationTokenIdentifier KIND " + kind);
3993    }
3994    int seqNum = Integer.valueOf(st.getValue("SEQUENCE_NUMBER"));
3995    String owner = st.getValue("OWNER");
3996    String renewer = st.getValue("RENEWER");
3997    String realuser = st.getValue("REALUSER");
3998    long issueDate = Long.valueOf(st.getValue("ISSUE_DATE"));
3999    long maxDate = Long.valueOf(st.getValue("MAX_DATE"));
4000    int masterKeyId = Integer.valueOf(st.getValue("MASTER_KEY_ID"));
4001    DelegationTokenIdentifier token =
4002        new DelegationTokenIdentifier(new Text(owner),
4003            new Text(renewer), new Text(realuser));
4004    token.setSequenceNumber(seqNum);
4005    token.setIssueDate(issueDate);
4006    token.setMaxDate(maxDate);
4007    token.setMasterKeyId(masterKeyId);
4008    return token;
4009  }
4010
4011  public static void delegationKeyToXml(ContentHandler contentHandler,
4012      DelegationKey key) throws SAXException {
4013    contentHandler.startElement("", "", "DELEGATION_KEY", new AttributesImpl());
4014    XMLUtils.addSaxString(contentHandler, "KEY_ID",
4015        Integer.valueOf(key.getKeyId()).toString());
4016    XMLUtils.addSaxString(contentHandler, "EXPIRY_DATE",
4017        Long.valueOf(key.getExpiryDate()).toString());
4018    if (key.getEncodedKey() != null) {
4019      XMLUtils.addSaxString(contentHandler, "KEY",
4020          Hex.encodeHexString(key.getEncodedKey()));
4021    }
4022    contentHandler.endElement("", "", "DELEGATION_KEY");
4023  }
4024  
4025  public static DelegationKey delegationKeyFromXml(Stanza st)
4026      throws InvalidXmlException {
4027    int keyId = Integer.valueOf(st.getValue("KEY_ID"));
4028    long expiryDate = Long.valueOf(st.getValue("EXPIRY_DATE"));
4029    byte key[] = null;
4030    try {
4031      key = Hex.decodeHex(st.getValue("KEY").toCharArray());
4032    } catch (DecoderException e) {
4033      throw new InvalidXmlException(e.toString());
4034    } catch (InvalidXmlException e) {
4035    }
4036    return new DelegationKey(keyId, expiryDate, key);
4037  }
4038
4039  public static void permissionStatusToXml(ContentHandler contentHandler,
4040      PermissionStatus perm) throws SAXException {
4041    contentHandler.startElement("", "", "PERMISSION_STATUS", new AttributesImpl());
4042    XMLUtils.addSaxString(contentHandler, "USERNAME", perm.getUserName());
4043    XMLUtils.addSaxString(contentHandler, "GROUPNAME", perm.getGroupName());
4044    fsPermissionToXml(contentHandler, perm.getPermission());
4045    contentHandler.endElement("", "", "PERMISSION_STATUS");
4046  }
4047
4048  public static PermissionStatus permissionStatusFromXml(Stanza st)
4049      throws InvalidXmlException {
4050    Stanza status = st.getChildren("PERMISSION_STATUS").get(0);
4051    String username = status.getValue("USERNAME");
4052    String groupname = status.getValue("GROUPNAME");
4053    FsPermission mode = fsPermissionFromXml(status);
4054    return new PermissionStatus(username, groupname, mode);
4055  }
4056
4057  public static void fsPermissionToXml(ContentHandler contentHandler,
4058      FsPermission mode) throws SAXException {
4059    XMLUtils.addSaxString(contentHandler, "MODE", Short.valueOf(mode.toShort())
4060        .toString());
4061  }
4062
4063  public static FsPermission fsPermissionFromXml(Stanza st)
4064      throws InvalidXmlException {
4065    short mode = Short.valueOf(st.getValue("MODE"));
4066    return new FsPermission(mode);
4067  }
4068
4069  private static void fsActionToXml(ContentHandler contentHandler, FsAction v)
4070      throws SAXException {
4071    XMLUtils.addSaxString(contentHandler, "PERM", v.SYMBOL);
4072  }
4073
4074  private static FsAction fsActionFromXml(Stanza st) throws InvalidXmlException {
4075    FsAction v = FSACTION_SYMBOL_MAP.get(st.getValue("PERM"));
4076    if (v == null)
4077      throw new InvalidXmlException("Invalid value for FsAction");
4078    return v;
4079  }
4080
4081  private static void appendAclEntriesToXml(ContentHandler contentHandler,
4082      List<AclEntry> aclEntries) throws SAXException {
4083    for (AclEntry e : aclEntries) {
4084      contentHandler.startElement("", "", "ENTRY", new AttributesImpl());
4085      XMLUtils.addSaxString(contentHandler, "SCOPE", e.getScope().name());
4086      XMLUtils.addSaxString(contentHandler, "TYPE", e.getType().name());
4087      if (e.getName() != null) {
4088        XMLUtils.addSaxString(contentHandler, "NAME", e.getName());
4089      }
4090      fsActionToXml(contentHandler, e.getPermission());
4091      contentHandler.endElement("", "", "ENTRY");
4092    }
4093  }
4094
4095  private static List<AclEntry> readAclEntriesFromXml(Stanza st) {
4096    List<AclEntry> aclEntries = Lists.newArrayList();
4097    if (!st.hasChildren("ENTRY"))
4098      return null;
4099
4100    List<Stanza> stanzas = st.getChildren("ENTRY");
4101    for (Stanza s : stanzas) {
4102      AclEntry e = new AclEntry.Builder()
4103        .setScope(AclEntryScope.valueOf(s.getValue("SCOPE")))
4104        .setType(AclEntryType.valueOf(s.getValue("TYPE")))
4105        .setName(s.getValueOrNull("NAME"))
4106        .setPermission(fsActionFromXml(s)).build();
4107      aclEntries.add(e);
4108    }
4109    return aclEntries;
4110  }
4111}