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>&#36;{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>.&#36;{anyFileName}.cloudstore.properties</code>
045 * <li><code>&#36;{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>&#36;{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}