Configuration API - type-safety and value validation

580 views
Skip to first unread message

Gunnar Morling

unread,
Oct 4, 2016, 8:04:39 AM10/4/16
to MicroProfile
Hi,

Similar to Alex' question on default values I'd like to get a discussion started around type-safety in a future configuration API (I realize it's just one small piece in the overall picture, but it's one close to my heart ;).

The API shown at JavaOne is not overly type-safe: It uses simple strings as keys; the value types as well as default values are assigned by the caller as far as I understand.

An interesting alternative would be to work with strongly typed configuration definitions instead. E.g. like so:

    @Config

    public interface SomeConfig {


        @Key( "com.example.host" )

        String host();


        @Key( "com.example.port" )

        int port();

    }


To read the configuration, there'd be a factory of sorts which creates an instance of such config contract, based on one or more config sources:


    SomeConfig someConfig = Config.get( SomeConfig.class )

        .withSource( new FileSource( "config.properties") )

        .withSource( new JvmOptionsSource() )

        .withSource( new EnvironmentVariablesSource() )

        .build;


Usage of the configuration would be fully type-safe by invoking methods on the config proxy object:


    String host = someConfig.host();

    int port = someConfig.port();


I.e. no mis-spelling in property names by config users or accidental casting of values into unforeseen types (behind the scenes there'd be a converter hierarchy as in other proposals; if specific values cannot be converted into the defined type, a meaningful error would be thrown).


Properties would be mandatory by default, if no value can be found, an exception will be raised. Of course there should be a notion of default values, for which Java 8 default methods come in handy (making this aspect a part of the property definition, which I think is desirable):


    @Config

    public interface SomeConfig {

        @Key( "com.example.port" )

        default int port() {

            return 80;

        }

    }


For cases where no meaningful global default can be given, java.util.Optional could be used in definitions:


    @Config

    public interface SomeConfig {

        @Key( "com.example.password" )

        Optional<String> password();

    }


And then:


    String password = someConfig.password().orElse( "top secret" );


Closely related to type-safety is validation of configured values. Bean Validation would be a great fit for this (Ok, I may be biased ;). Allowed values could be expressed using BV constraint annotations and actual values would be validated when reading from a config source:


   @Config

    public interface SomeConfig {


        @Key"com.example.host" )

        @Size(min=5)

        String host();


        @Key"com.example.port" )

        @Max( 65535 )

        int port();

    }


