Skip to content

Conversation

@Ladicek
Copy link
Member

@Ladicek Ladicek commented Jan 20, 2026

The application scope is now specified to be always active. This makes the specification simpler and more aligned with existing implementations, which already behave like that. (Registering custom application context either fails, in Weld, or is silently ignored, in ArC or OWB.)

Extensions are now specified to cause a deployment problem if registration of a custom context for an always active built-in scope is attempted. The dependent context was already specified as always active, and per previous paragraph, the application context is now as well. The spec never talks about the singleton context, but that is naturally also always active.

Further, build compatible extensions are now specified to allow registering custom contexts for the same set of scopes as portable extensions. Previously, registering custom contexts for built-in scopes was forbidden, which is too limiting and was never enforced (by the TCK and by implementations).

Fixes #925

@Ladicek Ladicek added this to the CDI 5.0 milestone Jan 20, 2026
@Ladicek Ladicek requested review from Azquelt and manovotn January 20, 2026 10:48
@Ladicek
Copy link
Member Author

Ladicek commented Jan 20, 2026

Ladicek added a commit to jakartaee/platform that referenced this pull request Jan 20, 2026
With the recent change in CDI itself [1], the application scope is now
always active. The CDI EE specification no longer has to specify this,
therefore this commit removes most of the specification text. What
remains is the specification of the payload of the event fired when
the application context is initialized or destroyed.

[1] jakartaee/cdi#927
@Ladicek
Copy link
Member Author

Ladicek commented Jan 20, 2026

For the record, what I propose to submit to the Jakarta Platform spec is at https://github.com/Ladicek/jakartaee-platform/commits/improve-app-scope/

An event with qualifier `@BeforeDestroyed(ApplicationScoped.class)` is synchronously fired when the application context is about to be destroyed, i.e. before the actual destruction.
An event with qualifier `@Destroyed(ApplicationScoped.class)` is synchronously fired when the application context is destroyed, i.e. after the actual destruction.

The application context is always active, since the event with qualifier `@Initialized(ApplicationScoped.class)` is fired and until the event with qualifier `@Destroyed(ApplicationScoped.class)` is fired.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I would rather frame the usability of the context by two events in which you can still use the context (@Initialized -> @BeforeDestroyed) but I suppose I can live with @Destroyed as well :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During @BeforeDestroyed notification, the application context is still guaranteed to be active. So I don't think you can say "until @BeforeDestroyed is fired", because that might be read as "during @BeforeDestroyed, the context is not active".

I think we could make it more precise by saying:

The application context is always active, starting after the event with qualifier @Initialized(ApplicationScoped.class) is fired and ending before the event with qualifier @Destroyed(ApplicationScoped.class) is fired.

WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though to be honest, we simply say

The @Dependent scope is always active.

and don't bother specifying when it starts and when it ends, even though there clearly are such points in time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During @BeforeDestroyed notification, the application context is still guaranteed to be active. So I don't think you can say "until @BeforeDestroyed is fired", because that might be read as "during @BeforeDestroyed, the context is not active".

I think we could make it more precise by saying:

The application context is always active, starting after the event with qualifier @Initialized(ApplicationScoped.class) is fired and ending before the event with qualifier @Destroyed(ApplicationScoped.class) is fired.

WDYT?

I was thinking something along the lines of:

The application context is always active beginning with firing of event with qualifier @Initialized(ApplicationScoped.class) up to and including the notification of @BeforeDestroyed(ApplicationScoped.class) observers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though to be honest, we simply say

The @Dependent scope is always active.

and don't bother specifying when it starts and when it ends, even though there clearly are such points in time.

The @Dependent scope doesn't fire the init/destroy events though. At least I think it doesn't as it won't make sense to observe those anyway 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm leaning towards this:

The application context is always active, starting before the event with qualifier @Initialized(ApplicationScoped.class) is fired and ending after the event with qualifier @BeforeDestroyed(ApplicationScoped.class) is fired.

This is because during notification of @Initialized(AppScoped.class), the app scope is already active, so it must start before. For symmetry, I'm saying it ends after @BeforeDestroyed(AppScoped.class). I could also say it ends "before @Destroyed(AppScoped.class)", but I don't like it as much and one could argue it would be somewhat less precise :-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry for nit picking but re-reading this section of the specification again, I am wondering if we aren't just re-stating the same thing.
The very few sentences prior to your addition already explain the events that mark the boundaries of app context lifecycle 🤔

What I mean is we already have this:

==== Application context lifecycle

The _application context_ is provided by a built-in context object for the built-in scope type `@ApplicationScoped`.

An event with qualifier `@Initialized(ApplicationScoped.class)` is synchronously fired when the application context is initialized.
An event with qualifier `@BeforeDestroyed(ApplicationScoped.class)` is synchronously fired when the application context is about to be destroyed, i.e. before the actual destruction.
An event with qualifier `@Destroyed(ApplicationScoped.class)` is synchronously fired when the application context is destroyed, i.e. after the actual destruction.

Copy link
Member Author

@Ladicek Ladicek Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you're fine with this just saying

The application scope is always active.

? :-)

That would be fine to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you're fine with this just saying

The application scope is always active.

? :-)

That would be fine to me.

Yes :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor

@manovotn manovotn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am in favor of this PR as it makes the spec text more concise and easier to read 👍

For the record, what I propose to submit to the Jakarta Platform spec is at https://github.com/Ladicek/jakartaee-platform/commits/improve-app-scope/

Seems good, I'd just remark in the PR that the wording implies no change in the actual behavior.

Comment on lines 35 to 36
* The application scope is always active, since the event with qualifier {@code @Initialized(ApplicationScoped.class)}
* is fired and until the event with qualifier {@code @Destroyed(ApplicationScoped.class)} is fired.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use of "since" here is wrong ("since" is usually for something that started in the past and is ongoing).

