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