I think such a safe API would be preferable over approaches like get("key", SomeValueType.class). Defining a dedicated config contract provides a "repository" of all the foreseen properties, their types and legitimate values (which also is nice from a documentation PoV). To give credit where credit is due, this API design is strongly inspired by OWNER (see http://owner.aeonbits.org/), and surely there are others in that spirit, too.


Personally I'd welcome such an approach in a future config JSR very much (if not instead of the more untyped alternatives at least as an additional layer on top of them).


What do others think?


--Gunnar





Mark Struberg

unread,
Oct 4, 2016, 8:53:00 AM10/4/16
to MicroProfile
Owner is a nice approach. I'd even go one step further:
Use @ProcessAnnotatedType to pick all configs up and then you can do

private @Inject SomeConfig someConfig;

and be done.

Of course all this should imo be an additional layer on top of a core configuration mechanism. 

LieGrue,
strub

Werner Keil

unread,
Oct 4, 2016, 10:36:46 AM10/4/16
to MicroProfile
Another interesting one.

DropWizard and SeedStack (https://groups.google.com/forum/?hl=en#!topic/microprofile/NFTt-etG-60 here earlier) follow pretty much the same path.

Spring offers both. Either a "property" via @Configuration
http://docs.spring.io/spring-framework/docs/4.0.4.RELEASE/javadoc-api/org/springframework/context/annotation/Configuration.html
The PropertyResolver http://docs.spring.io/spring-framework/docs/4.0.4.RELEASE/javadoc-api/org/springframework/core/env/PropertyResolver.html is very close to some of the proposed method signatures Dmitry has in his slides.

However, at least via Spring Boot there's a second approach closer to those mentioned here:
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html

Spring offers both. Whether a Config standard should also do, I guess that's a good thing to discuss when the JSR was created.

Regards,
Werner

Gunnar Morling

unread,
Oct 6, 2016, 1:47:06 PM10/6/16
to MicroProfile
Mark,

When you say

> Of course all this should imo be an additional layer on top of a core configuration mechanism. 

What's the reasoning behind this? I.e. when would you - as a user - prefer the "core mechanism" over the described alternative?

--Gunnar


On Tuesday, October 4, 2016 at 2:53:00 PM UTC+2, Mark Struberg wrote:

Mark Struberg

unread,
Oct 6, 2016, 3:09:12 PM10/6/16
to MicroProfile
Hi Gunnar!

With 'outer layer' I don't necessary mean another spec. But it's a different part of the mechanism.
It's not very obvious, please let me explain:

A configuration mechanism get's used in different parts of the application. It is for example extremely useful to use it in CDI Extensions. By doing so you can create something like we did with Apache DeltaSpike @Exclude [1]

Think about having an application which uses a performance tuned hardcore Oracle nativeQuery for searching. Or course only if your customer uses Oracle as DB - otherwise you like to fall back using the generic JPQL queries. In that case you can e.g. write something like

@ApplicationScoped
@Specializes
@Exclude(onExpression="dbvendor!=oracle")
public class OracleOptimisedSearchService extends JpqlSearchService {

This @Exclude annotation gets handled via a CDI Extension [2] and will use ProcessAnnotatedType#veto() to hide the bean if the condition is met.

Ok so far?

Now you have the following problem: the configuration needs to be available during the CDI Container bootstrap already. That means the 'config nucleus' itself must only use JavaSE and _not_ Java EE! You e.g. cannot use CDI beans or CDI scanning for the core config mechanism. Because you could only use those _after_ the JavaEE container is fully booted! This 'config nucleus' is what I meant with the 'core layer'. 

But of course one could add more functionality on top of this core layer! E.g. use CDI ProcessAnnotatedType to pick up any @Config interfaces and automatically register Beans for them. We already do something very similar with our @MessageBundle annotation in DeltaSpike [3]. Replace the mechanism which loads the messages from a resource bundle with a mechanism which looks up the configuration and you get a result very similar to Owner [4] (txs to Romain Manni-Bucau for showing me btw).
Whether this mechanism is part of a 'config spec' or an own jar makes not difference - in either case this mechanism is built 'on top' of the config core.

Makes sense?

LieGrue,
strub


Werner Keil

unread,
Oct 6, 2016, 3:27:51 PM10/6/16
to MicroProfile
Keep in mind, that interacting with the JavaEE container or other Java EE technologies is not aimed at the Java EE 8 release of a spec.

So some of that is more likely to be in "another spec", the one for EE 9 or beyond.

It doesn't keep other projects like Microprofile, DeltaSpike, Tamaya, etc. from exploring that earlier.

Regards,
Werner

Mark Struberg

unread,
Oct 6, 2016, 3:31:55 PM10/6/16
to MicroProfile
No idea what you mean. We have ways to 'interact with the JavaEE container' since J2EE days already. 
And especially since CDI-1.0 it's something the normal user does as daily business.

Werner Keil

unread,
Oct 6, 2016, 3:49:23 PM10/6/16
to MicroProfile
Many solutions in the Microservice or Microprofile space are "serverless" so they don't need a container from the time of J2EE.

Dmitry's and others slides from JavaOne did not explicitly mention annotations, but "Integration with other Java EE frameworks" should also apply to CDI or trying to configure say a JPA data source etc. via that configuration mechanism. That would be too disruptive and several parts of Java EE (not just JMS) won't be touched in EE 8, so utilizing the configuration API defined in the first step is meant for EE 9.

HTH,
Werner 

Werner Keil

unread,
Oct 6, 2016, 3:54:15 PM10/6/16
to MicroProfile
JPA is a good example, there's most likely no change before trying to add NoSQL support. That  is clearly a Java EE 9+ goal.

Werner Keil

unread,
Oct 7, 2016, 7:41:18 AM10/7/16
to MicroProfile
While the excurse on how DeltaSpike or Spring use DI mechanisms on top of a config API may be interesting at a later point, it did not answer Gunnar's original question.

Using annotations does not mean using CDI.
Bean Validation itself is the best example. JPA also offers to use it, but that does not require CDI, both JPA and Bean Validation are also injectable and usable in a Spring context, so they have no dependency to CDI.

I mentioned DropWizard here earlier, and that really shows a realistic path for type-safe beans or POJOS with a validation option:
http://www.dropwizard.io/1.0.2/docs/manual/configuration.html

If you combine the new JSON-B JSR with Bean Validation you get pretty much what DropWizard does on top of Jackson and BV.
DropWizard out of the box also supports YAML files behind its type-safe beans.

If that's of interest and in scope for a config standard, it should be doable to combine the way DropWizard does it with other approaches like Typesafe etc.

Werner

Mark Struberg

unread,
Oct 7, 2016, 4:26:10 PM10/7/16
to MicroProfile
It doesn't matter how you turn it - there are problems with the dynamics. Be it the lifecycle or simply the missing contract. 
How would you implement e.g. an @Exclude mechanism if you have only an annotation to define the key and no programmatic interface?
The Extension itself does not know which config keys it will need - they are fully dynamic and defined by the customer business code and not the Extension jar!

Werner Keil

unread,
Oct 8, 2016, 4:17:51 PM10/8/16
to MicroProfile
It's just not the right thread to discuss that. extensions be it CDI or others were not what Gunnar asked about..

Try to keep the threads focused on a few particular areas and not hijack them for other topics. 

Mark Struberg

unread,
Oct 8, 2016, 11:20:15 PM10/8/16
to MicroProfile
Gunnar asked why one would need a programmatic API:

> What's the reasoning behind this?

And I gave 2 examles why it's a must. Didn't you understand my arguments? Do I need to explain it to you again?

Gunnar Morling

unread,
Oct 9, 2016, 4:01:07 AM10/9/16
to MicroProfile
All,

A friendly reminder: please let's keep the communication on a professional and friendly level and focused to finding great solutions *together* for the problems at hand.

I'm sure everyone is doing the best they can, and what may seem odd or even wrong from one side, may be very reasonable given the experiences and backgrounds of someone else. So let's discuss different options by all means but not waste time on "proofing" that we are smarter than others; I hope you agree that this is not possible nor desirable to begin with.

Back to the actual issue:

> Now you have the following problem: the configuration needs to be available during the CDI Container bootstrap already. 

I agree that the configuration API should be independent from CDI but I don't think that by itself speaks against using a typed approach as proposed. It wouldn't have to use CDI to detect @Config classes. That's not needed really, the user would ask for a given config type, e.g. like so: MyConfig c = Config.get(MyConfig.class) and the config mechanism can take it from there.

Exposure of config types via CDI is interesting, but that could be built on top of the config API, no matter whether it's typed or not.

> This @Exclude annotation...

That's a very interesting use case.

Indeed I see how you'd benefit from a generic API for obtaining configuration values in order to implement this mechanism. How will this handle types of values, though? I.e. in order to implement it, wouldn't it be be very useful to know the type of a config property referenced in an expression - so you can validate expressions and e.g. make sure they are only compared to values of the right type?

I.e. what I would suggest is to have a generic method Object getValue(String key) which is *based on* the typed configuration definition and returns the value converted to the type of the property definition. This would facilitate use cases of generic consumers such as the @Exclude extension while making sure values are always correctly typed.

--Gunnar

Mark Struberg

unread,
Oct 9, 2016, 6:14:31 AM10/9/16
to MicroProfile
Hi Gunnar!

I start to get what you mean. Intersting thoughts, let's play through it.
Each of the config approaches is loosely separated into two parts. The internal mechanism (SPI + impl) and the user facing API.

In the original proposal (basically the same in DeltaSpike, Tamaya and my JSR proposal) the API part defines the type (e.g. like in [1], [2]).
Otoh if the API does _not_ define the Type then we need to have the whole internal part know about it.
That is possible but would make the configuration itself more complicated, isn't? For each and every configuration key you would need to now maintain 2 values: the config value and the configured type. At least I have no other idea where to get the type information from. Any better idea which I missed?

In a properties file that could look like:

myproject.mymodule.some.remote.url=https://blablub.com/somesoap/endpoint/
myproject.mymodule.some.remote.url.javaType=java.lang.String
myproject.mymodule.some.remote.cacheTime=5000
myproject.mymodule.some.remote.url.javaType=java.lang.Long
myproject.mymodule.some.remote.operatingModes=eager;fast;immutable
myproject.mymodule.some.remote.url.javaType=com.myproject.custom.ConfigType

And I fear you have to maintain it in every single ConfigSource where you overwrite this key (as you cannot rely that any lower ordinal one is there).
It is certainly a valid option and might even allow to detect configuration format errors early on.
But otoh you duplicate the amount of things to configure. And you might have a remote system serving the configuration (e.g. Zookeeper or Consul) which might not know your own type (custom.ConfigType is not on it's classpath).
Granted the later is not an everyday use case, so this argument is most probably not a blocker.


The other argument which speaks rather against it is that you would need to have a hardcoded type case at the consumer in any way if the API only returns an Object:

CustomConfig cc = (CustomConfig) config.get("myproject.mymodule.some.remote.operatingModes");

I don't see much benefit over:
CustomConfig cc = config.get("myproject.mymodule.some.remote.operatingModes", CustomConfig.class);

Could you share some code ideas? Maybe I misunderstood what you meant or simply missed an elegant way to use it.

txs and LieGrue,
strub

Werner Keil

unread,
Oct 9, 2016, 11:07:20 AM10/9/16
to MicroProfile
It's worth looking at how cfg4j does that: http://www.cfg4j.org/releases/latest/#users-guide

 // Get info about our dog. When you add more friends in the configuration file, object below will automatically reflect that change (by mutating friends() list).
    ReksioConfig reksioConfig = configurationProvider.bind("reksio", ReksioConfig.class);

Is along the lines of that second example Mark mentioned in the recent post.

It also offers getProperty() methods on another API element, think it's called ConfigurationProvider here, too;-)

And CFG4J offers extensions via Spring, Guice, and I bet (since Guice is the RI of JSR 330) it should also work with CDI.

Those extensions are certainly worthwhile, but let's focus on the actual API first and not put the cart before the horse;-)

There's a good reason Dmitry prioritized those for a follow-up likely in Java EE 9. Should everything else be done sooner, I'm sure he's flexible, but we don't want to run into the same endless delays Java SE (9) is facing because of too big initial goals.

Regards,
Werner

Mark Struberg

unread,
Oct 9, 2016, 12:03:55 PM10/9/16
to MicroProfile
Oh there are c&p errors in my sample config
 
myproject.mymodule.some.remote.url=https://blablub.com/somesoap/endpoint/
myproject.mymodule.some.remote.url.javaType=java.lang.String
myproject.mymodule.some.remote.cacheTime=5000
myproject.mymodule.some.remote.cacheTime.javaType=java.lang.Long
myproject.mymodule.some.remote.operatingModes=eager;fast;immutable
myproject.mymodule.some.remote.operatingModes.javaType=com.myproject.custom.ConfigType

The 'javaType' is of course always 1:1 with the configured value itself. Note that is just a first idea how this _could_ get implemented (if we go down this road). 
Happy to read about your ideas.

LieGrue,
strub

Gunnar Morling

unread,
Oct 9, 2016, 1:34:18 PM10/9/16
to MicroProfile

Hi Mark,

Type information would always be derived from the configuration definitions:

    @Config
    public interface SomeConfig {

        @Key( "com.example.host" )
        String host();

        @Key( "com.example.port" )
        int port();
    }

Users/application code would obtain values from a proxy generated for such configuration type. Naturally the returned value will be of the right type. For the generic API (as used by the @Exclude extension) a metamodel would be built, essentially a Map<String, Method> which would allow the implementation of get("com.example.host") to be routed to SomeConfig#host(). There is no need to express an property's type in each property source, the sole source for type information are configuration declarations such as the SomeConfig interface and the properties it declares.

> The other argument which speaks rather against it is that you would need to have a hardcoded type case at the consumer in any way if the API only returns an Object

None generic code (i.e. user code dealing sich specific properties) would obtain values via Config.get(SomeConfig.class).host(). No casting required at all (if the given value can be converted property). That's the beauty of this approach really: the definition of a property's type (and validation rules) is pushed to a single declaration which can be queried in a safe way instead of letting clients of the config deal with that.

Generic code (as the @ Exclude extension) would not deal with properties in a specific way, hence be using the generic get() API and be doing its business in an essentially reflective way I reckon. It could process values based on the given type (which we could expose through some metadata API) but this would happen in a generic fashion, i.e. I wouldn't expect an assignment to a typed variable in such generic code).

--Gunnar


--
You received this message because you are subscribed to a topic in the Google Groups "MicroProfile" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/microprofile/JRJXHqXpHZA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to microprofile+unsubscribe@googlegroups.com.
To post to this group, send email to microp...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/microprofile/7c9e11ee-b125-4db9-827e-b93a8f87e0c0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Mark Struberg

unread,
Oct 9, 2016, 2:01:22 PM10/9/16
to MicroProfile
I totally get the use case with the configuration class. It's the same like Owner does (http://owner.aeonbits.org/docs/usage/).
This is really nice for some app cases, but not enough for advanced cases.

I'm also not quite sure that a Map<String, Method> will work. If you look at the @Exclude extension then you don't have any method. The configured value will get used in an EL like expression. There is no Method.

But apart from that: are we d'accord that there is a low-level part (getting the config from the various ConfigSources) and a highlevel part (filling the provided class/instance with the configured values) ?
And do you also agree that the highlevel part can easily built as a layer on-top of the lowlevel part? 
And further that it won't work the other way around?

If so, then how should the low level API look like in your opinion (when direct routing to Methods are out of the game)?

LieGrue,
strub
To unsubscribe from this group and all its topics, send an email to microprofile...@googlegroups.com.

Gunnar Morling

unread,
Oct 9, 2016, 2:34:40 PM10/9/16
to Mark Struberg, MicroProfile
2016-10-09 20:01 GMT+02:00 Mark Struberg <markst...@gmail.com>:
I totally get the use case with the configuration class. It's the same like Owner does (http://owner.aeonbits.org/docs/usage/).
This is really nice for some app cases, but not enough for advanced cases.

I'm also not quite sure that a Map<String, Method> will work. If you look at the @Exclude extension then you don't have any method. The configured value will get used in an EL like expression. There is no Method.

The way I'd implement that extension is to have some sort of parser which delegates to expression resolvers.

Such resolver would know how to obtain the value for a referenced property. In the case it's a configuration property it'd invoke the generic get() method (e.g. get("com.example.host")) and feed the returned value into the expression tree. The get() implementation would indeed look up method host() when "com.example.host" is referenced in an expression, invoke that method and return the result.

But apart from that: are we d'accord that there is a low-level part (getting the config from the various ConfigSources) and a highlevel part (filling the provided class/instance with the configured values) ?

Not sure about the terms low and high-level. But yes, I agree that there is need for a generic get() method to support the use case you describe.

And do you also agree that the highlevel part can easily built as a layer on-top of the lowlevel part? 
And further that it won't work the other way around?

Not quite, see above. But I think we are getting to an implementation level already :) My point is that it shouldn't be users of the config API who specify a type when obtaining a configuration value. Instead this information should be declared once and users will obtain the right type when fetching a specific variable, no matter how the exact API may look like.

I.e. something like get("com.example.host", URL.class) is the kind of API I suggest to avoid.
To unsubscribe from this group and all its topics, send an email to microprofile+unsubscribe@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.

Werner Keil

unread,
Oct 9, 2016, 6:05:02 PM10/9/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
Spring has 2 annotations for configuration classes, the one closer to what e.g. Gunnar mentioned earlier is

Something like
@ConfigurationProperties(prefix="connection")
public class SomeConfig {

    private String host;

The marker or base class/interface like Owner's is similar to DropWizard, 

 public class ExampleConfiguration extends Configuration {
     @NotNull
     private String name;

     @Min(1)
     @Max(120)
     private int age;

     @JsonProperty
     public String getName() {
         return name;
     }
Dropwizard already has Bean Validation support built in as well, so you're not the only one who likes using it ;-)


Am Sonntag, 9. Oktober 2016 20:34:40 UTC+2 schrieb Gunnar Morling:


2016-10-09 20:01 GMT+02:00 Mark Struberg <markst...@gmail.com>:
I totally get the use case with the configuration class. It's the same like Owner does (http://owner.aeonbits.org/docs/usage/).
This is really nice for some app cases, but not enough for advanced cases.

I'm also not quite sure that a Map<String, Method> will work. If you look at the @Exclude extension then you don't have any method. The configured value will get used in an EL like expression. There is no Method.

The way I'd implement that extension is to have some sort of parser which delegates to expression resolvers.

Such resolver would know how to obtain the value for a referenced property. In the case it's a configuration property it'd invoke the generic get() method (e.g. get("com.example.host")) and feed the returned value into the expression tree. The get() implementation would indeed look up method host() when "com.example.host" is referenced in an expression, invoke that method and return the result.

But apart from that: are we d'accord that there is a low-level part (getting the config from the various ConfigSources) and a highlevel part (filling the provided class/instance with the configured values) ?

Not sure about the terms low and high-level. But yes, I agree that there is need for a generic get() method to support the use case you describe.

And do you also agree that the highlevel part can easily built as a layer on-top of the lowlevel part? 
And further that it won't work the other way around?

Not quite, see above. But I think we are getting to an implementation level already :) My point is that it shouldn't be users of the config API who specify a type when obtaining a configuration value. Instead this information should be declared once and users will obtain the right type when fetching a specific variable, no matter how the exact API may look like.

I.e. something like get("com.example.host", URL.class) is the kind of API I suggest to avoid.

There's some (likely reflection) "magic" Owner applies when a config class has exactly the same name as a properties file in the classpath

>Since the properties file does have the same name as the Java class, and they are located in the same package, the >OWNER API will be able to automatically associate them.

All other cases use pretty much the same lookup/get mechanism:

ServerConfig cfg = ConfigFactory.create(ServerConfig.class);

Regards,
Werner

Gunnar Morling

unread,
Oct 12, 2016, 2:31:07 AM10/12/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
Hi,

It's always a good idea to let some code speak, so I created a very basic sketch of what I have in mind and pushed it to my GitHub account:


This contains two modules:


* microprofile-config: The public API

* kingconf: King Conf, an implementation of that API


You can see in https://github.com/gunnarmorling/mp-config-proposal/blob/master/kingconf/src/test/java/de/gunnarmorling/kingconf/test/KingConfTest.java how it is used.


typedAccess() shows how most config users will typically work with it.


genericAccess() shows how a generic client such as the @Exclude extension would use it. Notice that no class parameter of sorts is passed into the get() method, but the returned value still is of the correct type (e.g. int, boolean) as the implementation of get() makes use of the typed API internally. In the same way, such generic client would benefit from default value handling and value validation (as defined by the author of the config property).


I hope this clarifies the idea a bit. Feedback welcome!


--Gunnar


Mark Struberg

unread,
Oct 12, 2016, 5:13:52 AM10/12/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
Hi Gunnar!

A little bit of feedback.

The highlevel Sample and annotation is a start.
The low level part is intentionally missing, right?

Let's not talk about implementation but about use cases first. 

1.) A user doesn't care where his configured values come from. The usage of the configuration inside your application should be totally decoupled from actually setting up the configuration infrastructure and the task of filling in the values. 
With other words: An application doesn't care whether the value comes from a "test.properties" file, from the classpath, from a database, injected via Docker or even from Zookeeper or Consul.

2.) There might be many "test.properties" files on the ClassPath. They need a well defined behaviour.

3.) The Ops guys need a way to check the current configuration. They need to know exactly which properties are configured.

4.) The Ops guys also need a way to tweak the configuration. The 'how' is of course depending on the constellation of the setup. Might be a simple -Dmy.property=foo, setting an env entry 'export MY_PROPERTY=foo' or even a git push to a central server, etc.

