Ignoring artifacts when running re-aug to isolate the current and new stores

Closes #20974
This commit is contained in:
Pedro Igor 2023-07-03 11:27:26 -03:00
parent 8cc04a6724
commit bde57ca839
7 changed files with 79 additions and 51 deletions

View file

@ -30,47 +30,41 @@ import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaAutoFlushListe
import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaEntityVersionListener; import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaEntityVersionListener;
import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaOptimisticLockingListener; import org.keycloak.models.map.storage.jpa.hibernate.listeners.JpaOptimisticLockingListener;
import java.util.Objects;
/** /**
* Adding listeners to Hibernate's entity manager for the JPA Map store. * Adding listeners to Hibernate's entity manager for the JPA Map store.
*/ */
public class EventListenerIntegrator implements Integrator { public class EventListenerIntegrator implements Integrator {
public static String JPA_MAP_STORAGE_ENABLED = "kc.jpa-map-storage-enabled";
@Override @Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor, public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor,
SessionFactoryServiceRegistry sessionFactoryServiceRegistry) { SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {
if (Objects.equals(sessionFactoryImplementor.getProperties().get(JPA_MAP_STORAGE_ENABLED), Boolean.TRUE.toString())) { final EventListenerRegistry eventListenerRegistry =
final EventListenerRegistry eventListenerRegistry = sessionFactoryServiceRegistry.getService(EventListenerRegistry.class);
sessionFactoryServiceRegistry.getService(EventListenerRegistry.class);
if (metadata.getDatabase().getDialect() instanceof CockroachDialect) { if (metadata.getDatabase().getDialect() instanceof CockroachDialect) {
// CockroachDB will always use serializable transactions, therefore no optimistic locking is necessary // CockroachDB will always use serializable transactions, therefore no optimistic locking is necessary
metadata.getEntityBindings().forEach(persistentClass -> { metadata.getEntityBindings().forEach(persistentClass -> {
if (persistentClass instanceof RootClass) { if (persistentClass instanceof RootClass) {
RootClass root = (RootClass) persistentClass; RootClass root = (RootClass) persistentClass;
root.setOptimisticLockStyle(OptimisticLockStyle.NONE); root.setOptimisticLockStyle(OptimisticLockStyle.NONE);
root.setVersion(null); root.setVersion(null);
root.setDeclaredVersion(null); root.setDeclaredVersion(null);
} }
}); });
// If the version column should be updated with an incrementing number on each change in the future, // If the version column should be updated with an incrementing number on each change in the future,
// implement a listener similar to JpaOptimisticLockingListener to increment it. // implement a listener similar to JpaOptimisticLockingListener to increment it.
} else { } else {
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, JpaOptimisticLockingListener.INSTANCE); eventListenerRegistry.appendListeners(EventType.PRE_INSERT, JpaOptimisticLockingListener.INSTANCE);
eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, JpaOptimisticLockingListener.INSTANCE); eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, JpaOptimisticLockingListener.INSTANCE);
eventListenerRegistry.appendListeners(EventType.PRE_DELETE, JpaOptimisticLockingListener.INSTANCE); eventListenerRegistry.appendListeners(EventType.PRE_DELETE, JpaOptimisticLockingListener.INSTANCE);
}
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, JpaEntityVersionListener.INSTANCE);
eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, JpaEntityVersionListener.INSTANCE);
eventListenerRegistry.appendListeners(EventType.PRE_DELETE, JpaEntityVersionListener.INSTANCE);
// replace auto-flush listener
eventListenerRegistry.setListeners(EventType.AUTO_FLUSH, JpaAutoFlushListener.INSTANCE);
} }
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, JpaEntityVersionListener.INSTANCE);
eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, JpaEntityVersionListener.INSTANCE);
eventListenerRegistry.appendListeners(EventType.PRE_DELETE, JpaEntityVersionListener.INSTANCE);
// replace auto-flush listener
eventListenerRegistry.setListeners(EventType.AUTO_FLUSH, JpaAutoFlushListener.INSTANCE);
} }
@Override @Override

View file