It also seems odd to say that the scope is always active and then place limits on when it's active.

Suggested change
* The application scope is always active, since the event with qualifier {@code @Initialized(ApplicationScoped.class)}
* is fired and until the event with qualifier {@code @Destroyed(ApplicationScoped.class)} is fired.
* The application scope is active from when the event with qualifier {@code @Initialized(ApplicationScoped.class)}
* is fired until the event with qualifier {@code @Destroyed(ApplicationScoped.class)} is fired.

I'm still thinking about the change as a whole, and I'm not totally happy with it yet, but I think this suggestion to fix the wording here is necessary if we do keep the change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also seems odd to say that the scope is always active and then place limits on when it's active.

Well, yes, I agree. However, we need to somehow set the limits on when you can start and stop using it while at the same time explaining that within those bounds it is just "always active".
I will gladly accept help of a native speaker in that regard :)

Also note the discussion we had earlier - #927 (comment)
I think we need to somehow merge that into the existing text...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just got rid of this complexity as agreed with @manovotn above :-) This part of javadoc now reads:

The application context is always active. It is destroyed when the application is shut down.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Azquelt what is your take on the current state of the PR? Does it look better this way?

Copy link
Member

@Azquelt Azquelt Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think "always" is too strong a requirement. At the very least the scope can only be active while the singular application context is active, and I would expect that some lifecycle events would occur before that.

In a tutorial or something, I think saying it's always active would be fine, but I'm not sure we can get away with that in the spec.

My second issue is that I'm still bothered by the fact that in an application server, there will probably be times where code is executing that's not part of an application and therefore there's no active application context on that thread, and therefore the scope is not active. Normally user code should not be running at this point, perhaps that's invisible to the user, but the reality of different applications being active on different threads makes the idea of saying that the scope is always active seem wrong.

I prefer the existing definition of when the application scope is active for Jakarta EE.

The application scope is now specified to be always active. This makes
the specification simpler and more aligned with existing implementations,
which already behave like that. (Registering custom application context
either fails, in Weld, or is silently ignored, in ArC or OWB.)

Extensions are now specified to cause a deployment problem if registration
of a custom context for an always active built-in scope is attempted.
The dependent context was already specified as always active, and per
previous paragraph, the application context is now as well. The spec never
talks about the singleton context, but that is naturally also always active.

Further, build compatible extensions are now specified to allow registering
custom contexts for the same set of scopes as portable extensions. Previously,
registering custom contexts for built-in scopes was forbidden, which is too
limiting and was never enforced (by the TCK and by implementations).
@Ladicek
Copy link
Member Author

Ladicek commented Jan 27, 2026

Added an extra commit that defines global scopes. I think that definition is reasonably precise, even to address @Azquelt's objections. To quote:

Several scopes defined by this specification are global.
A global scope has exactly one context object, which:

  • is activated once, before the event with qualifier @Initialized(ApplicationScoped.class) is fired,
  • is deactivated once, after the event with qualifier @BeforeDestroyed(ApplicationScoped.class) is fired,
  • is destroyed once, after it is deactivated.

A global scope is active on all application threads since activation of the context object and until deactivation of the context object.

Before merging the PR, I think I would prefer to squash the commits and reword the commit message, because these subjects are deeply intertwined.

@Azquelt
Copy link
Member

Azquelt commented Jan 27, 2026

I think that definition makes sense and I am happy with it.

I think this wording for the last sentence would be slightly better:

A global scope is active on all application threads from the point the context object is activated until it is deactivated.

@Ladicek
Copy link
Member Author

Ladicek commented Jan 28, 2026

I improved the definition of global scopes slightly. Activation and deactivation are operations that are not defined for all contexts, so I got rid of those. The new definition reads:

Several scopes defined by this specification are global.
A global scope has exactly one context object, which is:

  • initialized once, before the event with qualifier @Initialized(ApplicationScoped.class) is fired,
  • destroyed once, after the event with qualifier @BeforeDestroyed(ApplicationScoped.class) is fired,
  • active on all application threads from the point the context object is initialized until it is destroyed.

You'll note that the new definition says when the context object is active, not the scope. That is fine, because the scope is also active due to this provision: https://jakarta.ee/specifications/cdi/4.1/jakarta-cdi-spec-4.1.html#active_context

@Ladicek Ladicek force-pushed the improve-app-scope branch 2 times, most recently from 8afda17 to 4243913 Compare January 28, 2026 11:24
Certain built-in scopes are defined to be _global_ (_always active_
in previous parlance). A global scope has a single context object
and is active on all application threads for the duration of
the application.
@Ladicek
Copy link
Member Author

Ladicek commented Feb 10, 2026

I improved the definition of global scopes somewhat and squashed the extra commits into one (which is still to be squashed with the first commit in this PR).

There's still an illusion of a cycle between:

A global scope has exactly one context object, which is:

  • initialized once, when the container starts up, before the event with qualifier @Initialized(ApplicationScoped.class) is fired,

and

An event with qualifier @Initialized(ApplicationScoped.class) is synchronously fired when the application context is initialized.

I say illusion because the @Initialized event is actually fired after the initialization is complete, even though it doesn't say so explicitly. The text could be improved to read

An event with qualifier @Initialized(ApplicationScoped.class) is synchronously fired immediately after the application context is initialized.

But then there's a few more places that should be improved, and I didn't feel like attacking that problem right now. Perhaps I should.

CC @manovotn as agreed on the CDI call today :-)

Copy link
Contributor

@manovotn manovotn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I have no objections 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

when is the application scope active?

3 participants