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