Skip to content

Commit

Permalink
feat(core): Exit Archaius, Enter Apache Commons Config2
Browse files Browse the repository at this point in the history
Remove Archaius support, since it's outdated and unsupported;
Add Apache Commons Config2 support to fill the gap

Since Archaius is based on (I think) Apache Commons Configure v1, most of the concepts overlap. I've added a new ConfigurationBuilder called CompositeConfigurationBuilder which allows for the combination of both ConfigurationBuilder instances and Configuration instances in a single configuration builder, to facilitate the move.

BREAKING CHANGE: Archaius support is removed, including `ConfigurationManager()`
  • Loading branch information
erwinw committed Jan 26, 2022
1 parent 8c1f374 commit c58945a
Show file tree
Hide file tree
Showing 23 changed files with 496 additions and 211 deletions.
82 changes: 42 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,52 +8,49 @@ It is made of 4 modules that when combined give you a full, flexible and powerfu

## Usage

If you're okay with using Guice, Archaius and Jackson, add a dependency on `prop-all` (Gradle):
If you're okay with using Guice, Apache Commons Config2 and Jackson, add a dependency on `prop-all` (Gradle):

```groovy
implementation "io.pleo:prop-all:2.0.0"
```

All you need is to initialize the `AutoPropModule` by passing it all of the Guice Modules you'd like it to scan for `Prop<X>` dependencies.

