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 }