What CDI 2.0 could do for Java Configuration?

77 views
Skip to first unread message

Antoine Sabot-Durand

unread,
Jun 13, 2014, 1:48:12 PM6/13/14
to java-...@googlegroups.com
A few weeks ago I launched the idea to base Java config on some CDI SPI part. This post tries to go deeper in this idea by describing the rough idea and trying to expose the pros and cons of such an approach.

What this is about?

CDI is the only specification (to my knowledge) that provides a SPI allowing it to analyse, modify or create from scratch meta data (annotations) on Java Classes. Right now (in CDI 1.x) this can be done during CDI container startup using the extension mechanism.
To make short CDI provides the following interfaces: AnnotatedType, AnnotatedField, AnnotatedMethod, AnnotatedConstructor and AnnotatedParameter that allow an application to override annotations existing in a given class.More details can be found in CDI spec in chapter 11.4.
In CDI 1.x using these metadata manipulation interfaces force the application to stick to CDI lifecycle by supporting all CDI API, activating CDI in app, create an extension and listen to the following CDI events : BeforeBeanDiscovery, ProcessAnotatedType and AfterTypeDiscovery. So it's obvious that this mechanism in its current form cannot be easily used by another specification.
 The idea is to explore the possibility of reorganizing these SPI interfaces to make them easily usable by third party spec like Java Config

What should we provide in CDI 2.0?
  To ease the use of these metadata interfaces we should bring 2 main evolutions in CDI 2.0
  1. Introduce CDI modularity and a part dedicated to type discovery:
    by creating "parts" in CDI we could have smaller API & implementations that could be used by other spec / framework. One of these parts could be a "Type Discovery" part including the above interfaces and mechanism to inspect and modify them. this part could be bootstrapped a very early (why not a java agent for Java SE?) to collect metadata information and could live without all the others CDI high level stuff.
  2. Give access to these metadata at anytime:
    Right now the only way to inspect a meta data is to create a CDI extension and put an observer on one of the AnnotatedType related event. We need to provide a way to retrieve these metadata at runtime thru a metadata container. This will allow third party spec / Framework to access these information without having to stick to CDI lifecycle.
These evolutions are being discussed right now and the interest of Java Config regarding them weight significantly in the balance to decide if we go for them or not.
  
Proof Of Concept
Today the best proof of concept is probably Seam Config. As stated in an other thread it wasn't included in CDI 1.1 for all kind reasons regarding information storage mainly (see CDI-123). It's true that xml format is not the universal answer to this need but we could specify something more abstract to allow different file format (different flavor of XML / JSON), DB storage or using Mix-in classes like it is done in framework like Jackson.  
  
What is not covered here
This idea inspired by CDI spec doesn't cover the deployment descriptor part of configuration or the conditional configuration driven by business rule at runtime. But we could imagine evolution in the future that could fill these use case as well.

Conclusion
I'm sure we can find a lot of objection regarding this idea, the first being that it can't be the only answer to all configuration use cases. But I really think we should explore this approach to try to re-invent what is already existing and lost this nice opportunity to bring more consistency to Java EE.


Anatole Tresch

unread,
Jun 13, 2014, 7:21:17 PM6/13/14
to java-...@googlegroups.com
Hi Antoine

there are many things in your post. I just start picking one also lets see if other add their cents... ;-)

One thing that would be very nice, if I could programmatically bootstrap my container. This would be very easy to define the features I need and additionally also is very useful for mocking. Given your ideas above and the proposed high level componentization I see similarities with other containers, where I basically require two aspects:
  • a way to define components (and optionally perhaps also modules)
  • a way of controlled explicit interaction between them (synchronously or asynchronously)
So looking at your proposal I would see something like 
  • a container managing modules, whereas modules exist exactly once per container (I know OSGI also has versioning support here, but maybe we should keep it simple here)..
  • the container should provide some interfaces to register additional modules.
  • modules can expose components (beans), either statically or contextually.
  • the event layer basically is also nothing more than a shared module, providing synchronous and asynchronous communication features.
  • also interception and decoration can be seen as another module.
  • basic DI basically is nothing more than a module that scans the classpath for components and auto-registers them. So basic DI knows about classes, constructors, methods, annotations etc. Hereby basic DI can define some interception mechanisms to intercept injection.
  • contextual aspects could be added as a module that implements these interception mechanism.
So given that I coud write something like this:

  CDI container = new CDI.Builder().addModules(CDIInitialConfig.class, EventServices.class, ComponentContainer.class, InterceptionServices.class, DIScanner.class,
                                               ContextualServices.class).create();

Hereby the modules can be easily used as adapters, so the CDI container can also be programmatically configured. Given that I could for example easily setup a minimal container, even without component scanning:

  Container container = new Container.Builder().addModules(CDIInitialConfig.class, EventServices.class, ComponentContainer.class)
                                   .loadModules().create();
    ComponentContainer compCont = container.getModule(ComponentContainer.class);
  compCont.addPackage("com.mycomp.test").addBeans(MyBean.class, MyMockedBean.class, MyBean2.class);
  compCont.startup();

   
The above would allow me to setup very lean and modular containers as required, and also be able to share them across multiple ears/web applications in a EE context. A similar mechanism could be used to combine containers, e.g.

  Container container2 = new Container.Builder().addModules(CDIInitialConfig.class, EventServices.class, ComponentContainer.class)
                                   .loadModules().create();
  
