Repository navigation
Releases: lichtsinn/ageris
Release list
Ageris 0.7.0
Ageris 0.7.0 makes files stricter where a mistake went unnoticed, and smooths a few edges.
Get it
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-core</artifactId>
<version>0.7.0</version>
</dependency>implementation 'io.github.lichtsinn:ageris-core:0.7.0'Requires Java 17 or later. The Helidon module requires Java 27.
Changed
- A name or a key that appears twice is refused. An entry's name in its section, a context's name in
contexts, and each key in an entry and inmodifiers. Until now only a rule was checked, and elsewhere the last one counted without a word. A file that holds a section twice is refused as a whole. What an entry'svalueholds belongs to the application and is not checked. - A
commentis allowed inmodifiers, as it already was in an entry, a named context and a rule. - A record that asks for a binding gets a message that fits. It is told to become a class; the old message advised removing an annotation a record does not have.
ConfigurationLoader.awaitFirstAttemptwaits until every manager has loaded or has failed once, whereawaitFirstLoadwaits for success only. The Spring Boot starter and the Helidon module already behaved this way and now use it.- The OpenFeature provider gives up at once when its first attempt to load fails. It used to wait out its whole timeout before reporting the reason.
- A file name without a leading slash works with the file system fetcher:
featureflags.jsonis read like/featureflags.json. It used to be glued to the name of the application and end in "no such file". - A
first-load-timeoutthat cannot be right is refused by the Spring Boot starter and the Helidon module, with a message that names it: a negative one always, and zero whilefail-on-first-load-failureis true. Zero together withfail-on-first-load-failure=falsemeans not to wait.
Also new
- Example applications for Helidon SE and Spring Boot, in
examples. Both show a feature flag with targeting, a config, and a change to a file while the application runs. - The README says in which order a file and the code that reads it are released, under Releasing a change: for adding and removing a feature flag, a config and a policy.
- A logo, in
logo.
Upgrading from 0.6.0
- Check your files for a name or a key that appears twice. Such an entry loaded before, with the last one counting, and is refused now. As with any entry that cannot be loaded, the application keeps what it had until the file is fixed.
- If
first-load-timeoutis zero or negative, set it to a time longer than zero, or setfail-on-first-load-failureto false if the start should not wait. - A
commentinmodifiersneeds 0.7.0 in every application that reads the file. An older version cannot load an entry that has one.
Dependencies
Unchanged from 0.6.0: Jackson 2.22, slf4j 2.0 and commons-io 2.22. ageris-core is also tested with Jackson 2.15.4, slf4j 1.7.36 and commons-io 2.11.0; ageris-openfeature with the OpenFeature SDK from 1.16.0 to 1.23.0; ageris-spring-boot-starter with Spring Boot 4.0.0 to 4.1.1; and ageris-helidon with Helidon 27.0.0.
Ageris 0.6.0
Ageris 0.6.0 adds support for Helidon SE.
Get it
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-core</artifactId>
<version>0.6.0</version>
</dependency>implementation 'io.github.lichtsinn:ageris-core:0.6.0'Requires Java 17 or later. The Helidon module requires Java 27.
Using Ageris with Helidon
The new module ageris-helidon sets Ageris up in a Helidon 27 SE application from Helidon's configuration:
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-helidon</artifactId>
<version>0.6.0</version>
</dependency>ageris:
application: myapp
path: /etc/myapp/config
feature-flags:
files: ["/featureflags.json"]Create Ageris yourself with one call:
AgerisHelidon ageris = AgerisHelidon.create(config.get("ageris"));
FeatureFlagManager featureFlagManager = ageris.featureFlagManager();Or let Helidon's service registry inject the managers, with no setup code:
@Service.Singleton
class Greeter {
@Service.Inject
Greeter(FeatureFlagManager featureFlagManager) { ... }
}- The settings have the same names as in the Spring Boot starter.
- A manager is loaded before anything receives it. If the first attempt to load the files fails, the application does not start, and the error says why;
ageris.fail-on-first-load-failure: falselets it start anyway. - With Helidon's health support, a readiness check named
agerisreports the managers: up when loaded or stale, down when one never loaded. - With
ageris-openfeatureand the OpenFeature SDK present, anAgerisProvideris available as well. - Bindings of policies and other options are set through an
AgerisCustomizer; aConfigurationFetcherof your own reads the files from somewhere other than a directory. - With the service registry, nothing is set up while no files are set, so adding the dependency alone changes no application.
It is for Helidon SE on Helidon 27, and so needs Java 27. See Using Ageris with Helidon in the README.
Also changed
- The build of Ageris itself now also runs on Java 27, and releases are built with it.
ageris-core,ageris-openfeatureandageris-spring-boot-starterare still compiled for Java 17.
Upgrading from 0.5.0
Nothing to do.
Dependencies
Unchanged from 0.5.0: Jackson 2.22, slf4j 2.0 and commons-io 2.22. ageris-core is also tested with Jackson 2.15.4, slf4j 1.7.36 and commons-io 2.11.0; ageris-openfeature with the OpenFeature SDK from 1.16.0 to 1.23.0; ageris-spring-boot-starter with Spring Boot 4.0.0 to 4.1.1; and ageris-helidon with Helidon 27.0.0.
Ageris 0.5.0
Ageris 0.5.0 adds a Spring Boot starter.
Get it
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-core</artifactId>
<version>0.5.0</version>
</dependency>implementation 'io.github.lichtsinn:ageris-core:0.5.0'Requires Java 17 or later.
Using Ageris with Spring Boot
The new module ageris-spring-boot-starter sets Ageris up in a Spring Boot 4 application from a few properties, without setup code:
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-spring-boot-starter</artifactId>
<version>0.5.0</version>
</dependency>ageris.application=myapp
ageris.path=/etc/myapp/config
ageris.feature-flags.files=/featureflags.json
ageris.configs.files=/configs.json- The feature flag manager, the config manager and the JSON config manager are beans. Each exists only if its files are set.
- The loader starts with the application and stops with it. A manager is loaded before any bean receives it.
- If the first attempt to load the files fails, the application does not start, and the error says why.
ageris.fail-on-first-load-failure=falselets it start anyway. - An
AgerisCustomizerbean registers the bindings of policies and sets other options. AConfigurationFetcherbean reads the files from somewhere other than a directory. - With the health support of Spring Boot, as with Actuator, the health of the application shows Ageris: up when loaded or stale, down when a manager never loaded.
- With
ageris-openfeatureand the OpenFeature SDK present, the starter offers anAgerisProviderbean. - Without any files set the starter creates nothing, so adding the dependency alone changes no application.
It needs Spring Boot 4.0 or later. See Using Ageris with Spring Boot in the README.
Also changed
- A percentage in a rule is checked for its decimals as it is written.
0.10000000000000001was accepted as 0.1 before, and is now refused like any percentage with more than two decimals. - The failure of an entry is one line. The line break and the
at [Source: UNKNOWN; ...]that Jackson added are gone. - An OpenFeature lookup is cheaper again: an empty
ConfigurationContextno longer creates a map.
Upgrading from 0.4.0
Nothing to do, unless a file holds a percentage with more than two decimals that only slipped through because of rounding.
Dependencies
Unchanged from 0.4.0: Jackson 2.22, slf4j 2.0 and commons-io 2.22. ageris-core is also tested with Jackson 2.15.4, slf4j 1.7.36 and commons-io 2.11.0; ageris-openfeature with the OpenFeature SDK from 1.16.0 to 1.23.0; and ageris-spring-boot-starter with Spring Boot 4.0.0 to 4.1.1.
Ageris 0.4.0
Ageris 0.4.0 adds an OpenFeature provider, and makes evaluate cheaper.
Get it
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-core</artifactId>
<version>0.4.0</version>
</dependency>implementation 'io.github.lichtsinn:ageris-core:0.4.0'Requires Java 17 or later.
Using Ageris through OpenFeature
OpenFeature is a vendor-neutral API for feature flags. The new module ageris-openfeature lets an application read Ageris feature flags and JSON configs through it:
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-openfeature</artifactId>
<version>0.4.0</version>
</dependency>OpenFeatureAPI.getInstance().setProviderAndWait(new AgerisProvider(featureFlagManager, jsonConfigManager, loader));
Client client = OpenFeatureAPI.getInstance().getClient();
boolean active = client.getBooleanValue("enable-checkout", false, new ImmutableContext("joe"));- Boolean lookups are answered from the feature flags. Text, number and object lookups are answered from the JSON configs.
- Reasons and errors are reported as OpenFeature describes them:
STATIC,DEFAULT,TARGETING_MATCH,FLAG_NOT_FOUND,TYPE_MISMATCH. - The provider starts and stops the loader, and passes on updates and stale periods as OpenFeature events.
- The targeting key becomes the context type
user, or another type of your choice. Attributes keep their names. - The module brings
ageris-corewith it. The OpenFeature SDK is yours to add, in version 1.16.0 or later. - Configs of your own classes, and policies, stay on the config manager of Ageris; the same
Ageriscan serve both.
See Using Ageris through OpenFeature in the README.
Cheaper evaluate
evaluate returns a value together with its reason and the context that matched. It now builds the map of matches only when getMatches() is called, which most callers never do.
- In our measurement an
evaluatethrough two levels went from about 420 bytes per call to about 30, and took half the time. Evaluation.of(value, reason, "environment", "qa", ...)creates an evaluation without a map, which is also handy in tests.isActiveandgetConfigare unchanged: they create no objects at all.
Also changed
ConfigurationContext.getis now documented as the only method Ageris calls during a lookup. A subclass may override it to take its values from elsewhere.
Upgrading from 0.3.0
Nothing to do. Configuration files and the API of 0.3.0 work unchanged.
Dependencies
Unchanged from 0.3.0: Jackson 2.22, slf4j 2.0 and commons-io 2.22. ageris-core is also tested with Jackson 2.15.4, slf4j 1.7.36 and commons-io 2.11.0, and ageris-openfeature with the OpenFeature SDK from 1.16.0 to 1.23.0.
Ageris 0.3.0
Ageris 0.3.0 adds targeting: a feature flag, config or policy can now apply to a list of values and to a percentage of callers.
Get it
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-core</artifactId>
<version>0.3.0</version>
</dependency>implementation 'io.github.lichtsinn:ageris-core:0.3.0'Requires Java 17 or later.
Targeting
A modifier picks a nested entry by the exact value of its context type. It can now also hold rules for the values that have no entry of their own:
"modifiers": {
"type": "user",
"contexts": { "joe": { "value": true } },
"rules": [
{ "in": ["anna", "ben"], "value": true },
{ "percent": 10, "value": true }
]
}The feature flag is on for joe, for anna and ben, and for 10 percent of the other users.
- At each level a named entry in
contextswins, then the rules are tried from top to bottom, then the value of that level applies. - A percentage is decided by hashing the caller's value together with the name of the feature flag or config. Nothing is stored, the same caller always gets the same answer, and raising a percentage keeps everyone who was in.
- Rules work for feature flags, configs, JSON configs and policies alike, so a policy can be rolled out to a share of users.
- A lookup through rules creates no objects, like every other lookup.
- A malformed rule is refused when the file is loaded, and what was loaded before keeps being served.
See Targeting in the README.
Before you use rules in a file
Upgrade every application that reads the file first. An application on Ageris 0.2.0 does not know rules:
- With the defaults it cannot load the entry and keeps the configuration it loaded before.
- If it was told to skip entries it cannot load, it stops serving that entry.
- If it was given a JSON mapper that ignores unknown properties, it loads the entry and leaves the rules out, without a failure.
Files without rules mean what they meant in 0.2.0. The build now loads files in the 0.2.0 format and checks their answers, so that this stays true.
Also changed
- The failure of an entry now names the entry:
Cannot read enable-checkout: .... Before, the message began with the name of a Java exception. The file and the application are still named in front of it. - The build runs on Java 17 as well as Java 25.
Dependencies
Unchanged from 0.2.0: Jackson 2.22, slf4j 2.0 and commons-io 2.22. The library is also tested with Jackson 2.15.4, slf4j 1.7.36 and commons-io 2.11.0.
Ageris 0.2.0
Ageris is a Java library for feature flags, runtime configuration and policies. It runs inside your application and needs no server: it reads configuration files that you keep wherever you like, and refreshes them in the background.
This is the first release of Ageris. It continues Merci, originally developed at Medallia, Inc. and released under the Apache License 2.0. Ageris is not affiliated with or endorsed by Medallia.
Get it
<dependency>
<groupId>io.github.lichtsinn</groupId>
<artifactId>ageris-core</artifactId>
<version>0.2.0</version>
</dependency>implementation 'io.github.lichtsinn:ageris-core:0.2.0'Requires Java 17 or later.
What is in it
- Feature flags that turn code paths on and off without a deployment.
- Runtime configuration as typed Java objects, called configs, or as plain JSON.
- Policies: configs that have behavior and work with live objects of your application, which you bind on
Ageris. A configuration file can never supply or replace a binding. - Immutable by design. Write a config as an immutable class or a Java record, and it stays that way: Ageris never changes it after loading. When a file changes, Ageris creates new objects and replaces the old ones, so your code never sees a half-updated config.
- Values that depend on context. Any feature flag, config or policy can differ by environment, cluster, user or any other context you define.
- Safe updates. A file that cannot be loaded changes nothing; what was loaded before keeps being served. Unchanged content is not loaded again.
- A wait for the first load, and a status per manager: not loaded, loaded, or stale, with the reason for the last failure.
- Listeners that are called when a manager's content or status changes.
evaluate, a lookup that returns the value together with the reason and the context that matched.InMemoryAgeris, which gives tests the same managers an application uses, with values set from Java.
See the README for a guide with examples.
Coming from Merci
The configuration file format is unchanged. The coordinates, the package and some defaults are not; see Migrating from Merci to Ageris. The most important change: a configuration that cannot be loaded now holds back the update instead of being skipped.
Dependencies
Jackson 2.22, slf4j 2.0 and commons-io 2.22.
Not in this release
The web sample module is not published. It needs newer versions of Jersey and Jetty before it can run on Java 17.