001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io.monitor; 018 019import java.io.File; 020import java.io.IOException; 021import java.io.Serializable; 022import java.nio.file.Files; 023import java.nio.file.attribute.FileTime; 024import java.util.Objects; 025 026import org.apache.commons.io.FileUtils; 027import org.apache.commons.io.file.attribute.FileTimes; 028 029/** 030 * The state of a file or directory, capturing the following {@link File} attributes at a point in time. 031 * <ul> 032 * <li>File Name (see {@link File#getName()})</li> 033 * <li>Exists - whether the file exists or not (see {@link File#exists()})</li> 034 * <li>Directory - whether the file is a directory or not (see {@link File#isDirectory()})</li> 035 * <li>Last Modified Date/Time (see {@link FileUtils#lastModifiedUnchecked(File)})</li> 036 * <li>Length (see {@link File#length()}) - directories treated as zero</li> 037 * <li>Children - contents of a directory (see {@link File#listFiles(java.io.FileFilter)})</li> 038 * </ul> 039 * 040 * <h2>Custom Implementations</h2> 041 * <p> 042 * If the state of additional {@link File} attributes is required then create a custom 043 * {@link FileEntry} with properties for those attributes. Override the 044 * {@link #newChildInstance(File)} to return a new instance of the appropriate type. 045 * You may also want to override the {@link #refresh(File)} method. 046 * </p> 047 * <h2>Deprecating Serialization</h2> 048 * <p> 049 * <em>Serialization is deprecated and will be removed in 3.0.</em> 050 * </p> 051 * 052 * @see FileAlterationObserver 053 * @since 2.0 054 */ 055public class FileEntry implements Serializable { 056 057 private static final long serialVersionUID = -2505664948818681153L; 058 059 static final FileEntry[] EMPTY_FILE_ENTRY_ARRAY = {}; 060 061 /** The parent. */ 062 private final FileEntry parent; 063 064 /** My children. */ 065 private FileEntry[] children; 066 067 /** Monitored file. */ 068 private final File file; 069 070 /** Monitored file name. */ 071 private String name; 072 073 /** Whether the file exists. */ 074 private boolean exists; 075 076 /** Whether the file is a directory or not. */ 077 private boolean directory; 078 079 /** The file's last modified timestamp. */ 080 private SerializableFileTime lastModified = SerializableFileTime.EPOCH; 081 082 /** The file's length. */ 083 private long length; 084 085 /** 086 * Constructs a new monitor for a specified {@link File}. 087 * 088 * @param file The file being monitored. 089 */ 090 public FileEntry(final File file) { 091 this(null, file); 092 } 093 094 /** 095 * Constructs a new monitor for a specified {@link File}. 096 * 097 * @param parent The parent. 098 * @param file The file being monitored. 099 */ 100 public FileEntry(final FileEntry parent, final File file) { 101 this.file = Objects.requireNonNull(file, "file"); 102 this.parent = parent; 103 this.name = file.getName(); 104 } 105 106 /** 107 * Gets the directory's files. 108 * 109 * @return This directory's files or an empty 110 * array if the file is not a directory or the 111 * directory is empty. 112 */ 113 public FileEntry[] getChildren() { 114 return children != null ? children : EMPTY_FILE_ENTRY_ARRAY; 115 } 116 117 /** 118 * Gets the file being monitored. 119 * 120 * @return the file being monitored. 121 */ 122 public File getFile() { 123 return file; 124 } 125 126 /** 127 * Gets the last modified time from the last time it 128 * was checked. 129 * 130 * @return the last modified time in milliseconds. 131 */ 132 public long getLastModified() { 133 return lastModified.toMillis(); 134 } 135 136 /** 137 * Gets the last modified time from the last time it was checked. 138 * 139 * @return the last modified time. 140 * @since 2.12.0 141 */ 142 public FileTime getLastModifiedFileTime() { 143 return lastModified.unwrap(); 144 } 145 146 /** 147 * Gets the length. 148 * 149 * @return the length. 150 */ 151 public long getLength() { 152 return length; 153 } 154 155 /** 156 * Gets the level 157 * 158 * @return the level. 159 */ 160 public int getLevel() { 161 return parent == null ? 0 : parent.getLevel() + 1; 162 } 163 164 /** 165 * Gets the file name. 166 * 167 * @return the file name. 168 */ 169 public String getName() { 170 return name; 171 } 172 173 /** 174 * Gets the parent entry. 175 * 176 * @return the parent entry. 177 */ 178 public FileEntry getParent() { 179 return parent; 180 } 181 182 /** 183 * Tests whether the file is a directory or not. 184 * 185 * @return whether the file is a directory or not. 186 */ 187 public boolean isDirectory() { 188 return directory; 189 } 190 191 /** 192 * Tests whether the file existed the last time it 193 * was checked. 194 * 195 * @return whether the file existed. 196 */ 197 public boolean isExists() { 198 return exists; 199 } 200 201 /** 202 * Constructs a new child instance. 203 * <p> 204 * Custom implementations should override this method to return 205 * a new instance of the appropriate type. 206 * </p> 207 * 208 * @param file The child file. 209 * @return a new child instance. 210 */ 211 public FileEntry newChildInstance(final File file) { 212 return new FileEntry(this, file); 213 } 214 215 /** 216 * Refreshes the attributes from the {@link File}, indicating 217 * whether the file has changed. 218 * <p> 219 * This implementation refreshes the {@code name}, {@code exists}, 220 * {@code directory}, {@code lastModified} and {@code length} 221 * properties. 222 * </p> 223 * <p> 224 * The {@code exists}, {@code directory}, {@code lastModified} 225 * and {@code length} properties are compared for changes 226 * </p> 227 * 228 * @param file the file instance to compare to. 229 * @return {@code true} if the file has changed, otherwise {@code false}. 230 */ 231 public boolean refresh(final File file) { 232 // cache original values 233 final boolean origExists = exists; 234 final SerializableFileTime origLastModified = lastModified; 235 final boolean origDirectory = directory; 236 final long origLength = length; 237 238 // refresh the values 239 name = file.getName(); 240 exists = Files.exists(file.toPath()); 241 directory = exists && file.isDirectory(); 242 try { 243 setLastModified(exists ? FileUtils.lastModifiedFileTime(file) : FileTimes.EPOCH); 244 } catch (final IOException e) { 245 setLastModified(SerializableFileTime.EPOCH); 246 } 247 length = exists && !directory ? file.length() : 0; 248 249 // Return if there are changes 250 return exists != origExists || !lastModified.equals(origLastModified) || directory != origDirectory 251 || length != origLength; 252 } 253 254 /** 255 * Sets the directory's files. 256 * 257 * @param children This directory's files, may be null. 258 */ 259 public void setChildren(final FileEntry... children) { 260 this.children = children; 261 } 262 263 /** 264 * Sets whether the file is a directory or not. 265 * 266 * @param directory whether the file is a directory or not. 267 */ 268 public void setDirectory(final boolean directory) { 269 this.directory = directory; 270 } 271 272 /** 273 * Sets whether the file existed the last time it 274 * was checked. 275 * 276 * @param exists whether the file exists or not. 277 */ 278 public void setExists(final boolean exists) { 279 this.exists = exists; 280 } 281 282 /** 283 * Sets the last modified time from the last time it was checked. 284 * 285 * @param lastModified The last modified time. 286 * @since 2.12.0 287 */ 288 public void setLastModified(final FileTime lastModified) { 289 setLastModified(new SerializableFileTime(lastModified)); 290 } 291 292 /** 293 * Sets the last modified time from the last time it 294 * was checked. 295 * 296 * @param lastModified The last modified time in milliseconds. 297 */ 298 public void setLastModified(final long lastModified) { 299 setLastModified(FileTime.fromMillis(lastModified)); 300 } 301 302 void setLastModified(final SerializableFileTime lastModified) { 303 this.lastModified = lastModified; 304 } 305 306 /** 307 * Sets the length. 308 * 309 * @param length the length. 310 */ 311 public void setLength(final long length) { 312 this.length = length; 313 } 314 315 /** 316 * Sets the file name. 317 * 318 * @param name the file name. 319 */ 320 public void setName(final String name) { 321 this.name = name; 322 } 323}