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:
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
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?
// 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);
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.
To unsubscribe from this group and all its topics, send an email to microprofile...@googlegroups.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.
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?
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/1957b0d8-45e4-46ad-9ead-a40cc41ce758%40googlegroups.com.
@ConfigurationProperties(prefix="connection") public class SomeConfig { private String host;
public class ExampleConfiguration extends Configuration {
@NotNull
private String name;
@Min(1)
@Max(120)
private int age;
@JsonProperty
public String getName() {
return name;
}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.
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
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:9080And in your Database configuration you have myapp.some.soap.endpoint=http://{cluster.local}/some/endpointThis 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,strubPS: 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.
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/94e297f0-ed37-4b67-9371-dbc256918f07%40googlegroups.com.
| /** | |
| * 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(); |
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
--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/microprofile/201ab5ea-1aef-470f-b39d-a8f1339bf0a7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
public interface SomeConfig {
@Config( "com.example.host" )
String host();
@Config( "com.example.port" )
int port();
}
SomeConfig config = ConfigurationInjection.getConfigurationInjector()
.createTemplate(SomeConfig.class);
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
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
To view this discussion on the web visit https://groups.google.com/d/msgid/microprofile/5e932664-4ac3-42c6-80df-d2dbb70d866e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
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.
@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.
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>
</ej>
<source ordinal="500">http://shared:8080/config.xml</source>
<source ordinal="450">/cfg/myconf.json</source>
</config-sources>