keycloak-memoizeit
Changes
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java 12(+12 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java 2(+2 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java 147(+122 -25)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java 5(+3 -2)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java 0(+0 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java 0(+0 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/AbstractUserTest.java 52(+52 -0)
Details
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java
index 673bc7c..fc63b22 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java
@@ -17,7 +17,9 @@
package org.keycloak.testsuite.arquillian;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
*
@@ -33,6 +35,8 @@ public final class TestContext {
private final List<ContainerInfo> appServerBackendsInfo = new ArrayList<>();
private boolean adminLoggedIn;
+
+ private final Map customContext = new HashMap<>();
public TestContext(SuiteContext suiteContext, Class testClass) {
this.suiteContext = suiteContext;
@@ -88,4 +92,12 @@ public final class TestContext {
+ (isAdapterTest() ? "App server container: " + getAppServerInfo() + "\n" : "");
}
+ public Object getCustomValue(Object key) {
+ return customContext.get(key);
+ }
+
+ public void setCustomValue(Object key, Object value) {
+ customContext.put(key, value);
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
index 23103d3..d872914 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
@@ -43,6 +43,8 @@ public class IOUtil {
private static final Logger log = Logger.getLogger(IOUtil.class);
+ public static final File PROJECT_BUILD_DIRECTORY = new File(System.getProperty("project.build.directory", "target"));
+
public static <T> T loadJson(InputStream is, Class<T> type) {
try {
return JsonSerialization.readValue(is, type);
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java
index 1b2ec65..082a2a4 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java
@@ -14,15 +14,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.keycloak.testsuite.util;
-import java.text.MessageFormat;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.TreeMap;
+import org.apache.commons.io.IOUtils;
+import org.jboss.logging.Logger;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import static org.keycloak.testsuite.util.IOUtil.PROJECT_BUILD_DIRECTORY;
+import static org.jgroups.util.Util.assertTrue;
/**
*
@@ -30,45 +43,129 @@ import java.util.Map;
*/
public class Timer {
- private static Long time;
+ public static final Timer DEFAULT = new Timer();
- private static final Map<String, List<Long>> stats = new HashMap<>();
+ protected final Logger log = Logger.getLogger(Timer.class);
- public static void time() {
- time = new Date().getTime();
- }
+ protected static final File DATA_DIR = new File(PROJECT_BUILD_DIRECTORY, "stats/data");
+ protected static final File CHARTS_DIR = new File(PROJECT_BUILD_DIRECTORY, "stats/charts");
+
+ public static final String DEFAULT_OPERATION = "DEFAULT_OPERATION";
- public static void time(String operation) {
+ private Long time;
+ private String operation = DEFAULT_OPERATION;
+ private final Map<String, List<Long>> stats = new TreeMap<>();
+
+ public long elapsedTime() {
+ long elapsedTime = 0;
if (time == null) {
- System.out.println(MessageFormat.format("Starting timer for operation {0}", operation));
- time();
} else {
- long timeOrig = time;
- time();
- logOperation(operation, time - timeOrig);
- System.out.println(MessageFormat.format("Operation {0} took {1} ms", operation, time - timeOrig));
+ elapsedTime = new Date().getTime() - time;
}
+ return elapsedTime;
+ }
+
+ public void reset() {
+ reset(operation); // log last operation
}
- private static void logOperation(String operation, long delta) {
+ public void reset(String operation) {
+ reset(operation, true);
+ }
+
+ public void reset(String newOperation, boolean logOperationOnChange) {
+ if (time != null) {
+ if (operation.equals(newOperation) || logOperationOnChange) {
+ logOperation(operation, elapsedTime());
+ }
+ }
+ time = new Date().getTime();
+ if (!operation.equals(newOperation)) {
+ operation = newOperation;
+ log.info(String.format("Operation '%s' started.", newOperation));
+ }
+ }
+
+ private void logOperation(String operation, long duration) {
if (!stats.containsKey(operation)) {
stats.put(operation, new ArrayList<Long>());
}
- stats.get(operation).add(delta);
+ stats.get(operation).add(duration);
+ log.info(String.format("Operation '%s' took: %s ms", operation, duration));
+ }
+
+ public void clearStats() {
+ clearStats(true, true, true);
}
- public static void printStats() {
- if (!stats.isEmpty()) {
- System.out.println("OPERATION STATS:");
+ public void clearStats(boolean logStats, boolean saveData, boolean saveCharts) {
+ if (logStats) {
+ log.info("Timer Statistics:");
+ for (String op : stats.keySet()) {
+ long sum = 0;
+ for (Long duration : stats.get(op)) {
+ sum += duration;
+ }
+ log.info(String.format("Operation '%s' average: %s ms", op, sum / stats.get(op).size()));
+ }
}
- for (String op : stats.keySet()) {
- long sum = 0;
- for (Long t : stats.get(op)) {
- sum += t;
+ if (PROJECT_BUILD_DIRECTORY.exists()) {
+ DATA_DIR.mkdirs();
+ CHARTS_DIR.mkdirs();
+ for (String op : stats.keySet()) {
+ if (saveData) {
+ saveData(op);
+ }
+ if (saveCharts) {
+ saveChart(op);
+ }
}
- System.out.println(MessageFormat.format("Operation {0} average time: {1,number,#} ms", op, sum / stats.get(op).size()));
}
stats.clear();
}
+ private void saveData(String op) {
+ try {
+ File f = new File(DATA_DIR, op.replace(" ", "_") + ".txt");
+ if (!f.createNewFile()) {
+ throw new IOException("Couldn't create file: " + f);
+ }
+ OutputStream stream = new BufferedOutputStream(new FileOutputStream(f));
+ for (Long duration : stats.get(op)) {
+ IOUtils.write(duration.toString(), stream);
+ IOUtils.write("\n", stream);
+ }
+ stream.flush();
+ IOUtils.closeQuietly(stream);
+ } catch (IOException ex) {
+ log.error("Unable to save data for operation '" + op + "'", ex);
+ }
+ }
+
+ private void saveChart(String op) {
+ XYSeries series = new XYSeries(op);
+ int i = 0;
+ for (Long duration : stats.get(op)) {
+ series.add(++i, duration);
+ }
+ final XYSeriesCollection data = new XYSeriesCollection(series);
+ final JFreeChart chart = ChartFactory.createXYLineChart(
+ op,
+ "Operations",
+ "Duration (ms)",
+ data,
+ PlotOrientation.VERTICAL,
+ true,
+ true,
+ false
+ );
+ try {
+ ChartUtilities.saveChartAsPNG(
+ new File(CHARTS_DIR, op.replace(" ", "_") + ".png"),
+ chart, 640, 480);
+ } catch (IOException ex) {
+ log.warn("Unable to save chart for operation '" + op + "'.");
+ }
+ }
+
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
index 92dc9c5..1e5145c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
@@ -16,6 +16,7 @@
*/
package org.keycloak.testsuite;
+import java.io.File;
import org.keycloak.testsuite.arquillian.TestContext;
import java.util.ArrayList;
import java.util.List;
@@ -36,7 +37,6 @@ import org.keycloak.admin.client.resource.RealmsResource;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
-import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
import org.keycloak.testsuite.arquillian.SuiteContext;
import org.keycloak.testsuite.auth.page.WelcomePage;
@@ -52,6 +52,8 @@ import org.keycloak.testsuite.auth.page.login.OIDCLogin;
import org.keycloak.testsuite.auth.page.login.UpdatePassword;
import org.keycloak.testsuite.util.Timer;
import org.keycloak.testsuite.util.WaitUtils;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
/**
*
@@ -124,7 +126,6 @@ public abstract class AbstractKeycloakTest {
public void afterAbstractKeycloakTest() {
// removeTestRealms(); // keeping test realms after test to be able to inspect failures, instead deleting existing realms before import
// keycloak.close(); // keeping admin connection open
- Timer.printStats();
}
private void updateMasterAdminPassword() {
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java
old mode 100755
new mode 100644
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
old mode 100755
new mode 100644
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/AbstractUserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/AbstractUserTest.java
new file mode 100644
index 0000000..d7dbbdc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/user/AbstractUserTest.java
@@ -0,0 +1,52 @@
+package org.keycloak.testsuite.user;
+
+import javax.ws.rs.core.Response;
+import static javax.ws.rs.core.Response.Status.CREATED;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.getCreatedId;
+import static org.junit.Assert.assertEquals;
+import org.keycloak.testsuite.AbstractAuthTest;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractUserTest extends AbstractAuthTest {
+
+ protected UsersResource users() {
+ return testRealmResource().users();
+ }
+
+ protected UserResource user(UserRepresentation user) {
+ if (user.getId()==null) {
+ throw new IllegalStateException("User id cannot be null.");
+ }
+ return user(user.getId());
+ }
+
+ protected UserResource user(String id) {
+ return users().get(id);
+ }
+
+ public static UserRepresentation createUserRep(String username) {
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername(username);
+ user.setEmail(username + "@email.test");
+ return user;
+ }
+
+ public UserRepresentation createUser(UserRepresentation user) {
+ return createUser(users(), user);
+ }
+
+ public UserRepresentation createUser(UsersResource users, UserRepresentation user) {
+ Response response = users.create(user);
+ assertEquals(CREATED.getStatusCode(), response.getStatus());
+ user.setId(getCreatedId(response));
+ response.close();
+ return user;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml
new file mode 100644
index 0000000..11ec6e7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/jpa-performance/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!--
+ ~ Copyright 2016 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.
+ -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-other</artifactId>
+ <version>1.9.1.Final-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>integration-arquillian-tests-jpa-performance</artifactId>
+
+ <name>Keycloak JPA Performance Tests</name>
+
+</project>
diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/README.md b/testsuite/integration-arquillian/tests/other/jpa-performance/README.md
new file mode 100644
index 0000000..d389616
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/jpa-performance/README.md
@@ -0,0 +1,48 @@
+# Keycloak JPA Performance Tests
+
+## How to run
+
+1. Build the Arquilian Base Testsuite module: `/testsuite/integration-arquillian/base`
+2. Run the test from this module using `mvn test` or `mvn clean test`.
+
+Optional parameters:
+```
+-Dmany.users.count=10000
+-Dmany.users.batch=1000
+```
+
+### With MySQL
+
+Start dockerized MySQL:
+```
+docker run --name mysql-keycloak -e MYSQL_ROOT_PASSWORD=keycloak -e MYSQL_DATABASE=keycloak -e MYSQL_USER=keycloak -e MYSQL_PASSWORD=keycloak -d -p 3306:3306 mysql
+```
+
+Additional test parameters:
+```
+-Pclean-jpa
+-Dkeycloak.connectionsJpa.url=jdbc:mysql://localhost/keycloak
+-Dkeycloak.connectionsJpa.driver=com.mysql.jdbc.Driver
+-Dkeycloak.connectionsJpa.user=keycloak
+-Dkeycloak.connectionsJpa.password=keycloak
+```
+
+### With PostgreSQL
+
+Start dockerized PostgreSQL:
+```
+docker run --name postgres-keycloak -e POSTGRES_PASSWORD=keycloak -d -p 5432:5432 postgres
+```
+
+Additional test parameters:
+```
+-Pclean-jpa
+-Dkeycloak.connectionsJpa.url=jdbc:postgresql://localhost/postgres
+-Dkeycloak.connectionsJpa.driver=org.postgresql.Driver
+-Dkeycloak.connectionsJpa.user=postgres
+-Dkeycloak.connectionsJpa.password=keycloak
+```
+
+## Reports
+
+Test creates reports in `target/stats`.
diff --git a/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/java/org/keycloak/testsuite/user/ManyUsersTest.java b/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/java/org/keycloak/testsuite/user/ManyUsersTest.java
new file mode 100644
index 0000000..4916393
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/other/jpa-performance/src/test/java/org/keycloak/testsuite/user/ManyUsersTest.java
@@ -0,0 +1,119 @@
+package org.keycloak.testsuite.user;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.util.Timer;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.util.JsonSerialization;
+import static org.junit.Assert.fail;
+import org.keycloak.admin.client.resource.RealmResource;
+import static org.keycloak.testsuite.util.IOUtil.PROJECT_BUILD_DIRECTORY;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ManyUsersTest extends AbstractUserTest {
+
+ private static final int COUNT = Integer.parseInt(System.getProperty("many.users.count", "10000"));
+ private static final int BATCH = Integer.parseInt(System.getProperty("many.users.batch", "1000"));
+
+ private static final String REALM = "realm_with_many_users";
+
+ private List<UserRepresentation> users;
+
+ private final Timer realmTimer = new Timer();
+ private final Timer usersTimer = new Timer();
+
+ protected RealmResource realmResource() {
+ return realmsResouce().realm(REALM);
+ }
+
+ @Before
+ public void before() {
+ users = new LinkedList<>();
+ for (int i = 0; i < COUNT; i++) {
+ users.add(createUserRep("user" + i));
+ }
+
+ realmTimer.reset("create realm before test");
+ RealmRepresentation realm = new RealmRepresentation();
+ realm.setRealm(REALM);
+ realmsResouce().create(realm);
+ }
+
+ @After
+ public void after() {
+ realmTimer.clearStats(true, true, false);
+ usersTimer.clearStats();
+ }
+
+ @Test
+ public void manyUsers() throws IOException {
+ RealmRepresentation realm = realmResource().toRepresentation();
+ realm.setUsers(users);
+
+ // CREATE
+ realmTimer.reset("create " + users.size() + " users");
+ usersTimer.reset("create " + BATCH + " users");
+ int i = 0;
+ for (UserRepresentation user : users) {
+ createUser(realmResource().users(), user);
+ if (++i % BATCH == 0) {
+ usersTimer.reset();
+ log.info("Created users: " + i + " / " + users.size());
+ }
+ }
+ if (i % BATCH != 0) {
+ usersTimer.reset();
+ log.info("Created users: " + i + " / " + users.size());
+ }
+
+ // SAVE REALM
+ realmTimer.reset("save realm with " + users.size() + " users");
+ File realmFile = new File(PROJECT_BUILD_DIRECTORY, REALM + ".json");
+ JsonSerialization.writeValueToStream(new BufferedOutputStream(new FileOutputStream(realmFile)), realm);
+
+ // DELETE REALM
+ realmTimer.reset("delete realm with " + users.size() + " users");
+ realmResource().remove();
+ try {
+ realmResource().toRepresentation();
+ fail("realm not deleted");
+ } catch (Exception ex) {
+ log.debug("realm deleted");
+ }
+
+ // RE-IMPORT SAVED REALM
+ realmTimer.reset("re-import realm with " + realm.getUsers().size() + " users");
+ realmsResouce().create(realm);
+ realmTimer.reset("load " + realm.getUsers().size() + " users");
+ users = realmResource().users().search("", 0, Integer.MAX_VALUE);
+
+ // DELETE INDIVIDUAL USERS
+ realmTimer.reset("delete " + users.size() + " users");
+ usersTimer.reset("delete " + BATCH + " users", false);
+ i = 0;
+ for (UserRepresentation user : users) {
+ realmResource().users().get(user.getId()).remove();
+ if (++i % BATCH == 0) {
+ usersTimer.reset();
+ log.info("Deleted users: " + i + " / " + users.size());
+ }
+ }
+ if (i % BATCH != 0) {
+ usersTimer.reset();
+ log.info("Deleted users: " + i + " / " + users.size());
+ }
+ realmTimer.reset();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/other/pom.xml b/testsuite/integration-arquillian/tests/other/pom.xml
index 8b04c07..ec894b4 100644
--- a/testsuite/integration-arquillian/tests/other/pom.xml
+++ b/testsuite/integration-arquillian/tests/other/pom.xml
@@ -146,6 +146,12 @@
<module>mod_auth_mellon</module>
</modules>
</profile>
+ <profile>
+ <id>jpa-performance</id>
+ <modules>
+ <module>jpa-performance</module>
+ </modules>
+ </profile>
</profiles>
</project>
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index b2438dd..7addcc0 100644
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -108,6 +108,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
+ <project.build.directory>${project.build.directory}</project.build.directory>
<browser>${browser}</browser>
<firefox_binary>${firefox_binary}</firefox_binary>
<shouldDeploy>false</shouldDeploy>
@@ -223,6 +224,12 @@
<version>2.1.0.Alpha3</version><!-- TODO upgrade <arquillian-graphene.version> and use ${arquillian-graphene.version} -->
</dependency>
+ <dependency>
+ <groupId>jfree</groupId>
+ <artifactId>jfreechart</artifactId>
+ <version>1.0.13</version>
+ </dependency>
+
<!-- <dependency>
<groupId>org.arquillian.extension</groupId>
<artifactId>arquillian-recorder-reporter-impl</artifactId>
@@ -411,10 +418,83 @@
<groupId>org.codehaus.mojo</groupId>
<artifactId>xml-maven-plugin</artifactId>
</plugin>
+ <plugin>
+ <groupId>org.liquibase</groupId>
+ <artifactId>liquibase-maven-plugin</artifactId>
+ </plugin>
</plugins>
</build>
</profile>
+ <!-- MySQL -->
+ <profile>
+ <activation>
+ <property>
+ <name>keycloak.connectionsJpa.driver</name>
+ <value>com.mysql.jdbc.Driver</value>
+ </property>
+ </activation>
+ <id>mysql</id>
+ <dependencies>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ </dependency>
+ </dependencies>
+ </profile>
+
+ <!-- PostgreSQL -->
+ <profile>
+ <activation>
+ <property>
+ <name>keycloak.connectionsJpa.driver</name>
+ <value>org.postgresql.Driver</value>
+ </property>
+ </activation>
+ <id>postgresql</id>
+ <dependencies>
+ <dependency>
+ <groupId>org.postgresql</groupId>
+ <artifactId>postgresql</artifactId>
+ <version>${postgresql.version}</version>
+ </dependency>
+ </dependencies>
+ </profile>
+
+ <profile>
+ <id>clean-jpa</id>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.liquibase</groupId>
+ <artifactId>liquibase-maven-plugin</artifactId>
+ <configuration>
+ <changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
+
+ <url>${keycloak.connectionsJpa.url}</url>
+ <driver>${keycloak.connectionsJpa.driver}</driver>
+ <username>${keycloak.connectionsJpa.user}</username>
+ <password>${keycloak.connectionsJpa.password}</password>
+
+ <promptOnNonLocalDatabase>false</promptOnNonLocalDatabase>
+ <databaseClass>${keycloak.connectionsJpa.liquibaseDatabaseClass}</databaseClass>
+ </configuration>
+ <executions>
+ <execution>
+ <id>clean-jpa</id>
+ <phase>clean</phase>
+ <goals>
+ <goal>dropAll</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+
<profile>
<id>auth-server-wildfly</id>
<properties>