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 */ 018package org.apache.hadoop.hdfs.protocol; 019 020import java.util.Arrays; 021import java.util.Collections; 022import java.util.List; 023 024import org.apache.hadoop.fs.Path; 025import org.apache.hadoop.hdfs.DFSUtil; 026 027import 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 */ 035public 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}