Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add .enabled/.active configuration properties to Hibernate ORM, Reactive and Envers extensions #27134

Merged
merged 13 commits into from
Aug 11, 2022

Conversation

yrodiere
Copy link
Member

@yrodiere yrodiere commented Aug 4, 2022

Fixes #17516, and goes a bit further.

In a previous PR (#26840), I reworked the Hibernate Search extension so that it exposes two configuration properties:

  • quarkus.hibernate-search-orm.enabled, to enable/disable the extension a build time, for all persistence units. Setting it to false will disable almost all build steps of the extension. This is mostly useful for re-augmentation of Quarkus apps, to disable an extension that is not necessary in a given deployment.
  • quarkus.hibernate-search-orm.active/quarkus.hibernate-search-orm."some-pu".active, to activate/deactivate the extension for a particular persistence unit. Setting it to false will prevent Hibernate Search from starting at runtime for the corresponding persistence unit. This is mostly useful when entities are annotated with Hibernate Search annotations (@Indexed) but for some reason one wants to disable Hibernate Search in a given persistence unit; that can happen e.g. when importing entity classes from a common company JAR, which is a surprisingly (and regrettably, if you ask me) common practice. Critically, this is also a runtime property, which I suppose can be useful for some exotic use cases.

I did look into making the .enabled property available on a per-persistence unit basis, but honestly, it's much more complicated, so I personally would avoid that until we have a convincing use case.

This PR adds similar configuration properties for other extensions:

  • For Hibernate ORM and Hibernate Reactive:
    • quarkus.hibernate-orm.enabled
    • quarkus.hibernate-orm.active/quarkus.hibernate-orm."some-pu".active
  • For Hibernate Envers:
    • quarkus.hibernate-envers.enabled
    • quarkus.hibernate-envers.active/quarkus.hibernate-envers."some-pu".active

In the case of Envers, unfortunately, .active is a build-time property. That's because Envers adds entities to the metamodel, and we cannot remove those at runtime (they are added during static init). Hopefully one day we'll be able to "veto" entities at runtime init through some SPI, but this SPI doesn't exist yet, so .active will have to remain a build-time property until then.

I noticed while working on the Envers extension that all configuration is global, with no way of customizing it on a per-persistence unit basis. I don't know if that's on purpose; maybe we should create another issue to address that?

yrodiere added 13 commits August 4, 2022 14:43
So that an application using only programmatic bean retrieval
will work correctly.

This is useful for our own tests, but I think it makes sense in general
too, and that's how Hibernate ORM's SessionFactory/Session beans are
defined too.
…onScoped

For the same reasons as Hibernate ORM:

1. So that it can be mocked
2. More importantly, so that it is instantiated on first use (i.e. at
   runtime) and so that we can throw an exception when the persistence
   unit was deactivated through configuration properties.
… build

We'll need these at runtime in the next few commits.
@quarkus-bot quarkus-bot bot added area/hibernate-orm Hibernate ORM area/hibernate-search Hibernate Search area/hibernate-reactive Hibernate Reactive area/panache area/persistence OBSOLETE, DO NOT USE labels Aug 4, 2022
@quarkus-bot
Copy link

quarkus-bot bot commented Aug 4, 2022

/cc @Sanne, @gsmet

Map<String, LazyPersistenceUnit> persistenceUnitsBuilder = new HashMap<>();
for (String persistenceUnitName : jpaConfigSupport.persistenceUnitNames) {
persistenceUnitsBuilder.put(persistenceUnitName, new LazyPersistenceUnit(persistenceUnitName));
for (PersistenceUnitDescriptor descriptor : PersistenceUnitsHolder.getPersistenceUnitDescriptors()) {
Copy link
Member

Choose a reason for hiding this comment

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

We are sure that this will always be set properly? IIRC we ended up with injection because we had some race conditions. But it might have been with an old pattern and things might have changed.

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 don't know about that race condition and I don't see any failing test, so I can't say...

What I can say:

If for some reason tests start failing because the constructor of JPAConfig ends up being executed during static init (and before PersistenceUnitsHolder is initialized), we can make JPAConfig a synthetic bean and force runtime init. Or, worst case, we can make JPAConfig @ApplicationScoped, so that it's created on first use.

In fact, I can create a quick PR to make JPAConfig a synthetic bean right now. It seems more correct, regardless of whether it's necessary, because that bean is fundamentally a runtime bean. I'll open that PR soon.

Copy link
Member

@gsmet gsmet left a comment

Choose a reason for hiding this comment

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

I added a question. If you're sure things are fine, you can merge it.

@yrodiere yrodiere merged commit 9a772a8 into quarkusio:main Aug 11, 2022
@quarkus-bot quarkus-bot bot added this to the 2.12 - main milestone Aug 11, 2022
@quarkus-bot quarkus-bot bot added the kind/enhancement New feature or request label Aug 11, 2022
@yrodiere
Copy link
Member Author

Merged, thanks! I'll submit another PR to make the JPAConfig change safer.

@yrodiere
Copy link
Member Author

yrodiere commented Aug 11, 2022

Actually, after a bit of digging, Singleton beans are initialized when they are first injected or retrieved programmatically, just like ApplicationScoped beans:

@Override
public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
Objects.requireNonNull(contextual, "Contextual must not be null");
Objects.requireNonNull(creationalContext, "CreationalContext must not be null");
InjectableBean<T> bean = (InjectableBean<T>) contextual;
if (!Scopes.scopeMatches(this, bean)) {
throw Scopes.scopeDoesNotMatchException(this, bean);
}
return (T) instances.computeIfAbsent(bean.getIdentifier(), new Supplier<ContextInstanceHandle<?>>() {
@Override
public ContextInstanceHandle<?> get() {
return createInstanceHandle(bean, creationalContext);
}
}).get();
}

So as long as we only use inject/retrieve JPAConfig at runtime, we should be safe. And if we start using injecting/retrieving it during static init, in particular before PersistenceUnitsHolder is initialized, we'll get a nice exception:

private static void checkJPAInitialization() {
if (persistenceUnits == null) {
throw new RuntimeException("JPA not initialized yet by Quarkus: this is likely a bug.");
}
}

Which is better (well, at least more specific) than what we'd get if we attempted to use a runtime-initialized, synthetic CDI bean:

private String createMessage(String name, SyntheticBeanBuildItem bean) {
StringBuilder builder = new StringBuilder();
builder.append("Synthetic bean instance for ");
builder.append(bean.configurator().getImplClazz());
builder.append(" not initialized yet: ");
builder.append(name);
if (!bean.isStaticInit()) {
builder.append("\n\t- a synthetic bean initialized during RUNTIME_INIT must not be accessed during STATIC_INIT");
builder.append(
"\n\t- RUNTIME_INIT build steps that require access to synthetic beans initialized during RUNTIME_INIT should consume the SyntheticBeansRuntimeInitBuildItem");
}
return builder.toString();
}

So, I'll leave JPAConfig as it is now.

@yrodiere yrodiere deleted the orm-enabled-active branch October 28, 2022 08:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/hibernate-orm Hibernate ORM area/hibernate-reactive Hibernate Reactive area/hibernate-search Hibernate Search area/panache area/persistence OBSOLETE, DO NOT USE kind/enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

supporting of hibernate.integration.envers.enabled property
2 participants