Class ConfigFileStore

java.lang.Object
org.frontcache.io.ConfigFileStore

public class ConfigFileStore extends Object
Reads, hashes, backs up and atomically replaces a config file under FRONTCACHE_HOME/conf. Separate from FrontCacheIOServlet because every step in here has a way of being silently wrong, and none of them is about HTTP.
  • Method Details

    • normalize

      public static String normalize(String content)
      Makes submitted text safe to compare and to write.

      CRLF is the one that bites

      HTML form submission normalizes a textarea's line breaks to CRLF. Every config file in this project is LF, so without this the very first Save - even with no edit at all - rewrites every line ending in the file: git diff on a version-controlled FRONTCACHE_HOME shows the whole file changed, and the next content hash fails to match text the console itself just posted.

      The trailing newline is deliberately left alone

      Tempting to add one - conf/bots.conf on the e2e standalone node ends without it - and wrong: a browser does not add a trailing newline to a textarea's value, so adding one here would make "save the file unchanged" change the file. Saving must not alter a byte the operator did not alter, both because that is the promise the button makes and because it is what lets the e2e suite assert an exact round trip.
    • hash

      public static String hash(String content)
      Lowercase hex SHA-256 of the normalized text - the baseHash the console sends.
    • readOnDisk

      public static String readOnDisk(File file) throws IOException
      The file's current content, or "" when it does not exist yet. Deliberately NOT FCConfig.getConfigInputStream: that falls back to the classpath, and what a write has to be compared against is the file on disk it is about to replace. Comparing against a bundled default would let a concurrent edit through.
      Throws:
      IOException
    • backup

      public static String backup(File file) throws IOException
      Copies the current file aside as <name>.backup-<yyyyMMdd_HHmmss>, the naming backup-index already uses for the Lucene index.
      Returns:
      the backup's file name, or null when there was no file to back up
      Throws:
      IOException
    • write

      public static void write(File file, String content) throws IOException
      Replaces the file's content atomically.

      Why not just write the file

      A plain write leaves a window in which the file on disk is truncated, and something reads config files at unpredictable moments - a concurrent get-*-config, a concurrent reload, the node's own startup. A truncated conf/guard-rules.conf is a node with no guard rules, which is an outage that lasts until someone reloads it again. The temp file is created in the same directory as the target on purpose: ATOMIC_MOVE across filesystems throws, and /tmp on a container is routinely a different one.
      Throws:
      IOException