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