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