Class FilePersistenceManager

java.lang.Object
org.apache.felix.cm.file.FilePersistenceManager
All Implemented Interfaces:
PersistenceManager

public class FilePersistenceManager extends Object implements PersistenceManager
The FilePersistenceManager class stores configuration data in properties-like files inside a given directory. All configuration files are located in the same directory.

The configuration directory is set by either the FilePersistenceManager(String) constructor or the FilePersistenceManager(BundleContext, String) constructor. Refer to the respective JavaDocs for more information.

When this persistence manager is used by the Configuration Admin Service, the location may be configured using the ConfigurationManager bundle context property.

If the location is not set, the config directory in the current working directory (as set in the user.dir system property) is used. If the the location is set but, no such directory exists, the directory and any missing parent directories are created. If a file exists at the given location, the constructor fails.

Configuration files are created in the configuration directory by appending the extension .config to the PID of the configuration. The PID is converted into a relative path name by replacing enclosed dots to slashes. Non-symbolic-name characters in the PID are encoded with their Unicode character code in hexadecimal.

Examples of PID to name conversion:
PID Configuration File Name
sample sample.config
org.apache.felix.log.LogService org/apache/felix/log/LogService.config
sample.fläche sample/fl%00e8che.config

Mulithreading Issues

In a multithreaded environment the store(String, Dictionary) and load(String) methods may be called at the the quasi-same time for the same configuration PID. It may no happen, that the store method starts writing the file and the load method might at the same time read from the file currently being written and thus loading corrupt data (if data is available at all).