5.) You need a way to cache values. If you have e.g. a heavily used REST endpoint with 500 r/s then you would loose lots of performance by querying the value from your underlying config system. Doing file IO 500 times per second for just a single usage is not really a good thing.

6.) We should support lookup paths. E.g. different configurations depending on the database vendor or whether certain features are activated in an installation.
So first you lookup e.g. a myapp.dbvendor and depending on that you lookup for e.g. myapp.jdbc.driver.oracle or myapp.jdbc.driver.postgresql

7.) Same as 6 but for the ProjectStage. 

8.) Support for Variable replacement. Example from my production: You have e.g. a single config you add via -Dcluster.local=localhost:9080 and on the other jvm you have -Dcluster.local=localhost:9080
And in your Database configuration you have myapp.some.soap.endpoint=http://{cluster.local}/some/endpoint
This is great to avoid to always go via the loadbalancer if you have to hit local soap services (e.g. served by another app on the same box).
It's also nice if you have a lot of different configs which go to the same remote host. In that case you only have to change a single configuration instead of dozen.

9.) We need a low level API for container integration as you cannot always compile a class for getting configured values. This is just not flexible enough.

10.) We like to prevent logging out passwords in cleartext...

11.) We might like to support on the fly decryption of info stored in a PKI.

I'm sure I missed quite a few use cases. That was just what came to my mind quickly.
Let's go through the use cases and see which of them your solution can solve.

