001package co.codewizards.cloudstore.core.config; 002 003import java.util.Date; 004import java.util.List; 005import java.util.Map; 006import java.util.Properties; 007import java.util.regex.Matcher; 008import java.util.regex.Pattern; 009 010import co.codewizards.cloudstore.core.appid.AppIdRegistry; 011import co.codewizards.cloudstore.core.util.ISO8601; 012 013/** 014 * Configuration of CloudStore supporting inheritance of settings. 015 * <p> 016 * Obtain an instance via one of the following methods: 017 * <ul> 018 * <li>{@link ConfigImpl#getInstance()} 019 * <li>{@link ConfigImpl#getInstanceForDirectory(co.codewizards.cloudstore.core.oio.File)} 020 * <li>{@link ConfigImpl#getInstanceForFile(co.codewizards.cloudstore.core.oio.File)} 021 * </ul> 022 * <p> 023 * There is one {@code Config} instance available (lazily created, cached temporarily) for every 024 * directory and every file in a repository. Each {@code Config} inherits the settings from the 025 * parent-directory, if not explicitly overwritten. 026 * <p> 027 * The configuration is based on {@link Properties} files. Every property file is optional. If it 028 * does not exist, all settings are inherited. If it does exist, only those properties contained in 029 * the file are overriden. All properties not contained in the file are still inherited. Inheritance 030 * is thus applicable on every individual property. 031 * <p> 032 * Modifications, deletions, creations of properties files are detected during runtime (pretty immediately). 033 * Note, that this detection is based on the files' timestamps. Since most file systems have a granularity 034 * of 1 second (some even 2) for the last-modified-timestamp, multiple modifications in the same second might 035 * not be detected. 036 * <p> 037 * There is a global properties file in the user's home directory (or wherever {@link ConfigDir} 038 * points to): <code>${user.home}/.cloudstore/cloudstore.properties</code> 039 * <p> 040 * Additionally, every directory can optionally contain the following files: 041 * <ol> 042 * <li><code>.cloudstore.properties</code> 043 * <li><code>cloudstore.properties</code> 044 * <li><code>.${anyFileName}.cloudstore.properties</code> 045 * <li><code>${anyFileName}.cloudstore.properties</code> 046 * </ol> 047 * <p> 048 * The files 1. and 2. are applicable to the entire directory and all sub-directories and files in it. 049 * Usually, on GNU/Linux people will prefer 1., but when using Windows, files starting with a "." are 050 * sometimes a bit hard to deal with. Therefore, we support both. The file 2. overrides the settings of file 1.. 051 * <p> 052 * The files 3. and 4. are applicable only to the file <code>${anyFileName}</code>. Thus, if you want 053 * to set special behaviour for the file <code>example.db</code> only, you can create the file 054 * <code>.example.db.cloudstore.properties</code> in the same directory. 055 * 056 * @author Marco หงุ่ยตระกูล-Schulze - marco at codewizards dot co 057 */ 058public interface Config { 059 String APP_ID_SIMPLE_ID = AppIdRegistry.getInstance().getAppIdOrFail().getSimpleId(); 060 061 String PROPERTIES_FILE_NAME_SUFFIX = ".properties"; 062 063 String PROPERTIES_FILE_NAME_FOR_DIRECTORY_LOCAL = '.' + APP_ID_SIMPLE_ID + ".local" + PROPERTIES_FILE_NAME_SUFFIX; 064 065 String PROPERTIES_FILE_NAME_FOR_DIRECTORY = '.' + APP_ID_SIMPLE_ID + PROPERTIES_FILE_NAME_SUFFIX; 066 067 String PROPERTIES_FILE_NAME_PARENT_PREFIX = "parent."; 068 069 String PROPERTIES_FILE_NAME_PARENT = "parent" + PROPERTIES_FILE_NAME_SUFFIX; 070 071 /** 072 * Prefix used for system properties overriding configuration entries. 073 * <p> 074 * Every property in the configuration (i.e. in its properties files) can be overridden 075 * by a corresponding system property. The system property must be prefixed. 076 * <p> 077 * For example, to override the configuration property with the key "deferrableExecutor.timeout", 078 * you can pass the system property "cloudstore.deferrableExecutor.timeout" to the JVM. If the 079 * system property exists, the configuration is not consulted, but the system property value is 080 * used as shortcut. 081 * <p> 082 * Additionally, it is possible to override configuration entries via OS environment variables. 083 * Since an env var's name must not contain a dot ("."), all dots are replaced by underscores ("_"). 084 */ 085 String SYSTEM_PROPERTY_PREFIX = APP_ID_SIMPLE_ID + '.'; 086 087 /** 088 * Gets the property identified by the given key. 089 * <p> 090 * This method directly delegates to {@link Properties#getProperty(String, String)}. 091 * Thus, an empty String in the internal {@code Properties} is returned instead of the 092 * given {@code defaultValue}. The {@code defaultValue} is only returned, if neither 093 * the internal {@code Properties} of this {@code Config} nor any of its parents contains 094 * the entry. 095 * <p> 096 * <b>Important:</b> This is often not the desired behaviour. You might want to use 097 * {@link #getPropertyAsNonEmptyTrimmedString(String, String)} instead! 098 * <p> 099 * Every property can be overwritten by a system property prefixed with {@value #SYSTEM_PROPERTY_PREFIX}. 100 * If - for example - the key "updater.force" is to be read and a system property 101 * named "cloudstore.updater.force" is set, this system property is returned instead! 102 * @param key the key identifying the property. Must not be <code>null</code>. 103 * @param defaultValue the default value to fall back to, if neither this {@code Config}'s 104 * internal {@code Properties} nor any of its parents contains a matching entry. 105 * May be <code>null</code>. 106 * @return the property's value. Never <code>null</code> unless {@code defaultValue} is <code>null</code>. 107 * @see #getPropertyAsNonEmptyTrimmedString(String, String) 108 */ 109 String getProperty(final String key, final String defaultValue); 110 111 /** 112 * Gets the property identified by the given key; <b>not</b> taking inheritance into account. 113 * <p> 114 * This method corresponds to {@link #getProperty(String, String)}, but it does not fall back 115 * to any inherited value. 116 * <p> 117 * <b>Important:</b> This method should never be used in order to control the behaviour of the 118 * application! It is intended for use of administrative tools / UIs which need to read/write 119 * directly. 120 * 121 * @param key the key identifying the property. Must not be <code>null</code>. 122 * @return the property's value. <code>null</code>, if the property is not set. 123 * @see #setDirectProperty(String, String) 124 */ 125 String getDirectProperty(final String key); 126 127 /** 128 * Sets the property identified by the given key; <b>not</b> taking inheritance into account. 129 * @param key the key identifying the property. Must not be <code>null</code>. 130 * @param value the property's value. <code>null</code> removes the property from this concrete 131 * configuration instance. 132 * @see #getDirectProperty(String) 133 */ 134 void setDirectProperty(final String key, final String value); 135 136 /** 137 * Gets the property identified by the given key; {@linkplain String#trim() trimmed}. 138 * <p> 139 * In contrast to {@link #getProperty(String, String)}, this method falls back to the given 140 * {@code defaultValue}, if the internal {@code Properties} contains an empty {@code String} 141 * (after trimming) as value for the given {@code key}. 142 * <p> 143 * It therefore means that a value set to an empty {@code String} in the properties file means 144 * to use the program's default instead. It is therefore consistent with 145 * {@link #getPropertyAsLong(String, long)} and all other {@code getPropertyAs...(...)} 146 * methods. 147 * <p> 148 * The same rules apply to the fall-back-strategy from system property to environment variable and 149 * finally config files. 150 * <p> 151 * Every property can be overwritten by a system property prefixed with {@value #SYSTEM_PROPERTY_PREFIX}. 152 * If - for example - the key "updater.force" is to be read and a system property 153 * named "cloudstore.updater.force" is set, this system property is returned instead! 154 * @param key the key identifying the property. Must not be <code>null</code>. 155 * @param defaultValue the default value to fall back to, if neither this {@code Config}'s 156 * internal {@code Properties} nor any of its parents contains a matching entry or 157 * if this entry's value is an empty {@code String}. 158 * May be <code>null</code>. 159 * @return the property's value. Never <code>null</code> unless {@code defaultValue} is <code>null</code>. 160 */ 161 String getPropertyAsNonEmptyTrimmedString(final String key, final String defaultValue); 162 163 long getPropertyAsLong(final String key, final long defaultValue); 164 165 long getPropertyAsPositiveOrZeroLong(final String key, final long defaultValue); 166 167 int getPropertyAsInt(final String key, final int defaultValue); 168 169 int getPropertyAsPositiveOrZeroInt(final String key, final int defaultValue); 170 171 /** 172 * Gets the property identified by the given key. 173 * @param key the key identifying the property. Must not be <code>null</code>. 174 * @param defaultValue the default value to fall back to, if neither this {@code Config}'s 175 * internal {@code Properties} nor any of its parents contains a matching entry or 176 * if this entry's value does not match any possible enum value. Must not be <code>null</code>. 177 * If a <code>null</code> default value is required, use {@link #getPropertyAsEnum(String, Class, Enum)} 178 * instead! 179 * @return the property's value. Never <code>null</code>. 180 * @see #getPropertyAsEnum(String, Class, Enum) 181 * @see #getPropertyAsNonEmptyTrimmedString(String, String) 182 */ 183 <E extends Enum<E>> E getPropertyAsEnum(final String key, final E defaultValue); 184 185 /** 186 * Gets the property identified by the given key. 187 * @param key the key identifying the property. Must not be <code>null</code>. 188 * @param enumClass the enum's type. Must not be <code>null</code>. 189 * @param defaultValue the default value to fall back to, if neither this {@code Config}'s 190 * internal {@code Properties} nor any of its parents contains a matching entry or 191 * if this entry's value does not match any possible enum value. May be <code>null</code>. 192 * @return the property's value. Never <code>null</code> unless {@code defaultValue} is <code>null</code>. 193 * @see #getPropertyAsEnum(String, Enum) 194 * @see #getPropertyAsNonEmptyTrimmedString(String, String) 195 */ 196 <E extends Enum<E>> E getPropertyAsEnum(final String key, final Class<E> enumClass, final E defaultValue); 197 198 boolean getPropertyAsBoolean(final String key, final boolean defaultValue); 199 200 /** 201 * Gets the property identified by the given key. 202 * <p> 203 * The date must be {@link ISO8601}-parseable. If it is not parseable, a warning is logged and the 204 * default value is returned. 205 * @param key the key identifying the property. Must not be <code>null</code>. 206 * @param defaultValue the default value to fall back to, if neither this {@code Config}'s 207 * internal {@code Properties} nor any of its parents contains a matching entry or 208 * if this entry's value cannot be parsed as an {@link ISO8601}-encoded date+time value. 209 * May be <code>null</code>. 210 * @return the property's value. Never <code>null</code> unless {@code defaultValue} is <code>null</code>. 211 */ 212 Date getPropertyAsDate(String key, Date defaultValue); 213 214 /** 215 * Gets a version number that is guaranteed to be changed whenever the underlying files change. 216 * <p> 217 * It is <i>not</i> guaranteed to be incremented! Depending on the underlying change, a newer 218 * version number might be less than a previous version number! In most cases, however, the 219 * version number actually grows with each change. Code must not rely on this, but it is a 220 * helpful assumption for debugging. 221 * @return a version number that is guaranteed to be changed whenever the underlying files change. 222 */ 223 long getVersion(); 224 225 /** 226 * Gets all config-property-keys matching the given regular expression. 227 * <p> 228 * Just like {@link #getProperty(String, String)}, this method takes inheritance into account: 229 * It collects keys from the current {@code Config} instance and all its parents, recursively. 230 * <p> 231 * Note, that {@link Matcher#matches()} is used, i.e. the {@code regex} must match the entire 232 * config-key. 233 * <p> 234 * The given {@code regex} may contain capturing groups, whose results are returned in the {@code Map}'s 235 * values. Note, that the entire match (which is by convention the group 0) is ignored in the values, 236 * because it is the {@code Map}'s key, already. Hence, the first entry in the {@code Map}'s values 237 * (with index 0) corresponds to the first capturing group in the regular expression. 238 * <p> 239 * For example, let's say the regex is "ignore\[([^]]*)\]\.(.*)" and the following matching properties 240 * exist: 241 * <ul> 242 * <li>ignore[backup-file].namePattern=*.bak</li> 243 * <li>ignore[backup-file].enabled=false</li> 244 * <li>ignore[class-file].namePattern=*.class</li> 245 * </ul> 246 * <p> 247 * This leads to a resulting map with the following entries: 248 * <ul> 249 * <li>key: "ignore[backup-file].namePattern", value: "backup-file", "namePattern"</li> 250 * <li>key: "ignore[backup-file].enabled", value: "backup-file", "enabled"</li> 251 * <li>key: "ignore[class-file].namePattern", value: "class-file", "namePattern"</li> 252 * </ul> 253 * <p> 254 * @param regex the regular expression to look for. Must not be <code>null</code>. 255 * @return the keys found together with capturing groups. Never <code>null</code>. 256 */ 257 Map<String, List<String>> getKey2GroupsMatching(Pattern regex); 258 259}