/* * [ConfigForSender.java] * * Summary: Configuration variables for the Replicator sender. * * Copyright: (c) 2009-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: * 10.2 2009-04-03 tidy up code to check presence of necessary files to make it more WORA. */ package com.mindprod.replicatorsender; import com.mindprod.common18.Build; import com.mindprod.fastcat.FastCat; import com.mindprod.replicatorcommon.Config; import com.mindprod.replicatorcommon.ConfigForCompiler; import com.mindprod.replicatorcommon.MultiProperties; import com.mindprod.replicatorcommon.ReplicatorCommon; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.TimeUnit; /** * Configuration variables for the Replicator sender. * * @author Roedy Green, Canadian Mind Products * @version 10.2 2009-04-03 tidy up code to check presence of necessary files to make it more WORA. * @since 2009 */ final class ConfigForSender extends Config { /** * where the sender persists state in human readable form. Always in the SENDER_PERSIST_DIR e.g. * com/mindprod/replicatorsender */ public static final String SENDER_LOG = "sender.log"; /** * where the sender persists state. Always in the SENDER_PERSIST_DIR e.g. com/mindprod/replicatorsender */ public static final String SENDER_SER = "sender.ser"; // note CASE_SENSITIVE is hard coded in Config. // DEBUGGING is defined in Config. // Values for these fields come from the xxxreplicator.properties file. /** * e.g. none * Must be "basic" or "none", what sort of authorisation required to access the zips.. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String AUTHENTICATION; /** * "distributed" if you want excess whitespace removed from any distributed *.html This makes the files smaller and * faster to load. They look the same on the browser screen, but the raw HTML source is harder to understand. * "original" if you want the original HTML compacted as well as the distributed files. "none" if you want HTML * files distributed as-is. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String COMPACT_HTML; /** * e.g. 9 * Compression level 0 to 9. 0=uncompressed 9=as compressed as possible. Bigger number takes more elapsedTime to * prepare zips. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static int COMPRESSION_LEVEL; /** * Which individual directories in the base directory will be distributed. The directory but none of its * subdirectories will be distributed. The name may not begin or end with a \, though it may contain embedded \. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String[] DIRS_TO_DISTRIBUTE; /** * Which individual directories in the base directory will be withheld from distribution. The directory but none of * its subdirectories will be withheld. The name must not begin or end with a \, though it may contain embedded \. * You only need mention dirs that were previously included. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String[] DIRS_TO_WITHHOLD; /** * e.g. chk,class,cnt,log,zip,digest,nlx,tmp,ion * Which file extensions should be excluded. These are case INsensitive. Don't include the dot. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String[] EXTENSIONS_TO_WITHHOLD; /** * Which files in the base directory will be distributed. The filename must include the directory, but not the base. * The filename must not begin or end with a \, though it may contain embedded \. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String[] FILES_TO_DISTRIBUTE; /** * e.g. zips,replicator,renney,jgloss\snippet\vslick,projects\snippet\vslick,applets\snippet\vslick * Which individual files in the base directory will be withheld from distribution. The filename must include the * directory, but not the base. The filename must not begin or end with a \, though it may contain embedded \. You * only need mention files that were previously included. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String[] FILES_TO_WITHHOLD; /** * e.g. 0 * highest acceptable exit code from upload program. -1 means no checking. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static int HIGHEST_ACCEPTABLE_EXIT_CODE; /** * Records of files deleted prior to this will be dropped. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static long KEEP_DELETED_CUTOFF_TIMESTAMP; /** * Defines what we mean by old and recent files. We are more vigorous retiring older zips since changing them will * likely not affect clients who are reasonably up to date. millis since 1970. Computed, not a config parm. * This refers to the age of the zip files, not the age of the contents. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static long LAG_CUTOFF_TIMESTAMP; /** * e.g. 10000000 * Largest file to distribute in bytes. Suggest leaving at 20 megabytes. The largest possible is 2 gigabytes. Large * files are not spread over several zips. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static int LARGEST_FILE_TO_DISTRIBUTE; /** * e.g. 3 * max number of unsuccessful tries to complete an upload before giving up. */ public static int MAX_UPLOAD_TRIES; /** * e.g. 5% * Percent deadwood to tolerate in old zip files, e.g. older than LAG days old. If it goes higher than this, the zip * will be retired and its files will be assigned to a new zip files to get rid of the deadwood. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static int OLD_DEADWOOD_TOLERANCE_PERCENTAGE; /** * e.g. mindprod.com -- human name for the project -- he files to be distributed */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String PROJECT_NAME; /** * name of the properties file, from the ReplicatorSender command line. e.g. * E:\com\mindprod\replicatorsender\xxxreplicator.properties */ @SuppressWarnings( { "WeakerAccess", "NonConstantFieldWithUpperCaseName" } ) public static String PROPERTIES_FILE; /** * e.g. 20 * Percent deadwood to tolerate in recent zip files, e.g. younger than LAG days old. If it goes higher than this, * the zip will be retired and its files will be assigned to a new zip files to get rid of the deadwood. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static int RECENT_DEADWOOD_TOLERANCE_PERCENTAGE; /** * e.g. E:\sys\recover.exe * name of program to do the recover from a failed upload. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String RECOVER; /** * where the com.mindprod.replicator source files are kept using \, * without trailing \ e.g. E:\com\mindprod\replicator */ public static String REPLICATOR_SOURCE_DIR; /** * e.g. 120 * HowToProcess long in minutes the zip files sit in retirement untouched before we zero and delete them. We want to * make sure anyone with with dial-up still downloading an obsolete zip manifest can still complete successfully. * This setting should be longer than the longest expected download. Making this too long wastes disk space on your * server hanging on to obsolete zips. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static int RETIREMENT; /** * date/elapsedTime, any zip who retired prior to this, will be emaciated. in milliseconds since 1970. Computed, not * a config parm. */ public static long RETIREMENT_AXE_TIMESTAMP; /** * Directory without trailing \ where all the master files to be distributed are kept. Use local \ names * e.g. E:\mindprod for Windows. This name is purely for the local master copy, not for the website or the clients. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String SENDER_BASE_DIR; /** * e.g. E:\com\mindprod\replicatorsender * Directory without trailing \ where all the sender's state persistence files, namely sender.ser and sender.log are * stored. The files in this directory are not uploaded to the website for the clients. Use local names e.g. * E:\com\mindprod\replicatorsender for Windows. This name is purely for the local master copy, not for the website * or the clients. Make sure this directory is NOT included in the trees, dirs or files to distribute! */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String SENDER_PERSIST_DIR; @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String SENDER_ZIP_STAGING_DIR; /** * Alternate LAN source for clients behind a firewall. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String SUGGESTED_LAN_ZIP_URL; /** * e.g. C:\mindprod * Directory without trailing \ where all the master files to be distributed are kept. Use local clockSetter names * e.g. E:\mindprod for Windows. This name is purely for the local master copy, not for the website or the clients. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String SUGGESTED_RECEIVER_BASE_DIR; /** * e.g. C:\mpstaging * Directory without trailing \ where all the incoming archive zip files to be distributed are kept. Use local * clockSetter names e.g. E:\mindprod for Windows. This name is purely for the local master copy, not for the * website or the clients. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String SUGGESTED_RECEIVER_ZIP_STAGING_DIR; /** * e.g. * Which entire directory trees in the base directory will be distributed. The directory and all its * subdirectories * will be distributed. The name must not begin or end with a \, though it may contain embedded \. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String[] TREES_TO_DISTRIBUTE; // From here on sets up DEFAULT configuration on the client: // The actual values are in ConfigForReceiver. /** * Which entire directory trees in the base directory will be withheld from distribution. The directory and all its * subdirectories will be withheld, even if they appear in the list to be distributed. The name must not begin or * end with a \, though it may contain embedded \. You only need mention trees that were previously included. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String[] TREES_TO_WITHHOLD; /** * e.g. 2000000 * Approximate size in bytes of individual zip archive files distributed, prior to compression. usually 2 megabytes * 2000000, no commas It might be set smaller if users have very flaky connections and can't reliably download files * that big. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static int UNCOMPRESSED_IDEAL_ZIPSIZE; /** * e.g. com.mindprod.replicator * Globally unique name, starting with website name in reverse e.g. com.mindprod.replicator. This name allows The * Replicator * to keep several projects on the same machine from interfering with one another. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String UNIQUE_PROJECT_NAME; /** * e.g. false * true if you want untouch processing on the base directory to put all files back to their original dates if they * have not really changes since the last distribution. This will suppress distributing files that have not really * changed. false to leave dates alone, and redistribute file that new dates with the same contents. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static boolean UNTOUCH; /** * e.g. "F:\Program Files\netload\netload.exe" just as would appear in BTM line. * name of program to do the upload. Later may be extended to allow direct FTP * upload by the Replicator. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String UPLOAD; /** * e.g. http://mindprod.com/replicator * Where on the website the archive files are stored. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) public static String WEBSITE_ZIP_URL; /** * e.g. 30 * How long to keep track of the fact a given file was deleted. Measured in days. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) private static int KEEP_DELETED; /** * e.g. 5 * We optimise under the presumption that most users will check for updates more frequently than this many days * between checkins. Setting this number too large penalizes brand new users. Setting it too small penalises users * who update infrequently. * This refers to the age of the zip files, not the age of the contents. Measured in days. */ @SuppressWarnings( { "NonConstantFieldWithUpperCaseName" } ) private static int LAG; /** * Check that parameters specified are reasonable. */ private static void ensureConfigValid() { if ( REPLICATOR_SOURCE_DIR.endsWith( File.separator ) ) { throw new IllegalArgumentException( "REPLICATOR_SOURCE_DIR has trailing separator." ); } if ( SENDER_BASE_DIR.endsWith( File.separator ) ) { throw new IllegalArgumentException( "SENDER_BASE_DIR has trailing separator." ); } if ( SENDER_ZIP_STAGING_DIR.endsWith( File.separator ) ) { throw new IllegalArgumentException( "SENDER_ZIP_STAGING_DIR has trailing separator." ); } if ( SENDER_PERSIST_DIR.endsWith( File.separator ) ) { throw new IllegalArgumentException( "SENDER_PERSIST_DIR has trailing separator." ); } for ( String aTREES_TO_DISTRIBUTE : TREES_TO_DISTRIBUTE ) { if ( aTREES_TO_DISTRIBUTE.endsWith( File.separator ) ) { throw new IllegalArgumentException( "TREES_TO_DISTRIBUTE has trailing separator." ); } if ( aTREES_TO_DISTRIBUTE.startsWith( File.separator ) ) { throw new IllegalArgumentException( "TREES_TO_DISTRIBUTE has leading separator." ); } } for ( String aDIRS_TO_DISTRIBUTE : DIRS_TO_DISTRIBUTE ) { if ( aDIRS_TO_DISTRIBUTE.endsWith( File.separator ) ) { throw new IllegalArgumentException( "DIRS_TO_DISTRIBUTE has trailing separator." ); } if ( aDIRS_TO_DISTRIBUTE.startsWith( File.separator ) ) { throw new IllegalArgumentException( "DIRS_TO_DISTRIBUTE has leading separator." ); } } for ( String aFILES_TO_DISTRIBUTE : FILES_TO_DISTRIBUTE ) { if ( aFILES_TO_DISTRIBUTE.endsWith( File.separator ) ) { throw new IllegalArgumentException( "FILES_TO_DISTRIBUTE has trailing separator." ); } if ( aFILES_TO_DISTRIBUTE.startsWith( File.separator ) ) { throw new IllegalArgumentException( "FILES_TO_DISTRIBUTE has leading separator." ); } } for ( String aTREES_TO_WITHHOLD : TREES_TO_WITHHOLD ) { if ( aTREES_TO_WITHHOLD.endsWith( File.separator ) ) { throw new IllegalArgumentException( "TREES_TO_WITHHOLD has trailing separator." ); } if ( aTREES_TO_WITHHOLD.startsWith( File.separator ) ) { throw new IllegalArgumentException( "TREES_TO_WITHHOLD has leading separator." ); } } for ( String aDIRS_TO_WITHHOLD : DIRS_TO_WITHHOLD ) { if ( aDIRS_TO_WITHHOLD.endsWith( File.separator ) ) { throw new IllegalArgumentException( "DIRS_TO_WITHHOLD has trailing separator." ); } if ( aDIRS_TO_WITHHOLD.startsWith( File.separator ) ) { throw new IllegalArgumentException( "DIRS_TO_WITHHOLD has leading separator." ); } } for ( String aFILES_TO_WITHHOLD : FILES_TO_WITHHOLD ) { if ( aFILES_TO_WITHHOLD.endsWith( File.separator ) ) { throw new IllegalArgumentException( "FILES_TO_WITHHOLD has trailing separator." ); } if ( aFILES_TO_WITHHOLD.startsWith( File.separator ) ) { throw new IllegalArgumentException( "FILES_TO_WITHHOLD has leading separator." ); } } if ( SUGGESTED_RECEIVER_BASE_DIR.endsWith( File.separator ) ) { throw new IllegalArgumentException( "SUGGESTED_RECEIVER_BASE_DIR has trailing separator." ); } if ( SUGGESTED_RECEIVER_BASE_DIR.equalsIgnoreCase( SENDER_BASE_DIR ) ) { throw new IllegalArgumentException( "SUGGESTED_RECEIVER_BASE_DIR can't be same as SENDER_BASE_DIR." ); } if ( SUGGESTED_RECEIVER_ZIP_STAGING_DIR.endsWith( File.separator ) ) { throw new IllegalArgumentException( "SUGGESTED_RECEIVER_ZIP_STAGING_DIR has trailing separator." ); } if ( SUGGESTED_RECEIVER_ZIP_STAGING_DIR .equalsIgnoreCase( SENDER_ZIP_STAGING_DIR ) ) { throw new IllegalArgumentException( "SUGGESTED_RECEIVER_ZIP_STAGING_DIR can't be same as SENDER_ZIP_STAGING_DIR." ); } if ( !( UPLOAD != null && ( UPLOAD.length() == 0 || UPLOAD.contains( "exe" ) || UPLOAD.contains( "EXE" ) ) ) ) { throw new IllegalArgumentException( "UPLOAD (" + UPLOAD + ") must be empty or set to some fully qualified third party uploader exe." ); } if ( !( -1 <= HIGHEST_ACCEPTABLE_EXIT_CODE && HIGHEST_ACCEPTABLE_EXIT_CODE <= 99 ) ) { throw new IllegalArgumentException( "HIGHEST_ACCEPTABLE_EXIT_CODE must be empty or set to 0..99." ); } if ( !( RECOVER != null && ( RECOVER.length() == 0 || RECOVER.contains( "exe" ) || RECOVER.contains( "EXE" ) ) ) ) { throw new IllegalArgumentException( "RECOVER (" + RECOVER + ") must be empty or set to some program or script invocation to recover from a failed upload." ); } if ( !( 1 <= MAX_UPLOAD_TRIES && MAX_UPLOAD_TRIES <= 99 ) ) { throw new IllegalArgumentException( "MAX_UPLOAD_TRIES must be 1..99" ); } if ( !WEBSITE_ZIP_URL.startsWith( "http" ) ) { throw new IllegalArgumentException( "WEBSITE_ZIP_URL must start with http." ); } if ( !( SUGGESTED_LAN_ZIP_URL.startsWith( "http" ) || SUGGESTED_LAN_ZIP_URL .startsWith( "file:" ) ) ) { throw new IllegalArgumentException( "LAN_ZIP_URL must start with http:, https: or file:." ); } for ( String aEXTENSIONS_TO_WITHHOLD : EXTENSIONS_TO_WITHHOLD ) { if ( aEXTENSIONS_TO_WITHHOLD.indexOf( '.' ) >= 0 ) { throw new IllegalArgumentException( "EXTENSIONS_TO_WITHHOLD contains a ." ); } } // safely interned if ( !( AUTHENTICATION.equals( "basic" ) || AUTHENTICATION .equals( "none" ) ) ) { throw new IllegalArgumentException( "AUTHENTICATION must be basic or none." ); } // safely interned if ( !( COMPACT_HTML.equals( "original" ) || COMPACT_HTML.equals( "distributed" ) || COMPACT_HTML .equals( "none" ) ) ) { throw new IllegalArgumentException( "COMPACT_HTML must be original, distributed or none." ); } // ensure same dir not assigned to multiple purposes. final String[] mustBeUnique = { SENDER_BASE_DIR, SENDER_ZIP_STAGING_DIR, SENDER_PERSIST_DIR, SUGGESTED_RECEIVER_BASE_DIR, SUGGESTED_RECEIVER_ZIP_STAGING_DIR }; for ( int i = 0; i < mustBeUnique.length; i++ ) { mustBeUnique[ i ] = mustBeUnique[ i ].toLowerCase(); } // case-sensitive sort. Arrays.sort( mustBeUnique ); for ( int i = 1; i < mustBeUnique.length; i++ ) { if ( mustBeUnique[ i ].equals( mustBeUnique[ i - 1 ] ) ) { throw new IllegalArgumentException( "SENDER_BASE_DIR, SENDER_ZIP_STAGING_DIR, SENDER_PERSIST_DIR, " + "SUGGESTED_RECEIVER_BASE_DIR and SUGGESTED_RECEIVER_ZIP_STAGING_DIR must be unique. You may " + "not use the same dir for multiple purposes." ); } } } /** * get the the fully qualified localised filename of a file in the SENDER_PERSIST_DIR * * @param filename name of the file * * @return fully qualified localised filename of a file e.g. / or \ */ static String qualifyInSenderPersist( final String filename ) { return SENDER_PERSIST_DIR + File.separatorChar + filename; } /** * Display a parameter that has several values, separated by commas. * * @param name Name of the parameter * @param values array of Strings values of the parameter * * @return display string, without final \n. */ private static String show( String name, String[] values ) { final FastCat sb = new FastCat( ( values.length + 1 ) * 2 ); sb.append( name ); sb.append( ": " ); for ( final String value : values ) { sb.append( value ); sb.append( "," ); } sb.drop(); return sb.toString(); } /** * Display Sender Config information * * @return string representation of the entire Sender config. */ public static String dumpConfig() { final FastCat sb = new FastCat( 125 ); sb.append( "C O N F I G U R A T I O N" ); sb.append( "\n" ); sb.append( "VERSION: " ); sb.append( VERSION ); sb.append( "\n" ); sb.append( "BUILD: " ); sb.append( Build.BUILD_NUMBER ); sb.append( "\n" ); sb.append( "PROJECT_NAME: " ); sb.append( PROJECT_NAME ); sb.append( "\n" ); sb.append( "UNIQUE_PROJECT_NAME: " ); sb.append( UNIQUE_PROJECT_NAME ); sb.append( "\n" ); sb.append( "PROPERTIES_FILE: " ); sb.append( PROPERTIES_FILE ); sb.append( "\n" ); sb.append( "PROGRAM_DIR: " ); sb.append( PROGRAM_DIR ); sb.append( "\n" ); sb.append( "REPLICATOR_SOURCE_DIR: " ); sb.append( REPLICATOR_SOURCE_DIR ); sb.append( "\n" ); sb.append( "SENDER_BASE_DIR: " ); sb.append( SENDER_BASE_DIR ); sb.append( "\n" ); sb.append( "SENDER_ZIP_STAGING_DIR: " ); sb.append( SENDER_ZIP_STAGING_DIR ); sb.append( "\n" ); sb.append( "SENDER_PERSIST_DIR: " ); sb.append( SENDER_PERSIST_DIR ); sb.append( "\n" ); sb.append( show( "TREES_TO_DISTRIBUTE", TREES_TO_DISTRIBUTE ) ); sb.append( "\n" ); sb.append( show( "TREES_TO_WITHHOLD", TREES_TO_WITHHOLD ) ); sb.append( "\n" ); sb.append( show( "DIRS_TO_DISTRIBUTE", DIRS_TO_DISTRIBUTE ) ); sb.append( "\n" ); sb.append( show( "DIRS_TO_WITHHOLD", DIRS_TO_WITHHOLD ) ); sb.append( "\n" ); sb.append( show( "FILES_TO_DISTRIBUTE", FILES_TO_DISTRIBUTE ) ); sb.append( "\n" ); sb.append( show( "FILES_TO_WITHHOLD", FILES_TO_WITHHOLD ) ); sb.append( "\n" ); sb.append( show( "EXTENSIONS_TO_WITHHOLD", EXTENSIONS_TO_WITHHOLD ) ); sb.append( "\n" ); sb.append( "LAG: " ); sb.append( LAG ); sb.append( " days. [Tighter packing rules kick in for zips older than " ); sb.append( Config.TIMESTAMP_MILLISECOND_FORMAT.format( LAG_CUTOFF_TIMESTAMP ) ); sb.append( "]\n" ); sb.append( "RETIREMENT: " ); sb.append( RETIREMENT ); sb.append( " minutes. [Retired zips older than " ); sb.append( Config.TIMESTAMP_MILLISECOND_FORMAT.format( RETIREMENT_AXE_TIMESTAMP ) ); sb.append( " will be deleted.]\n" ); sb.append( "LARGEST_FILE_TO_DISTRIBUTE: " ); sb.append( LARGEST_FILE_TO_DISTRIBUTE ); sb.append( " bytes" ); sb.append( "\n" ); sb.append( "RECENT_DEADWOOD_TOLERANCE_PERCENTAGE: " ); sb.append( RECENT_DEADWOOD_TOLERANCE_PERCENTAGE ); sb.append( "%" ); sb.append( "\n" ); sb.append( "OLD_DEADWOOD_TOLERANCE_PERCENTAGE: " ); sb.append( OLD_DEADWOOD_TOLERANCE_PERCENTAGE ); sb.append( "%" ); sb.append( "\n" ); sb.append( "COMPRESSION_LEVEL: " ); sb.append( COMPRESSION_LEVEL ); sb.append( "\n" ); sb.append( "UNCOMPRESSED_IDEAL_ZIPSIZE: " ); sb.append( UNCOMPRESSED_IDEAL_ZIPSIZE ); sb.append( " bytes" ); sb.append( "\n" ); sb.append( "UNTOUCH: " ); sb.append( UNTOUCH ); sb.append( "\n" ); sb.append( "COMPACT_HTML: " ); sb.append( COMPACT_HTML ); sb.append( "\n" ); sb.append( "SUGGESTED_RECEIVER_BASE_DIR: " ); sb.append( SUGGESTED_RECEIVER_BASE_DIR ); sb.append( "\n" ); sb.append( "SUGGESTED_RECEIVER_ZIP_STAGING_DIR: " ); sb.append( SUGGESTED_RECEIVER_ZIP_STAGING_DIR ); sb.append( "\n" ); sb.append( "WEBSITE_ZIP_URL: " ); sb.append( WEBSITE_ZIP_URL ); sb.append( "\n" ); sb.append( "SUGGESTED_LAN_ZIP_URL: " ); sb.append( SUGGESTED_LAN_ZIP_URL ); sb.append( "\n" ); sb.append( "UPLOAD: " ); sb.append( UPLOAD ); sb.append( "\n" ); sb.append( "HIGHEST_ACCEPTABLE_EXIT_CODE: " ); sb.append( HIGHEST_ACCEPTABLE_EXIT_CODE ); sb.append( "\n" ); sb.append( "RECOVER: " ); sb.append( RECOVER ); sb.append( "\n" ); sb.append( "MAX_UPLOAD_TRIES: " ); sb.append( MAX_UPLOAD_TRIES ); sb.append( "\n" ); sb.append( "AUTHENTICATION: " ); sb.append( AUTHENTICATION ); sb.append( "\n" ); sb.append( "CASE_SENSITIVE: " ); sb.append( CASE_SENSITIVE ); sb.append( "\n" ); sb.append( "COMPILER: " ); sb.append( ConfigForCompiler.COMPILER ); sb.append( "\n" ); sb.append( "RUNTIME: " ); sb.append( System.getProperty( "java.version", "unknown" ) ); sb.append( "\n" ); sb.append( "DEBUGGING: " ); sb.append( DEBUGGING ); sb.append( "\n" ); return sb.toString(); } /** * Get values for properties from the xxxreplicator.properties file. * * @param propertiesFilename fully qualified absolute or relative filename of the xxxreplicator.properties file. */ @SuppressWarnings( { "UnusedAssignment" } ) public static void getConfigFromPropertiesFile( String propertiesFilename ) { /** * Key=value pairs, used to populate constants from a properties file. */ MultiProperties m = new MultiProperties( 40, .75f ); try { m.load( new FileInputStream( propertiesFilename ) ); } catch ( IOException e ) { final FastCat sb = new FastCat( 4 ); sb.append( "Problem reading " ); sb.append( propertiesFilename ); sb.append( "\n" ); sb.append( e.getMessage() ); ReplicatorCommon.fatal( sb.toString() ); } // CASE_SENSITIVE is hard-coded in Config.java at compile elapsedTime. // Values here are just the defaults. PROPERTIES_FILE = propertiesFilename; DEBUGGING = m.getBoolean( "DEBUGGING", false ); REPLICATOR_SOURCE_DIR = m.get( "REPLICATOR_SOURCE_DIR", "E:\\com\\mindprod\replicator" ); SENDER_BASE_DIR = m.get( "SENDER_BASE_DIR", "E:\\" ); SENDER_ZIP_STAGING_DIR = m.get( "SENDER_ZIP_STAGING_DIR", "E:\\replicator" ); SENDER_PERSIST_DIR = m.get( "SENDER_PERSIST_DIR", "E:\\com\\mindprod\\replicatorsender" ); TREES_TO_DISTRIBUTE = m.getMultiple( "TREES_TO_DISTRIBUTE" ); DIRS_TO_DISTRIBUTE = m.getMultiple( "DIRS_TO_DISTRIBUTE" ); FILES_TO_DISTRIBUTE = m.getMultiple( "FILES_TO_DISTRIBUTE" ); TREES_TO_WITHHOLD = m.getMultiple( "TREES_TO_WITHHOLD" ); DIRS_TO_WITHHOLD = m.getMultiple( "DIRS_TO_WITHHOLD" ); FILES_TO_WITHHOLD = m.getMultiple( "FILES_TO_WITHHOLD" ); EXTENSIONS_TO_WITHHOLD = m.getMultiple( "EXTENSIONS_TO_WITHHOLD" ); LARGEST_FILE_TO_DISTRIBUTE = m.getInt( "LARGEST_FILE_TO_DISTRIBUTE", 20000000 ); KEEP_DELETED = m.getInt( "KEEP_DELETED", 30 ); // measured in days. KEEP_DELETED_CUTOFF_TIMESTAMP = System.currentTimeMillis() - TimeUnit.DAYS.toMillis( KEEP_DELETED ); LAG = m.getInt( "LAG", 5 ); /** * LAG days ago timestamp. No longer round to multiple of LAG days. Truncate to UTC midnight. * This way we move the goal post only once a day. */ LAG_CUTOFF_TIMESTAMP = TimeUnit.DAYS.toMillis( TimeUnit.MICROSECONDS.toDays( System.currentTimeMillis() ) - LAG ); RETIREMENT = m.getInt( "RETIREMENT", 120 ); // in minutes // how old previously created zips will be retired, not when ones we create now will. RETIREMENT_AXE_TIMESTAMP = System.currentTimeMillis() - TimeUnit.MINUTES.toMillis( RETIREMENT ); RECENT_DEADWOOD_TOLERANCE_PERCENTAGE = m.getInt( "RECENT_DEADWOOD_TOLERANCE_%", 50 ); OLD_DEADWOOD_TOLERANCE_PERCENTAGE = m.getInt( "OLD_DEADWOOD_TOLERANCE_%", 5 ); UNCOMPRESSED_IDEAL_ZIPSIZE = m.getInt( "UNCOMPRESSED_IDEAL_ZIPSIZE", 1000000 ); COMPRESSION_LEVEL = Math.min( Math.max( 0, m.getInt( "COMPRESSION_LEVEL", 9 ) ), 9 ); UNTOUCH = m.getBoolean( "UNTOUCH", false ); COMPACT_HTML = m.get( "COMPACT_HTML", "distributed" ); PROJECT_NAME = m.get( "PROJECT_NAME", "A Project" ); UNIQUE_PROJECT_NAME = m.get( "UNIQUE_PROJECT_NAME", "com.somedomain" ); SUGGESTED_RECEIVER_BASE_DIR = m.get( "SUGGESTED_RECEIVER_BASE_DIR", "E:\\replicator" ); SUGGESTED_RECEIVER_ZIP_STAGING_DIR = m.get( "SUGGESTED_RECEIVER_ZIP_STAGING_DIR", "E:\\replicatorstaging" ); WEBSITE_ZIP_URL = m.get( "WEBSITE_ZIP_URL", "http://mindprod.com/replicator.html" ); SUGGESTED_LAN_ZIP_URL = m.get( "SUGGESTED_LAN_ZIP_URL", "file:///E:/replicator" ); UPLOAD = m.get( "UPLOAD", "" ); HIGHEST_ACCEPTABLE_EXIT_CODE = m.getInt( "HIGHEST_ACCEPTABLE_EXIT_CODE", -1 ); RECOVER = m.get( "RECOVER", "" ); MAX_UPLOAD_TRIES = m.getInt( "MAX_UPLOAD_TRIES", 1 ); AUTHENTICATION = m.get( "AUTHENTICATION", "none" ); // we no longer need the Multiproperties object. All values are now in // static constants. m = null; // free RAM // if bad will throw exception caught at highest level. ensureConfigValid(); } }