LieGrue,
strub

PS: your genericTest only works because assertThat takes an Object. For any explicit type you would need a hardcoded cast, right? The genericTest also still needs MyConfig.class which is exactly the part you don't have in an Extension jar.

Mark Struberg

unread,
Oct 12, 2016, 5:20:02 AM10/12/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
Oh I did miss an important point: We need a way to pickup dynamic configuration changes without having to restart the whole application. 
E.g. if the underlying configuration changes (e.g. the content of test.properties on the disk, or a database table entry) then it should be available dynamically during runtime.
That doesn't have to be instantly, but it should work somehow.

There are 2 ways to implement that: 'push' or 'pull'. Not going into details but the 'pull' approach is much easier to implement. That migh explain why I have the cacheFor(TimeUnit, amount) in my proposal [1].

LieGrue,
strub

Emily Jiang

unread,
Oct 12, 2016, 5:29:27 AM10/12/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
I think the OWNER API defeated the purpose of ordering property files. The api directly maps to the specific property file. There is no override, no priority support.

As for the API mentioned in Dmitry's deck, I think it can satisfy a large number of the user cases but it is not quite type safe.
In order to be type safe, the annotation injection is a much preferred solution. I think both solutions should be supported. As far as the dynamic support, I think it is much easier to be achieved via the programmatic API rather than via injection.

I would suggest we create the API e.g. (similar to Mark and Dmitry's suggestions)
package io.microprofile.config;

public interface ConfigProvider {

    Config getConfig();
    ....
}

package io.microprofile.config;

public interface Config {

    String getPorperty(String name);
    String getProperty(String name, String defaultValue);
    <T> T getProperty(String key, Class<T> type);
}
Of course there are more apis to be created for ConfigSource to express the ordinal value.

Annotations: Based on Mark's example, I would expect the following usage of the property injection.
public class BeanProp {

    @Property(name="myproject.mymodule.some.remote.url") String url;
    @Property(name="myproject.mymodule.some.remote.cacheTime") Long cacheTime;
    @Property(name="myproject.mymodule.some.remote.operatingModes") com.myproject.custom.ConfigType opetingModes;
}
As per Mark's DeltaSpike @Exclude user case, I suggest no property injection should be supported. The property should be looked up via a system property lookup approach.

I think our approach should align with Dmitry's proposal. Thoughts?

My 2cents...
Emily

Gunnar Morling

unread,
Oct 12, 2016, 5:59:28 AM10/12/16
to Mark Struberg, MicroProfile, Gunnar Morling
Great feedback, Mark! Answers inline.

2016-10-12 11:13 GMT+02:00 Mark Struberg <markst...@gmail.com>:
Hi Gunnar!

A little bit of feedback.

The highlevel Sample and annotation is a start.
The low level part is intentionally missing, right?

No, not really. There is typed and generic access, which is what's needed IMO.
 
Let's not talk about implementation but about use cases first. 

+1 

1.) A user doesn't care where his configured values come from. The usage of the configuration inside your application should be totally decoupled from actually setting up the configuration infrastructure and the task of filling in the values. 
With other words: An application doesn't care whether the value comes from a "test.properties" file, from the classpath, from a database, injected via Docker or even from Zookeeper or Consul.

