/* * [FD.java] * * Summary: File Descriptor for a file to be uploaded to a server. * * Copyright: (c) 2011-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2011-12-03 initial release */ package com.mindprod.bulkftp; import com.mindprod.common18.EIO; import com.mindprod.fastcat.FastCat; import org.jetbrains.annotations.NotNull; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.zip.GZIPOutputStream; import static java.lang.System.*; /** * File Descriptor for a file to be uploaded to a server. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2011-12-04 initial releas * @since 2011-12-04 */ class FD implements Comparable, Serializable { private static final long serialVersionUID = 1L; /** * track files potentially to upload both locally and on server. */ private static HashMap fds; /** * track how many flags in collection are set */ private static int flagCount = 0; /** * filename to upload, webroot relative */ private final String filenameWithSlashes; /** * used is detecting files that have been deleted, no longer present */ private transient boolean flag; /** * timestamp last modified on server */ private long lastModifiedOnClient; /** * timestamp last modified locally */ private long lastModifiedOnServer; /** * size of file in bytes locally, -1 means no longer on client */ private long sizeOnClient; /** * size of file in bytes on server. -1 means no longer on server */ private long sizeOnServer; /** * constructor * * @param filenameWithSlashes webroot relative filename with slashes * @param lastModifiedOnClient long timestamp last time file modified on client * @param lastModifiedOnServer long timestamp last time file uploaded to server * @param sizeOnClient file size in bytes on client * @param sizeOnServer file size in bytes on server */ public FD( final String filenameWithSlashes, final long lastModifiedOnClient, final long lastModifiedOnServer, final long sizeOnClient, final long sizeOnServer ) { this.filenameWithSlashes = filenameWithSlashes; this.lastModifiedOnClient = lastModifiedOnClient; this.lastModifiedOnServer = lastModifiedOnServer; this.sizeOnClient = sizeOnClient; this.sizeOnServer = sizeOnServer; } /** * clear flags of all FD records */ public static void clearAllFlags() { for ( FD fd : fds.values() ) { fd.flag = false; } flagCount = 0; } /** * find FD information given filename * * @param filenameWithSlashes webroot relative filename with slashes * * @return corresponding FD or null if no match. */ public static FD get( final String filenameWithSlashes ) { return fds.get( filenameWithSlashes ); } /** * get count of how many FD records have flag turned on. * * @return flagCount */ public static int getFlagCount() { return flagCount; } /** * get access to the entire body of FD objects vai Interator * * @return Iterator over collection of FD objects. */ public static Iterator iterator() { return fds.values().iterator(); } /** * add a FD object to the global collection * * @param filenameWithSlashes webroot relative filename with slashes * @param fd file information packet to add. * * @return usually null. previous FD files under this name otherwise. */ public static FD put( final String filenameWithSlashes, final FD fd ) { return fds.put( filenameWithSlashes, fd ); } /** * restore state information about client and server files from hard disk * * @return true if failed */ public static boolean restore() { boolean failed = false; try { // O P E N final ObjectInputStream ois = EIO.getObjectInputStream( Config.STATE_SAVE_FILE, 16 * 64, true ); // R E A D if ( FD.serialVersionUID != ( Long ) ois.readObject() ) { err.println( "out of date state file" ); failed = true; } else { final FD[] fdArray = ( FD[] ) ois.readObject(); // restore with 30% excess capacity for speed fds = new HashMap<>( fdArray.length * 130 / 100 ); // rebuild the HashMap from scratch. for ( FD fd : fdArray ) { fds.put( fd.getFilenameWithSlashes(), fd ); } } // C L O S E ois.close(); } catch ( IOException e ) { failed = true; } catch ( ClassNotFoundException e ) { failed = true; } if ( failed ) { err.println( "Unable to restore previous state. Starting from scratch." ); fds = new HashMap<>( 10000 ); } return failed; } /** * save state of our records of files on client and server to hard disk * * @throws java.io.IOException if save fails */ public static void save() throws IOException { // O P E N final FileOutputStream fos = new FileOutputStream( Config.STATE_SAVE_FILE, false /* append */ ); final GZIPOutputStream gzos = new GZIPOutputStream( fos, 16 * 32 /* buffsize in bytes */ ); final ObjectOutputStream oos = new ObjectOutputStream( gzos ); // W R I T E oos.writeObject( FD.serialVersionUID ); // Generics and serialization do not play well together, so we convert to an array. // This does not clone the FD objects. oos.writeObject( fds.values().toArray( new FD[ fds.size() ] ) ); // C L O S E oos.close(); } /** * get access to the entire body of FD objects as an UnmodifiableCollection. Can modified fields of elements, * but not add/remove. */ public static Collection values() { return Collections.unmodifiableCollection( fds.values() ); } /** * Sorts alphabetically. * Defines default the sort order for FD Objects. * Compare this FD with another FD with JDK 1.5+ generics. * Compares filenameWithSlashes case insensitively. * Informally, returns (this-other) or +ve if this sorts after other. * The Java source code for this Comparable implementation was generated by the * Canadian Mind Products ComparatorCutter Applet at http://mindprod.com/applet/comparatorcutter.html * * @param other other FD to compare with this one * * @return +ve if this>other, 0 if this==other, -ve if this<other */ public final int compareTo( @NotNull FD other ) { int diff = this.filenameWithSlashes.compareToIgnoreCase( other.filenameWithSlashes ); if ( diff != 0 ) { return diff; } return this.filenameWithSlashes.compareTo( other.filenameWithSlashes ); } /** * getter for filenameWithSlashes * * @return webroot relative filename with slashes. */ public String getFilenameWithSlashes() { return filenameWithSlashes; } /** * getter for lastModifiedOnClient * * @return long timestamp of when file last modified on client. */ public long getLastModifiedOnClient() { return lastModifiedOnClient; } /** * setter for lastModifiedOnClient * * @param lastModifiedOnClient UTC long timestamp of when file last modified on client. */ public void setLastModifiedOnClient( final long lastModifiedOnClient ) { this.lastModifiedOnClient = lastModifiedOnClient; } /** * getter for lastModifiedOnServer * * @return UTC long timestamp of when file last uploaded to server. */ public long getLastModifiedOnServer() { return lastModifiedOnServer; } /** * setter for lastModifiedOnServer * * @param lastModifiedOnServer UTC long timestamp of when file last uploaded to server. */ public void setLastModifiedOnServer( final long lastModifiedOnServer ) { this.lastModifiedOnServer = lastModifiedOnServer; } /** * get size of file on client * * @return size of file in bytes on client, -1 file no longer exists. */ public long getSizeOnClient() { return sizeOnClient; } /** * set size of file on client * * @param sizeOnClient size of file in bytes on client, -1 file no longer exists. */ public void setSizeOnClient( final long sizeOnClient ) { this.sizeOnClient = sizeOnClient; } /** * get size of file on server * * @param sizeOnServer size of file in bytes on server, -1 file no longer exists. */ public long getSizeOnServer() { return sizeOnServer; } /** * set size of file on server * * @param sizeOnServer size of file in bytes on server, -1 file no longer exists. */ public void setSizeOnServer( final long sizeOnServer ) { this.sizeOnServer = sizeOnServer; } /** * does this FD record have is flag set? * the flag is used in detecting files that have been deleted. * * @return true or false */ public boolean isFlag() { return flag; } /** * set the value of the flag * * @param flag true or flase */ public void setFlag( boolean flag ) { if ( flag != this.flag ) { if ( flag ) { flagCount++; } else { flagCount--; } this.flag = flag; } } /** * display all fields for debugging * * @return sting representing the FD object . */ @Override public String toString() { final FastCat sb = new FastCat( 9 ); sb.append( filenameWithSlashes ); sb.append( " lastModified c:s " ); sb.append( lastModifiedOnClient ); sb.append( " : " ); sb.append( lastModifiedOnServer ); sb.append( " size c:s " ); sb.append( sizeOnClient ); sb.append( " : " ); sb.append( sizeOnServer ); return sb.toString(); } }