diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
index a0ca45d4fb..d851c9b522 100755
--- a/model/jpa/pom.xml
+++ b/model/jpa/pom.xml
@@ -29,6 +29,18 @@
Keycloak Model JPA
+
+ org.h2.Driver
+ keycloak
+ sa
+
+ jdbc:h2:mem:test;MVCC=TRUE;DB_CLOSE_DELAY=-1
+ com.h2database
+ h2
+ ${h2.version}
+ file:${project.build.directory}/dependency/log4j.properties
+
+
org.bouncycastle
@@ -60,10 +72,6 @@
-
- com.h2database
- h2
-
jakarta.persistence
jakarta.persistence-api
@@ -92,6 +100,50 @@
junit
test
+
+ org.hamcrest
+ hamcrest-all
+ test
+
+
+ log4j
+ log4j
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+ org.keycloak
+ keycloak-saml-core
+
+
+ ${jdbc.mvn.groupId}
+ ${jdbc.mvn.artifactId}
+ ${jdbc.mvn.version}
+ test
+
+
+
+
+ maven-surefire-plugin
+
+
+ ${keycloak.connectionsJpa.driver}
+ ${keycloak.connectionsJpa.database}
+ ${keycloak.connectionsJpa.user}
+ ${keycloak.connectionsJpa.password}
+ ${keycloak.connectionsJpa.url}
+ file:${project.build.directory}/test-classes/log4j.properties
+
+
+
+
+
diff --git a/model/jpa/src/test/java/org/keycloak/events/jpa/JpaAdminEventQueryTest.java b/model/jpa/src/test/java/org/keycloak/events/jpa/JpaAdminEventQueryTest.java
new file mode 100644
index 0000000000..4f06ff2ea4
--- /dev/null
+++ b/model/jpa/src/test/java/org/keycloak/events/jpa/JpaAdminEventQueryTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.events.jpa;
+
+import org.keycloak.Config.Scope;
+import org.keycloak.common.ClientConnection;
+import org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory;
+import org.keycloak.connections.jpa.JpaConnectionSpi;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory;
+import org.keycloak.connections.jpa.updater.JpaUpdaterSpi;
+import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory;
+import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionSpi;
+import org.keycloak.connections.jpa.updater.liquibase.lock.LiquibaseDBLockProviderFactory;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventStoreProvider;
+import org.keycloak.events.EventStoreProviderFactory;
+import org.keycloak.events.EventStoreSpi;
+import org.keycloak.events.EventType;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RealmSpi;
+import org.keycloak.models.dblock.DBLockSpi;
+import org.keycloak.models.jpa.JpaRealmProviderFactory;
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.ProviderManager;
+import org.keycloak.provider.Spi;
+import org.keycloak.services.DefaultKeycloakSession;
+import org.keycloak.services.DefaultKeycloakSessionFactory;
+import com.google.common.collect.ImmutableSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class JpaAdminEventQueryTest {
+
+ private static final Set> ALLOWED_SPIS = ImmutableSet.>builder()
+ .add(DBLockSpi.class)
+ .add(EventStoreSpi.class)
+ .add(JpaConnectionSpi.class)
+ .add(JpaUpdaterSpi.class)
+ .add(LiquibaseConnectionSpi.class)
+ .add(RealmSpi.class)
+ .build();
+
+ private static final Set> ALLOWED_FACTORIES = ImmutableSet.>builder()
+ .add(DefaultJpaConnectionProviderFactory.class)
+ .add(EventStoreProviderFactory.class)
+ .add(JpaUpdaterProviderFactory.class)
+ .add(JpaRealmProviderFactory.class)
+ .add(LiquibaseConnectionProviderFactory.class)
+ .add(LiquibaseDBLockProviderFactory.class)
+ .build();
+
+ private static final DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory() {
+ @Override
+ protected boolean isEnabled(ProviderFactory factory, Scope scope) {
+ return super.isEnabled(factory, scope) && ALLOWED_FACTORIES.stream().filter(c -> c.isAssignableFrom(factory.getClass())).findAny().isPresent();
+ }
+
+ @Override
+ protected Map, Map> loadFactories(ProviderManager pm) {
+ spis.removeIf(s -> ! ALLOWED_SPIS.contains(s.getClass()));
+ return super.loadFactories(pm);
+ }
+ };
+ static { factory.init(); }
+
+ private final KeycloakSession session = new DefaultKeycloakSession(factory);
+ private final EventStoreProvider eventStore = session.getProvider(EventStoreProvider.class);
+
+ @Before
+ public void startTransaction() {
+ session.getTransactionManager().begin();
+ }
+
+ @After
+ public void stopTransaction() {
+ session.getTransactionManager().rollback();
+ }
+
+ @Test
+ public void testClear() {
+ eventStore.clear();
+ }
+
+ @Test
+ public void testQuery() {
+ RealmModel realm = session.realms().createRealm("realm");
+ ClientConnection cc = new DummyClientConnection();
+ eventStore.onEvent(new EventBuilder(realm, null, cc).event(EventType.LOGIN).user("u1").getEvent());
+ eventStore.onEvent(new EventBuilder(realm, null, cc).event(EventType.LOGIN).user("u2").getEvent());
+ eventStore.onEvent(new EventBuilder(realm, null, cc).event(EventType.LOGIN).user("u3").getEvent());
+ eventStore.onEvent(new EventBuilder(realm, null, cc).event(EventType.LOGIN).user("u4").getEvent());
+
+ assertThat(eventStore.createQuery()
+ .firstResult(2)
+ .getResultStream()
+ .collect(Collectors.counting()),
+ is(2L)
+ );
+ }
+
+ private static class DummyClientConnection implements ClientConnection {
+
+ @Override
+ public String getRemoteAddr() {
+ return "remoteAddr";
+ }
+
+ @Override
+ public String getRemoteHost() {
+ return "remoteHost";
+ }
+
+ @Override
+ public int getRemotePort() {
+ return -1;
+ }
+
+ @Override
+ public String getLocalAddr() {
+ return "localAddr";
+ }
+
+ @Override
+ public int getLocalPort() {
+ return -2;
+ }
+ }
+
+}
diff --git a/model/jpa/src/test/resources/log4j.properties b/model/jpa/src/test/resources/log4j.properties
new file mode 100644
index 0000000000..2d89ff6a06
--- /dev/null
+++ b/model/jpa/src/test/resources/log4j.properties
@@ -0,0 +1,42 @@
+#
+# 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.
+#
+
+log4j.rootLogger=info, keycloak
+
+log4j.appender.keycloak=org.apache.log4j.ConsoleAppender
+log4j.appender.keycloak.layout=org.apache.log4j.EnhancedPatternLayout
+keycloak.testsuite.logging.pattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
+log4j.appender.keycloak.layout.ConversionPattern=${keycloak.testsuite.logging.pattern}
+
+# Logging with "info" when running test from IDE, but disabled when running test with "mvn" . Both cases can be overriden by use system property "keycloak.logging.level" (eg. -Dkeycloak.logging.level=debug )
+log4j.logger.org.keycloak=${keycloak.logging.level:debug}
+
+keycloak.testsuite.logging.level=debug
+log4j.logger.org.keycloak.testsuite=${keycloak.testsuite.logging.level}
+
+# Enable to view loaded SPI and Providers
+log4j.logger.org.keycloak.services.DefaultKeycloakSessionFactory=debug
+log4j.logger.org.keycloak.provider.ProviderManager=debug
+# log4j.logger.org.keycloak.provider.FileSystemProviderLoaderFactory=debug
+
+# Liquibase updates logged with "info" by default. Logging level can be changed by system property "keycloak.liquibase.logging.level"
+keycloak.liquibase.logging.level=info
+log4j.logger.org.keycloak.connections.jpa.updater.liquibase=${keycloak.liquibase.logging.level}
+log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=debug
+
+# Enable to log short stack traces for log entries enabled with StackUtil.getShortStackTrace() calls
+# log4j.logger.org.keycloak.STACK_TRACE=trace