ConfigPropertyEditor
files.
These files use the format described by
Properties.load(java.io.Reader).
The values for keys that start with
- ebase64. are encrypted using either GPG or a symmetric cipherand then encoded as printable ASCII characters using the basic Base-64 encoding using the alphabet specified in Table 1 of RFC 4648. When a symmetric cipher is used, the Base-64 encoding is prefaced with the string "===".
- base64. are encoded as printable ASCII characters using the basic Base-64 encoding using the alphabet specified in Table 1 of RFC 4648.
The motivation is that sometimes values are repeated and it is both tedious and error-prone to replace each instance when there is a change. For example
allows one to switch from "dark mode" by simply changing two values.foregroundColor = white backgroundColor = rbg(10,10,20) headingColor = $(foregroundColor) textColor = $(foregroundColor) errorColor = red
Substitution loops are not supported:
will fail because the recursion will not terminate. This class does not test for this error, but the classforegroundColor = $(backgroundColor) backgroundColor = $(foregroundColor) headingColor = $(foregroundColor) textColor = $(foregroundColor) errorColor = red
ConfigPropertyEditor
does check and will not allow a file containing this error to be
written.
Finally, this class duplicates some of the functionality provided
by ConfigPropertyEditor. The reason for
the duplication is that
- the amount of code is small.
- the JAR file containing
ConfigPropertyEditoris large enough that it would be wasteful to require its module when the only functionality needed is that provided byConfigPropUtilities.
-
Field Summary
Fields -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic String[]decodeRecipients(String recipients) Decode a string representing GPG recipients Encoded recipients are first Base-64 decoded and the resulting string is then split at new-line characters to create the array.static StringencodeRecipients(String[] recipients) Encode an array of GPG recipients.static StringencodeRecipients(List<String> recipients) Encode a list of GPG recipients.Get keys associated with a cycle.static char[]getDecryptedProperty(Properties props, String key, char[] passphrase) Get the value, decrypted if necessary, stored in an instance ofPropertiesunder a given key.static char[]getDecryptedProperty(Properties props, String key, char[] passphrase, String gpgdir) Get the value, decrypted if necessary, stored in an instance ofPropertiesunder a given key and a GPG home directory.static char[]getPassphrase(Supplier<char[]> supplier) Get the passphrase.static StringgetProperty(Properties props, String key) Get the value stored in an instance ofPropertiesunder a given key.static PropertiesnewInstance(byte[] data, String mediaType, boolean gzipped) Create a newPropertiesobject given a byte array that represents an instance ofProperties.static PropertiesnewInstance(File f) Create a new instance ofProperties, loading its keys and values from a file.static PropertiesnewInstance(File f, String mediaType) Create a new instance ofProperties, loading its keys and values from a file, and checking the file's media type.static PropertiesCreate a new instance ofProperties, loading its keys and values from an input stream.static PropertiesnewInstance(InputStream is, String mediaType) Create a new instance ofProperties, loading its keys and values from an input sream, and checking the stream's media type.static PropertiesnewInstance(String b64data, String mediaType) Create a newPropertiesobject given Base-64 encoded data.static voidsetProperty(Properties props, String key, char[] value, char[] password) Set a property for an instance ofPropertieswhen symmetric encryption is used and the value being encrypted is an array of char.static voidsetProperty(Properties props, String key, char[] value, String gpgdir, String[] recipients) Set a property for an instance ofPropertieswhen GPG encryption is used and the value being encrypted is an array of char.static voidsetProperty(Properties props, String key, String value) Set a property for an instance ofProperties.static voidsetProperty(Properties props, String key, String value, boolean literal) Set a property for an instance ofProperties.static voidsetProperty(Properties props, String key, String value, char[] password) Set a property for an instance ofPropertieswhen symmetric encryption is used.static voidsetProperty(Properties props, String key, String value, String gpgdir, String[] recipients) Set a property for an instance ofPropertieswhen GPG encryption is used.static voidstore(Properties props, File file, String mediaType) Store a properties file given an output file, using theConfigPropertyEditorformat.static voidstore(Properties props, OutputStream os, String mediaType) Store a properties file given an output stream, using theConfigPropertyEditorformat.static Stringstore(Properties props, String mediaType) Store a properties file given an output stream, using theConfigPropertyEditorformat.static byte[]storeBytes(Properties props, String mediaType, boolean gzip) Store a properties file as an array of bytes.
-
Field Details
-
EMPTY_CHAR_ARRAY
public static final char[] EMPTY_CHAR_ARRAY
-
-
Constructor Details
-
ConfigPropUtilities
public ConfigPropUtilities()
-
-
Method Details
-
newInstance
Create a new instance ofProperties, loading its keys and values from a file.- Parameters:
f- the file from which to load properties- Returns:
- a new instance of
Properties - Throws:
IOException- if an IO error occurred
-
newInstance
Create a new instance ofProperties, loading its keys and values from an input stream. The stream is not automatically closed.- Parameters:
is- the input stream from which to load properties- Returns:
- a new instance of
Properties - Throws:
IOException- if an IO error occurred
-
newInstance
Create a new instance ofProperties, loading its keys and values from a file, and checking the file's media type.Media types are encoded in the first line of the file, which is expected to be
where MEDIATYPE is the media (or MIME type) as defined in RFC 2045 and subsequent RFCs.#(!M.T MEDIATYPE)- Parameters:
f- the file from which to load propertiesmediaType- the expected media type for the file- Returns:
- a new instance of
Properties - Throws:
IOException- if an IO error occurred
-
newInstance
Create a new instance ofProperties, loading its keys and values from an input sream, and checking the stream's media type.Media types are encoded in the first line of the file, which is expected to be
where MEDIATYPE is the media (or MIME type) as defined in RFC 2045 and subsequent RFCs. The mediatype is converted to lower case for testing. The stream is not automatically closed.#(!M.T MEDIATYPE)- Parameters:
is- the input stream from which to load propertiesmediaType- the expected media type for the file- Returns:
- a new instance of
Properties - Throws:
IOException- if an IO error occurred
-
newInstance
Create a newPropertiesobject given Base-64 encoded data.- Parameters:
b64data- a string containing Base64-encoded GZIP data. Media types are encoded in the first line of the file, which is expected to be
where MEDIATYPE is the media (or MIME type) as defined in RFC 2045 and subsequent RFCs. The mediatype is converted to lower case for testing.#(!M.T MEDIATYPE)mediaType- the expected media type for the file- Returns:
- a new instance of
Properties - Throws:
IOException- if the media type does not match that of the Base-64 encoded representation
-
newInstance
public static Properties newInstance(byte[] data, String mediaType, boolean gzipped) throws IOException Create a newPropertiesobject given a byte array that represents an instance ofProperties. Media types are encoded in the first line of the file, which is expected to be
where MEDIATYPE is the media (or MIME type) as defined in RFC 2045 and subsequent RFCs. The mediatype is converted to lower case for testing.#(!M.T MEDIATYPE)- Parameters:
data- the byte representationmediaType- the expected media (MIME) typegzipped- true if GZIP compression is used; false otherwise- Returns:
- a new instance of
Properties - Throws:
IOException
-
getPassphrase
Get the passphrase. The default supplier obtains the passphrase from the system console.- Parameters:
supplier- aSupplierthat will provide the passphrase; null for a default- Returns:
- the passphrase
-
getDecryptedProperty
public static char[] getDecryptedProperty(Properties props, String key, char[] passphrase) throws GeneralSecurityException Get the value, decrypted if necessary, stored in an instance ofPropertiesunder a given key.- Parameters:
props- the propertieskey- the keypassphrase- the GPG passphrase for decryption.- Returns:
- the decrypted value
- Throws:
GeneralSecurityException- if decryption failed
-
getDecryptedProperty
public static char[] getDecryptedProperty(Properties props, String key, char[] passphrase, String gpgdir) throws GeneralSecurityException Get the value, decrypted if necessary, stored in an instance ofPropertiesunder a given key and a GPG home directory. The GPG home directory is the argument for the GPG --homedir command-line option.When gpgdir is non-null, a GPG TOFU (Trust On First Use) trust model is used.
- Parameters:
props- the propertieskey- the keypassphrase- the GPG passphrase for decryptiongpgdir- the GPG 'home' directory to use; null for the default- Returns:
- the decrypted value
- Throws:
GeneralSecurityException- if decryption failed
-
getCyclicKeys
Get keys associated with a cycle. This method will return the keys that appear in a cycle immediately aftergetProperty(Properties,String)throws an exception.This method is intended for debugging, not general use.
- Returns:
- a set of keys
-
getProperty
public static String getProperty(Properties props, String key) throws IllegalStateException, IllegalArgumentException Get the value stored in an instance ofPropertiesunder a given key. Values whose keys start with "base64." are decoded using a Base-64 decoder. Otherwise, the sequence "$(KEY)", where KEY is some key, is replaced with the value stored for KEY in the value provided for the given key.If an error occurs due to a key recursively referencing itself, the method
getCyclicKeys()can be used to help find the cycle.- Parameters:
props- the instance ofPropertiesstoring key-value pairs.key- the key- Returns:
- the value for the given key; null if the key does not exist
- Throws:
IllegalStateException- if a key is part of a cyclic referenceIllegalArgumentException
-
setProperty
public static void setProperty(Properties props, String key, String value) throws IllegalArgumentException Set a property for an instance ofProperties. When decoded, each '$$' will be replaced with a single '$' and substrings of the form "$(KEY)" will be replaced with the value for the specified KEY.- Parameters:
props- the instance ofPropertieskey- the property keyvalue- the property value- Throws:
IllegalArgumentException- if the key starts with "ebase64."- See Also:
-
setProperty
public static void setProperty(Properties props, String key, String value, boolean literal) throws IllegalArgumentException Set a property for an instance ofProperties. When the argumentliteralis false, a '$' must be escaped by replacing it with the pair "$$", and substrings of the form "$(KEY)" are replaced with the value of KEY. A check for circularity is not performed at this point. Each "$" in the value will be replaced with "$$" because ofConfigPropertyEditorconventions, unless the key starts with "base64.", in which case the value will be Base-64 encoded.- Parameters:
props- the instance ofPropertieskey- the property keyvalue- the property valueliteral- true if the value is a literal string; false if variable substitution is allowed- Throws:
IllegalArgumentException- if the key starts with "ebase64."- See Also:
-
setProperty
public static void setProperty(Properties props, String key, String value, char[] password) throws IllegalArgumentException Set a property for an instance ofPropertieswhen symmetric encryption is used.Note: When a
ConfigPropertyEditoris used, either all encrypted entries should use either symmetric encryption or public key encryption, but these should not be mixed. All encrypted entries should use the same password or passphrase.- Parameters:
props- the instance ofPropertieskey- the property keyvalue- the property valuepassword- the password for symmetric encryption- Throws:
IllegalArgumentException- if the password is null or empty, or if the key does not start with "ebase64."- See Also:
-
setProperty
public static void setProperty(Properties props, String key, char[] value, char[] password) throws IllegalArgumentException Set a property for an instance ofPropertieswhen symmetric encryption is used and the value being encrypted is an array of char.Note: When a
ConfigPropertyEditoris used, either all encrypted entries should use either symmetric encryption or public key encryption, but these should not be mixed. All encrypted entries should use the same password or passphrase.- Parameters:
props- the instance ofPropertieskey- the property keyvalue- the property valuepassword- the password for symmetric encryption- Throws:
IllegalArgumentException- if the password is null or empty, or if the key does not start with "ebase64."- See Also:
-
setProperty
public static void setProperty(Properties props, String key, String value, String gpgdir, String[] recipients) throws IllegalArgumentException Set a property for an instance ofPropertieswhen GPG encryption is used. The recipient's list must contain strings that GPG can use to look up a public key.Note: When a
ConfigPropertyEditoris used, either all encrypted entries should use either symmetric encryption or public key encryption, but these should not be mixed. All encrypted entries should use the same password or passphrase.When gpgdir is non-null, a GPG TOFU (Trust On First Use) trust model is used.
- Parameters:
props- the instance ofPropertieskey- the property keyvalue- the property valuegpgdir- The GPG home directory used to store public an private keys; null for the defaultrecipients- the recipients- Throws:
IllegalArgumentException- if there are no recipients or if the key does not start with "ebase64"- See Also:
-
setProperty
public static void setProperty(Properties props, String key, char[] value, String gpgdir, String[] recipients) throws IllegalArgumentException Set a property for an instance ofPropertieswhen GPG encryption is used and the value being encrypted is an array of char. The recipient's list must contain strings that GPG can use to look up a public key.Note: When a
ConfigPropertyEditoris used, either all encrypted entries should use either symmetric encryption or public key encryption, but these should not be mixed. All encrypted entries should use the same password or passphrase.When gpgdir is non-null, a GPG TOFU (Trust On First Use) trust model is used.
- Parameters:
props- the instance ofPropertieskey- the property keyvalue- the property valuegpgdir- The GPG home directory used to store public an private keys; null for the defaultrecipients- the recipients- Throws:
IllegalArgumentException- if there are no recipients or if the key does not start with "ebase64"- See Also:
-
store
Store a properties file given an output file, using theConfigPropertyEditorformat. This method usesProperties.store(Writer,String)with a writer that uses the UTF-8 character set with CRLF as an end-of-line delimiter.- Parameters:
props- the properties objectfile- the filemediaType- the media (or MIME) type- Throws:
IOException- if an IO error occurred
-
store
Store a properties file given an output stream, using theConfigPropertyEditorformat. This method usesProperties.store(Writer,String)with a writer that uses the UTF-8 character set with CRLF as an end-of-line delimiter.- Parameters:
props- the properties objectos- the output streammediaType- the media (or MIME) type- Throws:
IOException- if an IO error occurred
-
store
Store a properties file given an output stream, using theConfigPropertyEditorformat. The string is produced by in effect first creating a text file using the UTF-8 charset and with a CRLF sequence terminating each line, compressing that file, and then base-64 encoding the compressed file. The text-file format is that produced byProperties.store(Writer,String). This byte array is finally base-64 encoded and turned into a string using the UTF-8 character set. The properties file will start with a comment "#(!M.T " MEDIATYPE)" where MEDIATYPE is the media type in lower case.- Parameters:
props- the properties objectmediaType- the media (or MIME) type- Returns:
- a string representation of a Properties object
-
storeBytes
Store a properties file as an array of bytes. The byte array is produced by in effect first creating a text file using the UTF-8 charset and with a CRLF sequence terminating each line, optionally compressing that file. The text-file format is that produced byProperties.store(Writer,String). The properties file will start with a comment "#(!M.T " MEDIATYPE)" where MEDIATYPE is the media type in lower case.- Parameters:
props- the properties objectmediaType- the media (or MIME) typegzip- true if GZIP compression is used; false otherwise- Returns:
- a byte array storing an instance of
Properties.
-
encodeRecipients
Encode a list of GPG recipients. The recipients first turned into a series of UTF-8 encoded bytes, separated by a new-line character, and the resulting sequence of bytes is Base64 encoded.- Parameters:
recipients- the recipients- Returns:
- a string encoding the recipients; null if the argument is null
-
encodeRecipients
Encode an array of GPG recipients. The recipients first turned into a series of UTF-8 encoded bytes, separated by a new-line character, and the resulting sequence of bytes is Base64 encoded.- Parameters:
recipients- the recipients- Returns:
- a string encoding the recipients; null if the argument is null
-
decodeRecipients
Decode a string representing GPG recipients Encoded recipients are first Base-64 decoded and the resulting string is then split at new-line characters to create the array.- Parameters:
recipients- the encoded recipients- Returns:
- an array each element of which is a recipient; an empty array if there are no recipients; null if the argument is null empty array if there are no recipients
-