Add storage-jpa-db
property into Quarkus. Distinguish postgres and crdb for jpa map store.
Closes #17305
This commit is contained in:
parent
97969e141c
commit
31e4c5cb7e
12 changed files with 119 additions and 25 deletions
|
@ -20,7 +20,7 @@ public class DatabaseOptions {
|
|||
.category(OptionCategory.DATABASE)
|
||||
.description("The database vendor.")
|
||||
.defaultValue("dev-file")
|
||||
.expectedValues(Database::getAliases)
|
||||
.expectedValues(Database::getLegacyStoreAliases)
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.List;
|
|||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import org.keycloak.config.database.Database;
|
||||
|
||||
public class StorageOptions {
|
||||
|
||||
|
@ -333,6 +334,14 @@ public class StorageOptions {
|
|||
.description("Root directory for file map store.")
|
||||
.build();
|
||||
|
||||
public static final Option<String> STORAGE_JPA_DB = new OptionBuilder<>("storage-jpa-db", String.class)
|
||||
.category(OptionCategory.STORAGE)
|
||||
.defaultValue(Database.Vendor.POSTGRES.name().toLowerCase())
|
||||
.expectedValues(Database::getAvailableMapStoreAliases)
|
||||
.description("The database vendor for jpa map storage.")
|
||||
.buildTime(true)
|
||||
.build();
|
||||
|
||||
private static String descriptionForStorageAreas(String areaAsText) {
|
||||
return "Sets a storage mechanism for " + areaAsText + ".";
|
||||
}
|
||||
|
@ -344,4 +353,11 @@ public class StorageOptions {
|
|||
private static List<String> getExpectedCacheNames() {
|
||||
return Stream.concat(Stream.of("all"), AutogeneratedHotRodDescriptors.ENTITY_DESCRIPTOR_MAP.values().stream().map(HotRodEntityDescriptor::getCacheName).distinct()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static Optional<Database.Vendor> getDatabaseVendor(String databaseKind) {
|
||||
return Stream.of(Database.Vendor.values())
|
||||
.filter(Database.Vendor::isEnabledOnNewStore)
|
||||
.filter(v -> v.isOfKind(databaseKind))
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,11 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
|
@ -94,12 +96,31 @@ public final class Database {
|
|||
return Optional.of(vendor.dialect.apply(alias));
|
||||
}
|
||||
|
||||
public static List<String> getAliases() {
|
||||
return DATABASES.keySet().stream().sorted().collect(Collectors.toList());
|
||||
/**
|
||||
* @return List of aliases of databases enabled on legacy store.
|
||||
*/
|
||||
public static List<String> getLegacyStoreAliases() {
|
||||
return DATABASES.entrySet().stream()
|
||||
.filter(e -> e.getValue().isEnabledOnLegacyStore())
|
||||
.map(Entry::getKey)
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return List of aliases of databases enabled on jpa map store.
|
||||
*/
|
||||
public static List<String> getAvailableMapStoreAliases() {
|
||||
return Stream.of(Database.Vendor.values())
|
||||
.filter(Database.Vendor::isEnabledOnNewStore)
|
||||
.map(v -> v.aliases)// may be replaced by Database.Vendor::getAliases if we add the getter into Database.Vendor
|
||||
.flatMap(Stream::of)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public enum Vendor {
|
||||
H2("h2",
|
||||
Enabled.LEGACY_ONLY,
|
||||
"org.h2.jdbcx.JdbcDataSource",
|
||||
"org.h2.Driver",
|
||||
"io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect",
|
||||
|
@ -146,6 +167,7 @@ public final class Database {
|
|||
"dev-mem", "dev-file"
|
||||
),
|
||||
MYSQL("mysql",
|
||||
Enabled.LEGACY_ONLY,
|
||||
"com.mysql.cj.jdbc.MysqlXADataSource",
|
||||
"com.mysql.cj.jdbc.Driver",
|
||||
"org.hibernate.dialect.MySQL8Dialect",
|
||||
|
@ -153,6 +175,7 @@ public final class Database {
|
|||
asList("org.keycloak.connections.jpa.updater.liquibase.UpdatedMySqlDatabase")
|
||||
),
|
||||
MARIADB("mariadb",
|
||||
Enabled.LEGACY_ONLY,
|
||||
"org.mariadb.jdbc.MariaDbDataSource",
|
||||
"org.mariadb.jdbc.Driver",
|
||||
"org.hibernate.dialect.MariaDBDialect",
|
||||
|
@ -160,15 +183,25 @@ public final class Database {
|
|||
asList("org.keycloak.connections.jpa.updater.liquibase.UpdatedMariaDBDatabase")
|
||||
),
|
||||
POSTGRES("postgresql",
|
||||
Enabled.ENABLED,
|
||||
"org.postgresql.xa.PGXADataSource",
|
||||
"org.postgresql.Driver",
|
||||
"io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL10Dialect",
|
||||
"jdbc:postgresql://${kc.db-url-host:localhost}:${kc.db-url-port:5432}/${kc.db-url-database:keycloak}${kc.db-url-properties:}",
|
||||
asList("liquibase.database.core.PostgresDatabase", "liquibase.database.core.CockroachDatabase",
|
||||
"org.keycloak.connections.jpa.updater.liquibase.PostgresPlusDatabase"),
|
||||
asList("liquibase.database.core.PostgresDatabase", "org.keycloak.connections.jpa.updater.liquibase.PostgresPlusDatabase"),
|
||||
"postgres"
|
||||
),
|
||||
COCKROACH(POSTGRES.databaseKind, //needs to be aligned with https://quarkus.io/guides/datasource#default-datasource
|
||||
Enabled.MAP_STORE_ONLY,
|
||||
POSTGRES.xaDriver,
|
||||
POSTGRES.nonXaDriver,
|
||||
"org.hibernate.dialect.CockroachDB201Dialect",
|
||||
"jdbc:postgresql://${kc.db-url-host:localhost}:${kc.db-url-port:26257}/${kc.db-url-database:keycloak}${kc.db-url-properties:}",
|
||||
List.of("liquibase.database.core.CockroachDatabase"),
|
||||
"cockroach"
|
||||
),
|
||||
MSSQL("mssql",
|
||||
Enabled.LEGACY_ONLY,
|
||||
"com.microsoft.sqlserver.jdbc.SQLServerXADataSource",
|
||||
"com.microsoft.sqlserver.jdbc.SQLServerDriver",
|
||||
"org.hibernate.dialect.SQLServer2016Dialect",
|
||||
|
@ -177,6 +210,7 @@ public final class Database {
|
|||
"mssql"
|
||||
),
|
||||
ORACLE("oracle",
|
||||
Enabled.LEGACY_ONLY,
|
||||
"oracle.jdbc.xa.client.OracleXADataSource",
|
||||
"oracle.jdbc.driver.OracleDriver",
|
||||
"org.hibernate.dialect.Oracle12cDialect",
|
||||
|
@ -185,6 +219,7 @@ public final class Database {
|
|||
);
|
||||
|
||||
final String databaseKind;
|
||||
final Enabled enabled;
|
||||
final String xaDriver;
|
||||
final String nonXaDriver;
|
||||
final Function<String, String> dialect;
|
||||
|
@ -192,20 +227,21 @@ public final class Database {
|
|||
final List<String> liquibaseTypes;
|
||||
final String[] aliases;
|
||||
|
||||
Vendor(String databaseKind, String xaDriver, String nonXaDriver, String dialect, String defaultUrl, List<String> liquibaseTypes,
|
||||
Vendor(String databaseKind, Enabled enabled, String xaDriver, String nonXaDriver, String dialect, String defaultUrl, List<String> liquibaseTypes,
|
||||
String... aliases) {
|
||||
this(databaseKind, xaDriver, nonXaDriver, alias -> dialect, alias -> defaultUrl, liquibaseTypes, aliases);
|
||||
this(databaseKind, enabled, xaDriver, nonXaDriver, alias -> dialect, alias -> defaultUrl, liquibaseTypes, aliases);
|
||||
}
|
||||
|
||||
Vendor(String databaseKind, String xaDriver, String nonXaDriver, String dialect, Function<String, String> defaultUrl,
|
||||
Vendor(String databaseKind, Enabled enabled, String xaDriver, String nonXaDriver, String dialect, Function<String, String> defaultUrl,
|
||||
List<String> liquibaseTypes, String... aliases) {
|
||||
this(databaseKind, xaDriver, nonXaDriver, alias -> dialect, defaultUrl, liquibaseTypes, aliases);
|
||||
this(databaseKind, enabled, xaDriver, nonXaDriver, alias -> dialect, defaultUrl, liquibaseTypes, aliases);
|
||||
}
|
||||
|
||||
Vendor(String databaseKind, String xaDriver, String nonXaDriver, Function<String, String> dialect, Function<String, String> defaultUrl,
|
||||
Vendor(String databaseKind, Enabled enabled, String xaDriver, String nonXaDriver, Function<String, String> dialect, Function<String, String> defaultUrl,
|
||||
List<String> liquibaseTypes,
|
||||
String... aliases) {
|
||||
this.databaseKind = databaseKind;
|
||||
this.enabled = enabled;
|
||||
this.xaDriver = xaDriver;
|
||||
this.nonXaDriver = nonXaDriver;
|
||||
this.dialect = dialect;
|
||||
|
@ -214,6 +250,14 @@ public final class Database {
|
|||
this.aliases = aliases.length == 0 ? new String[] { databaseKind } : aliases;
|
||||
}
|
||||
|
||||
public boolean isEnabledOnLegacyStore() {
|
||||
return enabled.legacyStore;
|
||||
}
|
||||
|
||||
public boolean isEnabledOnNewStore() {
|
||||
return enabled.jpaMapStore;
|
||||
}
|
||||
|
||||
public boolean isOfKind(String dbKind) {
|
||||
return databaseKind.equals(dbKind);
|
||||
}
|
||||
|
@ -223,4 +267,18 @@ public final class Database {
|
|||
return databaseKind.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Enabled {
|
||||
final static Enabled LEGACY_ONLY = new Enabled(true, false);
|
||||
final static Enabled MAP_STORE_ONLY = new Enabled(false, true);
|
||||
final static Enabled ENABLED = new Enabled(true, true);
|
||||
|
||||
final boolean legacyStore;
|
||||
final boolean jpaMapStore;
|
||||
|
||||
private Enabled(boolean legacyStore, boolean jpaMapStore) {
|
||||
this.legacyStore = legacyStore;
|
||||
this.jpaMapStore = jpaMapStore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Optional;
|
|||
|
||||
import static java.util.Optional.of;
|
||||
import static org.keycloak.config.StorageOptions.STORAGE;
|
||||
import static org.keycloak.config.StorageOptions.STORAGE_JPA_DB;
|
||||
import static org.keycloak.quarkus.runtime.Messages.invalidDatabaseVendor;
|
||||
import static org.keycloak.quarkus.runtime.configuration.Configuration.getRawValue;
|
||||
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
|
||||
|
@ -99,7 +100,7 @@ final class DatabasePropertyMappers {
|
|||
|
||||
if (url.isPresent()) {
|
||||
if (isJpaStore()) {
|
||||
return Database.getDefaultUrl(Database.Vendor.POSTGRES.name().toLowerCase());
|
||||
return Database.getDefaultUrl(getJpaStoreDbVendor().name().toLowerCase());
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
@ -109,7 +110,8 @@ final class DatabasePropertyMappers {
|
|||
|
||||
private static Optional<String> getXaOrNonXaDriver(Optional<String> value, ConfigSourceInterceptorContext context) {
|
||||
if (isJpaStore()) {
|
||||
return Database.getDriver(Database.Vendor.POSTGRES.name().toLowerCase(), false);
|
||||
// always use XA driver with jpa map store
|
||||
return Database.getDriver(getJpaStoreDbVendor().name().toLowerCase(), true);
|
||||
}
|
||||
|
||||
ConfigValue xaEnabledConfigValue = context.proceed("kc.transaction-xa-enabled");
|
||||
|
@ -133,7 +135,7 @@ final class DatabasePropertyMappers {
|
|||
|
||||
private static Optional<String> toDatabaseKind(Optional<String> db, ConfigSourceInterceptorContext context) {
|
||||
if (isJpaStore()) {
|
||||
return Database.getDatabaseKind(Database.Vendor.POSTGRES.name().toLowerCase());
|
||||
return Database.getDatabaseKind(getJpaStoreDbVendor().name().toLowerCase());
|
||||
}
|
||||
|
||||
Optional<String> databaseKind = Database.getDatabaseKind(db.get());
|
||||
|
@ -142,14 +144,14 @@ final class DatabasePropertyMappers {
|
|||
return databaseKind;
|
||||
}
|
||||
|
||||
addInitializationException(invalidDatabaseVendor(db.get(), Database.getAliases()));
|
||||
addInitializationException(invalidDatabaseVendor(db.get(), Database.getLegacyStoreAliases()));
|
||||
|
||||
return of("h2");
|
||||
}
|
||||
|
||||
private static Optional<String> resolveDatabaseVendor(Optional<String> db, ConfigSourceInterceptorContext context) {
|
||||
if (isJpaStore()) {
|
||||
return of(Database.Vendor.POSTGRES.name().toLowerCase());
|
||||
return Optional.of(getJpaStoreDbVendor().name().toLowerCase());
|
||||
}
|
||||
|
||||
if (db.isEmpty()) {
|
||||
|
@ -204,16 +206,13 @@ final class DatabasePropertyMappers {
|
|||
return Database.getDialect("dev-file");
|
||||
}
|
||||
|
||||
private static String getDefaultVendor() {
|
||||
if (isJpaStore()) {
|
||||
return Database.Vendor.POSTGRES.name().toLowerCase();
|
||||
}
|
||||
|
||||
return "dev-file";
|
||||
}
|
||||
|
||||
private static boolean isJpaStore() {
|
||||
String storage = getRawValue(NS_KEYCLOAK_PREFIX.concat(STORAGE.getKey()));
|
||||
return storage != null && StorageOptions.StorageType.jpa.name().equals(storage);
|
||||
}
|
||||
|
||||
private static Database.Vendor getJpaStoreDbVendor() {
|
||||
String storageJpaDb = getRawValue(NS_KEYCLOAK_PREFIX.concat(STORAGE_JPA_DB.getKey()));
|
||||
return StorageOptions.getDatabaseVendor(storageJpaDb).orElse(Database.Vendor.POSTGRES);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -303,6 +303,11 @@ final class StoragePropertyMappers {
|
|||
.to("kc.spi-map-storage-file-dir")
|
||||
.mapFrom("storage")
|
||||
.paramLabel("dir")
|
||||
.build(),
|
||||
fromOption(StorageOptions.STORAGE_JPA_DB)
|
||||
.to("kc.spi-map-storage-jpa-db")
|
||||
.mapFrom("storage")
|
||||
.paramLabel("type")
|
||||
.build()
|
||||
};
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ public class TransactionPropertyMappers {
|
|||
|
||||
if (storage != null && StorageOptions.StorageType.jpa.name().equals(storage.getValue())) {
|
||||
isJtaEnabled = true;
|
||||
isXaEnabled = false;
|
||||
isXaEnabled = true;
|
||||
}
|
||||
|
||||
if (!isJtaEnabled) {
|
||||
|
|
|
@ -87,6 +87,9 @@ Storage (Experimental):
|
|||
Experimental: Sets the port of the Infinispan server.
|
||||
--storage-hotrod-username <username>
|
||||
Experimental: Sets the username of the Infinispan user.
|
||||
--storage-jpa-db <type>
|
||||
Experimental: The database vendor for jpa map storage. Possible values are:
|
||||
postgres, cockroach. Default: postgres.
|
||||
|
||||
Database:
|
||||
|
||||
|
|
|
@ -87,6 +87,9 @@ Storage (Experimental):
|
|||
Experimental: Sets the port of the Infinispan server.
|
||||
--storage-hotrod-username <username>
|
||||
Experimental: Sets the username of the Infinispan user.
|
||||
--storage-jpa-db <type>
|
||||
Experimental: The database vendor for jpa map storage. Possible values are:
|
||||
postgres, cockroach. Default: postgres.
|
||||
|
||||
Database:
|
||||
|
||||
|
|
|
@ -93,6 +93,9 @@ Storage (Experimental):
|
|||
Experimental: Sets the port of the Infinispan server.
|
||||
--storage-hotrod-username <username>
|
||||
Experimental: Sets the username of the Infinispan user.
|
||||
--storage-jpa-db <type>
|
||||
Experimental: The database vendor for jpa map storage. Possible values are:
|
||||
postgres, cockroach. Default: postgres.
|
||||
|
||||
Database:
|
||||
|
||||
|
|
|
@ -93,6 +93,9 @@ Storage (Experimental):
|
|||
Experimental: Sets the port of the Infinispan server.
|
||||
--storage-hotrod-username <username>
|
||||
Experimental: Sets the username of the Infinispan user.
|
||||
--storage-jpa-db <type>
|
||||
Experimental: The database vendor for jpa map storage. Possible values are:
|
||||
postgres, cockroach. Default: postgres.
|
||||
|
||||
Database:
|
||||
|
||||
|
|
|
@ -894,6 +894,8 @@
|
|||
<keycloak.map.storage.connectionsJpa.user>${keycloak.map.storage.connectionsJpa.user}</keycloak.map.storage.connectionsJpa.user>
|
||||
<keycloak.map.storage.connectionsJpa.password>${keycloak.map.storage.connectionsJpa.password}</keycloak.map.storage.connectionsJpa.password>
|
||||
|
||||
<keycloak.storage.connections.vendor>postgres</keycloak.storage.connections.vendor>
|
||||
|
||||
<keycloak.authorization.map.storage.provider>jpa</keycloak.authorization.map.storage.provider>
|
||||
<keycloak.authSession.map.storage.provider>jpa</keycloak.authSession.map.storage.provider>
|
||||
<keycloak.client.map.storage.provider>jpa</keycloak.client.map.storage.provider>
|
||||
|
@ -1074,6 +1076,8 @@
|
|||
<keycloak.map.storage.connectionsJpa.user>${keycloak.map.storage.connectionsJpa.user}</keycloak.map.storage.connectionsJpa.user>
|
||||
<keycloak.map.storage.connectionsJpa.password>${keycloak.map.storage.connectionsJpa.password}</keycloak.map.storage.connectionsJpa.password>
|
||||
|
||||
<keycloak.storage.connections.vendor>cockroach</keycloak.storage.connections.vendor>
|
||||
|
||||
<keycloak.authorization.map.storage.provider>jpa</keycloak.authorization.map.storage.provider>
|
||||
<keycloak.authSession.map.storage.provider>jpa</keycloak.authSession.map.storage.provider>
|
||||
<keycloak.client.map.storage.provider>jpa</keycloak.client.map.storage.provider>
|
||||
|
|
|
@ -37,7 +37,7 @@ public enum StoreProvider {
|
|||
@Override
|
||||
public void addStoreOptions(List<String> commands) {
|
||||
commands.add("--storage=" + getAlias());
|
||||
getDbVendor().ifPresent(vendor -> commands.add("--db=" + vendor));
|
||||
getDbVendor().ifPresent(vendor -> commands.add("--storage-jpa-db=" + vendor));
|
||||
commands.add("--db-url=" + System.getProperty("keycloak.map.storage.connectionsJpa.url"));
|
||||
commands.add("--db-username=" + System.getProperty("keycloak.map.storage.connectionsJpa.user"));
|
||||
commands.add("--db-password=" + System.getProperty("keycloak.map.storage.connectionsJpa.password"));
|
||||
|
|
Loading…
Reference in a new issue