GH-8680: Check DB for table on start (#8690)
* GH-8680: Check DB for table on start Fixes https://github.com/spring-projects/spring-integration/issues/8680 If database is not initialized properly before application start, we may lose messages at runtime when we fail to insert data into DB * Implement `SmartLifecycle` on `JdbcMessageStore`, `JdbcChannelMessageStore`, `JdbcMetadataStore`, and `DefaultLockRepository` to perform `SELECT COUNT` query in `start()` to fail fast if no required table is present. * Refactor `AbstractJdbcChannelMessageStoreTests` into JUnit 5 and use `MySqlContainerTest` for more coverage * Fix newly failed tests which had DB not initialized before * Exclude `commons-logging` from `commons-dbcp2` dependency to avoid classpath conflict * Document the new feature * * Fix HTTP URL in the `DataSource-mysql-context.xml` * Fix language in docs Co-authored-by: Gary Russell <grussell@vmware.com> * * Add `setCheckDatabaseOnStart(false)` to disable the check query for all the SI JDBC components * Fix language in Javadocs Co-authored-by: Gary Russell <grussell@vmware.com> --------- Co-authored-by: Gary Russell <grussell@vmware.com>
This commit is contained in:
@@ -740,7 +740,9 @@ project('spring-integration-jdbc') {
|
||||
testImplementation "org.apache.derby:derbyclient:$derbyVersion"
|
||||
testImplementation "org.postgresql:postgresql:$postgresVersion"
|
||||
testImplementation "mysql:mysql-connector-java:$mysqlVersion"
|
||||
testImplementation "org.apache.commons:commons-dbcp2:$commonsDbcp2Version"
|
||||
testImplementation ("org.apache.commons:commons-dbcp2:$commonsDbcp2Version") {
|
||||
exclude group: 'commons-logging'
|
||||
}
|
||||
testImplementation 'org.testcontainers:mysql'
|
||||
testImplementation 'org.testcontainers:postgresql'
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@@ -29,6 +30,8 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
@@ -45,6 +48,12 @@ import org.springframework.util.Assert;
|
||||
* Otherwise, it opens a possibility to break {@link java.util.concurrent.locks.Lock} contract,
|
||||
* where {@link JdbcLockRegistry} uses non-shared {@link java.util.concurrent.locks.ReentrantLock}s
|
||||
* for local synchronizations.
|
||||
* <p>
|
||||
* This class implements {@link SmartLifecycle} and calls
|
||||
* {@code SELECT COUNT(REGION) FROM %sLOCK} query
|
||||
* according to the provided prefix on {@link #start()} to check if required table is present in DB.
|
||||
* The application context will fail to start if the table is not present.
|
||||
* This check can be disabled via {@link #setCheckDatabaseOnStart(boolean)}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Artem Bilan
|
||||
@@ -56,7 +65,10 @@ import org.springframework.util.Assert;
|
||||
* @since 4.3
|
||||
*/
|
||||
public class DefaultLockRepository
|
||||
implements LockRepository, InitializingBean, ApplicationContextAware, SmartInitializingSingleton {
|
||||
implements LockRepository, InitializingBean, ApplicationContextAware, SmartInitializingSingleton,
|
||||
SmartLifecycle {
|
||||
|
||||
private static final LogAccessor LOGGER = new LogAccessor(DefaultLockRepository.class);
|
||||
|
||||
/**
|
||||
* Default value for the table prefix property.
|
||||
@@ -72,6 +84,8 @@ public class DefaultLockRepository
|
||||
|
||||
private final JdbcTemplate template;
|
||||
|
||||
private final AtomicBoolean started = new AtomicBoolean();
|
||||
|
||||
private Duration ttl = DEFAULT_TTL;
|
||||
|
||||
private String prefix = DEFAULT_TABLE_PREFIX;
|
||||
@@ -116,6 +130,10 @@ public class DefaultLockRepository
|
||||
WHERE REGION=? AND LOCK_KEY=? AND CLIENT_ID=?
|
||||
""";
|
||||
|
||||
private String countAllQuery = """
|
||||
SELECT COUNT(REGION) FROM %sLOCK
|
||||
""";
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
@@ -126,6 +144,8 @@ public class DefaultLockRepository
|
||||
|
||||
private TransactionTemplate serializableTransactionTemplate;
|
||||
|
||||
private boolean checkDatabaseOnStart = true;
|
||||
|
||||
/**
|
||||
* Constructor that initializes the client id that will be associated for
|
||||
* all the locks persisted by the store instance to a random {@link UUID}.
|
||||
@@ -293,6 +313,7 @@ public class DefaultLockRepository
|
||||
this.insertQuery = String.format(this.insertQuery, this.prefix);
|
||||
this.countQuery = String.format(this.countQuery, this.prefix);
|
||||
this.renewQuery = String.format(this.renewQuery, this.prefix);
|
||||
this.countAllQuery = String.format(this.countAllQuery, this.prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -325,6 +346,41 @@ public class DefaultLockRepository
|
||||
this.serializableTransactionTemplate = new TransactionTemplate(this.transactionManager, transactionDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* The flag to perform a database check query on start or not.
|
||||
* @param checkDatabaseOnStart false to not perform the database check.
|
||||
* @since 6.2
|
||||
*/
|
||||
public void setCheckDatabaseOnStart(boolean checkDatabaseOnStart) {
|
||||
this.checkDatabaseOnStart = checkDatabaseOnStart;
|
||||
if (!checkDatabaseOnStart) {
|
||||
LOGGER.info("The 'DefaultLockRepository' won't be started automatically " +
|
||||
"and required table is not going be checked.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return this.checkDatabaseOnStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (this.started.compareAndSet(false, true) && this.checkDatabaseOnStart) {
|
||||
this.template.queryForObject(this.countAllQuery, Integer.class); // If no table in DB, an exception is thrown
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.started.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.started.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.defaultTransactionTemplate.executeWithoutResult(
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
|
||||
package org.springframework.integration.jdbc.metadata;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.integration.metadata.ConcurrentMetadataStore;
|
||||
@@ -34,6 +38,12 @@ import org.springframework.util.Assert;
|
||||
* where <code>*</code> is the target database type.
|
||||
* <p>
|
||||
* The transaction management is required to use this {@link ConcurrentMetadataStore}.
|
||||
* <p>
|
||||
* This class implements {@link SmartLifecycle} and calls
|
||||
* {@code SELECT COUNT(METADATA_KEY) FROM %sMETADATA_STORE} query
|
||||
* according to the provided prefix on {@link #start()} to check if required table is present in DB.
|
||||
* The application context will fail to start if the table is not present.
|
||||
* This check can be disabled via {@link #setCheckDatabaseOnStart(boolean)}.
|
||||
*
|
||||
* @author Bojan Vukasovic
|
||||
* @author Artem Bilan
|
||||
@@ -41,7 +51,9 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingBean {
|
||||
public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingBean, SmartLifecycle {
|
||||
|
||||
private static final LogAccessor LOGGER = new LogAccessor(JdbcMetadataStore.class);
|
||||
|
||||
private static final String KEY_CANNOT_BE_NULL = "'key' cannot be null";
|
||||
|
||||
@@ -52,6 +64,8 @@ public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingB
|
||||
|
||||
private final JdbcOperations jdbcTemplate;
|
||||
|
||||
private final AtomicBoolean started = new AtomicBoolean();
|
||||
|
||||
private String tablePrefix = DEFAULT_TABLE_PREFIX;
|
||||
|
||||
private String region = "DEFAULT";
|
||||
@@ -93,6 +107,12 @@ public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingB
|
||||
HAVING COUNT(*)=0
|
||||
""";
|
||||
|
||||
private String countQuery = """
|
||||
SELECT COUNT(METADATA_KEY) FROM %sMETADATA_STORE
|
||||
""";
|
||||
|
||||
private boolean checkDatabaseOnStart = true;
|
||||
|
||||
/**
|
||||
* Instantiate a {@link JdbcMetadataStore} using provided dataSource {@link DataSource}.
|
||||
* @param dataSource a {@link DataSource}
|
||||
@@ -137,7 +157,7 @@ public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingB
|
||||
* Specify a row lock hint for the query in the lock-based operations.
|
||||
* Defaults to {@code FOR UPDATE}. Can be specified as an empty string,
|
||||
* if the target RDBMS doesn't support locking on tables from queries.
|
||||
* The value depends from RDBMS vendor, e.g. SQL Server requires {@code WITH (ROWLOCK)}.
|
||||
* The value depends on the RDBMS vendor, e.g. SQL Server requires {@code WITH (ROWLOCK)}.
|
||||
* @param lockHint the RDBMS vendor-specific lock hint.
|
||||
* @since 5.0.7
|
||||
*/
|
||||
@@ -154,6 +174,42 @@ public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingB
|
||||
this.replaceValueByKeyQuery = String.format(this.replaceValueByKeyQuery, this.tablePrefix);
|
||||
this.removeValueQuery = String.format(this.removeValueQuery, this.tablePrefix);
|
||||
this.putIfAbsentValueQuery = String.format(this.putIfAbsentValueQuery, this.tablePrefix, this.tablePrefix);
|
||||
this.countQuery = String.format(this.putIfAbsentValueQuery, this.tablePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* The flag to perform a database check query on start or not.
|
||||
* @param checkDatabaseOnStart false to not perform the database check.
|
||||
* @since 6.2
|
||||
*/
|
||||
public void setCheckDatabaseOnStart(boolean checkDatabaseOnStart) {
|
||||
this.checkDatabaseOnStart = checkDatabaseOnStart;
|
||||
if (!checkDatabaseOnStart) {
|
||||
LOGGER.info("The 'DefaultLockRepository' won't be started automatically " +
|
||||
"and required table is not going be checked.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return this.checkDatabaseOnStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (this.started.compareAndSet(false, true) && this.checkDatabaseOnStart) {
|
||||
this.jdbcTemplate.queryForObject(this.countQuery, Integer.class); // If no table in DB, an exception is thrown
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.started.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.started.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -162,7 +218,7 @@ public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingB
|
||||
Assert.notNull(key, KEY_CANNOT_BE_NULL);
|
||||
Assert.notNull(value, "'value' cannot be null");
|
||||
while (true) {
|
||||
//try to insert if does not exists
|
||||
//try to insert if the entry does not exist
|
||||
int affectedRows = tryToPutIfAbsent(key, value);
|
||||
if (affectedRows > 0) {
|
||||
//it was not in the table, so we have just inserted
|
||||
@@ -218,7 +274,7 @@ public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingB
|
||||
Assert.notNull(key, KEY_CANNOT_BE_NULL);
|
||||
Assert.notNull(value, "'value' cannot be null");
|
||||
while (true) {
|
||||
//try to insert if does not exist, if exists we will try to update it
|
||||
//try to insert if the entry does not exist, if it exists we will try to update it
|
||||
int affectedRows = tryToPutIfAbsent(key, value);
|
||||
if (affectedRows == 0) {
|
||||
//since value is not inserted, means it is already present
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -23,6 +23,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
@@ -31,6 +32,7 @@ import java.util.function.Supplier;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.log.LogAccessor;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
@@ -73,6 +75,11 @@ import org.springframework.util.StringUtils;
|
||||
* The SQL scripts for creating the table are packaged
|
||||
* under {@code org/springframework/integration/jdbc/schema-*.sql},
|
||||
* where {@code *} denotes the target database type.
|
||||
* <p>
|
||||
* This class implements {@link SmartLifecycle} and calls {@link #getMessageGroupCount()}
|
||||
* on {@link #start()} to check if required table is present in DB.
|
||||
* The application context will fail to start if the table is not present.
|
||||
* This check can be disabled via {@link #setCheckDatabaseOnStart(boolean)}.
|
||||
*
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
@@ -83,7 +90,7 @@ import org.springframework.util.StringUtils;
|
||||
* @since 2.2
|
||||
*/
|
||||
@ManagedResource
|
||||
public class JdbcChannelMessageStore implements PriorityCapableChannelMessageStore, InitializingBean {
|
||||
public class JdbcChannelMessageStore implements PriorityCapableChannelMessageStore, InitializingBean, SmartLifecycle {
|
||||
|
||||
private static final LogAccessor LOGGER = new LogAccessor(JdbcChannelMessageStore.class);
|
||||
|
||||
@@ -121,6 +128,8 @@ public class JdbcChannelMessageStore implements PriorityCapableChannelMessageSto
|
||||
|
||||
private final Lock idCacheWriteLock = this.idCacheLock.writeLock();
|
||||
|
||||
private final AtomicBoolean started = new AtomicBoolean();
|
||||
|
||||
private ChannelMessageStoreQueryProvider channelMessageStoreQueryProvider;
|
||||
|
||||
private String region = DEFAULT_REGION;
|
||||
@@ -145,6 +154,8 @@ public class JdbcChannelMessageStore implements PriorityCapableChannelMessageSto
|
||||
|
||||
private boolean priorityEnabled;
|
||||
|
||||
private boolean checkDatabaseOnStart = true;
|
||||
|
||||
/**
|
||||
* Convenient constructor for configuration use.
|
||||
*/
|
||||
@@ -411,6 +422,41 @@ public class JdbcChannelMessageStore implements PriorityCapableChannelMessageSto
|
||||
this.jdbcTemplate.afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* The flag to perform a database check query on start or not.
|
||||
* @param checkDatabaseOnStart false to not perform the database check.
|
||||
* @since 6.2
|
||||
*/
|
||||
public void setCheckDatabaseOnStart(boolean checkDatabaseOnStart) {
|
||||
this.checkDatabaseOnStart = checkDatabaseOnStart;
|
||||
if (!checkDatabaseOnStart) {
|
||||
LOGGER.info("The 'DefaultLockRepository' won't be started automatically " +
|
||||
"and required table is not going be checked.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return this.checkDatabaseOnStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (this.started.compareAndSet(false, true) && this.checkDatabaseOnStart) {
|
||||
getMessageGroupCount(); // If no table in DB, an exception is thrown
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.started.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.started.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a message in the database. The groupId identifies the channel for which
|
||||
* the message is to be stored.
|
||||
|
||||
@@ -26,11 +26,13 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.core.serializer.Deserializer;
|
||||
import org.springframework.core.serializer.Serializer;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
@@ -66,6 +68,11 @@ import org.springframework.util.StringUtils;
|
||||
* please consider using the channel-specific {@link JdbcChannelMessageStore} instead.
|
||||
* This implementation is intended for correlation components (e.g. {@code <aggregator>}),
|
||||
* {@code <delayer>} and similar.
|
||||
* <p>
|
||||
* This class implements {@link SmartLifecycle} and calls {@link #getMessageGroupCount()}
|
||||
* on {@link #start()} to check if required tables are present in DB.
|
||||
* The application context will fail to start if the table is not present.
|
||||
* This check can be disabled via {@link #setCheckDatabaseOnStart(boolean)}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Oleg Zhurakousky
|
||||
@@ -77,7 +84,8 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JdbcMessageStore extends AbstractMessageGroupStore implements MessageStore, BeanClassLoaderAware {
|
||||
public class JdbcMessageStore extends AbstractMessageGroupStore
|
||||
implements MessageStore, BeanClassLoaderAware, SmartLifecycle {
|
||||
|
||||
/**
|
||||
* Default value for the table prefix property.
|
||||
@@ -234,6 +242,8 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa
|
||||
|
||||
private final Map<Query, String> queryCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final AtomicBoolean started = new AtomicBoolean();
|
||||
|
||||
private String region = "DEFAULT";
|
||||
|
||||
private String tablePrefix = DEFAULT_TABLE_PREFIX;
|
||||
@@ -247,6 +257,8 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa
|
||||
|
||||
private LobHandler lobHandler = new DefaultLobHandler();
|
||||
|
||||
private boolean checkDatabaseOnStart = true;
|
||||
|
||||
/**
|
||||
* Create a {@link MessageStore} with all mandatory properties.
|
||||
* @param dataSource a {@link DataSource}
|
||||
@@ -331,6 +343,41 @@ public class JdbcMessageStore extends AbstractMessageGroupStore implements Messa
|
||||
this.deserializer.addAllowedPatterns(patterns);
|
||||
}
|
||||
|
||||
/**
|
||||
* The flag to perform a database check query on start or not.
|
||||
* @param checkDatabaseOnStart false to not perform the database check.
|
||||
* @since 6.2
|
||||
*/
|
||||
public void setCheckDatabaseOnStart(boolean checkDatabaseOnStart) {
|
||||
this.checkDatabaseOnStart = checkDatabaseOnStart;
|
||||
if (!checkDatabaseOnStart) {
|
||||
logger.info("The 'DefaultLockRepository' won't be started automatically " +
|
||||
"and required table is not going be checked.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoStartup() {
|
||||
return this.checkDatabaseOnStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
if (this.started.compareAndSet(false, true) && this.checkDatabaseOnStart) {
|
||||
getMessageGroupCount(); // If no table in DB, an exception is thrown
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
this.started.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return this.started.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> removeMessage(UUID id) {
|
||||
Message<?> message = getMessage(id);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
* Copyright 2016-2023 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.
|
||||
@@ -20,8 +20,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.core.serializer.DefaultDeserializer;
|
||||
@@ -81,7 +81,7 @@ public class JdbcMessageStoreParserTests {
|
||||
assertThat(ReflectionTestUtils.getField(store, "lobHandler")).isEqualTo(context.getBean(LobHandler.class));
|
||||
}
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -19,8 +19,8 @@ package org.springframework.integration.jdbc.config;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
@@ -42,6 +42,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.1
|
||||
*
|
||||
*/
|
||||
@@ -54,7 +56,7 @@ public class StoredProcMessageHandlerParserTests {
|
||||
private static volatile int adviceCalled;
|
||||
|
||||
@Test
|
||||
public void testProcedureNameIsSet() throws Exception {
|
||||
public void testProcedureNameIsSet() {
|
||||
setUp("basicStoredProcOutboundChannelAdapterTest.xml", getClass());
|
||||
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(this.consumer);
|
||||
@@ -72,7 +74,7 @@ public class StoredProcMessageHandlerParserTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testProcedurepParametersAreSet() throws Exception {
|
||||
public void testProcedureParametersAreSet() {
|
||||
setUp("basicStoredProcOutboundChannelAdapterTest.xml", getClass());
|
||||
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(this.consumer);
|
||||
@@ -114,7 +116,7 @@ public class StoredProcMessageHandlerParserTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testSqlParametersAreSet() throws Exception {
|
||||
public void testSqlParametersAreSet() {
|
||||
setUp("basicStoredProcOutboundChannelAdapterTest.xml", getClass());
|
||||
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(this.consumer);
|
||||
@@ -161,15 +163,15 @@ public class StoredProcMessageHandlerParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adviceCalled() throws Exception {
|
||||
public void adviceCalled() {
|
||||
setUp("advisedStoredProcOutboundChannelAdapterTest.xml", getClass());
|
||||
|
||||
MessageHandler handler = TestUtils.getPropertyValue(this.consumer, "handler", MessageHandler.class);
|
||||
handler.handleMessage(new GenericMessage<String>("foo"));
|
||||
handler.handleMessage(new GenericMessage<>("foo"));
|
||||
assertThat(adviceCalled).isEqualTo(1);
|
||||
}
|
||||
|
||||
@After
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
<int:channel id="target"/>
|
||||
<jdbc:embedded-database id="dataSource" type="HSQL"/>
|
||||
|
||||
<int-jdbc:message-store id="messageStore" data-source="dataSource"/>
|
||||
|
||||
<int-jdbc:stored-proc-outbound-channel-adapter id="storedProcedureOutboundChannelAdapter"
|
||||
<int-jdbc:stored-proc-outbound-channel-adapter id="storedProcedureOutboundChannelAdapter"
|
||||
data-source="dataSource" channel="target"
|
||||
stored-procedure-name="testProcedure1">
|
||||
<int-jdbc:sql-parameter-definition name="username" direction="IN" type="VARCHAR"/>
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xmlns:int-jdbc="http://www.springframework.org/schema/integration/jdbc"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xmlns:int-jdbc="http://www.springframework.org/schema/integration/jdbc"
|
||||
xmlns:int="http://www.springframework.org/schema/integration"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
|
||||
http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd
|
||||
http://www.springframework.org/schema/integration/jdbc https://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<int:channel id="target"/>
|
||||
<jdbc:embedded-database id="dataSource" type="HSQL"/>
|
||||
|
||||
<int-jdbc:message-store id="messageStore" data-source="dataSource"/>
|
||||
<int:channel id="target"/>
|
||||
|
||||
<int-jdbc:stored-proc-outbound-channel-adapter id="storedProcedureOutboundChannelAdapter"
|
||||
data-source="dataSource" channel="target"
|
||||
stored-procedure-name="testProcedure1">
|
||||
<int-jdbc:sql-parameter-definition name="username" direction="IN" type="VARCHAR"/>
|
||||
<int-jdbc:sql-parameter-definition name="password" direction="OUT" />
|
||||
<int-jdbc:sql-parameter-definition name="age" direction="INOUT" type="INTEGER" scale="5"/>
|
||||
<int-jdbc:sql-parameter-definition name="description" />
|
||||
<int-jdbc:parameter name="username" value="kenny" type="java.lang.String"/>
|
||||
<int-jdbc:parameter name="description" value="Who killed Kenny?"/>
|
||||
<int-jdbc:parameter name="password" expression="payload.username"/>
|
||||
<int-jdbc:parameter name="age" value="30" type="java.lang.Integer"/>
|
||||
<jdbc:embedded-database id="dataSource" type="HSQL"/>
|
||||
|
||||
<int-jdbc:stored-proc-outbound-channel-adapter id="storedProcedureOutboundChannelAdapter"
|
||||
data-source="dataSource" channel="target"
|
||||
stored-procedure-name="testProcedure1">
|
||||
<int-jdbc:sql-parameter-definition name="username" direction="IN" type="VARCHAR"/>
|
||||
<int-jdbc:sql-parameter-definition name="password" direction="OUT"/>
|
||||
<int-jdbc:sql-parameter-definition name="age" direction="INOUT" type="INTEGER" scale="5"/>
|
||||
<int-jdbc:sql-parameter-definition name="description"/>
|
||||
<int-jdbc:parameter name="username" value="kenny" type="java.lang.String"/>
|
||||
<int-jdbc:parameter name="description" value="Who killed Kenny?"/>
|
||||
<int-jdbc:parameter name="password" expression="payload.username"/>
|
||||
<int-jdbc:parameter name="age" value="30" type="java.lang.Integer"/>
|
||||
</int-jdbc:stored-proc-outbound-channel-adapter>
|
||||
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
http://www.springframework.org/schema/integration/jdbc https://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<jdbc:embedded-database id="dataSource" type="HSQL"/>
|
||||
<jdbc:embedded-database id="dataSource">
|
||||
<jdbc:script location="org/springframework/integration/jdbc/schema-drop-h2.sql"/>
|
||||
<jdbc:script location="org/springframework/integration/jdbc/schema-h2.sql" />
|
||||
</jdbc:embedded-database>
|
||||
|
||||
<int-jdbc:message-store id="messageStore" data-source="dataSource"/>
|
||||
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
http://www.springframework.org/schema/integration/jdbc https://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<jdbc:embedded-database id="dataSource" type="HSQL"/>
|
||||
|
||||
<jdbc:embedded-database id="dataSource">
|
||||
<jdbc:script location="org/springframework/integration/jdbc/schema-drop-h2.sql"/>
|
||||
<jdbc:script location="org/springframework/integration/jdbc/schema-h2.sql" />
|
||||
</jdbc:embedded-database>
|
||||
|
||||
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
|
||||
<constructor-arg ref="dataSource"/>
|
||||
</bean>
|
||||
|
||||
@@ -5,7 +5,10 @@
|
||||
http://www.springframework.org/schema/integration/jdbc https://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<jdbc:embedded-database id="dataSource" type="HSQL" />
|
||||
<jdbc:embedded-database id="dataSource">
|
||||
<jdbc:script location="org/springframework/integration/jdbc/schema-drop-h2.sql"/>
|
||||
<jdbc:script location="org/springframework/integration/jdbc/schema-h2.sql" />
|
||||
</jdbc:embedded-database>
|
||||
|
||||
<bean id="serializer" class="org.springframework.integration.jdbc.config.JdbcMessageStoreParserTests$EnhancedSerializer" />
|
||||
|
||||
|
||||
@@ -2,15 +2,19 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xmlns:int-jdbc="http://www.springframework.org/schema/integration/jdbc"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
|
||||
http://www.springframework.org/schema/integration/jdbc https://www.springframework.org/schema/integration/jdbc/spring-integration-jdbc.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<jdbc:embedded-database id="dataSource" type="HSQL"/>
|
||||
|
||||
<int-jdbc:message-store id="messageStore" data-source="dataSource" lob-handler="lobHandler" region="FOO" table-prefix="BAR_"/>
|
||||
|
||||
|
||||
<bean id="messageStore" class="org.springframework.integration.jdbc.store.JdbcMessageStore">
|
||||
<constructor-arg ref="dataSource"/>
|
||||
<property name="lobHandler" ref="lobHandler"/>
|
||||
<property name="region" value="FOO"/>
|
||||
<property name="tablePrefix" value="BAR_"/>
|
||||
<property name="checkDatabaseOnStart" value="false"/>
|
||||
</bean>
|
||||
|
||||
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2022 the original author or authors.
|
||||
* Copyright 2016-2023 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.
|
||||
@@ -29,17 +29,20 @@ import java.util.concurrent.locks.Lock;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.h2.jdbc.JdbcSQLSyntaxErrorException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.util.UUIDConverter;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -68,6 +71,9 @@ public class JdbcLockRegistryTests {
|
||||
@Autowired
|
||||
private DataSource dataSource;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
@@ -483,6 +489,20 @@ public class JdbcLockRegistryTests {
|
||||
toUUID("foo:5"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void noTableThrowsExceptionOnStart() {
|
||||
try (TestUtils.TestApplicationContext testApplicationContext = TestUtils.createTestApplicationContext()) {
|
||||
DefaultLockRepository client = new DefaultLockRepository(this.dataSource);
|
||||
client.setPrefix("TEST_");
|
||||
client.setTransactionManager(this.transactionManager);
|
||||
testApplicationContext.registerBean("client", client);
|
||||
assertThatExceptionOfType(ApplicationContextException.class)
|
||||
.isThrownBy(testApplicationContext::refresh)
|
||||
.withRootCauseExactlyInstanceOf(JdbcSQLSyntaxErrorException.class)
|
||||
.withStackTraceContaining("Table \"TEST_LOCK\" not found");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Lock> getRegistryLocks(JdbcLockRegistry registry) {
|
||||
return TestUtils.getPropertyValue(registry, "locks", Map.class);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2022 the original author or authors.
|
||||
* Copyright 2017-2023 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.
|
||||
@@ -18,15 +18,19 @@ package org.springframework.integration.jdbc.metadata;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.derby.shared.common.error.StandardException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Bojan Vukasovic
|
||||
@@ -105,4 +109,17 @@ public class JdbcMetadataStoreTests {
|
||||
assertThat(bar).isEqualTo("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void noTableThrowsExceptionOnStart() {
|
||||
try (TestUtils.TestApplicationContext testApplicationContext = TestUtils.createTestApplicationContext()) {
|
||||
JdbcMetadataStore jdbcMetadataStore = new JdbcMetadataStore(this.dataSource);
|
||||
jdbcMetadataStore.setTablePrefix("TEST_");
|
||||
testApplicationContext.registerBean("jdbcMetadataStore", jdbcMetadataStore);
|
||||
assertThatExceptionOfType(ApplicationContextException.class)
|
||||
.isThrownBy(testApplicationContext::refresh)
|
||||
.withRootCauseExactlyInstanceOf(StandardException.class)
|
||||
.withStackTraceContaining("Table/View 'TEST_METADATA_STORE' does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
* Copyright 2021-2023 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.
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.integration.jdbc.mysql;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.testcontainers.containers.MySQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
@@ -57,4 +60,13 @@ public interface MySqlContainerTest {
|
||||
return MY_SQL_CONTAINER.getPassword();
|
||||
}
|
||||
|
||||
static DataSource dataSource() {
|
||||
BasicDataSource dataSource = new BasicDataSource();
|
||||
dataSource.setDriverClassName(getDriverClassName());
|
||||
dataSource.setUrl(getJdbcUrl());
|
||||
dataSource.setUsername(getUsername());
|
||||
dataSource.setPassword(getPassword());
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -23,7 +23,6 @@ import java.util.UUID;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@@ -509,12 +508,7 @@ public class MySqlJdbcMessageStoreTests implements MySqlContainerTest {
|
||||
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
BasicDataSource dataSource = new BasicDataSource();
|
||||
dataSource.setDriverClassName(MySqlContainerTest.getDriverClassName());
|
||||
dataSource.setUrl(MySqlContainerTest.getJdbcUrl());
|
||||
dataSource.setUsername(MySqlContainerTest.getUsername());
|
||||
dataSource.setPassword(MySqlContainerTest.getPassword());
|
||||
return dataSource;
|
||||
return MySqlContainerTest.dataSource();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -37,11 +37,13 @@ import org.apache.commons.dbcp2.PoolingDataSource;
|
||||
import org.apache.commons.pool2.ObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.hsqldb.HsqlException;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||
import org.springframework.integration.IntegrationMessageHeaderAccessor;
|
||||
@@ -51,6 +53,7 @@ import org.springframework.integration.history.MessageHistory;
|
||||
import org.springframework.integration.store.MessageGroup;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.predicate.MessagePredicate;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.util.UUIDConverter;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.PreparedStatementSetter;
|
||||
@@ -62,6 +65,7 @@ import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -577,6 +581,19 @@ public class JdbcMessageStoreTests {
|
||||
pooledMessageStore.removeMessageGroup(groupId);
|
||||
}
|
||||
|
||||
@Test
|
||||
void noTableThrowsExceptionOnStart() {
|
||||
try (TestUtils.TestApplicationContext testApplicationContext = TestUtils.createTestApplicationContext()) {
|
||||
JdbcMessageStore jdbcMessageStore = new JdbcMessageStore(this.dataSource);
|
||||
jdbcMessageStore.setTablePrefix("TEST_");
|
||||
testApplicationContext.registerBean("jdbcMessageStore", jdbcMessageStore);
|
||||
assertThatExceptionOfType(ApplicationContextException.class)
|
||||
.isThrownBy(testApplicationContext::refresh)
|
||||
.withRootCauseExactlyInstanceOf(HsqlException.class)
|
||||
.withStackTraceContaining("user lacks privilege or object not found: TEST_MESSAGE_GROUP");
|
||||
}
|
||||
}
|
||||
|
||||
public void methodForCollectionOfPayloads(Collection<String> payloads) {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -21,9 +21,8 @@ import java.sql.SQLException;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.serializer.support.SerializingConverter;
|
||||
@@ -33,7 +32,7 @@ import org.springframework.jdbc.support.lob.DefaultLobHandler;
|
||||
import org.springframework.jdbc.support.lob.LobHandler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
@@ -50,13 +49,13 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringJUnitConfig
|
||||
@DirtiesContext
|
||||
public abstract class AbstractJdbcChannelMessageStoreTests {
|
||||
|
||||
protected static final String TEST_MESSAGE_GROUP = "AbstractJdbcChannelMessageStoreTests";
|
||||
|
||||
private static final String REGION = "AbstractJdbcChannelMessageStoreTests";
|
||||
protected static final String REGION = "AbstractJdbcChannelMessageStoreTests";
|
||||
|
||||
@Autowired
|
||||
protected DataSource dataSource;
|
||||
@@ -69,8 +68,8 @@ public abstract class AbstractJdbcChannelMessageStoreTests {
|
||||
@Autowired
|
||||
protected ChannelMessageStoreQueryProvider queryProvider;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
messageStore = new JdbcChannelMessageStore(dataSource);
|
||||
messageStore.setRegion(REGION);
|
||||
messageStore.setChannelMessageStoreQueryProvider(queryProvider);
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
|
||||
<property name="dataSource" ref="dataSource" />
|
||||
<!-- <property name="defaultTimeout" value="2000" /> -->
|
||||
<!-- Postgres 9 does not support send timeouts. You may see an exception like:
|
||||
"java.sql.SQLFeatureNotSupportedException: Method org.postgresql.jdbc4.Jdbc4PreparedStatement.setQueryTimeout(int) is not yet implemented."
|
||||
-->
|
||||
<property name="defaultTimeout" value="2000" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/jdbc https://www.springframework.org/schema/jdbc/spring-jdbc.xsd">
|
||||
|
||||
<import resource="classpath:org/springframework/integration/jdbc/store/channel/DataSource-common-context.xml" />
|
||||
<import resource="classpath:org/springframework/integration/jdbc/store/channel/DataSource-common-context.xml"/>
|
||||
|
||||
<bean id="dataSource" destroy-method="close"
|
||||
class="org.apache.commons.dbcp2.BasicDataSource">
|
||||
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
|
||||
<property name="url" value="jdbc:mysql://localhost/test" />
|
||||
<property name="username" value="root" />
|
||||
<property name="password" value="" />
|
||||
<property name="initialSize" value="10" />
|
||||
</bean>
|
||||
<bean id="dataSource" class="org.springframework.integration.jdbc.mysql.MySqlContainerTest"
|
||||
factory-method="dataSource"/>
|
||||
|
||||
<bean id="queryProvider" class="org.springframework.integration.jdbc.store.channel.MySqlChannelMessageStoreQueryProvider"/>
|
||||
<jdbc:initialize-database ignore-failures="ALL">
|
||||
<jdbc:script location="classpath:org/springframework/integration/jdbc/schema-drop-mysql.sql" />
|
||||
<jdbc:script location="classpath:org/springframework/integration/jdbc/schema-mysql.sql" />
|
||||
</jdbc:initialize-database>
|
||||
|
||||
<bean id="queryProvider"
|
||||
class="org.springframework.integration.jdbc.store.channel.MySqlChannelMessageStoreQueryProvider"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2019 the original author or authors.
|
||||
* Copyright 2016-2023 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.
|
||||
@@ -16,11 +16,37 @@
|
||||
|
||||
package org.springframework.integration.jdbc.store.channel;
|
||||
|
||||
import org.h2.jdbc.JdbcSQLSyntaxErrorException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.ApplicationContextException;
|
||||
import org.springframework.integration.jdbc.store.JdbcChannelMessageStore;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @author Manuel Jordan
|
||||
* @since 4.3
|
||||
*/
|
||||
@ContextConfiguration
|
||||
public class H2JdbcChannelMessageStoreTests extends AbstractJdbcChannelMessageStoreTests {
|
||||
|
||||
@Test
|
||||
void noTableThrowsExceptionOnStart() {
|
||||
try (TestUtils.TestApplicationContext testApplicationContext = TestUtils.createTestApplicationContext()) {
|
||||
JdbcChannelMessageStore jdbcChannelMessageStore = new JdbcChannelMessageStore(this.dataSource);
|
||||
jdbcChannelMessageStore.setTablePrefix("TEST_");
|
||||
jdbcChannelMessageStore.setRegion(REGION);
|
||||
jdbcChannelMessageStore.setChannelMessageStoreQueryProvider(this.queryProvider);
|
||||
testApplicationContext.registerBean("jdbcChannelMessageStore", jdbcChannelMessageStore);
|
||||
assertThatExceptionOfType(ApplicationContextException.class)
|
||||
.isThrownBy(testApplicationContext::refresh)
|
||||
.withRootCauseExactlyInstanceOf(JdbcSQLSyntaxErrorException.class)
|
||||
.withStackTraceContaining("Table \"TEST_CHANNEL_MESSAGE\" not found");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -16,15 +16,15 @@
|
||||
|
||||
package org.springframework.integration.jdbc.store.channel;
|
||||
|
||||
import org.junit.Ignore;
|
||||
|
||||
import org.springframework.integration.jdbc.mysql.MySqlContainerTest;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
/**
|
||||
* @author Gunnar Hillert
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@Ignore
|
||||
@ContextConfiguration
|
||||
public class MySqlJdbcChannelMessageStoreTests extends AbstractJdbcChannelMessageStoreTests {
|
||||
public class MySqlJdbcChannelMessageStoreTests extends AbstractJdbcChannelMessageStoreTests
|
||||
implements MySqlContainerTest {
|
||||
|
||||
}
|
||||
|
||||
@@ -386,6 +386,7 @@ Furthermore, the index for `PriorityChannel` is commented out because it is not
|
||||
|
||||
NOTE: When using the `OracleChannelMessageStoreQueryProvider`, the priority channel index **must** be added because it is included in a hint in the query.
|
||||
|
||||
[[jdbc-db-init]]
|
||||
==== Initializing the Database
|
||||
|
||||
Before starting to use JDBC message store components, you should provision a target database with the appropriate objects.
|
||||
@@ -397,6 +398,10 @@ A common way to use these scripts is to reference them in a https://docs.spring.
|
||||
Note that the scripts are provided as samples and as specifications of the required table and column names.
|
||||
You may find that you need to enhance them for production use (for, example, by adding index declarations).
|
||||
|
||||
Starting with version 6.2, the `JdbcMessageStore`, `JdbcChannelMessageStore`, `JdbcMetadataStore`, and `DefaultLockRepository` implement `SmartLifecycle` and perform a`SELECT COUNT` query, on their respective tables, in the `start()` method to ensure that the required table (according to the provided prefix) is present in the target database.
|
||||
If the required table does not exist, the application context fails to start.
|
||||
The check can be disabled via `setCheckDatabaseOnStart(false)`.
|
||||
|
||||
[[jdbc-message-store-generic]]
|
||||
==== The Generic JDBC Message Store
|
||||
|
||||
|
||||
@@ -45,3 +45,9 @@ See <<./web-sockets.adoc#websocket-client-container-attributes, WebSockets Suppo
|
||||
|
||||
The `KafkaMessageSource` now extracts an `ErrorHandlingDeserializer` configuration from the consumer properties and re-throws `DeserializationException` extracted from failed record headers.
|
||||
See <<./kafka.adoc#kafka-inbound-pollable, Kafka Inbound Channel Adapter>> for more information.
|
||||
|
||||
[[x6.2-jdbc]]
|
||||
=== JDBC Support Changes
|
||||
|
||||
The `JdbcMessageStore`, `JdbcChannelMessageStore`, `JdbcMetadataStore`, and `DefaultLockRepository` implement `SmartLifecycle` and perform a`SELECT COUNT` query, on their respective tables, in the `start()` method to ensure that the required table (according to the provided prefix) is present in the target database.
|
||||
See <<./jdbc.adoc#jdbc-db-init, Initializing the Database>> for more information.
|
||||
|
||||
Reference in New Issue
Block a user