I see it a bit differently. As an application author I want to define the sources of configuration, i.e. I want to decide wether it's test.properties, some external service etc. I also want to be in charge of priority of these sources, it's not something which a framework could answer in a global fashion. YMMV.
 
2.) There might be many "test.properties" files on the ClassPath. They need a well defined behaviour.

+1 

3.) The Ops guys need a way to check the current configuration. They need to know exactly which properties are configured.

+1 

4.) The Ops guys also need a way to tweak the configuration. The 'how' is of course depending on the constellation of the setup. Might be a simple -Dmy.property=foo, setting an env entry 'export MY_PROPERTY=foo' or even a git push to a central server, etc.

Sure, there'd be config sources for all kinds of things, VM properties, environment variables etc. As said above I don't think it's needed to add additional sources at runtime (we are talking about microservices in a DevOps environment, so if a new source is needed, it can be added, tested and the app redeployed in a short time).
 
5.) You need a way to cache values. If you have e.g. a heavily used REST endpoint with 500 r/s then you would loose lots of performance by querying the value from your underlying config system. Doing file IO 500 times per second for just a single usage is not really a good thing.

+1 

6.) We should support lookup paths. E.g. different configurations depending on the database vendor or whether certain features are activated in an installation.
So first you lookup e.g. a myapp.dbvendor and depending on that you lookup for e.g. myapp.jdbc.driver.oracle or myapp.jdbc.driver.postgresql

How would that "support" look like? Isn't this something an app developer would just do in their code? 

7.) Same as 6 but for the ProjectStage. 

Could you elaborate on that a bit? 

8.) Support for Variable replacement. Example from my production: You have e.g. a single config you add via -Dcluster.local=localhost:9080 and on the other jvm you have -Dcluster.local=localhost:9080
And in your Database configuration you have myapp.some.soap.endpoint=http://{cluster.local}/some/endpoint
This is great to avoid to always go via the loadbalancer if you have to hit local soap services (e.g. served by another app on the same box).
It's also nice if you have a lot of different configs which go to the same remote host. In that case you only have to change a single configuration instead of dozen.

+1 

9.) We need a low level API for container integration as you cannot always compile a class for getting configured values. This is just not flexible enough.

That seems to be the main point of disagreement :)

I see how generic access is needed and that's why I added a way for it. I don't see a use case for working with properties which are not "known". That's my point: An application should declare the properties (and their types, defaults, validation rules) it plans to leverage. These defined properties may be accessed type or generically.

Maybe there are use cases where that's not sufficient, but I haven't seen them so far. You say it's not flexible enough, but the @Exclude extension can be implemented with the suggested means. What others do you have in mind?

10.) We like to prevent logging out passwords in cleartext...

+1 

11.) We might like to support on the fly decryption of info stored in a PKI.

+1. Probably would be subject of a specific config source? 

I'm sure I missed quite a few use cases. That was just what came to my mind quickly.
Let's go through the use cases and see which of them your solution can solve.

Great to focus on use cases. I didn't really mean to address all of them with that quick PoC I did. Rather to show how I'd envision type-safe and generic access to configured values in terms of an API. I think most of these requirements could be added easily.
 

LieGrue,
strub

PS: your genericTest only works because assertThat takes an Object. For any explicit type you would need a hardcoded cast, right?

Hum, not sure I'm following. The test is about to show that get() will return configured values converted into the type mandated by the definition of the property in question. E.g. "port" is an int. The test would fail if it didn't expect and int, or if the configured value couldn't be converted into an int or the property definition of port() would be of another type.
 
The genericTest also still needs MyConfig.class which is exactly the part you don't have in an Extension jar.

Yes, of course you don't know the MyConfig.class by name in that extension. But the application knows the list of @Config classes it wants to work with and can expose that information to generic consumers such as that extension.

IMO you don't want such an extension to access any possible configuration types and sources it can get hold of, but rather provide it with a specific set of config classes and sources relevant in a given context. In CDI this could be e.g. done via a simple producer method written by the user.
To unsubscribe from this group and all its topics, send an email to microprofile+unsubscribe@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.

Werner Keil

unread,
Oct 12, 2016, 6:09:41 AM10/12/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
Probably the best example of a solution doing this right now is Archaius.

It's a good point, but not in scope for the first phase of the config standard Dmitry was talking about (and should propose in the near future).

Werner

Emily Jiang

unread,
Oct 12, 2016, 8:27:07 AM10/12/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
I think my earlier attempt of posting replies was lost in transmission.... Here is what I thought:

What Mark proposed the user stories are good. I think generally it has the following three requirements:
1. allow the property files defined in different locations - inside app, outside app
2. provide a way to order the property files
3. cope with dynamic changes

I think we should agree on the API and annotations.

As for the API mentioned in Dmitry's deck, I think it can satisfy a large number of the user cases but it is not quite type safe.
In order to be type safe, the annotation injection is a much preferred solution. I think both solutions should be supported. As far as the dynamic support, I think it is much easier to be achieved via the programmatic API rather than via injection.

