Remove possibility to start embedded HotRod server in hotrod-map module
Closes #13247
This commit is contained in:
parent
cf249fc6ae
commit
ec808d28bb
13 changed files with 163 additions and 140 deletions
|
@ -30,18 +30,6 @@
|
|||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-remote-query-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-server-router</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-server-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-server-runtime</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan.protostream</groupId>
|
||||
<artifactId>protostream-processor</artifactId>
|
||||
|
|
|
@ -16,26 +16,7 @@
|
|||
*/
|
||||
package org.keycloak.models.map.storage.hotRod.common;
|
||||
|
||||
import org.infinispan.manager.DefaultCacheManager;
|
||||
import org.infinispan.query.dsl.Query;
|
||||
import org.infinispan.rest.RestServer;
|
||||
import org.infinispan.rest.configuration.RestServerConfigurationBuilder;
|
||||
import org.infinispan.server.configuration.endpoint.SinglePortServerConfigurationBuilder;
|
||||
import org.infinispan.server.hotrod.HotRodServer;
|
||||
import org.infinispan.server.hotrod.configuration.HotRodServerConfigurationBuilder;
|
||||
import org.infinispan.server.router.RoutingTable;
|
||||
import org.infinispan.server.router.configuration.SinglePortRouterConfiguration;
|
||||
import org.infinispan.server.router.router.impl.singleport.SinglePortEndpointRouter;
|
||||
import org.infinispan.server.router.routes.Route;
|
||||
import org.infinispan.server.router.routes.RouteDestination;
|
||||
import org.infinispan.server.router.routes.RouteSource;
|
||||
import org.infinispan.server.router.routes.hotrod.HotRodServerRouteDestination;
|
||||
import org.infinispan.server.router.routes.rest.RestServerRouteDestination;
|
||||
import org.infinispan.server.router.routes.singleport.SinglePortRouteSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
||||
|
@ -44,51 +25,6 @@ public class HotRodUtils {
|
|||
|
||||
public static final int DEFAULT_MAX_RESULTS = Integer.MAX_VALUE >> 1;
|
||||
|
||||
/**
|
||||
* Not suitable for a production usage. Only for development and test purposes.
|
||||
* Also do not use in clustered environment.
|
||||
* @param hotRodServer HotRodServer
|
||||
* @param hotRodCacheManager DefaultCacheManager
|
||||
* @param embeddedPort int
|
||||
*/
|
||||
public static void createHotRodMapStoreServer(HotRodServer hotRodServer, DefaultCacheManager hotRodCacheManager, int embeddedPort) {
|
||||
HotRodServerConfigurationBuilder hotRodServerConfigurationBuilder = new HotRodServerConfigurationBuilder();
|
||||
hotRodServerConfigurationBuilder.startTransport(false);
|
||||
hotRodServerConfigurationBuilder.port(embeddedPort);
|
||||
hotRodServer.start(hotRodServerConfigurationBuilder.build(), hotRodCacheManager);
|
||||
|
||||
RestServerConfigurationBuilder restServerConfigurationBuilder = new RestServerConfigurationBuilder();
|
||||
restServerConfigurationBuilder.startTransport(false);
|
||||
restServerConfigurationBuilder.port(embeddedPort);
|
||||
RestServer restServer = new RestServer();
|
||||
restServer.start(restServerConfigurationBuilder.build(), hotRodCacheManager);
|
||||
|
||||
SinglePortRouteSource routeSource = new SinglePortRouteSource();
|
||||
Set<Route<? extends RouteSource, ? extends RouteDestination>> routes = new HashSet<>();
|
||||
routes.add(new Route<>(routeSource, new HotRodServerRouteDestination("hotrod", hotRodServer)));
|
||||
routes.add(new Route<>(routeSource, new RestServerRouteDestination("rest", restServer)));
|
||||
|
||||
SinglePortRouterConfiguration singlePortRouter = new SinglePortServerConfigurationBuilder().port(embeddedPort).build();
|
||||
SinglePortEndpointRouter endpointServer = new SinglePortEndpointRouter(singlePortRouter);
|
||||
endpointServer.start(new RoutingTable(routes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Not suitable for a production usage. Only for development and test purposes.
|
||||
* Also do not use in clustered environment.
|
||||
* @param embeddedPort int
|
||||
*/
|
||||
public static void createHotRodMapStoreServer(int embeddedPort) {
|
||||
DefaultCacheManager hotRodCacheManager = null;
|
||||
try {
|
||||
hotRodCacheManager = new DefaultCacheManager("config/infinispan.xml");
|
||||
} catch (IOException e) {
|
||||
new RuntimeException("Cannot initialize cache manager!", e);
|
||||
}
|
||||
|
||||
HotRodUtils.createHotRodMapStoreServer(new HotRodServer(), hotRodCacheManager, embeddedPort);
|
||||
}
|
||||
|
||||
public static <T> Query<T> paginateQuery(Query<T> query, Integer first, Integer max) {
|
||||
if (first != null && first > 0) {
|
||||
query = query.startOffset(first);
|
||||
|
|
|
@ -30,7 +30,6 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.map.storage.hotRod.common.HotRodEntityDescriptor;
|
||||
import org.keycloak.models.map.storage.hotRod.common.HotRodUtils;
|
||||
import org.keycloak.models.map.storage.hotRod.common.CommonPrimitivesProtoSchemaInitializer;
|
||||
import org.keycloak.models.map.storage.hotRod.common.HotRodVersionUtils;
|
||||
|
||||
|
@ -89,14 +88,10 @@ public class DefaultHotRodConnectionProviderFactory implements HotRodConnectionP
|
|||
}
|
||||
|
||||
public void lazyInit() {
|
||||
if (config.getBoolean("embedded", false)) {
|
||||
HotRodUtils.createHotRodMapStoreServer(config.getInt("embeddedPort", 11444));
|
||||
}
|
||||
|
||||
ConfigurationBuilder remoteBuilder = new ConfigurationBuilder();
|
||||
remoteBuilder.addServer()
|
||||
.host(config.get("host", "localhost"))
|
||||
.port(config.getInt("port", 11444))
|
||||
.port(config.getInt("port", 11222))
|
||||
.clientIntelligence(ClientIntelligence.HASH_DISTRIBUTION_AWARE)
|
||||
.marshaller(new ProtoStreamMarshaller());
|
||||
|
||||
|
|
1
pom.xml
1
pom.xml
|
@ -178,6 +178,7 @@
|
|||
https://issues.redhat.com/browse/KEYCLOAK-17585?focusedCommentId=16002705&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-16002705
|
||||
-->
|
||||
<microprofile-metrics-api.version>2.3</microprofile-metrics-api.version>
|
||||
<testcontainers.version>1.16.3</testcontainers.version>
|
||||
|
||||
<!-- Maven Plugins -->
|
||||
<replacer.plugin.version>1.3.5</replacer.plugin.version>
|
||||
|
|
|
@ -469,28 +469,6 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-server-rest</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-server-router</artifactId>
|
||||
<version>${infinispan.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
<artifactId>infinispan-cachestore-remote</artifactId>
|
||||
|
|
|
@ -55,7 +55,6 @@
|
|||
<arquillian-tomcat9-container-version>1.1.0.Final</arquillian-tomcat9-container-version>
|
||||
<undertow-embedded.version>1.0.0.Alpha2</undertow-embedded.version>
|
||||
<version.org.wildfly.extras.creaper>1.6.1</version.org.wildfly.extras.creaper>
|
||||
<testcontainers.version>1.5.1</testcontainers.version>
|
||||
<appium.client.version>6.1.0</appium.client.version>
|
||||
|
||||
<!--migration properties-->
|
||||
|
|
|
@ -48,3 +48,59 @@ mvn test -Pjpa -Dtest=ClientModelTest \
|
|||
```
|
||||
|
||||
The results are available in the `target/profile.html` file.
|
||||
|
||||
Usage of Testcontainers
|
||||
-----------------------
|
||||
|
||||
Some profiles within model tests require running 3rd party software, for
|
||||
example, database or Infinispan. For running these we are using
|
||||
[Testcontainers](https://www.testcontainers.org/). This may require some
|
||||
additional configuration of your container engine.
|
||||
|
||||
#### Podman settings
|
||||
|
||||
For more details see the following [Podman guide from Quarkus webpage](https://quarkus.io/guides/podman).
|
||||
|
||||
Specifically, these steps are required:
|
||||
```shell
|
||||
# Enable the podman socket with Docker REST API (only needs to be done once)
|
||||
systemctl --user enable podman.socket --now
|
||||
|
||||
# Set the required environment variables (need to be run everytime or added to profile)
|
||||
export DOCKER_HOST=unix:///run/user/${UID}/podman/podman.sock
|
||||
```
|
||||
|
||||
Testcontainers are using [ryuk](https://hub.docker.com/r/testcontainers/ryuk)
|
||||
to cleanup containers after tests. To make this work with Podman add the
|
||||
following line to `~/.testcontainers.properties`
|
||||
```shell
|
||||
ryuk.container.privileged=true
|
||||
```
|
||||
Alternatively, disable usage of ryuk (using this may result in stale containers
|
||||
still running after tests finish. This is not recommended especially if you are
|
||||
executing tests from Intellij IDE as it [may not stop](https://youtrack.jetbrains.com/issue/IDEA-190385)
|
||||
the containers created during test run).
|
||||
```shell
|
||||
export TESTCONTAINERS_RYUK_DISABLED=true #not recommended - see above!
|
||||
```
|
||||
|
||||
#### Docker settings
|
||||
|
||||
To use Testcontainers with Docker it is necessary to
|
||||
[make Docker available for non-root users](https://docs.docker.com/engine/install/linux-postinstall/).
|
||||
|
||||
Running HotRod tests with external Infinispan
|
||||
---------------------------------------------
|
||||
|
||||
By default, Model tests with `hot-rod` profile spawn a new Infinispan container
|
||||
with each test execution. It is also possible, to configure Model tests to
|
||||
connect to an external instance of Infinispan. To do so, execute tests with
|
||||
the following command:
|
||||
```shell
|
||||
mvn test -Phot-rod \
|
||||
-Dhot-rod.start-container=false \
|
||||
-Dhot-rod.connection.host=<host> \
|
||||
-Dhot-rod.connection.port=<port> \
|
||||
-Dhot-rod.connection.username=<username> \
|
||||
-Dhot-rod.connection.password=<password>
|
||||
```
|
||||
|
|
|
@ -120,6 +120,12 @@
|
|||
<artifactId>postgresql</artifactId>
|
||||
<version>${postgresql.driver.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>${testcontainers.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -171,7 +177,14 @@
|
|||
<keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>${keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase}</keycloak.userSessions.infinispan.preloadOfflineSessionsFromDatabase>
|
||||
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
|
||||
<org.jboss.logging.provider>log4j</org.jboss.logging.provider>
|
||||
<infinispan.version>${infinispan.version}</infinispan.version>
|
||||
</systemPropertyVariables>
|
||||
<properties>
|
||||
<property>
|
||||
<name>listener</name>
|
||||
<value>org.keycloak.testsuite.model.AfterSuiteListener</value>
|
||||
</property>
|
||||
</properties>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
|
|
@ -84,29 +84,6 @@ public class HotRodServerRule extends ExternalResource {
|
|||
InfinispanUtil.setTimeServiceToKeycloakTime(hotRodCacheManager2);
|
||||
}
|
||||
|
||||
public void createHotRodMapStoreServer() {
|
||||
hotRodCacheManager = configureHotRodCacheManager("hotrod/infinispan.xml");
|
||||
hotRodServer = new HotRodServer();
|
||||
|
||||
HotRodUtils.createHotRodMapStoreServer(hotRodServer, hotRodCacheManager, 11444);
|
||||
|
||||
org.infinispan.client.hotrod.configuration.ConfigurationBuilder remoteBuilder = new org.infinispan.client.hotrod.configuration.ConfigurationBuilder();
|
||||
org.infinispan.client.hotrod.configuration.Configuration cfg = remoteBuilder
|
||||
.addServers(hotRodServer.getHost() + ":" + hotRodServer.getPort()).build();
|
||||
remoteCacheManager = new RemoteCacheManager(cfg);
|
||||
}
|
||||
|
||||
private DefaultCacheManager configureHotRodCacheManager(String configPath) {
|
||||
DefaultCacheManager manager = null;
|
||||
try {
|
||||
manager = new DefaultCacheManager(configPath);
|
||||
} catch (IOException e) {
|
||||
new RuntimeException(e);
|
||||
}
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
private void getCaches(String... cache) {
|
||||
for (String c: cache) {
|
||||
hotRodCacheManager.getCache(c, true);
|
||||
|
|
|
@ -72,4 +72,8 @@ public class KeycloakModelParameters {
|
|||
public void beforeSuite(Config cf) {
|
||||
|
||||
}
|
||||
|
||||
public void afterSuite() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2022 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.testsuite.model;
|
||||
|
||||
import org.junit.runner.Result;
|
||||
import org.junit.runner.notification.RunListener;
|
||||
|
||||
public class AfterSuiteListener extends RunListener {
|
||||
|
||||
@Override
|
||||
public void testRunFinished(Result result) throws Exception {
|
||||
KeycloakModelTest.closeKeycloakSessionFactory();
|
||||
for (KeycloakModelParameters kmp : KeycloakModelTest.MODEL_PARAMETERS) {
|
||||
kmp.afterSuite();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,8 +17,7 @@
|
|||
package org.keycloak.testsuite.model.parameters;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.authorization.store.StoreFactorySpi;
|
||||
import org.keycloak.events.EventStoreSpi;
|
||||
import org.keycloak.models.ActionTokenStoreSpi;
|
||||
|
@ -31,7 +30,6 @@ import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderF
|
|||
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
||||
import org.keycloak.models.map.client.MapClientProviderFactory;
|
||||
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
|
||||
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
|
||||
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
|
||||
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
|
||||
import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory;
|
||||
|
@ -51,10 +49,14 @@ import org.keycloak.provider.ProviderFactory;
|
|||
import org.keycloak.provider.Spi;
|
||||
import org.keycloak.sessions.AuthenticationSessionSpi;
|
||||
import org.keycloak.testsuite.model.Config;
|
||||
import org.keycloak.testsuite.model.HotRodServerRule;
|
||||
import org.keycloak.testsuite.model.KeycloakModelParameters;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -62,6 +64,24 @@ import java.util.Set;
|
|||
*/
|
||||
public class HotRodMapStorage extends KeycloakModelParameters {
|
||||
|
||||
private final Logger LOG = Logger.getLogger(getClass());
|
||||
public static final String PORT = System.getProperty("hot-rod.connection.port", "11222");
|
||||
public static String HOST = System.getProperty("hot-rod.connection.host");
|
||||
public static final String USERNAME = System.getProperty("hot-rod.connection.username", "admin");
|
||||
public static final String PASSWORD = System.getProperty("hot-rod.connection.password", "admin");
|
||||
public static final Boolean START_CONTAINER = Boolean.valueOf(System.getProperty("hot-rod.start-container", "true"));
|
||||
|
||||
private static final String ZERO_TO_255
|
||||
= "(\\d{1,2}|(0|1)\\"
|
||||
+ "d{2}|2[0-4]\\d|25[0-5])";
|
||||
private static final String IP_ADDRESS_REGEX
|
||||
= ZERO_TO_255 + "\\."
|
||||
+ ZERO_TO_255 + "\\."
|
||||
+ ZERO_TO_255 + "\\."
|
||||
+ ZERO_TO_255;
|
||||
|
||||
private static final Pattern IP_ADDRESS_PATTERN = Pattern.compile("listening on (" + IP_ADDRESS_REGEX + "):" + PORT);
|
||||
|
||||
static final Set<Class<? extends Spi>> ALLOWED_SPIS = ImmutableSet.<Class<? extends Spi>>builder()
|
||||
.add(HotRodConnectionSpi.class)
|
||||
.build();
|
||||
|
@ -69,10 +89,12 @@ public class HotRodMapStorage extends KeycloakModelParameters {
|
|||
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
|
||||
.add(HotRodMapStorageProviderFactory.class)
|
||||
.add(HotRodConnectionProviderFactory.class)
|
||||
.add(ConcurrentHashMapStorageProviderFactory.class) // TODO: this should be removed when we have a HotRod implementation for each area
|
||||
.build();
|
||||
|
||||
private HotRodServerRule hotRodServerRule = new HotRodServerRule();
|
||||
private final GenericContainer<?> hotRodContainer = new GenericContainer("quay.io/infinispan/server:" + System.getProperty("infinispan.version"))
|
||||
.withEnv("USER", USERNAME)
|
||||
.withEnv("PASS", PASSWORD)
|
||||
.withNetworkMode("host");
|
||||
|
||||
@Override
|
||||
public void updateConfig(Config cf) {
|
||||
|
@ -99,19 +121,38 @@ public class HotRodMapStorage extends KeycloakModelParameters {
|
|||
.config("dir", "${project.build.directory:target}")
|
||||
.config("keyType.single-use-objects", "string");
|
||||
|
||||
if (HOST == null && START_CONTAINER) {
|
||||
Matcher matcher = IP_ADDRESS_PATTERN.matcher(hotRodContainer.getLogs());
|
||||
if (!matcher.find()) {
|
||||
LOG.errorf("Cannot find IP address of the infinispan server in log:\\n%s ", hotRodContainer.getLogs());
|
||||
throw new IllegalStateException("Cannot find IP address of the Infinispan server. See test log for Infinispan container log.");
|
||||
}
|
||||
HOST = matcher.group(1);
|
||||
}
|
||||
|
||||
cf.spi(HotRodConnectionSpi.NAME).provider(DefaultHotRodConnectionProviderFactory.PROVIDER_ID)
|
||||
.config("enableSecurity", "false")
|
||||
.config("configureRemoteCaches", "false");
|
||||
.config("host", HOST)
|
||||
.config("port", PORT)
|
||||
.config("username", USERNAME)
|
||||
.config("password", PASSWORD)
|
||||
.config("configureRemoteCaches", "true");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeSuite(Config cf) {
|
||||
hotRodServerRule.createHotRodMapStoreServer();
|
||||
if (START_CONTAINER) {
|
||||
hotRodContainer
|
||||
.withStartupTimeout(Duration.ofMinutes(5))
|
||||
.waitingFor(Wait.forLogMessage(".*Infinispan Server.*started in.*", 1))
|
||||
.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement classRule(Statement base, Description description) {
|
||||
return hotRodServerRule.apply(base, description);
|
||||
public void afterSuite() {
|
||||
if (START_CONTAINER) {
|
||||
hotRodContainer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public HotRodMapStorage() {
|
||||
|
|
|
@ -98,7 +98,6 @@ public class UserSessionConcurrencyTest extends KeycloakModelTest {
|
|||
// This is basically the same as JpaMapKeycloakTransaction#read method is doing after calling lockUserSessionsForModification() method
|
||||
if (isHotRodStore) {
|
||||
SYNC_USESSION.lock();
|
||||
releaseLockOnTransactionCommit(session, SYNC_USESSION);
|
||||
}
|
||||
|
||||
UserSessionModel uSession = lockUserSessionsForModification(session, () -> session.sessions().getUserSession(realm, uId));
|
||||
|
@ -110,6 +109,10 @@ public class UserSessionConcurrencyTest extends KeycloakModelTest {
|
|||
|
||||
cSession.setNote(OIDCLoginProtocol.STATE_PARAM, "state-" + n);
|
||||
|
||||
if (isHotRodStore) {
|
||||
releaseLockOnTransactionCommit(session, SYNC_USESSION);
|
||||
}
|
||||
|
||||
return null;
|
||||
}));
|
||||
|
||||
|
|
Loading…
Reference in a new issue