Skip to content

Provide database specific JdbcIndexedSessionRepository customizers #1726

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,22 +29,32 @@
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.MapSession;
import org.springframework.session.config.SessionRepositoryCustomizer;
import org.springframework.session.jdbc.JdbcIndexedSessionRepository.JdbcSession;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;

/**
* Base class for {@link JdbcIndexedSessionRepository} integration tests.
Expand All @@ -57,15 +67,27 @@ abstract class AbstractJdbcIndexedSessionRepositoryITests {

private static final String INDEX_NAME = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME;

@Autowired
private ApplicationContext applicationContext;

@Autowired
private DataSource dataSource;

@Autowired
private JdbcIndexedSessionRepository repository;

private JdbcOperations jdbcOperations;

private LobHandler lobHandler;

private SecurityContext context;

private SecurityContext changedContext;

@BeforeEach
void setUp() {
this.jdbcOperations = new JdbcTemplate(this.dataSource);
this.lobHandler = new DefaultLobHandler();
this.context = SecurityContextHolder.createEmptyContext();
this.context.setAuthentication(new UsernamePasswordAuthenticationToken("username-" + UUID.randomUUID(), "na",
AuthorityUtils.createAuthorityList("ROLE_USER")));
Expand Down Expand Up @@ -759,6 +781,32 @@ void saveWithLargeAttribute() {
assertThat((byte[]) session.getAttribute(attributeName)).hasSize(arraySize);
}

@Test // gh-1213
void saveNewSessionAttributeConcurrently() {
JdbcSession session = this.repository.createSession();
this.repository.save(session);
String attributeName = "attribute1";
String attributeValue = "value1";
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
this.jdbcOperations.update("INSERT INTO SPRING_SESSION_ATTRIBUTES VALUES (?, ?, ?)", (ps) -> {
ps.setString(1, (String) ReflectionTestUtils.getField(session, "primaryKey"));
ps.setString(2, attributeName);
lobCreator.setBlobAsBytes(ps, 3, "value2".getBytes());
});
}
session.setAttribute(attributeName, attributeValue);
if (this.applicationContext.getBeansOfType(SessionRepositoryCustomizer.class).isEmpty()) {
// without DB specific upsert configured we're seeing duplicate key error
assertThatExceptionOfType(DuplicateKeyException.class).isThrownBy(() -> this.repository.save(session));
}
else {
// with DB specific upsert configured we're fine
assertThatCode(() -> this.repository.save(session)).doesNotThrowAnyException();
assertThat((String) this.repository.findById(session.getId()).getAttribute(attributeName))
.isEqualTo(attributeValue);
}
}

private String getSecurityName() {
return this.context.getAuthentication().getName();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,7 +17,6 @@
package org.springframework.session.jdbc;

import java.time.Duration;
import java.time.temporal.ChronoUnit;

import org.testcontainers.containers.Db2Container;
import org.testcontainers.containers.JdbcDatabaseContainer;
Expand All @@ -38,169 +37,42 @@ final class DatabaseContainers {
private DatabaseContainers() {
}

static Db211Container db211() {
return new Db211Container();
static Db2Container db2() {
return new Db2Container("ibmcom/db2:11.5.4.0");
}

static MariaDBContainer mariaDb5() {
return new MariaDb5Container();
static MariaDBContainer<?> mariaDb() {
return new MariaDBContainer<>("mariadb:10.5.7");
}

static MariaDBContainer mariaDb10() {
return new MariaDb10Container();
static MySQLContainer<?> mySql() {
return new MySQLContainer<>("mysql:8.0.22");
}

static MySQLContainer mySql5() {
return new MySql5Container();
}

static MySQLContainer mySql8() {
return new MySql8Container();
}

static OracleXeContainer oracleXe() {
return new OracleXeContainer();
}

static PostgreSQLContainer postgreSql9() {
return new PostgreSql9Container();
}

static PostgreSQLContainer postgreSql10() {
return new PostgreSql10Container();
}

static PostgreSQLContainer postgreSql11() {
return new PostgreSql11Container();
}

static MSSQLServerContainer sqlServer2017() {
return new SqlServer2017Container();
}

private static class Db211Container extends Db2Container {

Db211Container() {
super("ibmcom/db2:11.5.0.0a");
}

}

private static class MariaDb5Container extends MariaDBContainer<MariaDb5Container> {

MariaDb5Container() {
super("mariadb:5.5.64");
}

@Override
protected void configure() {
super.configure();
setCommand("mysqld", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci",
"--innodb_large_prefix", "--innodb_file_format=barracuda", "--innodb-file-per-table");
}

}

private static class MariaDb10Container extends MariaDBContainer<MariaDb10Container> {

MariaDb10Container() {
super("mariadb:10.4.8");
}

@Override
protected void configure() {
super.configure();
setCommand("mysqld", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci");
}

}

private static class MySql5Container extends MySQLContainer<MySql5Container> {

MySql5Container() {
super("mysql:5.7.27");
}

@Override
protected void configure() {
super.configure();
setCommand("mysqld", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci");
}

@Override
public String getDriverClassName() {
return "com.mysql.cj.jdbc.Driver";
}

}

private static class MySql8Container extends MySQLContainer<MySql8Container> {

MySql8Container() {
super("mysql:8.0.17");
}

@Override
protected void configure() {
super.configure();
setCommand("mysqld", "--default-authentication-plugin=mysql_native_password");
}

@Override
public String getDriverClassName() {
return "com.mysql.cj.jdbc.Driver";
}

}
static OracleContainer oracle() {
return new OracleContainer() {

private static class OracleXeContainer extends OracleContainer {
@Override
protected void configure() {
this.waitStrategy = new LogMessageWaitStrategy().withRegEx(".*DATABASE IS READY TO USE!.*\\s")
.withStartupTimeout(Duration.ofMinutes(10));
addEnv("ORACLE_PWD", getPassword());
}

@Override
protected void configure() {
super.configure();
this.waitStrategy = new LogMessageWaitStrategy().withRegEx(".*DATABASE IS READY TO USE!.*\\s")
.withStartupTimeout(Duration.of(10, ChronoUnit.MINUTES));
setShmSize(1024L * 1024L * 1024L);
addEnv("ORACLE_PWD", getPassword());
}

@Override
protected void waitUntilContainerStarted() {
getWaitStrategy().waitUntilReady(this);
}
@Override
protected void waitUntilContainerStarted() {
getWaitStrategy().waitUntilReady(this);
}

};
}

private static class PostgreSql9Container extends PostgreSQLContainer<PostgreSql9Container> {

PostgreSql9Container() {
super("postgres:9.6.15");
}

}

private static class PostgreSql10Container extends PostgreSQLContainer<PostgreSql10Container> {

PostgreSql10Container() {
super("postgres:10.10");
}

static PostgreSQLContainer<?> postgreSql() {
return new PostgreSQLContainer<>("postgres:13.0");
}

private static class PostgreSql11Container extends PostgreSQLContainer<PostgreSql11Container> {

PostgreSql11Container() {
super("postgres:11.5");
}

}

private static class SqlServer2017Container extends MSSQLServerContainer<SqlServer2017Container> {

SqlServer2017Container() {
super("mcr.microsoft.com/mssql/server:2017-CU16");
}

static MSSQLServerContainer<?> sqlServer() {
return new MSSQLServerContainer<>("mcr.microsoft.com/mssql/server:2019-CU8-ubuntu-16.04");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2014-2020 the original author or authors.
*
* 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
*
* https://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.springframework.session.jdbc;

import org.junit.jupiter.api.extension.ExtendWith;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;

/**
* Integration tests for {@link JdbcIndexedSessionRepository} using IBM DB2 database with
* {@link Db2JdbcIndexedSessionRepositoryCustomizer}.
*
* @author Vedran Pavic
*/
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration
class Db2JdbcIndexedSessionRepositoryCustomizerITests extends Db2JdbcIndexedSessionRepositoryITests {

@Configuration
static class CustomizerConfig extends Config {

@Bean
Db2JdbcIndexedSessionRepositoryCustomizer db2JdbcIndexedSessionRepositoryCustomizer() {
return new Db2JdbcIndexedSessionRepositoryCustomizer();
}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2014-2019 the original author or authors.
* Copyright 2014-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,21 +27,21 @@
import org.springframework.test.context.web.WebAppConfiguration;

/**
* Integration tests for {@link JdbcIndexedSessionRepository} using IBM DB2 11.x database.
* Integration tests for {@link JdbcIndexedSessionRepository} using IBM DB2 database.
*
* @author Vedran Pavic
*/
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration
class Db211JdbcIndexedSessionRepositoryITests extends AbstractContainerJdbcIndexedSessionRepositoryITests {
class Db2JdbcIndexedSessionRepositoryITests extends AbstractContainerJdbcIndexedSessionRepositoryITests {

@Configuration
static class Config extends BaseContainerConfig {

@Bean
Db2Container databaseContainer() {
Db2Container databaseContainer = DatabaseContainers.db211();
Db2Container databaseContainer = DatabaseContainers.db2();
databaseContainer.start();
return databaseContainer;
}
Expand Down
Loading