ComponentContainer compCont = container.getModule(ComponentContainer.class).addParentContainer(container);
  container2.startup();

Summarizing we would get an incredible flexible container. Many of the features, e.g. the Service SPI in Weld would basically match these concepts. Beside that this infrastructure would allow us to build up a configuration layer in an early system stage, it would also greatly simplify testing, because I could easily create the runtime container as needed for my tests. Beside a common contract for modules, each module can define its own API, e.g. the ComponentContainer.class can provide meta-data on the components known, ...
Of course, thinking on such things requires several other topics to be considered like classloader isolation or modules/component isolation in general. Also versioning of modules/components could be a topic, but it is arguable if we really want to rebuild thw whole OSGI ;-) Beside that I think the basic complexity should be manageable. What also would be considered is backward compatibility...

And of course, such a flexible container infrastructure would also be usable on small devices and as a base for configuration...

So these are my spontanous ideas. Hope other will also provide their cents (e.g. Antonio ;-) ).

Best,
Anatole
    
 




Kevin Sutter

unread,
Jun 16, 2014, 4:45:49 PM6/16/14
to java-...@googlegroups.com
Hi,
Good ideas.  But, as I am reading through these notes, I keep wondering about re-inventing the OSGi wheel.  Anatole brought up the same question twice in his post:

>  (I know OSGI also has versioning support here, but maybe we should keep it simple here.)
Of course, thinking on such things requires several other topics to be considered like classloader isolation or modules/component isolation in general.  Also versioning of modules/components could be a topic, but it is arguable if we really want to rebuild the whole OSGI ;-)


I'm just bringing this up to show that many of these topics have previously been addressed by OSGi.  Granted, if there are issues with the OSGi approach and how it might integrate with Java EE, then let's discuss those issues.  But, as Anatole said, let's not re-invent the whole OSGi.

Thanks,
Kevin


--
You received this message because you are subscribed to the Google Groups "java-config" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-config...@googlegroups.com.
Visit this group at http://groups.google.com/group/java-config.
To view this discussion on the web visit https://groups.google.com/d/msgid/java-config/31609139-7744-4c5b-8087-1687a89cb458%40googlegroups.com.

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

Anatole Tresch

unread,
Jun 16, 2014, 7:13:48 PM6/16/14
to
Hi,

I think we do not need OSGI ;-), I do another, simpler example to illustrate this. Basically what we need a minimal DI container providing
@Inject (consuming things), @Produces (providing things). This minimal container must provide well defined hooks, 
where additional functionality can be added:
  • The startup process will load the modules into the Container, e.g. using a @Module (or whatever name) annotation. @Priority annotations can be used for ordering /overriding of components.
  • The container may send out @Initialized, @Destroyed events using a ModuleDescriptor. The ModuleDescriptor can be used for doing the wiring for subsequent logic.
  • After all modules are loaded and prepared (wired) we can then send out AllModulesInitialized event, which then can trigger the startup of the CDI container:
    • Loading portable extensions
    • Scanning annotations and load beans
    • Setting up/wiring all the other aspects, such as events, contexts, interceptors, decorators, ...

Given that we can define allmost all services with default DI mechanisms, e.g.:

@Module
public class EventQueueModule{

    public void addEventHook(@Observes AnnotatedMethod method){
        // todo register method if its an observer
    }
    @Produces
    public Event<?> getEventQueue(InjectionPoint injectionPoint){
        // todo - implement Event to publish events to listeners synchronously/asynchronously
        return null;
    }
    @Produces @Asynchronous
    public Event<?> getEventQueue(InjectionPoint injectionPoint){
        // todo - implement Event to publish events to listeners synchronously/asynchronously
        return null;
    }

    public void moduleLoaded(@Observes @Initialized ModuleDescriptor desc){
       // wire up
    }

}

Consequently functionalities can easily operate, either by depending on each other directly, or by sending synch/asynch events, Also modules can
define SPIs that may be implemented by external modules, e.g. a BeanContainer module could provide an interception hook:

public static interface MethodInterceptor{
   InvocationContext intercept(InvocationContext ctx);
}


And another module (or multiple ones with priorities) then can implement it:

@Module
public class InterceptorModule{

    private static final MethodInterceptor NO_INTERCEPTOR = new MethodInterceptor(){
        public InvocationContext intercept(InvocationContext ctx){
            return ctx;
        }
    };

    @Produces
    @Priority(10)
    MethodInterceptor getInterceptor(){
        return NO_INTERCEPTOR;
    }

}

Given such a Container, e.g. with a ConfigurationManager loaded, we can then combine this with subsequent containers:
  • combining containers, hereby using the configuration container as a component delegate.
  • Exposing the ConfigurationManager or Configuration for the concrete ear/war only as a component, e.g. by using a producer bean.

So basically the bootstrap container provides only a small set of operations and a well defined (simple) component setup phase. This would be still very flexible but much leaner than OSGI ;-) Though one would be able to implement OSGI or more advanced features, such as versioned beans, on top of that. Nevertheless these are only my personal ideas (could even be that something crucial is missing here), I would hold on and see how Antoine's ideas look like in more detail..

Cheers,
Anatole

To unsubscribe from this group and stop receiving emails from it, send an email to java-config+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages