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