Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [config-dev] Proposal for CDI integration

On Fri, Jul 2, 2021 at 8:32 AM Laird Nelson <ljnelson@xxxxxxxxx> wrote:
On Thu, Jul 1, 2021 at 4:48 PM David Lloyd <david.lloyd@xxxxxxxxxx> wrote:
Please review the entire proposal and, when replying, limit quoting to
the specific points where you have questions or comments.

I think and work slowly (on purpose) but will look at this in detail after the upcoming holiday weekend.  My first thought is that we need more elucidation of the roles involved in this proposal (and any others like it) and why the given proposed constructs are worthy of specification.  JNDI can be a useful guide here.

I will not be able to make the Thursday, July 8 meeting.

I personally can't comment on the details of this proposal yet because mentally I'm still only at the foundations under it (I'm slow). I can say at least the following, staying deliberately at a high level, though not as articulately as I'd like, and I hope it's useful.

One of the assumptions this proposal is founded on—and the assumption is neither good nor bad, it just "is"—is that this configuration service as proposed will at some level be a root object/coalescing type acquisition service that supplies a root object/coalescing type to the component developer when asked.  That is, the component developer in this proposal would, I presume, typically use the service to acquire a strongly-typed root object or node representing basically the whole configuration and then use that root object or node to further acquire what she needs.

(Please bear in mind I am speaking at a high level here: of course there may be various bits and pieces the component developer has to supply during acquisition, knobs she has to turn, etc. but at the end of this process my understanding is she receives some kind of strongly typed object that is unique to her application and that represents its configuration.  If she wants the configured port of the Frobnicator subcomponent of her application, she calls rootObject.getFrobnicator().getPort(), or some such recipe.)

For a real world example, this is very much in the spirit of how DropWizard works [0].  I am not ascribing any value good or bad to this product or its approach; it just smells the same in this regard.

(Contrast this with, say, MicroProfile Config's early days, where scalars were deliberately chosen to be the atomic units for their simplicity, and "coalescing types" were deliberately left off the table (even though you can't really avoid them, and they sort of came back in 2.0).  David's proposal at least makes the root object/coalescing type framing explicit: we can discuss whether the root object breaks down further (i.e. does it actually need to coalesce anything?), but it seems that in this proposal the coin of the realm is a strongly-typed Java object representing the strongly-typed configuration of an application.)

(I think it's very important to decide as a specification group whether we want to fundamentally specify this kind of root object/coalescing type acquisition subsystem, or fundamentally specify a scalar-fetching-and-converting subsystem (I have my opinions but they are not important right now).  The APIs and supporting cruft will look very different in these two different cases.)

So, again, leaving the details aside, we have a proposal where the component developer says "get me the root thing", and then magic happens of some variety that is out of the component developer's control, as it should be, and she now has the data she needs sourced from wherever it was sourced from and she carries on.  Fine.

One of the nice things about this kind of root object acquisition service approach where the strongly typed root object is fundamentally what the component developer uses is that the application assembler can now take delivery of black box components and may not need to know anything about the namespace the components' developers are using, because the component developers in this scenario aren't really using a namespace at all.  That is, the component developer says at the highest possible level, "get me the configuration object", not "get me the value for the configuration key named 'a.b'".  In the latter case, the assembler might need to perform a namespace translation from "a.b" to her own application server's environment namespace (maybe "a.b" maps to "c.d"; for examples of this sort of thing look at group-to-role mapping in Jakarta EE).  Of course, that presupposes the assembler can somehow learn or discover that the component is asking for "a.b", which is now another thing you have to specify.

Anyway, if you can dodge this kind of namespace translation, then you can also dodge what necessarily follows: now the assembler may not need to introspect the .war file at all for the names it uses.  I don't have any proof, but I suspect from reading the tea leaves that this is one of the reasons DropWizard went with this approach.  I seem to recall that resource adapter archives used Java Beans (complete with BeanInfo etc.) for this sort of thing for basically the same reasons, but don't quote me on that.

Finally, the application deployer needs to know how to set up the non-Java stuff so that the root object that the component developer is asking for gets "made" in the proper way from it.  Historically, in Java EE, the deployer works with proprietary application server tooling to do this sort of thing, which begs the question here: why would we standardize how the root object is made?  Which role's pain do we alleviate?  (That's a genuine question, not a "clearly the answer is we shouldn't"-flavored question.)

Maybe all we have to standardize here is how the component developer acquires the root object?  Perhaps, in other words, GlassLibertySphere wants to mandate that there are three and only three supported textual formats from which a root object can be made, and WildLogicFlyTail says that some kind of optimized binary format is the only way that a root object can be made in their ecosystem.  Why would we want to mandate that these products come together in this area?  The deployer is already (supposed to be) an expert in her platform's tooling, so she's going to keep doing what she's used to and what the platform permits.  In this thought experiment, do any of these have any effect on the component developer?  No, they do not, or at least not that I can see.  Maybe there's some aspect of the application assembler's pain I'm overlooking?

Anyway, I think even if we decide ultimately to specify how the root object (or any coalescing type) must be made from raw materials (thus reducing this area of application servers to a commodity) we should at least entertain the possibility that this could be completely left up to vendors.  But I'm always a fan of underspecifying, so I freely admit my bias is showing here: I believe you specify "hard" where the component developer is involved and "less hard" where the other roles are involved, since they are inherently coupled to the (chaotic) external environment.

Finally, in terms of prior art, we might want to look at JNDI (no! wait! don't leave! stay with me! :-)) because it took great care to specify how a root object/coalescing type might be acquired without mandating how it is assembled [1].  For example, from a Context you can ask for an Object of any type bound to any name, and then it is up to the installed ObjectFactory implementation in effect for that name in that JNDI implementation to synthesize the resulting Object out of raw materials in any way it sees fit.  The component developer doesn't know about any of this, of course, and the JNDI implementation's expert is the only one who cares about how stuff is bound into the Context tree (a nice separation of roles and specification/implementation split—note that almost nothing is specified about how an ObjectFactory must work).

The API is dated and clunky and Frankenstein, sure, but maybe the concepts are worth exploring, since a root object/coalescing type acquisition service is basically a read-only naming service with very few (perhaps zero) names.  Also, given that at least as of this writing according to the platform specification JNDI must be a part of any Jakarta EE profile [2], we might at least try to understand what tradeoffs were made in its design in this area only.

David, thanks very much for your thoughtful writeup.


[0]:; note in particular: "Each Application subclass has a single type parameter: that of its matching Configuration subclass." That's the root object/coalescing type.

Back to the top