@ -420,9 +420,6 @@ public class JpaMapStorageProviderFactory implements
logger.warnf("Database %s used without lockTimeout option configured. This can result in deadlock where one connection waits for a pessimistic write lock forever.", databaseShortName); logger.warnf("Database %s used without lockTimeout option configured. This can result in deadlock where one connection waits for a pessimistic write lock forever.", databaseShortName);
} }
// Pass on the property to 'EventListenerIntegrator' to activate the necessary event listeners for JPA map storage
properties.put(EventListenerIntegrator.JPA_MAP_STORAGE_ENABLED, Boolean.TRUE.toString());
logger.trace("Creating EntityManagerFactory"); logger.trace("Creating EntityManagerFactory");
ParsedPersistenceXmlDescriptor descriptor = PersistenceXmlParser.locateIndividualPersistenceUnit( ParsedPersistenceXmlDescriptor descriptor = PersistenceXmlParser.locateIndividualPersistenceUnit(
JpaMapStorageProviderFactory.class.getClassLoader() JpaMapStorageProviderFactory.class.getClassLoader()

View file

@ -5,5 +5,6 @@ public class ClassLoaderOptions {
public static final Option<String> IGNORE_ARTIFACTS = new OptionBuilder<>("class-loader-ignore-artifacts", String.class) public static final Option<String> IGNORE_ARTIFACTS = new OptionBuilder<>("class-loader-ignore-artifacts", String.class)
.category(OptionCategory.GENERAL) .category(OptionCategory.GENERAL)
.hidden() .hidden()
.buildTime(true)
.build(); .build();
} }

View file

@ -121,7 +121,6 @@ import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Comparator; import java.util.Comparator;
import java.util.Enumeration; import java.util.Enumeration;
@ -129,7 +128,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Properties; import java.util.Properties;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -166,7 +164,7 @@ class KeycloakProcessor {
private static final Map<String, Function<ScriptProviderMetadata, ProviderFactory>> DEPLOYEABLE_SCRIPT_PROVIDERS = new HashMap<>(); private static final Map<String, Function<ScriptProviderMetadata, ProviderFactory>> DEPLOYEABLE_SCRIPT_PROVIDERS = new HashMap<>();
private static final String KEYCLOAK_SCRIPTS_JSON_PATH = "META-INF/keycloak-scripts.json"; private static final String KEYCLOAK_SCRIPTS_JSON_PATH = "META-INF/keycloak-scripts.json";
private static final List<Class<? extends ProviderFactory>> IGNORED_PROVIDER_FACTORY = Arrays.asList( private static final List<Class<? extends ProviderFactory>> IGNORED_PROVIDER_FACTORY = List.of(
JBossJtaTransactionManagerLookup.class, JBossJtaTransactionManagerLookup.class,
DefaultJpaConnectionProviderFactory.class, DefaultJpaConnectionProviderFactory.class,
DefaultLiquibaseConnectionProvider.class, DefaultLiquibaseConnectionProvider.class,
@ -319,12 +317,6 @@ class KeycloakProcessor {
final Optional<String> lockTimeoutConfigValue = getOptionalValue("spi-map-storage-jpa-lock-timeout"); final Optional<String> lockTimeoutConfigValue = getOptionalValue("spi-map-storage-jpa-lock-timeout");
lockTimeoutConfigValue.ifPresent(v -> unitProperties.setProperty(AvailableSettings.JAKARTA_LOCK_TIMEOUT, v)); lockTimeoutConfigValue.ifPresent(v -> unitProperties.setProperty(AvailableSettings.JAKARTA_LOCK_TIMEOUT, v));
final ConfigValue storage = getKcConfigValue(StorageOptions.STORAGE.getKey());
if (storage != null && Objects.equals(storage.getValue(), StorageOptions.StorageType.jpa.name())) {
// if JPA map storage is enabled, pass on the property to 'EventListenerIntegrator' to activate the necessary event listeners for JPA map storage
unitProperties.setProperty(EventListenerIntegrator.JPA_MAP_STORAGE_ENABLED, Boolean.TRUE.toString());
}
unitProperties.setProperty(AvailableSettings.QUERY_STARTUP_CHECKING, Boolean.FALSE.toString()); unitProperties.setProperty(AvailableSettings.QUERY_STARTUP_CHECKING, Boolean.FALSE.toString());
String dbKind = defaultDataSource.getDbKind(); String dbKind = defaultDataSource.getDbKind();

View file

@ -22,14 +22,19 @@ import static org.keycloak.quarkus.runtime.Environment.isDevMode;
import static org.keycloak.quarkus.runtime.cli.Picocli.println; import static org.keycloak.quarkus.runtime.cli.Picocli.println;
import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.getAllCliArgs; import static org.keycloak.quarkus.runtime.configuration.ConfigArgsConfigSource.getAllCliArgs;
import org.keycloak.config.ClassLoaderOptions;
import org.keycloak.config.OptionCategory; import org.keycloak.config.OptionCategory;
import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.Messages; import org.keycloak.quarkus.runtime.Messages;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
import io.quarkus.bootstrap.runner.QuarkusEntryPoint; import io.quarkus.bootstrap.runner.QuarkusEntryPoint;
import io.quarkus.bootstrap.runner.RunnerClassLoader; import io.quarkus.bootstrap.runner.RunnerClassLoader;
import io.quarkus.runtime.configuration.ProfileManager; import io.quarkus.runtime.configuration.ProfileManager;
import io.smallrye.config.ConfigValue;
import org.keycloak.utils.StringUtil;
import picocli.CommandLine; import picocli.CommandLine;
import picocli.CommandLine.Command; import picocli.CommandLine.Command;
@ -71,6 +76,9 @@ public final class Build extends AbstractCommand implements Runnable {
try { try {
beforeReaugmentationOnWindows(); beforeReaugmentationOnWindows();
configureBuildClassLoader();
QuarkusEntryPoint.main(); QuarkusEntryPoint.main();
if (!isDevMode()) { if (!isDevMode()) {
@ -84,6 +92,16 @@ public final class Build extends AbstractCommand implements Runnable {
} }
} }
private static void configureBuildClassLoader() {
ConfigValue ignoredArtifacts = Configuration.getCurrentBuiltTimeProperty(
MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + ClassLoaderOptions.IGNORE_ARTIFACTS.getKey());
if (ignoredArtifacts != null && StringUtil.isNotBlank(ignoredArtifacts.getValue())) {
// ignored artifacts must be set prior to starting re-augmentation
System.setProperty("quarkus.class-loading.removed-artifacts", ignoredArtifacts.getValue());
}
}
@Override @Override
public boolean includeBuildTime() { public boolean includeBuildTime() {
return true; return true;

View file

@ -4,12 +4,19 @@ import static org.keycloak.quarkus.runtime.Environment.getCurrentOrCreateFeature
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption; import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
import io.smallrye.config.ConfigSourceInterceptorContext; import io.smallrye.config.ConfigSourceInterceptorContext;
import java.util.HashSet;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.keycloak.common.Profile; import org.keycloak.common.Profile;
import org.keycloak.common.Profile.Feature; import org.keycloak.common.Profile.Feature;
import org.keycloak.config.ClassLoaderOptions; import org.keycloak.config.ClassLoaderOptions;
import org.keycloak.config.StorageOptions;
import org.keycloak.quarkus.runtime.Environment; import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
final class ClassLoaderPropertyMappers { final class ClassLoaderPropertyMappers {
@ -27,14 +34,27 @@ final class ClassLoaderPropertyMappers {
private static Optional<String> resolveIgnoredArtifacts(Optional<String> value, ConfigSourceInterceptorContext context) { private static Optional<String> resolveIgnoredArtifacts(Optional<String> value, ConfigSourceInterceptorContext context) {
if (Environment.isRebuildCheck() || Environment.isRebuild()) { if (Environment.isRebuildCheck() || Environment.isRebuild()) {
Profile profile = getCurrentOrCreateFeatureProfile(); Profile profile = getCurrentOrCreateFeatureProfile();
Set<String> ignoredArtifacts = new HashSet<>();
if (profile.getFeatures().get(Feature.FIPS)) { if (profile.getFeatures().get(Feature.FIPS)) {
return Optional.of( ignoredArtifacts.addAll(List.of(
"org.bouncycastle:bcprov-jdk15on,org.bouncycastle:bcpkix-jdk15on,org.bouncycastle:bcutil-jdk15on,org.keycloak:keycloak-crypto-default"); "org.bouncycastle:bcprov-jdk15on", "org.bouncycastle:bcpkix-jdk15on", "org.bouncycastle:bcutil-jdk15on", "org.keycloak:keycloak-crypto-default"));
} else {
ignoredArtifacts.addAll(List.of(
"org.keycloak:keycloak-crypto-fips1402", "org.bouncycastle:bc-fips", "org.bouncycastle:bctls-fips", "org.bouncycastle:bcpkix-fips"));
} }
return Optional.of( Optional<String> storage = Configuration.getOptionalValue(
"org.keycloak:keycloak-crypto-fips1402,org.bouncycastle:bc-fips,org.bouncycastle:bctls-fips,org.bouncycastle:bcpkix-fips"); MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + StorageOptions.STORAGE.getKey());
if (storage.isEmpty()) {
ignoredArtifacts.add("org.keycloak:keycloak-model-map-jpa");
ignoredArtifacts.add("org.keycloak:keycloak-model-map-hot-rod");
ignoredArtifacts.add("org.keycloak:keycloak-model-map");
ignoredArtifacts.add("org.keycloak:keycloak-model-map-file");
}
return Optional.of(String.join(",", ignoredArtifacts));
} }
return value; return value;

View file

@ -17,17 +17,19 @@
package org.keycloak.quarkus.runtime.storage.database.jpa; package org.keycloak.quarkus.runtime.storage.database.jpa;
import static org.keycloak.config.StorageOptions.STORAGE;
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Optional; import java.util.Optional;
import jakarta.enterprise.inject.Instance; import jakarta.enterprise.inject.Instance;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.EntityManagerFactory;
import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.internal.SessionImpl; import org.keycloak.config.StorageOptions.StorageType;
import org.keycloak.config.StorageOptions;
import org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory; import org.keycloak.models.map.storage.jpa.JpaMapStorageProviderFactory;
import org.keycloak.quarkus.runtime.configuration.Configuration;
import io.quarkus.arc.Arc; import io.quarkus.arc.Arc;
import io.quarkus.hibernate.orm.PersistenceUnit; import io.quarkus.hibernate.orm.PersistenceUnit;
@ -36,7 +38,7 @@ public class QuarkusJpaMapStorageProviderFactory extends JpaMapStorageProviderFa
@Override @Override
public String getId() { public String getId() {
return StorageOptions.StorageType.jpa.getProvider(); return StorageType.jpa.getProvider();
} }
@Override @Override
@ -81,4 +83,8 @@ public class QuarkusJpaMapStorageProviderFactory extends JpaMapStorageProviderFa
return Optional.empty(); return Optional.empty();
} }
@Override
public boolean isSupported() {
return StorageType.jpa.name().equals(Configuration.getOptionalValue(NS_KEYCLOAK_PREFIX + STORAGE.getKey()).orElse(null));
}
} }