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 * http://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 * @see FileAlterationObserver 052 * @since 2.0 053 */ 054public class FileEntry implements Serializable { 055 056 private static final long serialVersionUID = -2505664948818681153L; 057 058 static final FileEntry[] EMPTY_FILE_ENTRY_ARRAY = {}; 059 060 /** The parent. */ 061 private final FileEntry parent; 062 063 /** My children. */ 064 private FileEntry[] children; 065 066 /** Monitored file. */ 067 private final File file; 068 069 /** Monitored file name. */ 070 private String name; 071 072 /** Whether the file exists. */ 073 private boolean exists; 074 075 /** Whether the file is a directory or not. */ 076 private boolean directory; 077 078 /** The file's last modified timestamp. */ 079 private SerializableFileTime lastModified = SerializableFileTime.EPOCH; 080 081 /** The file's length. */ 082 private long length; 083 084 /** 085 * Constructs a new monitor for a specified {@link File}. 086 * 087 * @param file The file being monitored 088 */ 089 public FileEntry(final File file) { 090 this(null, file); 091 } 092 093 /** 094 * Constructs a new monitor for a specified {@link File}. 095 * 096 * @param parent The parent. 097 * @param file The file being monitored. 098 */ 099 public FileEntry(final FileEntry parent, final File file) { 100 this.file = Objects.requireNonNull(file, "file"); 101 this.parent = parent; 102 this.name = file.getName(); 103 } 104 105 /** 106 * Gets the directory's files. 107 * 108 * @return This directory's files or an empty 109 * array if the file is not a directory or the 110 * directory is empty 111 */ 112 public FileEntry[] getChildren() { 113 return children != null ? children : EMPTY_FILE_ENTRY_ARRAY; 114 } 115 116 /** 117 * Gets the file being monitored. 118 * 119 * @return the file being monitored 120 */ 121 public File getFile() { 122 return file; 123 } 124 125 /** 126 * Gets the last modified time from the last time it 127 * was checked. 128 * 129 * @return the last modified time in milliseconds. 130 */ 131 public long getLastModified() { 132 return lastModified.toMillis(); 133 } 134 135 /** 136 * Gets the last modified time from the last time it was checked. 137 * 138 * @return the last modified time. 139 * @since 2.12.0 140 */ 141 public FileTime getLastModifiedFileTime() { 142 return lastModified.unwrap(); 143 } 144 145 /** 146 * Gets the length. 147 * 148 * @return the length 149 */ 150 public long getLength() { 151 return length; 152 } 153 154 /** 155 * Gets the level 156 * 157 * @return the level 158 */ 159 public int getLevel() { 160 return parent == null ? 0 : parent.getLevel() + 1; 161 } 162 163 /** 164 * Gets the file name. 165 * 166 * @return the file name 167 */ 168 public String getName() { 169 return name; 170 } 171 172 /** 173 * Gets the parent entry. 174 * 175 * @return the parent entry 176 */ 177 public FileEntry getParent() { 178 return parent; 179 } 180 181 /** 182 * Tests whether the file is a directory or not. 183 * 184 * @return whether the file is a directory or not 185 */ 186 public boolean isDirectory() { 187 return directory; 188 } 189 190 /** 191 * Tests whether the file existed the last time it 192 * was checked. 193 * 194 * @return whether the file existed 195 */ 196 public boolean isExists() { 197 return exists; 198 } 199 200 /** 201 * Constructs a new child instance. 202 * <p> 203 * Custom implementations should override this method to return 204 * a new instance of the appropriate type. 205 * </p> 206 * 207 * @param file The child file 208 * @return a new child instance 209 */ 210 public FileEntry newChildInstance(final File file) { 211 return new FileEntry(this, file); 212 } 213 214 /** 215 * Refreshes the attributes from the {@link File}, indicating 216 * whether the file has changed. 217 * <p> 218 * This implementation refreshes the {@code name}, {@code exists}, 219 * {@code directory}, {@code lastModified} and {@code length} 220 * properties. 221 * </p> 222 * <p> 223 * The {@code exists}, {@code directory}, {@code lastModified} 224 * and {@code length} properties are compared for changes 225 * </p> 226 * 227 * @param file the file instance to compare to 228 * @return {@code true} if the file has changed, otherwise {@code false} 229 */ 230 public boolean refresh(final File file) { 231 // cache original values 232 final boolean origExists = exists; 233 final SerializableFileTime origLastModified = lastModified; 234 final boolean origDirectory = directory; 235 final long origLength = length; 236 237 // refresh the values 238 name = file.getName(); 239 exists = Files.exists(file.toPath()); 240 directory = exists && file.isDirectory(); 241 try { 242 setLastModified(exists ? FileUtils.lastModifiedFileTime(file) : FileTimes.EPOCH); 243 } catch (final IOException e) { 244 setLastModified(SerializableFileTime.EPOCH); 245 } 246 length = exists && !directory ? file.length() : 0; 247 248 // Return if there are changes 249 return exists != origExists || !lastModified.equals(origLastModified) || directory != origDirectory 250 || length != origLength; 251 } 252 253 /** 254 * Sets the directory's files. 255 * 256 * @param children This directory's files, may be null 257 */ 258 public void setChildren(final FileEntry... children) { 259 this.children = children; 260 } 261 262 /** 263 * Sets whether the file is a directory or not. 264 * 265 * @param directory whether the file is a directory or not 266 */ 267 public void setDirectory(final boolean directory) { 268 this.directory = directory; 269 } 270 271 /** 272 * Sets whether the file existed the last time it 273 * was checked. 274 * 275 * @param exists whether the file exists or not 276 */ 277 public void setExists(final boolean exists) { 278 this.exists = exists; 279 } 280 281 /** 282 * Sets the last modified time from the last time it was checked. 283 * 284 * @param lastModified The last modified time. 285 * @since 2.12.0 286 */ 287 public void setLastModified(final FileTime lastModified) { 288 setLastModified(new SerializableFileTime(lastModified)); 289 } 290 291 /** 292 * Sets the last modified time from the last time it 293 * was checked. 294 * 295 * @param lastModified The last modified time in milliseconds. 296 */ 297 public void setLastModified(final long lastModified) { 298 setLastModified(FileTime.fromMillis(lastModified)); 299 } 300 301 void setLastModified(final SerializableFileTime lastModified) { 302 this.lastModified = lastModified; 303 } 304 305 /** 306 * Sets the length. 307 * 308 * @param length the length 309 */ 310 public void setLength(final long length) { 311 this.length = length; 312 } 313 314 /** 315 * Sets the file name. 316 * 317 * @param name the file name 318 */ 319 public void setName(final String name) { 320 this.name = name; 321 } 322}