```java
List<Module> modules = ...
AutoPropModule autoPropModule = new AutoPropModule("io.pleo", // Package prefix
modules,
new ArchaiusPropFactory(),
new JacksonParserFactory());
modules.add(autoPropModule);
Guice.createInjector(modules);
All you need is to initialize the `AutoPropModule` by passing it all the Guice Modules you'd like it to scan for `Prop<X>` dependencies.

```kotlin
val modules: List<Module> = ...
val autoPropModule = AutoPropModule(
"io.pleo", // Package prefix
modules,
CommonsConfigPropFactory(),
JacksonParserFactory(),
)
modules.add(autoPropModule)
Guice.createInjector(modules)
```

And then you can simply add a `@Named("myPropName") Prop<X>` to your class, like this

```java
public class MyServiceClient {
private Prop<String> serviceUrl;

// The @Named annotation is required. A detailed exception will be thrown when
// bootstrapping the Guice injector if it is missing.
@Inject
public MyServiceClient(@Named("service.url") Prop<String> serviceUrl) {
this.serviceUrl = serviceUrl;
}
And then you can add a `@Named("myPropName") Prop<X>` to your class, like this:

```kotlin
// The @Named annotation is required. A detailed exception will be thrown when
// bootstrapping the Guice injector if it is missing.
class MyServiceClient @Inject constructor(
@Named("service.url") Prop<String> private val serviceUrl: Prop<String>
) {
// ...
public Health getHealth() {
return Unirest.get(serviceUrl.get() + "/rest/health").asObject(Health.class).getBody();
}
fun getHealth(): Health =
Unirest.get(serviceUrl.get() + "/rest/health")
.asObject(Health.class)
.getBody()
}
```

You can also use default values using the `@Default` annotation

```java
public class MyThing {
@Inject
public MyThing(@Default("localhost"@Named("service.host") Prop<String> serviceHost) {
...
}
```kotlin
class MyThing @Inject constructor(
@Default("localhost") @Named("service.host") val serviceHost: Prop<String>
) {
// ...
}
```

Expand All @@ -71,13 +68,13 @@ As well as all types that can be deserialized by Jackson.

### How does it work

`AutoPropModule` will scan all of the modules that you provide and will find all InjectionPoints that require a `Prop<X>` instance.
`AutoPropModule` will scan the modules that you provide and will find all InjectionPoints that require a `Prop<X>` instance.

It will determine the type parameter of the `Prop<X>` and dynamically generate a parser for this `Prop<X>`.

It will then initialize an Archaius property based on the `@Named` annotation and the parser.
It will then initialize a property based on the `@Named` annotation and the parser.

Finally it dynamically binds this new `Prop<X>` instance in Guice. Guice does the rest of the magic.
Finally, it dynamically binds this new `Prop<X>` instance in Guice. Guice does the rest of the magic.

## The Modules

Expand All @@ -89,25 +86,30 @@ The classes that your application will use. It has no dependencies and is extrem

The Google Guice integration. Will automatically detect all `Prop<X>` that are required by your application and bind these to the right `Prop<X>` instance.

### prop-archaius
### prop-commons-config

The Netflix Archaius integration. Will fetch `Prop<X>` values using Archaius which can be configured to read properties from many many configuration repositories.
The Apache Commons Config integration. Will fetch `Prop<X>` values using [Apache Commons Config v2](https://commons.apache.org/proper/commons-configuration) which can be configured to read properties from many configuration repositories.

The `CommonsConfigPropFactory` takes an optional ConfigurationBuilder argument which defines how the configuration
is read. By default `config.properties` is read from the class path and read; many other options exist; see the [Commons Config v2 User Guide](https://commons.apache.org/proper/commons-configuration/userguide/user_guide.html).

### prop-jackson

The Jackson integration. Allows using serialized JSON as `Prop<X>` values so you can use `Prop<MyComplexObject>` as long as `MyComplexObject` can be desrialized from a JSON String.
The Jackson integration. Allows using serialized JSON as `Prop<X>` values, so you can use `Prop<MyComplexObject>` as long as `MyComplexObject` can be deserialized from a JSON String.

The `JacksonParserFactory` optionally takes a ObjectMapper instance as an argument; by default a vanilla ObjectMapper is set.

## Extending

You can easily customize the behavior of prop. The two main extension points are `PropFactory` and `ParserFactory`.

### PropFactory

`PropFactory` takes a property name and a parse function and must return a `Prop<X>`. The default implementation is `ArchaiusPropFactory`.
`PropFactory` takes a property name, a parse function and a default value, and must return a `Prop<X>`. The default implementation is `CommonsConfigPropFactory`.

### ParserFactory

`ParserFactory` takes a `java.reflect.Type` and returns a `java.util.Function` that can transform a `String` into an instace of the right Type. The default implementation is `JacksonParserFactory`.
`ParserFactory` takes a `java.reflect.Type` and returns a lambda that can transform a `String` into an instance of the right Type. The default implementation is `JacksonParserFactory`.

### Other

Expand Down
2 changes: 1 addition & 1 deletion prop-all/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ dependencies {
api project(':prop-core')
api project(':prop-guice')
api project(':prop-jackson')
api project(':prop-archaius')
api project(':prop-commons-config')
}
15 changes: 13 additions & 2 deletions prop-all/src/test/kotlin/io/pleo/prop/PropTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.google.inject.Module
import com.google.inject.PrivateModule
import com.google.inject.assistedinject.FactoryModuleBuilder
import io.mockk.mockk
import io.pleo.prop.archaius.ArchaiusPropFactory
import io.pleo.prop.commonsconfig.CommonsConfigPropFactory
import io.pleo.prop.guice.AutoPropModule
import io.pleo.prop.guice.internal.FailedToCreatePropException
import io.pleo.prop.guice.internal.RequiredNamedAnnotationException
Expand Down Expand Up @@ -45,6 +45,14 @@ class PropTest {
}
)

val mappedEnumExpected = mapOf(
ComplexObjects.ParsingStage.PENDING_HUMAN_REVIEW to "This is pending human review",
ComplexObjects.ParsingStage.UNDER_HUMAN_REVIEW to "This is under human review",
ComplexObjects.ParsingStage.OUTPUT to "This is output",
ComplexObjects.ParsingStage.FINISHED to "This is finished",
ComplexObjects.ParsingStage.ERROR to "This is error",
)

val complexObjects = injector.getInstance(ComplexObjects::class.java)
val complexObjectPropValue = complexObjects.myComplexObjectProp()
assertThat(complexObjectPropValue.name).isEqualTo("Rush B")
Expand All @@ -59,6 +67,9 @@ class PropTest {

val stringPropValue = complexObjects.myStringProp()
assertThat(stringPropValue).isEqualTo("awp")

val mappedEnumValue = complexObjects.myMappedEnumProp()
assertThat(mappedEnumValue).isEqualTo(mappedEnumExpected)
}

@Test
Expand Down Expand Up @@ -286,7 +297,7 @@ class PropTest {
val autoPropModule = AutoPropModule(
"io.pleo",
modules.toList(),
ArchaiusPropFactory(),
CommonsConfigPropFactory(),
JacksonParserFactory(),
)

Expand Down
13 changes: 12 additions & 1 deletion prop-all/src/test/kotlin/io/pleo/prop/objects/ComplexObjects.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,15 @@ class ComplexObjects @Inject constructor(
@Named("io.pleo.test.prop1") val withConstructor: Prop<InjectedObjectWithConstructor>,
@Named("io.pleo.test.prop2") val myListOfComplexObjectProp: Prop<List<InjectedObject>>,
@Named("io.pleo.test.prop3") val myStringProp: Prop<String>,
)
@Named("io.pleo.test.prop7") val myMappedEnumProp: Prop<Map<ParsingStage, String>>,
) {
enum class ParsingStage {
UPLOADED,
OCR,
PENDING_HUMAN_REVIEW,
UNDER_HUMAN_REVIEW,
OUTPUT,
FINISHED,
ERROR,
}
}
9 changes: 8 additions & 1 deletion prop-all/src/test/resources/config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,11 @@ io.pleo.test.prop2=[{"name":"dustII", "age":3}, {"name":"inferno", "age":16}]
io.pleo.test.prop3=awp
io.pleo.test.prop4=usp
io.pleo.test.prop5=invalid$!json%}{
io.pleo.test.prop6=100.00
io.pleo.test.prop6=100.00
io.pleo.test.prop7={\
"PENDING_HUMAN_REVIEW":"This is pending human review",\
"UNDER_HUMAN_REVIEW":"This is under human review",\
"OUTPUT":"This is output",\
"FINISHED":"This is finished",\
"ERROR":"This is error"\
}
9 changes: 0 additions & 9 deletions prop-archaius/build.gradle

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

9 changes: 9 additions & 0 deletions prop-commons-config/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
description = 'Apache Config Configuration 2 implementation of PropFactory'

dependencies {
api project(':prop-core')
api group: 'org.apache.commons', name: 'commons-configuration2', version:'2.7'
api group: 'commons-beanutils', name: 'commons-beanutils', version:'1.9.4'

testImplementation group: 'com.google.guava', name: 'guava', version:'21.0'
}

0 comments on commit c58945a

Please sign in to comment.