@ -80,6 +80,8 @@ public class FileSnapshot {
* /
* /
public static final long UNKNOWN_SIZE = - 1 ;
public static final long UNKNOWN_SIZE = - 1 ;
private static final Object MISSING_FILEKEY = new Object ( ) ;
/ * *
/ * *
* A FileSnapshot that is considered to always be modified .
* A FileSnapshot that is considered to always be modified .
* < p >
* < p >
@ -88,7 +90,7 @@ public class FileSnapshot {
* snapshot contains only invalid status information .
* snapshot contains only invalid status information .
* /
* /
public static final FileSnapshot DIRTY = new FileSnapshot ( - 1 , - 1 ,
public static final FileSnapshot DIRTY = new FileSnapshot ( - 1 , - 1 ,
UNKNOWN_SIZE , Duration . ZERO ) ;
UNKNOWN_SIZE , Duration . ZERO , MISSING_FILEKEY ) ;
/ * *
/ * *
* A FileSnapshot that is clean if the file does not exist .
* A FileSnapshot that is clean if the file does not exist .
@ -98,7 +100,7 @@ public class FileSnapshot {
* path does not exist .
* path does not exist .
* /
* /
public static final FileSnapshot MISSING_FILE = new FileSnapshot ( 0 , 0 , 0 ,
public static final FileSnapshot MISSING_FILE = new FileSnapshot ( 0 , 0 , 0 ,
Duration . ZERO ) {
Duration . ZERO , MISSING_FILEKEY ) {
@Override
@Override
public boolean isModified ( File path ) {
public boolean isModified ( File path ) {
return FS . DETECTED . exists ( path ) ;
return FS . DETECTED . exists ( path ) ;
@ -119,17 +121,26 @@ public class FileSnapshot {
long read = System . currentTimeMillis ( ) ;
long read = System . currentTimeMillis ( ) ;
long modified ;
long modified ;
long size ;
long size ;
Object fileKey = null ;
Duration fsTimerResolution = FS
Duration fsTimerResolution = FS
. getFsTimerResolution ( path . toPath ( ) . getParent ( ) ) ;
. getFsTimerResolution ( path . toPath ( ) . getParent ( ) ) ;
try {
try {
BasicFileAttributes fileAttributes = FS . DETECTED . fileAttributes ( path ) ;
BasicFileAttributes fileAttributes = FS . DETECTED . fileAttributes ( path ) ;
modified = fileAttributes . lastModifiedTime ( ) . toMillis ( ) ;
modified = fileAttributes . lastModifiedTime ( ) . toMillis ( ) ;
size = fileAttributes . size ( ) ;
size = fileAttributes . size ( ) ;
fileKey = getFileKey ( fileAttributes ) ;
} catch ( IOException e ) {
} catch ( IOException e ) {
modified = path . lastModified ( ) ;
modified = path . lastModified ( ) ;
size = path . length ( ) ;
size = path . length ( ) ;
fileKey = MISSING_FILEKEY ;
}
}
return new FileSnapshot ( read , modified , size , fsTimerResolution ) ;
return new FileSnapshot ( read , modified , size , fsTimerResolution ,
fileKey ) ;
}
private static Object getFileKey ( BasicFileAttributes fileAttributes ) {
Object fileKey = fileAttributes . fileKey ( ) ;
return fileKey = = null ? MISSING_FILEKEY : fileKey ;
}
}
/ * *
/ * *
@ -149,7 +160,8 @@ public class FileSnapshot {
* /
* /
public static FileSnapshot save ( long modified ) {
public static FileSnapshot save ( long modified ) {
final long read = System . currentTimeMillis ( ) ;
final long read = System . currentTimeMillis ( ) ;
return new FileSnapshot ( read , modified , - 1 , Duration . ZERO ) ;
return new FileSnapshot ( read , modified , - 1 , Duration . ZERO ,
MISSING_FILEKEY ) ;
}
}
/** Last observed modification time of the path. */
/** Last observed modification time of the path. */
@ -169,13 +181,20 @@ public class FileSnapshot {
/** measured filesystem timestamp resolution */
/** measured filesystem timestamp resolution */
private Duration fsTimestampResolution ;
private Duration fsTimestampResolution ;
/ * *
* Object that uniquely identifies the given file , or { @code
* null } if a file key is not available
* /
private Object fileKey ;
private FileSnapshot ( long read , long modified , long size ,
private FileSnapshot ( long read , long modified , long size ,
@NonNull Duration fsTimestampResolution ) {
@NonNull Duration fsTimestampResolution , @NonNull Object fileKey ) {
this . lastRead = read ;
this . lastRead = read ;
this . lastModified = modified ;
this . lastModified = modified ;
this . fsTimestampResolution = fsTimestampResolution ;
this . fsTimestampResolution = fsTimestampResolution ;
this . size = size ;
this . size = size ;
this . cannotBeRacilyClean = notRacyClean ( read ) ;
this . cannotBeRacilyClean = notRacyClean ( read ) ;
this . fileKey = fileKey ;
}
}
/ * *
/ * *
@ -204,15 +223,20 @@ public class FileSnapshot {
public boolean isModified ( File path ) {
public boolean isModified ( File path ) {
long currLastModified ;
long currLastModified ;
long currSize ;
long currSize ;
Object currFileKey ;
try {
try {
BasicFileAttributes fileAttributes = FS . DETECTED . fileAttributes ( path ) ;
BasicFileAttributes fileAttributes = FS . DETECTED . fileAttributes ( path ) ;
currLastModified = fileAttributes . lastModifiedTime ( ) . toMillis ( ) ;
currLastModified = fileAttributes . lastModifiedTime ( ) . toMillis ( ) ;
currSize = fileAttributes . size ( ) ;
currSize = fileAttributes . size ( ) ;
currFileKey = getFileKey ( fileAttributes ) ;
} catch ( IOException e ) {
} catch ( IOException e ) {
currLastModified = path . lastModified ( ) ;
currLastModified = path . lastModified ( ) ;
currSize = path . length ( ) ;
currSize = path . length ( ) ;
currFileKey = MISSING_FILEKEY ;
}
}
return ( currSize ! = UNKNOWN_SIZE & & currSize ! = size ) | | isModified ( currLastModified ) ;
return isSizeChanged ( currSize )
| | isFileKeyChanged ( currFileKey )
| | isModified ( currLastModified ) ;
}
}
/ * *
/ * *
@ -252,7 +276,8 @@ public class FileSnapshot {
* @return true if the two snapshots share the same information .
* @return true if the two snapshots share the same information .
* /
* /
public boolean equals ( FileSnapshot other ) {
public boolean equals ( FileSnapshot other ) {
return lastModified = = other . lastModified & & size = = other . size ;
return lastModified = = other . lastModified & & size = = other . size
& & Objects . equals ( fileKey , other . fileKey ) ;
}
}
/** {@inheritDoc} */
/** {@inheritDoc} */
@ -274,7 +299,8 @@ public class FileSnapshot {
/** {@inheritDoc} */
/** {@inheritDoc} */
@Override
@Override
public int hashCode ( ) {
public int hashCode ( ) {
return Objects . hash ( Long . valueOf ( lastModified ) , Long . valueOf ( size ) ) ;
return Objects . hash ( Long . valueOf ( lastModified ) , Long . valueOf ( size ) ,
fileKey ) ;
}
}
/** {@inheritDoc} */
/** {@inheritDoc} */
@ -291,7 +317,7 @@ public class FileSnapshot {
Locale . US ) ;
Locale . US ) ;
return "FileSnapshot[modified: " + f . format ( new Date ( lastModified ) )
return "FileSnapshot[modified: " + f . format ( new Date ( lastModified ) )
+ ", read: " + f . format ( new Date ( lastRead ) ) + ", size:" + size
+ ", read: " + f . format ( new Date ( lastRead ) ) + ", size:" + size
+ "]" ;
+ ", fileKey: " + fileKey + " ]" ;
}
}
private boolean notRacyClean ( long read ) {
private boolean notRacyClean ( long read ) {
@ -327,4 +353,12 @@ public class FileSnapshot {
//
//
return true ;
return true ;
}
}
private boolean isFileKeyChanged ( Object currFileKey ) {
return currFileKey ! = MISSING_FILEKEY & & ! currFileKey . equals ( fileKey ) ;
}
private boolean isSizeChanged ( long currSize ) {
return currSize ! = UNKNOWN_SIZE & & currSize ! = size ;
}
}
}