diff --git a/distribution/server-x-dist/assembly.xml b/distribution/server-x-dist/assembly.xml
index 2c47edc2b0..fcf9596486 100755
--- a/distribution/server-x-dist/assembly.xml
+++ b/distribution/server-x-dist/assembly.xml
@@ -104,6 +104,10 @@
conf
+
+
+ conf
+
diff --git a/distribution/server-x-dist/pom.xml b/distribution/server-x-dist/pom.xml
index df4c4c72c1..9947610236 100755
--- a/distribution/server-x-dist/pom.xml
+++ b/distribution/server-x-dist/pom.xml
@@ -70,6 +70,12 @@
jar
target/keycloak-quarkus-server
+
+ org.keycloak
+ keycloak-quarkus-server-app
+ jar
+ target/keycloak-quarkus-server-app
+
diff --git a/distribution/server-x-dist/src/main/content/conf/README.md b/distribution/server-x-dist/src/main/content/conf/README.md
new file mode 100644
index 0000000000..2075398ff2
--- /dev/null
+++ b/distribution/server-x-dist/src/main/content/conf/README.md
@@ -0,0 +1,3 @@
+# Configure the server
+
+Use files in this directory to configure the server
\ No newline at end of file
diff --git a/distribution/server-x-dist/src/main/content/conf/keycloak.properties b/distribution/server-x-dist/src/main/content/conf/keycloak.properties
deleted file mode 100644
index d55cc50ec8..0000000000
--- a/distribution/server-x-dist/src/main/content/conf/keycloak.properties
+++ /dev/null
@@ -1,12 +0,0 @@
-# Default and non-production grade database vendor
-db=h2-file
-
-# Default, and insecure, and non-production grade configuration for the development profile
-%dev.http.enabled=true
-%dev.db.username = sa
-%dev.db.password = keycloak
-
-# Logging configuration. INFO is the default level for most of the categories
-#quarkus.log.level = DEBUG
-quarkus.log.category."org.jboss.resteasy.resteasy_jaxrs.i18n".level=WARN
-quarkus.log.category."org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup".level=WARN
diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java
index 30422a89cb..57eb2b8b1a 100644
--- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java
+++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java
@@ -102,7 +102,7 @@ class KeycloakProcessor {
*
* @param recorder
*/
- @Record(ExecutionTime.STATIC_INIT)
+ @Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
void configureProviders(KeycloakRecorder recorder) {
Profile.setInstance(recorder.createProfile());
diff --git a/quarkus/runtime/src/main/java/org/keycloak/configuration/Database.java b/quarkus/runtime/src/main/java/org/keycloak/configuration/Database.java
new file mode 100644
index 0000000000..7d08979d70
--- /dev/null
+++ b/quarkus/runtime/src/main/java/org/keycloak/configuration/Database.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2020 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+
+class Database {
+
+ private static Map DATABASES = new HashMap<>();
+
+ static {
+ for (Vendor vendor : Vendor.values()) {
+ DATABASES.put(vendor.name().toLowerCase(), vendor);
+
+ for (String alias : vendor.aliases) {
+ DATABASES.put(alias, vendor);
+ }
+ }
+ }
+
+ static boolean isSupported(String alias) {
+ return DATABASES.containsKey(alias);
+ }
+
+ static Optional getDefaultUrl(String alias) {
+ Vendor vendor = DATABASES.get(alias);
+
+ if (vendor == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(vendor.defaultUrl.apply(alias));
+ }
+
+ static Optional getDriver(String alias) {
+ Vendor vendor = DATABASES.get(alias);
+
+ if (vendor == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(vendor.driver);
+ }
+
+ static Optional getDialect(String alias) {
+ Vendor vendor = DATABASES.get(alias);
+
+ if (vendor == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(vendor.dialect.apply(alias));
+ }
+
+ private enum Vendor {
+ H2("org.h2.jdbcx.JdbcDataSource", "io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect",
+ new Function() {
+ @Override
+ public String apply(String alias) {
+ if ("h2-file".equalsIgnoreCase(alias)) {
+ return "jdbc:h2:file:${kc.home.dir:${kc.db.url.path:~}}/${kc.data.dir:data}/keycloakdb${kc.db.url.properties:;;AUTO_SERVER=TRUE}";
+ }
+ return "jdbc:h2:mem:keycloakdb${kc.db.url.properties:}";
+ }
+ }, "h2-mem", "h2-file"),
+ MYSQL("com.mysql.cj.jdbc.MysqlXADataSource", "org.hibernate.dialect.MySQL8Dialect",
+ "jdbc:mysql://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}"),
+ MARIADB("org.mariadb.jdbc.MySQLDataSource", "org.hibernate.dialect.MariaDBDialect",
+ "jdbc:mariadb://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}"),
+ POSTGRES("org.postgresql.xa.PGXADataSource", new Function() {
+ @Override
+ public String apply(String alias) {
+ if ("postgres-95".equalsIgnoreCase(alias)) {
+ return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL95Dialect";
+ }
+ return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL10Dialect";
+ }
+ }, "jdbc:postgresql://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}",
+ "postgres-95", "postgres-10");
+
+ final String driver;
+ final Function dialect;
+ final Function defaultUrl;
+ final String[] aliases;
+
+ Vendor(String driver, String dialect, String defaultUrl, String... aliases) {
+ this(driver, (alias) -> dialect, (alias) -> defaultUrl, aliases);
+ }
+
+ Vendor(String driver, String dialect, Function defaultUrl, String... aliases) {
+ this(driver, (alias) -> dialect, defaultUrl, aliases);
+ }
+
+ Vendor(String driver, Function dialect, String defaultUrl, String... aliases) {
+ this(driver, dialect, (alias) -> defaultUrl, aliases);
+ }
+
+ Vendor(String driver, Function dialect, Function defaultUrl, String... aliases) {
+ this.driver = driver;
+ this.dialect = dialect;
+ this.defaultUrl = defaultUrl;
+ this.aliases = aliases;
+ }
+ }
+}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/configuration/KeycloakPropertiesConfigSource.java b/quarkus/runtime/src/main/java/org/keycloak/configuration/KeycloakPropertiesConfigSource.java
index f516ac4e6c..f730f39397 100644
--- a/quarkus/runtime/src/main/java/org/keycloak/configuration/KeycloakPropertiesConfigSource.java
+++ b/quarkus/runtime/src/main/java/org/keycloak/configuration/KeycloakPropertiesConfigSource.java
@@ -17,15 +17,12 @@
package org.keycloak.configuration;
-import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
@@ -62,13 +59,9 @@ public abstract class KeycloakPropertiesConfigSource extends PropertiesConfigSou
return Collections.emptyMap();
}
try (Closeable ignored = is) {
- try (InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8)) {
- try (BufferedReader br = new BufferedReader(isr)) {
- final Properties properties = new Properties();
- properties.load(br);
- return transform((Map) (Map) properties);
- }
- }
+ Properties properties = new Properties();
+ properties.load(is);
+ return transform((Map) (Map) properties);
} catch (IOException e) {
throw new IOError(e);
}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/configuration/PropertyMappers.java b/quarkus/runtime/src/main/java/org/keycloak/configuration/PropertyMappers.java
index 5b608d076a..a5b790766f 100644
--- a/quarkus/runtime/src/main/java/org/keycloak/configuration/PropertyMappers.java
+++ b/quarkus/runtime/src/main/java/org/keycloak/configuration/PropertyMappers.java
@@ -53,8 +53,7 @@ public final class PropertyMappers {
Boolean enabled = Boolean.valueOf(value);
ConfigValue proxy = context.proceed(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + "proxy");
- if ("dev".equalsIgnoreCase(ProfileManager.getActiveProfile()) ||
- (proxy != null && "edge".equalsIgnoreCase(proxy.getValue()))) {
+ if (Environment.isDevMode() || (proxy != null && "edge".equalsIgnoreCase(proxy.getValue()))) {
enabled = true;
}
@@ -119,74 +118,17 @@ public final class PropertyMappers {
}
private static void configureDatabasePropertyMappers() {
- createBuildTimeProperty("db", "quarkus.hibernate-orm.dialect", (db, context) -> {
- switch (db.toLowerCase()) {
- case "h2-file":
- case "h2-mem":
- return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusH2Dialect";
- case "mariadb":
- return "org.hibernate.dialect.MariaDBDialect";
- case "mysql":
- return "org.hibernate.dialect.MySQL8Dialect";
- case "postgres-95":
- return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL95Dialect";
- case "postgres": // shorthand for the recommended postgres version
- case "postgres-10":
- return "io.quarkus.hibernate.orm.runtime.dialect.QuarkusPostgreSQL10Dialect";
- }
- return null;
- }, null);
- create("db", "quarkus.datasource.jdbc.driver", (db, context) -> {
- switch (db.toLowerCase()) {
- case "h2-file":
- case "h2-mem":
- return "org.h2.jdbcx.JdbcDataSource";
- case "mariadb":
- return "org.mariadb.jdbc.MySQLDataSource";
- case "mysql":
- return "com.mysql.cj.jdbc.MysqlXADataSource";
- case "postgres":
- case "postgres-95":
- case "postgres-10":
- return "org.postgresql.xa.PGXADataSource";
- }
- return null;
- }, null);
- create("db", "quarkus.datasource.db-kind", (db, context) -> {
- switch (db.toLowerCase()) {
- case "h2-file":
- case "h2-mem":
- return "h2";
- case "mariadb":
- return "mariadb";
- case "mysql":
- return "mysql";
- case "postgres":
- case "postgres-95":
- case "postgres-10":
- return "postgresql";
+ createBuildTimeProperty("db", "quarkus.hibernate-orm.dialect", (db, context) -> Database.getDialect(db).orElse(null), null);
+ create("db", "quarkus.datasource.jdbc.driver", (db, context) -> Database.getDriver(db).orElse(null), null);
+ createBuildTimeProperty("db", "quarkus.datasource.db-kind", (db, context) -> {
+ if (Database.isSupported(db)) {
+ return db;
}
addInitializationException(invalidDatabaseVendor(db, "h2-file", "h2-mem", "mariadb", "mysql", "postgres", "postgres-95", "postgres-10"));
return "h2";
}, "The database vendor. Possible values are: h2-mem, h2-file, mariadb, mysql, postgres95, postgres10.");
create("db", "quarkus.datasource.jdbc.transactions", (db, context) -> "xa", null);
- create("db.url", "db", "quarkus.datasource.jdbc.url", (value, context) -> {
- switch (value.toLowerCase()) {
- case "h2-file":
- return "jdbc:h2:file:${kc.home.dir:${kc.db.url.path:~}}/${kc.data.dir:data}/keycloakdb${kc.db.url.properties:;;AUTO_SERVER=TRUE}";
- case "h2-mem":
- return "jdbc:h2:mem:keycloakdb${kc.db.url.properties:}";
- case "mariadb":
- return "jdbc:mariadb://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}";
- case "postgres":
- case "postgres-95":
- case "postgres-10":
- return "jdbc:postgresql://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}";
- case "mysql":
- return "jdbc:mysql://${kc.db.url.host:localhost}/${kc.db.url.database:keycloak}${kc.db.url.properties:}";
- }
- return value;
- }, "The database JDBC URL. If not provided a default URL is set based on the selected database vendor. For instance, if using 'postgres', the JDBC URL would be 'jdbc:postgresql://localhost/keycloak'. The host, database and properties can be overridden by setting the following system properties, respectively: -Dkc.db.url.host, -Dkc.db.url.database, -Dkc.db.url.properties.");
+ create("db.url", "db", "quarkus.datasource.jdbc.url", (value, context) -> Database.getDefaultUrl(value).orElse(value), "The database JDBC URL. If not provided a default URL is set based on the selected database vendor. For instance, if using 'postgres', the JDBC URL would be 'jdbc:postgresql://localhost/keycloak'. The host, database and properties can be overridden by setting the following system properties, respectively: -Dkc.db.url.host, -Dkc.db.url.database, -Dkc.db.url.properties.");
create("db.username", "quarkus.datasource.username", "The database username.");
create("db.password", "quarkus.datasource.password", "The database password", true);
create("db.schema", "quarkus.datasource.schema", "The database schema.");
@@ -212,11 +154,6 @@ public final class PropertyMappers {
.anyMatch(entry -> entry.getValue().getFrom().equals(name) && entry.getValue().isBuildTime());
}
- public static boolean isSupported(String name) {
- return PropertyMapper.MAPPERS.entrySet().stream()
- .anyMatch(entry -> toCLIFormat(entry.getValue().getFrom()).equals(name));
- }
-
public static String toCLIFormat(String name) {
if (name.indexOf('.') == -1) {
return name;
diff --git a/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusCacheManagerProvider.java b/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusCacheManagerProvider.java
index 12b41d3d25..d9c1fe3f9a 100644
--- a/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusCacheManagerProvider.java
+++ b/quarkus/runtime/src/main/java/org/keycloak/provider/quarkus/QuarkusCacheManagerProvider.java
@@ -17,14 +17,9 @@
package org.keycloak.provider.quarkus;
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
+import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.stream.Collectors;
import org.infinispan.commons.util.FileLookupFactory;
import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
@@ -45,8 +40,7 @@ public final class QuarkusCacheManagerProvider implements ManagedCacheManagerPro
@Override
public C getCacheManager(Config.Scope config) {
try {
- String configurationAsString = loadConfigurationToString(config);
- ConfigurationBuilderHolder builder = new ParserRegistry().parse(configurationAsString);
+ ConfigurationBuilderHolder builder = new ParserRegistry().parse(loadConfiguration(config));
if (builder.getNamedConfigurationBuilders().get("sessions").clustering().cacheMode().isClustered()) {
configureTransportStack(config, builder);
@@ -58,17 +52,12 @@ public final class QuarkusCacheManagerProvider implements ManagedCacheManagerPro
}
}
- private String loadConfigurationToString(Config.Scope config) throws FileNotFoundException {
- BufferedReader configurationReader = new BufferedReader(new InputStreamReader(loadConfiguration(config), StandardCharsets.UTF_8));
- return configurationReader.lines().collect(Collectors.joining(System.lineSeparator()));
- }
-
- private InputStream loadConfiguration(Config.Scope config) throws FileNotFoundException {
+ private URL loadConfiguration(Config.Scope config) {
String pathPrefix;
String homeDir = Environment.getHomeDir();
if (homeDir == null) {
- log.warn("Keycloak home directory not set.");
+ log.warn("Keycloak home directory not set");
pathPrefix = "";
} else {
pathPrefix = homeDir + "/conf/";
@@ -78,18 +67,24 @@ public final class QuarkusCacheManagerProvider implements ManagedCacheManagerPro
String configFile = config.get("configFile");
if (configFile != null) {
Path configPath = Paths.get(pathPrefix + configFile);
+ String path;
if (configPath.toFile().exists()) {
- log.infof("Loading cache configuration from %s", configPath);
- return FileLookupFactory.newInstance()
- .lookupFileStrict(configPath.toUri(), Thread.currentThread().getContextClassLoader());
+ path = configPath.toFile().getAbsolutePath();
} else {
- log.infof("Loading cache configuration from %s", configPath);
- return FileLookupFactory.newInstance()
- .lookupFileStrict(configPath.getFileName().toString(), Thread.currentThread().getContextClassLoader());
+ path = configPath.getFileName().toString();
}
+
+ log.infof("Loading cluster configuration from %s", configPath);
+ URL url = FileLookupFactory.newInstance().lookupFileLocation(path, Thread.currentThread().getContextClassLoader());
+
+ if (url == null) {
+ throw new IllegalArgumentException("Could not load cluster configuration file at [" + configPath + "]");
+ }
+
+ return url;
} else {
- throw new IllegalStateException("Option 'configFile' needs to be specified");
+ throw new IllegalArgumentException("Option 'configFile' needs to be specified");
}
}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java b/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java
index a4dcf84aac..0b2912f569 100644
--- a/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java
+++ b/quarkus/runtime/src/main/java/org/keycloak/util/Environment.java
@@ -79,4 +79,8 @@ public final class Environment {
public static SmallRyeConfig getConfig() {
return KeycloakRecorder.getConfig();
}
+
+ public static boolean isDevMode() {
+ return "dev".equalsIgnoreCase(getProfile());
+ }
}
diff --git a/quarkus/server/src/main/resources/META-INF/keycloak.properties b/quarkus/server/src/main/resources/META-INF/keycloak.properties
index 93c0d102d5..1e9124c065 100644
--- a/quarkus/server/src/main/resources/META-INF/keycloak.properties
+++ b/quarkus/server/src/main/resources/META-INF/keycloak.properties
@@ -5,4 +5,9 @@ db=h2-file
%dev.http.enabled=true
%dev.db.username = sa
%dev.db.password = keycloak
-%dev.cluster=local
\ No newline at end of file
+%dev.cluster=local
+
+# Logging configuration. INFO is the default level for most of the categories
+#quarkus.log.level = DEBUG
+quarkus.log.category."org.jboss.resteasy.resteasy_jaxrs.i18n".level=WARN
+quarkus.log.category."org.infinispan.transaction.lookup.JBossStandaloneJTAManagerLookup".level=WARN
\ No newline at end of file