fabric8io / fabric8

fabric8 is an open source microservices platform based on Docker, Kubernetes and Jenkins
http://fabric8.io/
1.76k stars 504 forks source link

provider a nicer DS-ish way to do configuration thats more toolable #110

Closed jstrachan closed 10 years ago

jstrachan commented 11 years ago

Note this idea probably won't be possible to use in vanilla standards based DS - it may require either a spec change; a custom DS implementation or some build time code generation:

But rather than DS way of doing

@Activate
public init(Map properties) { ...}

@Modified
public update(Map properties) { ...}

it'd be nice to be able to use typesafe property injection instead like either this...

@Property("foo.bar") 
public void setFoo(String foo) { ...}

// default the config key to ' bar'
@Property
public void setBar(int bar) { ... }

notice the easy natural programming model; works for initial or updates; and avoids having to mess around with thread safe maps and so forth.

Whats even cooler though is we can then enumerate easily via introspection all the properties that are injected, any Bean Validation annotations, javadoc comments etc; so we can then generate a json-schema document for the properties used - so we can then generate a nice auto-generated, typesafe UI for editting the properties!

One downside is there's no hook that @Activate or @Modified is completed though. I guess in those cases; where you really want to know all the properties have been set you could just add an @Activate / @Modified function too?

Or allow annotated parameters on @Activate / @Modified?

@Activate
public init(@Property("foo.bar") String foo, @Property int bar) { ...}

@Modified
public update(@Property("foo.bar") String foo, @Property int bar) { ...}

though its not very DRY though; there's copy/paste there really. So this is simpler of you want a 'post-init / post-update hook)

@Property("foo.bar") 
public void setFoo(String foo) { ...}

// default the config key to ' bar'
@Property
public void setBar(int bar) { ... }

@Activate
public init() {
   // all properties initialised!
}

@Modified
public update() {
  // all properties updated!
}
iocanel commented 11 years ago

Currently, you are able to annotation fields as properties, the existing @Property annotation even provides a label,a description and a default value that makes it also very tooling friendly.

There are 2 drawbacks though:

i) Properties are not injected, instead they are passed to @Activate and @Modified methods as a map. ii) We generally feed data to configuration admin, via text files and the type is often lost.

For (i) we could add a method to the AbstractComponent that could be invoked in the @Activate and @Modified methods to apply the configuration automatically (maybe with the aid of reflection?).

We could take it one step further and have a conversion mechanism, similar to the convertions used in blueprint to convert properties to the type of the annotated field?

gnodet commented 11 years ago

We could have a look at iPojo too. http://felix.apache.org/documentation/subprojects/apache-felix-ipojo/apache-felix-ipojo-gettingstarted/how-to-use-ipojo-annotations.html#property

sully6768 commented 11 years ago

At some point you should look at the BND annotations for Metatype support. There is an api that is shipped a part of it that will bind the Map to the Interface to ensure type safety.

https://github.com/sully6768/karaf-sandbox/blob/trunk/scr/examples/src/main/java/org/apache/karaf/scr/examples/component/MetaTypeManagedComponent.java#L50

https://github.com/sully6768/karaf-sandbox/blob/trunk/scr/examples/src/main/java/org/apache/karaf/scr/examples/component/MetaTypeManagedComponentConfig.java

This would also allow you to use the Metatype service for interface generation in hawt.io.

jstrachan commented 11 years ago

An injected configuration interface isn't a bad idea really; especially if its considered to be immutable and thread safe. Can it then be passed into @Activate / @Modified? Or is it injected as a @Reference?

Having @Property injection points on setters seems a bit more natural to typical Java folks (then its a stand alone bean with setters without a separate 'config' public interface); though I guess having an interface for configuration options is cleaner in general than beans with getters/setters really; i.e. there's on function call on activation/modification.

Plus this approach to configuration is kinda nice in the general DI sense - as folks can then just use constructor injection passing in the 'config properties' interface ;)

jstrachan commented 10 years ago

Anyone done a deep dive on iPojo to compare it to DS in terms of weight and functionality? I wonder if its worth giving iPojo a try?

My guts telling me DS plus the config admin @Property injection would probably be enough for us for now (and have minimal extra weight and the smallest migration path) but its worth doing a fair comparison I guess.

Ideally, one day, the 'portable runtime' - it'd be nice to try use standard annotations where possible; e.g. @Inject, @PostConstruct, @PreDestroy where possible, then a set of extra OSGi service / DS specific annotations when required (then folks could reuse the same bytecode with different DI libraries like CDI or spring). Though thats a reasonable amount of effort right now ;).

I guess there's lots of code where @Reference could be replaced with @Inject - apart from when things like cardinality is required.

jstrachan commented 10 years ago

OK @Property injection fields is now working if you extend AbstractFieldInjectionComponent like this example test case - the added benefit is a nice auto-generated Config Admin UI in hawtio too!

jstrachan commented 10 years ago

I've spun up a separate issue to deal with bugs I've seen for badly annotated @Reference (where bind/unbind were not correctly implemented) #521 other than that we can close this one now - thanks for all the feedback!