Skip to content

Releases: lichtsinn/ageris

Ageris 0.7.0

Choose a tag to compare

@lichtsinn lichtsinn released this 15 Oct 01:41

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 in modifiers. 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's value holds belongs to the application and is not checked.
  • A comment is allowed in modifiers, 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.awaitFirstAttempt waits until every manager has loaded or has failed once, where awaitFirstLoad waits 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.json is read like /featureflags.json. It used to be glued to the name of the application and end in "no such file".
  • A first-load-timeout that 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 while fail-on-first-load-failure is true. Zero together with fail-on-first-load-failure=false means 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-timeout is zero or negative, set it to a time longer than zero, or set fail-on-first-load-failure to false if the start should not wait.
  • A comment in modifiers needs 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

Choose a tag to compare

@lichtsinn lichtsinn released this 11 Oct 05:53

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: false lets it start anyway.
  • With Helidon's health support, a readiness check named ageris reports the managers: up when loaded or stale, down when one never loaded.
  • With ageris-openfeature and the OpenFeature SDK present, an AgerisProvider is available as well.
  • Bindings of policies and other options are set through an AgerisCustomizer; a ConfigurationFetcher of 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-openfeature and ageris-spring-boot-starter are 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

Choose a tag to compare

@lichtsinn lichtsinn released this 11 Oct 04:18

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=false lets it start anyway.
  • An AgerisCustomizer bean registers the bindings of policies and sets other options. A ConfigurationFetcher bean 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-openfeature and the OpenFeature SDK present, the starter offers an AgerisProvider bean.
  • 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.10000000000000001 was 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 ConfigurationContext no 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

Choose a tag to compare

@lichtsinn lichtsinn released this 11 Oct 03:03

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-core with 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 Ageris can 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 evaluate through 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.
  • isActive and getConfig are unchanged: they create no objects at all.

Also changed

  • ConfigurationContext.get is 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

Choose a tag to compare

@lichtsinn lichtsinn released this 10 Oct 23:48

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 contexts wins, 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

Choose a tag to compare

@lichtsinn lichtsinn released this 09 Oct 22:58

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.