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