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.protocol;
019
020 import java.util.Arrays;
021 import java.util.Collections;
022 import java.util.List;
023
024 import org.apache.hadoop.fs.Path;
025 import org.apache.hadoop.hdfs.DFSUtil;
026 import org.apache.hadoop.hdfs.server.namenode.snapshot.INodeDirectorySnapshottable.SnapshotDiffInfo;
027
028 /**
029 * This class represents to end users the difference between two snapshots of
030 * the same directory, or the difference between a snapshot of the directory and
031 * its current state. Instead of capturing all the details of the diff, which
032 * is stored in {@link SnapshotDiffInfo}, this class only lists where the
033 * changes happened and their types.
034 */
035 public class SnapshotDiffReport {
036 private final static String LINE_SEPARATOR = System.getProperty(
037 "line.separator", "\n");
038
039 /**
040 * Types of the difference, which include CREATE, MODIFY, DELETE, and RENAME.
041 * Each type has a label for representation: +/M/-/R represent CREATE, MODIFY,
042 * DELETE, and RENAME respectively.
043 */
044 public enum DiffType {
045 CREATE("+"),
046 MODIFY("M"),
047 DELETE("-"),
048 RENAME("R");
049
050 private final String label;
051
052 private DiffType(String label) {
053 this.label = label;
054 }
055
056 public String getLabel() {
057 return label;
058 }
059
060 public static DiffType getTypeFromLabel(String label) {
061 if (label.equals(CREATE.getLabel())) {
062 return CREATE;
063 } else if (label.equals(MODIFY.getLabel())) {
064 return MODIFY;
065 } else if (label.equals(DELETE.getLabel())) {
066 return DELETE;
067 } else if (label.equals(RENAME.getLabel())) {
068 return RENAME;
069 }
070 return null;
071 }
072 };
073
074 /**
075 * Representing the full path and diff type of a file/directory where changes
076 * have happened.
077 */
078 public static class DiffReportEntry {
079 /** The type of the difference. */
080 private final DiffType type;
081 /**
082 * The relative path (related to the snapshot root) of the file/directory
083 * where changes have happened
084 */
085 private final byte[] relativePath;
086
087 public DiffReportEntry(DiffType type, byte[] path) {
088 this.type = type;
089 this.relativePath = path;
090 }
091
092 public DiffReportEntry(DiffType type, byte[][] pathComponents) {
093 this.type = type;
094 this.relativePath = DFSUtil.byteArray2bytes(pathComponents);
095 }
096
097 @Override
098 public String toString() {
099 return type.getLabel() + "\t" + getRelativePathString();
100 }
101
102 public DiffType getType() {
103 return type;
104 }
105
106 public String getRelativePathString() {
107 String path = DFSUtil.bytes2String(relativePath);
108 if (path.isEmpty()) {
109 return Path.CUR_DIR;
110 } else {
111 return Path.CUR_DIR + Path.SEPARATOR + path;
112 }
113 }
114
115 public byte[] getRelativePath() {
116 return relativePath;
117 }
118
119 @Override
120 public boolean equals(Object other) {
121 if (this == other) {
122 return true;
123 }
124 if (other != null && other instanceof DiffReportEntry) {
125 DiffReportEntry entry = (DiffReportEntry) other;
126 return type.equals(entry.getType())
127 && Arrays.equals(relativePath, entry.getRelativePath());
128 }
129 return false;
130 }
131
132 @Override
133 public int hashCode() {
134 return Arrays.hashCode(relativePath);
135 }
136 }
137
138 /** snapshot root full path */
139 private final String snapshotRoot;
140
141 /** start point of the diff */
142 private final String fromSnapshot;
143
144 /** end point of the diff */
145 private final String toSnapshot;
146
147 /** list of diff */
148 private final List<DiffReportEntry> diffList;
149
150 public SnapshotDiffReport(String snapshotRoot, String fromSnapshot,
151 String toSnapshot, List<DiffReportEntry> entryList) {
152 this.snapshotRoot = snapshotRoot;
153 this.fromSnapshot = fromSnapshot;
154 this.toSnapshot = toSnapshot;
155 this.diffList = entryList != null ? entryList : Collections
156 .<DiffReportEntry> emptyList();
157 }
158
159 /** @return {@link #snapshotRoot}*/
160 public String getSnapshotRoot() {
161 return snapshotRoot;
162 }
163
164 /** @return {@link #fromSnapshot} */
165 public String getFromSnapshot() {
166 return fromSnapshot;
167 }
168
169 /** @return {@link #toSnapshot} */
170 public String getLaterSnapshotName() {
171 return toSnapshot;
172 }
173
174 /** @return {@link #diffList} */
175 public List<DiffReportEntry> getDiffList() {
176 return diffList;
177 }
178
179 @Override
180 public String toString() {
181 StringBuilder str = new StringBuilder();
182 String from = fromSnapshot == null || fromSnapshot.isEmpty() ?
183 "current directory" : "snapshot " + fromSnapshot;
184 String to = toSnapshot == null || toSnapshot.isEmpty() ? "current directory"
185 : "snapshot " + toSnapshot;
186 str.append("Difference between " + from + " and " + to
187 + " under directory " + snapshotRoot + ":" + LINE_SEPARATOR);
188 for (DiffReportEntry entry : diffList) {
189 str.append(entry.toString() + LINE_SEPARATOR);
190 }
191 return str.toString();
192 }
193 }