To prevent this situation from happening, the methods use synchronization and temporary files as follows:

  • The store(String, Dictionary) method writes a temporary file with file extension .tmp. When done, the file is renamed to actual configuration file name as implied by the PID. This last step of renaming the file is synchronized on the FilePersistenceManager instance.
  • The load(String) method is completeley synchronized on the FilePersistenceManager instance such that the store(java.lang.String, java.util.Dictionary) method might inadvertantly try to replace the file while it is being read.
  • Finally the Iterator returned by getDictionaries() is implemented such that any temporary configuration file is just ignored.
  • Field Details

    • DEFAULT_CONFIG_DIR

      public static final String DEFAULT_CONFIG_DIR
      The default configuration data directory if no location is configured (value is "config").
      See Also:
    • DEFAULT_PERSISTENCE_MANAGER_NAME

      public static final String DEFAULT_PERSISTENCE_MANAGER_NAME
      The name of this persistence manager when registered in the service registry. (value is "file").
      See Also:
    • FILE_EXT

      private static final String FILE_EXT
      The extension of the configuration files.
      See Also:
    • TMP_EXT

      private static final String TMP_EXT
      The extension of the configuration files, while they are being written (value is ".tmp").
      See Also:
    • VALID_PATH_CHARS

      private static final BitSet VALID_PATH_CHARS
    • acc

      private final AccessControlContext acc
      The access control context we use in the presence of a security manager.
    • location

      private final File location
      The abstract path name of the configuration files.
    • isWin

      private final boolean isWin
      Flag indicating whether this instance is running on a Windows platform or not.
  • Constructor Details

    • FilePersistenceManager

      public FilePersistenceManager(String location)
      Creates an instance of this persistence manager using the given location as the directory to store and retrieve the configuration files.

      This constructor resolves the configuration file location as follows:

      • If location is null, the config directory in the current working directory as specified in the user.dir system property is assumed.
      • Otherwise the named directory is used.
      • If the directory name resolved in the first or second step is not an absolute path, it is resolved to an absolute path calling the File.getAbsoluteFile() method.
      • If a non-directory file exists as the location found in the previous step or the named directory (including any parent directories) cannot be created, an IllegalArgumentException is thrown.

      This constructor is equivalent to calling FilePersistenceManager(BundleContext, String) with a null BundleContext.

      Parameters:
      location - The configuration file location. If this is null the config directory below the current working directory is used.
      Throws:
      IllegalArgumentException - If the location exists but is not a directory or does not exist and cannot be created.
    • FilePersistenceManager

      public FilePersistenceManager(org.osgi.framework.BundleContext bundleContext, String location)
      Creates an instance of this persistence manager using the given location as the directory to store and retrieve the configuration files.

      This constructor resolves the configuration file location as follows:

      • If location is null, the config directory in the persistent storage area of the bundle identified by bundleContext is used.
      • If the framework does not support persistent storage area for bundles in the filesystem or if bundleContext is null, the config directory in the current working directory as specified in the user.dir system property is assumed.
      • Otherwise the named directory is used.
      • If the directory name resolved in the first, second or third step is not an absolute path and a bundleContext is provided which provides access to persistent storage area, the directory name is resolved as being inside the persistent storage area. Otherwise the directory name is resolved to an absolute path calling the File.getAbsoluteFile() method.
      • If a non-directory file exists as the location found in the previous step or the named directory (including any parent directories) cannot be created, an IllegalArgumentException is thrown.
      Parameters:
      bundleContext - The BundleContext to optionally get the data location for the configuration files. This may be null, in which case this constructor acts exactly the same as calling FilePersistenceManager(String).
      location - The configuration file location. If this is null the config directory below the current working directory is used.
      Throws:
      IllegalArgumentException - If the location exists but is not a directory or does not exist and cannot be created.
      IllegalStateException - If the bundleContext is not valid.
  • Method Details

    • equalsNameWithPrefixPlusOneDigit

      private static boolean equalsNameWithPrefixPlusOneDigit(String name, String prefix)
    • isWinReservedName

      private static boolean isWinReservedName(String name)
    • encodePid

      String encodePid(String pid)
      Encodes a Service PID to a filesystem path as described in the class JavaDoc above.

      This method is not part of the API of this class and is declared package private to enable JUnit testing on it. This method may be removed or modified at any time without notice.

      Parameters:
      pid - The Service PID to encode into a relative path name.
      Returns:
      The relative path name corresponding to the Service PID.
    • appendEncoded

      private void appendEncoded(StringBuilder buf, char c)
    • getLocation

      public File getLocation()
      Returns the directory in which the configuration files are written as a File object.
      Returns:
      The configuration file location.
    • getDictionaries

      public Enumeration getDictionaries()
      Loads configuration data from the configuration location and returns it as Dictionary objects.

      This method is a lazy implementation, which is just one configuration file ahead of the current enumeration location.

      Specified by:
      getDictionaries in interface PersistenceManager
      Returns:
      an enumeration of configuration data returned as instances of the Dictionary class.
    • delete

      public void delete(String pid)
      Deletes the file for the given identifier.
      Specified by:
      delete in interface PersistenceManager
      Parameters:
      pid - The identifier of the configuration file to delete.
    • _privilegedDelete

      private void _privilegedDelete(String pid)
    • _delete

      private void _delete(String pid)
    • exists

      public boolean exists(String pid)
      Returns true if a (configuration) file exists for the given identifier.
      Specified by:
      exists in interface PersistenceManager
      Parameters:
      pid - The identifier of the configuration file to check.
      Returns:
      true if the file exists
    • _privilegedExists

      private boolean _privilegedExists(String pid)
    • _exists

      private boolean _exists(String pid)
    • load

      public Dictionary load(String pid) throws IOException
      Reads the (configuration) for the given identifier into a Dictionary object.
      Specified by:
      load in interface PersistenceManager
      Parameters:
      pid - The identifier of the configuration file to delete.
      Returns:
      The configuration read from the file. This Dictionary may be empty if the file contains no configuration information or is not properly formatted.
      Throws:
      IOException - If an error occurs loading the dictionary. An IOException must also be thrown if no dictionary exists for the given identifier.
    • _privilegedLoad

      private Dictionary _privilegedLoad(File cfgFile) throws IOException
      Throws:
      IOException
    • _load

      Dictionary _load(File cfgFile) throws IOException
      Loads the contents of the cfgFile into a new Dictionary object.
      Parameters:
      cfgFile - The file from which to load the data.
      Returns:
      A new Dictionary object providing the file contents.
      Throws:
      FileNotFoundException - If the given file does not exist.
      IOException - If an error occurrs reading the configuration file.
    • store

      public void store(String pid, Dictionary props) throws IOException
      Stores the contents of the Dictionary in a file denoted by the given identifier.
      Specified by:
      store in interface PersistenceManager
      Parameters:
      pid - The identifier of the configuration file to which to write the configuration contents.
      props - The configuration data to write.
      Throws:
      IOException - If an error occurrs writing the configuration data.
    • _privilegedStore

      private void _privilegedStore(String pid, Dictionary props) throws IOException
      Throws:
      IOException
    • _store

      private void _store(String pid, Dictionary props) throws IOException
      Throws:
      IOException
    • getFile

      File getFile(String pid)
      Creates an abstract path name for the pid encoding it as follows:
      • Dots (.) are replaced by File.separatorChar
      • Characters not matching [a-zA-Z0-9 _-] are encoded with a percent character (%) and a 4-place hexadecimal unicode value.
      Before returning the path name, the parent directory and any ancestors are created.
      Parameters:
      pid - The identifier for which to create the abstract file name.
      Returns:
      The abstract path name, which the parent directory path created.