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
027 import com.google.common.base.Objects;
028
029 /**
030 * This class represents to end users the difference between two snapshots of
031 * the same directory, or the difference between a snapshot of the directory and
032 * its current state. Instead of capturing all the details of the diff, this
033 * class only lists where the 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 1) the file/directory
083 * where changes have happened, or 2) the source file/dir of a rename op.
084 */
085 private final byte[] sourcePath;
086 private final byte[] targetPath;
087
088 public DiffReportEntry(DiffType type, byte[] sourcePath) {
089 this(type, sourcePath, null);
090 }
091
092 public DiffReportEntry(DiffType type, byte[][] sourcePathComponents) {
093 this(type, sourcePathComponents, null);
094 }
095
096 public DiffReportEntry(DiffType type, byte[] sourcePath, byte[] targetPath) {
097 this.type = type;
098 this.sourcePath = sourcePath;
099 this.targetPath = targetPath;
100 }
101
102 public DiffReportEntry(DiffType type, byte[][] sourcePathComponents,
103 byte[][] targetPathComponents) {
104 this.type = type;
105 this.sourcePath = DFSUtil.byteArray2bytes(sourcePathComponents);
106 this.targetPath = targetPathComponents == null ? null : DFSUtil
107 .byteArray2bytes(targetPathComponents);
108 }
109
110 @Override
111 public String toString() {
112 String str = type.getLabel() + "\t" + getPathString(sourcePath);
113 if (type == DiffType.RENAME) {
114 str += " -> " + getPathString(targetPath);
115 }
116 return str;
117 }
118
119 public DiffType getType() {
120 return type;
121 }
122
123 static String getPathString(byte[] path) {
124 String pathStr = DFSUtil.bytes2String(path);
125 if (pathStr.isEmpty()) {
126 return Path.CUR_DIR;
127 } else {
128 return Path.CUR_DIR + Path.SEPARATOR + pathStr;
129 }
130 }
131
132 public byte[] getSourcePath() {
133 return sourcePath;
134 }
135
136 public byte[] getTargetPath() {
137 return targetPath;
138 }
139
140 @Override
141 public boolean equals(Object other) {
142 if (this == other) {
143 return true;
144 }
145 if (other != null && other instanceof DiffReportEntry) {
146 DiffReportEntry entry = (DiffReportEntry) other;
147 return type.equals(entry.getType())
148 && Arrays.equals(sourcePath, entry.getSourcePath())
149 && Arrays.equals(targetPath, entry.getTargetPath());
150 }
151 return false;
152 }
153
154 @Override
155 public int hashCode() {
156 return Objects.hashCode(getSourcePath(), getTargetPath());
157 }
158 }
159
160 /** snapshot root full path */
161 private final String snapshotRoot;
162
163 /** start point of the diff */
164 private final String fromSnapshot;
165
166 /** end point of the diff */
167 private final String toSnapshot;
168
169 /** list of diff */
170 private final List<DiffReportEntry> diffList;
171
172 public SnapshotDiffReport(String snapshotRoot, String fromSnapshot,
173 String toSnapshot, List<DiffReportEntry> entryList) {
174 this.snapshotRoot = snapshotRoot;
175 this.fromSnapshot = fromSnapshot;
176 this.toSnapshot = toSnapshot;
177 this.diffList = entryList != null ? entryList : Collections
178 .<DiffReportEntry> emptyList();
179 }
180
181 /** @return {@link #snapshotRoot}*/
182 public String getSnapshotRoot() {
183 return snapshotRoot;
184 }
185
186 /** @return {@link #fromSnapshot} */
187 public String getFromSnapshot() {
188 return fromSnapshot;
189 }
190
191 /** @return {@link #toSnapshot} */
192 public String getLaterSnapshotName() {
193 return toSnapshot;
194 }
195
196 /** @return {@link #diffList} */
197 public List<DiffReportEntry> getDiffList() {
198 return diffList;
199 }
200
201 @Override
202 public String toString() {
203 StringBuilder str = new StringBuilder();
204 String from = fromSnapshot == null || fromSnapshot.isEmpty() ?
205 "current directory" : "snapshot " + fromSnapshot;
206 String to = toSnapshot == null || toSnapshot.isEmpty() ? "current directory"
207 : "snapshot " + toSnapshot;
208 str.append("Difference between " + from + " and " + to
209 + " under directory " + snapshotRoot + ":" + LINE_SEPARATOR);
210 for (DiffReportEntry entry : diffList) {
211 str.append(entry.toString() + LINE_SEPARATOR);
212 }
213 return str.toString();
214 }
215 }