I would suggest we create the API e.g. (similar to Mark and Dmitry's suggestions)

1. API:

package io.microprofile.config;

public interface ConfigProvider {

    Config getConfig();
    ....
}

package io.microprofile.config;

public interface Config {

    String getPorperty(String name);
    String getProperty(String name, String defaultValue);
    <T> T getProperty(String key, Class<T> type);
}
Of course there are more apis to be created for ConfigSource to express the ordinal value.

2. Annotations:


Annotations: Based on Mark's example, I would expect the following usage of the property injection.
public class BeanProp {

    @Property(name="myproject.
mymodule.some.remote.url") String url;
    @Property(name="myproject.mymodule.some.remote.cacheTime") Long cacheTime;
    @Property(name="myproject.mymodule.some.remote.operatingModes") com.myproject.custom.ConfigType opetingModes;
}


I think our approach should align with Dmitry's proposal. Thoughts?

p.s. I think the OWNER API defeated the purpose of ordering property files. The api directly maps to the specific property file. There is no override, no priority support. The Archaius adds the dynamism on top of Apache Commons, right? Tamaya is still in incubating. Maybe we should agree on APIs and we choose which one is closer to our requirement and start contribution.

My 2cents...
Emily

Werner Keil

unread,
Oct 12, 2016, 4:15:15 PM10/12/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
Emily/all,

It's certainly possible to sketch an API under "io.microprofile", "org.eclipse.microprofile" etc. but the API or standard should arise from an Expert Group of a JSR, at least that's what Dmitry proposed at JavaOne.

APIs in other places would be stubs or placeholders. E.g. Tamaya also is separated into a dedicated API and "core/impl" part.

Where the actual standard could find its Reference Implementation, I guess that is still under discussion. Given everything officially proposed under Microprofile (mostly demos right now) is meant to use the Apache 2 License, its position for such RI is about as good or bad as Tamaya (and many other projects like Archaius, they are largely under the Apache license, too, OWNER seems different) or Apache Commons Config.

With few exceptions there is still a strong resistance at Oracle against the Apache License for parts of the "platform" (Java SE or EE)
Unless those were officially changed by Oracle and the Java (EE) platform architects, it probably won't work with an RI under Apache License.
Dmitry already leads a JSR with an Eclipse RI btw. so maybe there could be options in that direction (also since Microprofile has the intent to become an Eclipse project sooner or later)

About type-safety, I still don't see why everybody talks about DI or CDI annotations as a prerequisite?

There are different approaches mainly an API level "value holder" along the lines of Dmitry's Config element.
Based on his slides Dmitry intended that to be string-based, adding type-safety through marshalling or serialization.
Very much like JAX-B or JSON-B do this from the respective string formats.

Based on Dmitry's role in JSON-B that seems understandable.

Gunnar coming from another JSR (Bean Validation) has a different view.
The POJO bean binding solution seen in Dropwizard, Owner, etc.

Some frameworks offer both, especially Spring config or CFG4J for all I know.

Whether or not a configuration standard does both, that's up to the EG (ultimately the Spec Lead) to decide.

Support for dynamic properties like Archaius is something the spec only aims for in a follow-up release.
Keep in mind, Dmitry plans to get the Final release out in the course of 2017, so splitting the features like other JSRs (e.g. 375) do makes sense.

A more generic value holder similar to Archaius'
/**
* Base interface for Archaius properties. Provides common methods across all
* property implementations.
*
* @param <T> The value type of the property
*/
public interface Property<T> {
/**
* Get the latest value for the given property
*
* @return the latest property value
*/
T getValue();
[...]

is certainly worth discussing once a standard is formally proposed.

That's the place where there have to be agreements on API annotations or both.

The core API should not depend on any DI framework. Take all the existing successful solutions.
Archaius gets used together with Spring, Guice and pretty sure CDI somewhere, too.
So do most others like CFG4J or Dropwizard.

Regards,
Werner

Werner Keil

unread,
Oct 13, 2016, 5:18:52 AM10/13/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
Btw. as Owner was mentioned several times, some ideas may be good, but it contains code that was obviously copied from the JDK and other places.
Worse leaving the JDK bits in a package "sun.net.www..." means Owner will most likely fail under Java SE 9 at least these parts or everything that's calling them.

Not looking at possible legal and IP issues, I leave that to others;-)

Werner

Emily Jiang

unread,
Oct 13, 2016, 9:23:24 AM10/13/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
One of the missions of MicroProfile community is to prototype some ideas, e.g. Config. In this community, we can define the API and implement it. If it is useful, we can then influence the JSR community and get it adopted in EE stack if appropriate. The reason to discuss this config is purely driven by the microservice application needs. Since the Config JSR has not even started (planned for 2017) and there is an imminent need to have this Config, it is perfect for MicroProfile community to start working on it. Having said that, it is sensible to align with Dmitry's proposal so that this config can feed into the Config JSR.

As for API and annotation, it should be two separate things. API should not depend on the annotation. I think no one has proposed such a dependency. In order to improve the usage, I can see annotation might be quite appealing. Anyway, it can be addressed later.

I think the first step is to define the API. As for the package name, I think it makes more sense to start with io.microprofile. We can start the package name discussion in a separate thread.

Thoughts?
Emily

Werner Keil

unread,
Oct 13, 2016, 11:45:04 AM10/13/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
There is nothing wrong in doing those kinds of things in multiple places.

Time-wise, there is of course the ongoing pledge to join Eclipse Foundation. The only official artifacts here https://github.com/microprofile are demos and documentation.

I guess you could ask somebody to add stuff there now, but it will take more time for Eclipse IP lawyers to review if you do now.
Hard to say, how long the process takes till such proposal is out or accepted. That does not mean projects or repositories can't exist on GitHub, there are quite a few new Eclipse projects with GitHub as leading or only repository, but the IP regime should be the same as for other Eclipse projects regardless of a package name being "org.eclipse.microprofile" or "io.microprofile", that's mainly a shortcut, e.g. Polarsys or LocationTech also got separate TLDs and GitHub repositories like https://github.com/LocationTech.

Those of you at IBM or Red Hat probably have no big problem contributing to Eclipse, your companies already do in many places;-)
For everyone who has not done before, see this example from uDig https://github.com/locationtech/udig-platform/blob/master/CONTRIBUTING.textile and related links. Without a contributor agreement you won't be able to contribute code, so there's probably a good argument in waiting till Microprofile made that move and was accepted. Otherwise everything contributed by those who don't has to be thrown out or they need to sign it first.

Eclipse plans to change its IP regime, but until it's done with that (might take longer than JavaOne 2017 or the projected Java EE 8 release date, who knows;-) the existing one guides projects that contribute their code to Eclipse and plan to develop there.

Also see the thread by Wayne Beaton on how things are done at Eclipse.

Werner

Wayne Beaton

unread,
Oct 13, 2016, 12:20:41 PM10/13/16
to microp...@googlegroups.com

There is no reason to wait to contribute. You and your community can continue to make contributions to the project before and during the transition to the Eclipse Foundation.

Be sure to track provenance by ensuring that the contributors provide their author credentials in the Git commits (you're probably doing this anyway). Contributions don't just get rejected because they arrived before the project moved over to the Eclipse Foundation. The IP Team knows how to deal with this sort of stuff.

You may consider getting contributors to sign the Eclipse Contributor Agreement [1] now. Especially if there is some chance that connecting with a contributor later will be difficult.

FWIW, The Eclipse Foundation's intellectual property team are not lawyers. They are, however, experts in intellectual property management.

Wayne

[1] https://www.eclipse.org/legal/ECA.php
--
You received this message because you are subscribed to the Google Groups "MicroProfile" group.
To unsubscribe from this group and stop receiving emails from it, send an email to microprofile...@googlegroups.com.

To post to this group, send email to microp...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
Wayne Beaton
@waynebeaton
The Eclipse Foundation

Anatole Tresch

unread,
Oct 13, 2016, 2:28:19 PM10/13/16
to MicroProfile
Agreed. This feature is called a "Template" in Tamaya and is provided with the tamaya-injection module. There you would write:

    public interface SomeConfig {


        @Config( "com.example.host" )

        String host();


        @Config( "com.example.port" )

        int port();

    }


Then you can use:

SomeConfig config = ConfigurationInjection.getConfigurationInjector()
                             .createTemplate(SomeConfig.class);


Current Tamaya injection modules (both SE and CDI) do not yet support injection of these types, but this could be easily added.

J Anatole


Am Dienstag, 4. Oktober 2016 14:04:39 UTC+2 schrieb Gunnar Morling:
Hi,

Similar to Alex' question on default values I'd like to get a discussion started around type-safety in a future configuration API (I realize it's just one small piece in the overall picture, but it's one close to my heart ;).

The API shown at JavaOne is not overly type-safe: It uses simple strings as keys; the value types as well as default values are assigned by the caller as far as I understand.

An interesting alternative would be to work with strongly typed configuration definitions instead. E.g. like so:

    @Config

    public interface SomeConfig {


        @Key( "com.example.host" )

        String host();


        @Key( "com.example.port" )

        int port();

    }


To read the configuration, there'd be a factory of sorts which creates an instance of such config contract, based on one or more config sources:


    SomeConfig someConfig = Config.get( SomeConfig.class )

        .withSource( new FileSource( "config.properties") )

        .withSource( new JvmOptionsSource() )

        .withSource( new EnvironmentVariablesSource() )

        .build;


Usage of the configuration would be fully type-safe by invoking methods on the config proxy object:


    String host = someConfig.host();

    int port = someConfig.port();


I.e. no mis-spelling in property names by config users or accidental casting of values into unforeseen types (behind the scenes there'd be a converter hierarchy as in other proposals; if specific values cannot be converted into the defined type, a meaningful error would be thrown).


Properties would be mandatory by default, if no value can be found, an exception will be raised. Of course there should be a notion of default values, for which Java 8 default methods come in handy (making this aspect a part of the property definition, which I think is desirable):


    @Config

    public interface SomeConfig {

        @Key( "com.example.port" )

        default int port() {

            return 80;

        }

    }


For cases where no meaningful global default can be given, java.util.Optional could be used in definitions:


    @Config

    public interface SomeConfig {

        @Key( "com.example.password" )

        Optional<String> password();

    }


And then:


    String password = someConfig.password().orElse( "top secret" );


Closely related to type-safety is validation of configured values. Bean Validation would be a great fit for this (Ok, I may be biased ;). Allowed values could be expressed using BV constraint annotations and actual values would be validated when reading from a config source:


   @Config

    public interface SomeConfig {


        @Key"com.example.host" )

        @Size(min=5)

        String host();


        @Key"com.example.port" )

        @Max( 65535 )

        int port();

    }


I think such a safe API would be preferable over approaches like get("key", SomeValueType.class). Defining a dedicated config contract provides a "repository" of all the foreseen properties, their types and legitimate values (which also is nice from a documentation PoV). To give credit where credit is due, this API design is strongly inspired by OWNER (see http://owner.aeonbits.org/), and surely there are others in that spirit, too.


Personally I'd welcome such an approach in a future config JSR very much (if not instead of the more untyped alternatives at least as an additional layer on top of them).


What do others think?


--Gunnar





Anatole Tresch

unread,
Oct 13, 2016, 2:44:11 PM10/13/16
to MicroProfile
Hi all

I just add my 2 cents on the key format: in etcd meta-values are separated from "real" keys by starting with a '_'. I completely open about the scheme used finally, but I recommend heavily to have something that clerly separates real from meta-keys, e.g.

myproject.mymodule.some.remote.url=https://blablub.com/somesoap/endpoint/
_myproject.mymodule.some.remote.url.javaType=java.net.URL

or

myproject.mymodule.some.remote.url=https://blablub.com/somesoap/endpoint/
myproject.mymodule.some.remote.url._javaType=java.net.URL

The spec could additionally then define some of these special meta-keys and define their exact meaning and overriding policy to be applied, e.g.

FIELD       DESCRIPTION                                   MUTABILITY        ERROR BEHAVIOUR
_javaType   The fully qualified class name of the type.   Non overridable.  Deployment Error

This would allow to define the configuration types along the code that consumes it...

Werner Keil

unread,
Oct 13, 2016, 3:19:34 PM10/13/16
to MicroProfile
Should existing committers and project leads of other Eclipse projects also sign the Eclipse Contributor Agreement or is being a committer member (I signed an agreement for that a long time ago when I first contributed to Babel and a few others) sufficient?

In organizations like LocationTech it seems at least Andrea (Ross) helps on behalf of Eclipse Foundation with creating a new repository or similar tasks. If Emily, Anatole, Gunnar or others wanted to add a new repository for say "config" I guess they had to ask one of the admins or owners among https://github.com/orgs/microprofile/people

At least until that was formalized via some kind of steering committee or project leads after a successful transition.

Werner

Wayne Beaton

unread,
Oct 13, 2016, 3:42:42 PM10/13/16
to microp...@googlegroups.com

Signing the ECA is never wrong.

In general, the committer agreements apply to work done on specific projects on which the individual has been granted committer status. Somebody who is a committer on one Eclipse project should still sign the ECA to make contributions to other Eclipse projects.

We don't currently provide first class support for the notion of project-specific organizations on GitHub. We are working on it. In the meantime, we create repositories for Eclipse projects in the https://github.com/eclipse organization.

Wayne


For more options, visit https://groups.google.com/d/optout.

Emily Jiang

unread,
Oct 13, 2016, 5:34:35 PM10/13/16
to MicroProfile
Thank you Wayne for the clear explanation on the legal and transition process!
+1 on Werner's suggestion on creating "config" on  https://github.com/orgs/microprofile while waiting for transition. I'll work on the creation of this space for us to collaborate. I am so excited and confident that we can come up with an awesome config API shortly:o.

Thanks
Emily

Werner Keil

unread,
Oct 14, 2016, 7:33:40 AM10/14/16
to MicroProfile
I signed the membership agreement first for a particular project, but later I signed another one that had a "wildcard" for any possible project so I guess I should be good.

Thanks,
Werner

Gunnar Morling

unread,
Oct 15, 2016, 3:58:13 AM10/15/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org


On Wednesday, October 12, 2016 at 2:27:07 PM UTC+2, Emily Jiang wrote:
I think my earlier attempt of posting replies was lost in transmission.... Here is what I thought:

What Mark proposed the user stories are good. I think generally it has the following three requirements:
1. allow the property files defined in different locations - inside app, outside app
2. provide a way to order the property files
3. cope with dynamic changes

I think we should agree on the API and annotations.

As for the API mentioned in Dmitry's deck, I think it can satisfy a large number of the user cases but it is not quite type safe.
In order to be type safe, the annotation injection is a much preferred solution. I think both solutions should be supported. As far as the dynamic support, I think it is much easier to be achieved via the programmatic API rather than via injection.

I would suggest we create the API e.g. (similar to Mark and Dmitry's suggestions)

1. API:
package io.microprofile.config;

public interface ConfigProvider {

    Config getConfig();
    ....
}

package io.microprofile.config;

public interface Config {

    String getPorperty(String name);
    String getProperty(String name, String defaultValue);
    <T> T getProperty(String key, Class<T> type);
}

Personally I'm still doubtful about this API (which is why I started this thread to begin with).

It pushes handling of default values, types and validation to the *consumers* of the configuration which makes it much harder to apply consistent behaviour for these aspects across an application.

The proposed type-safe alternative approach pushes this responsibility to a single *declaration* of a configuration property and all consumers can benefit from that in a consistent way.
 
Of course there are more apis to be created for ConfigSource to express the ordinal value.

2. Annotations:

Annotations: Based on Mark's example, I would expect the following usage of the property injection.
public class BeanProp {

    @Property(name="myproject.
mymodule.some.remote.url") String url;
    @Property(name="myproject.mymodule.some.remote.cacheTime") Long cacheTime;
    @Property(name="myproject.mymodule.some.remote.operatingModes") com.myproject.custom.ConfigType opetingModes;
}

Instead of injection into fields I'd rather use methods as shown in my original mail. This gives much more flexibility for implementors, e.g. it doesn't require non-final fields which is nice from a concurrent usage PoV.
 
I think our approach should align with Dmitry's proposal. Thoughts?

At this point it's best to identify intended use cases and explore different approaches which seem appropriate.
 
p.s. I think the OWNER API defeated the purpose of ordering property files. The api directly maps to the specific property file. There is no override, no priority support. The Archaius adds the dynamism on top of Apache Commons, right? Tamaya is still in incubating. Maybe we should agree on APIs and we choose which one is closer to our requirement and start contribution.

I had foreseen the notion of builders to address overriding and priorities in my quick sketch. It's my believe that the developer of an enterprise application should decide the matter of ordering and the builder API would allow them to do so.

Chatting with Mark, he brought up the use case of 3rd party libraries and packaged apps which should be able to use the config mechanism.

In my picture the former could be addressed by passing the list of config sources as controlled by the embedding application when bootstrapping such lib. Packaged applications (which you e.g. buy and deploy on your own server) are a bit more tricky. Probably they'd have to expose some config on their own which allows to specify the list of config sources.

Werner Keil

unread,
Oct 18, 2016, 9:52:08 AM10/18/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org
Working with services based on Spring Framework and Spring Boot at the current client, those new services use various aspects of Spring Config.

With either very simple

@Configuration
@ConfigurationProperties("application")
public class ApplicationConfig {
   private String name;


   public String getName() {
     return name;
  }

   public void setName(String name) {
      this.name = name;
   }
etc.

Spring as in most other cases is based on JavaBeans getters and setters almost everywhere.

Backed in a simple case by a file named application.properties. For profiles like "dev", "test", etc. (similar to what e.g. DeltaSpike calls stage) this would be application-dev.properties, etc.

Custom types may also be used via annotations like

@Configuration
public class BeanConfig {
   @Bean
   public MyBeanClass aBean() {
...

That looks close to what Gunnar suggested using methods. Here Spring doesn't require getter/setter pairs for example.

While based on a mostly string-based property holder underneath, for end users/application developers Tamaya allows a somewhat similar experience
http://tamaya.apache.org/extensions/mod_cdi.html

@RequestScoped
public class ConfiguredClass{

    @Config
    private String name;

    @Config
    @ConfigDefault("${sys:java.version}")
    private String javaVersion2;

    @Config
    @ConfigDefault("5")
    private Integer int1;

    ...
Note the documentation may in some cases differ from the latest SNAPSHOT, but according to Anatole's recent JavaOne session the extension is fairly stable, I think he also did a quick demo of that.
It could be seen a little closer to Spring's @Bean annotations which are also required on every method (in addition to the class annotation @Configuration)

Both Spring and Tamaya among a few similar examples demonstrate, how an underlying API can be combined with Spring, CDI or other annotations.
And while I don't think this was done at Tamaya yet, I see no reason why Bean Validation should not work for such a case, too ;-)

Werner

Emily Jiang

unread,
Oct 18, 2016, 5:25:32 PM10/18/16
to MicroProfile, markst...@gmail.com, gun...@hibernate.org


On Saturday, October 15, 2016 at 8:58:13 AM UTC+1, Gunnar Morling wrote:


On Wednesday, October 12, 2016 at 2:27:07 PM UTC+2, Emily Jiang wrote:
I think my earlier attempt of posting replies was lost in transmission.... Here is what I thought:

What Mark proposed the user stories are good. I think generally it has the following three requirements:
1. allow the property files defined in different locations - inside app, outside app
2. provide a way to order the property files
3. cope with dynamic changes

I think we should agree on the API and annotations.

As for the API mentioned in Dmitry's deck, I think it can satisfy a large number of the user cases but it is not quite type safe.
In order to be type safe, the annotation injection is a much preferred solution. I think both solutions should be supported. As far as the dynamic support, I think it is much easier to be achieved via the programmatic API rather than via injection.

I would suggest we create the API e.g. (similar to Mark and Dmitry's suggestions)

1. API:
package io.microprofile.config;

public interface ConfigProvider {

    Config getConfig();
    ....
}

package io.microprofile.config;

public interface Config {

    String getPorperty(String name);
    String getProperty(String name, String defaultValue);
    <T> T getProperty(String key, Class<T> type);
}

Personally I'm still doubtful about this API (which is why I started this thread to begin with).

It pushes handling of default values, types and validation to the *consumers* of the configuration which makes it much harder to apply consistent behaviour for these aspects across an application.

The proposed type-safe alternative approach pushes this responsibility to a single *declaration* of a configuration property and all consumers can benefit from that in a consistent way.
 <ej> I think the API provides a way to retrieve configuration. As for whether or how to cater for type safe feature, it is down to consumers by either using API directly or using annotation to declare all properties in one class, as per your suggestion. Actually, it matches the configuration descriptor as per Dmitry's slide 42. One thing to consider is that the properties can be dynamically updated, e.g. the setter method should be called whenever the property value was changed in a file. I think the annotation adds complexity when dealing with properties' dynamic changes. Hence, I would like to focus on API first and then work on annotations (type safe feature).</ej>
 
Of course there are more apis to be created for ConfigSource to express the ordinal value.

2. Annotations:

Annotations: Based on Mark's example, I would expect the following usage of the property injection.
public class BeanProp {

    @Property(name="myproject.
mymodule.some.remote.url") String url;
    @Property(name="myproject.mymodule.some.remote.cacheTime") Long cacheTime;
    @Property(name="myproject.mymodule.some.remote.operatingModes") com.myproject.custom.ConfigType opetingModes;
}

Instead of injection into fields I'd rather use methods as shown in my original mail. This gives much more flexibility for implementors, e.g. it doesn't require non-final fields which is nice from a concurrent usage PoV.
 <ej> I'm ok with methods, which copes with dynamic changes better than fields.</ej>
I think our approach should align with Dmitry's proposal. Thoughts?

At this point it's best to identify intended use cases and explore different approaches which seem appropriate.
 
p.s. I think the OWNER API defeated the purpose of ordering property files. The api directly maps to the specific property file. There is no override, no priority support. The Archaius adds the dynamism on top of Apache Commons, right? Tamaya is still in incubating. Maybe we should agree on APIs and we choose which one is closer to our requirement and start contribution.

I had foreseen the notion of builders to address overriding and priorities in my quick sketch. It's my believe that the developer of an enterprise application should decide the matter of ordering and the builder API would allow them to do so.

Chatting with Mark, he brought up the use case of 3rd party libraries and packaged apps which should be able to use the config mechanism.

In my picture the former could be addressed by passing the list of config sources as controlled by the embedding application when bootstrapping such lib. Packaged applications (which you e.g. buy and deploy on your own server) are a bit more tricky. Probably they'd have to expose some config on their own which allows to specify the list of config sources.
 <ej>When defining datasource, you have specify the ordinal. As per Dmitry's slide 38,

<config-sources>
  <
source ordinal="500">http://shared:8080/config.xml</source>
  <
source ordinal="450">/cfg/myconf.json</source>
</
config-sources>

</ej>
Reply all
Reply to author
Forward
0 new messages