INT-4306: Add support for JdbcMetadataStore

JIRA: https://jira.spring.io/browse/INT-4306

Fixed checkstyle errors

Fixed checkstyle errors

Fixed checkstyle errors and sql schema

Manually fixed derby sql schema

Fixing some issues

added region support

small refactorings

test checkstyle fix

integration tests and fixes

integration tests and fixes
This commit is contained in:
Bojan Vukasovic
2017-06-24 15:37:51 +02:00
committed by Artem Bilan
parent 54b0800d08
commit 47003077cc
29 changed files with 505 additions and 11 deletions

View File

@@ -337,6 +337,9 @@ project('spring-integration-file') {
testCompile project(":spring-integration-redis")
testCompile project(":spring-integration-redis").sourceSets.test.output
testCompile project(":spring-integration-gemfire")
testCompile project(":spring-integration-jdbc")
testCompile "org.apache.derby:derby:$derbyVersion"
testCompile "org.apache.derby:derbyclient:$derbyVersion"
testCompile "redis.clients:jedis:$jedisVersion"
}
}

0
composite_filter_bugfix Normal file
View File

View File

@@ -0,0 +1,4 @@
# Placeholders for Derby:
int.drop.script=classpath:/org/springframework/integration/jdbc/schema-drop-derby.sql
int.schema.script=classpath:/org/springframework/integration/jdbc/schema-derby.sql
int.database.incrementer.class=org.springframework.jdbc.support.incrementer.DerbyMaxValueIncrementer

View File

@@ -0,0 +1,26 @@
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<jdbc:embedded-database id="dataSource" type="DERBY"/>
<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
<jdbc:script location="${int.drop.script}"/>
<jdbc:script location="${int.schema.script}"/>
</jdbc:initialize-database>
<context:property-placeholder location="int-${ENVIRONMENT:derby}.properties"
system-properties-mode="OVERRIDE"
ignore-unresolvable="true"
order="1"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>

View File

@@ -26,17 +26,24 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sql.DataSource;
import org.apache.geode.cache.CacheFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.integration.gemfire.metadata.GemfireMetadataStore;
import org.springframework.integration.jdbc.metadata.JdbcMetadataStore;
import org.springframework.integration.metadata.ConcurrentMetadataStore;
import org.springframework.integration.redis.metadata.RedisMetadataStore;
import org.springframework.integration.redis.rules.RedisAvailable;
import org.springframework.integration.redis.rules.RedisAvailableTests;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
@@ -44,8 +51,14 @@ import org.springframework.integration.redis.rules.RedisAvailableTests;
* @since 4.0
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext // close at the end after class
public class PersistentAcceptOnceFileListFilterExternalStoreTests extends RedisAvailableTests {
@Autowired
private DataSource dataSource;
@Test
@RedisAvailable
public void testFileSystemWithRedisMetadataStore() throws Exception {
@@ -69,6 +82,13 @@ public class PersistentAcceptOnceFileListFilterExternalStoreTests extends RedisA
this.testFileSystem(new GemfireMetadataStore(new CacheFactory().create()));
}
@Test
public void testFileSystemWithJdbcMetadataStore() throws Exception {
JdbcMetadataStore metadataStore = new JdbcMetadataStore(dataSource);
metadataStore.afterPropertiesSet();
this.testFileSystem(metadataStore);
}
private void testFileSystem(ConcurrentMetadataStore store) throws Exception {
final AtomicBoolean suspend = new AtomicBoolean();

View File

@@ -0,0 +1,223 @@
/*
* Copyright 2017 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
*
* 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.springframework.integration.jdbc.metadata;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.integration.metadata.ConcurrentMetadataStore;
import org.springframework.integration.metadata.MetadataStore;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
/**
* Implementation of {@link MetadataStore} using a relational database via JDBC. SQL scripts to create the necessary
* tables are packaged as <code>org/springframework/integration/jdbc/schema-*.sql</code>, where <code>*</code> is the
* target database type.
*
* @author Bojan Vukasovic
* @since 5.0
*/
public class JdbcMetadataStore implements ConcurrentMetadataStore, InitializingBean {
/**
* Default value for the table prefix property.
*/
public static final String DEFAULT_TABLE_PREFIX = "INT_";
private volatile String tablePrefix = DEFAULT_TABLE_PREFIX;
private volatile String region = "DEFAULT";
private String getValueQuery = "SELECT METADATA_VALUE FROM %SMETADATA_STORE WHERE METADATA_KEY=? AND REGION=?";
private String getValueForUpdateQuery = "SELECT METADATA_VALUE FROM %SMETADATA_STORE WHERE METADATA_KEY=? AND REGION=? FOR UPDATE";
private String replaceValueQuery = "UPDATE %SMETADATA_STORE SET METADATA_VALUE=? WHERE METADATA_KEY=? AND METADATA_VALUE=? AND REGION=?";
private String replaceValueByKeyQuery = "UPDATE %SMETADATA_STORE SET METADATA_VALUE=? WHERE METADATA_KEY=? AND REGION=?";
private String removeValueQuery = "DELETE FROM %SMETADATA_STORE WHERE METADATA_KEY=? AND REGION=?";
private String putIfAbsentValueQuery = "INSERT INTO %SMETADATA_STORE(METADATA_KEY, METADATA_VALUE, REGION) "
+ "SELECT ?, ?, ? FROM %SMETADATA_STORE WHERE METADATA_KEY=? AND REGION=? HAVING COUNT(*)=0";
private final JdbcOperations jdbcTemplate;
@Override
public void afterPropertiesSet() throws Exception {
this.getValueQuery = String.format(this.getValueQuery, this.tablePrefix);
this.getValueForUpdateQuery = String.format(this.getValueForUpdateQuery, this.tablePrefix);
this.replaceValueQuery = String.format(this.replaceValueQuery, this.tablePrefix);
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);
}
/**
* Instantiate a {@link JdbcMetadataStore} using provided dataSource {@link DataSource}.
* @param dataSource a {@link DataSource}
*/
public JdbcMetadataStore(DataSource dataSource) {
this(new JdbcTemplate(dataSource));
}
/**
* Instantiate a {@link JdbcMetadataStore} using provided jdbcOperations {@link JdbcOperations}.
* @param jdbcOperations a {@link JdbcOperations}
*/
public JdbcMetadataStore(JdbcOperations jdbcOperations) {
Assert.notNull(jdbcOperations, "'jdbcOperations' must not be null");
this.jdbcTemplate = jdbcOperations;
}
/**
* Public setter for the table prefix property. This will be prefixed to all the table names before queries are
* executed. Defaults to {@link #DEFAULT_TABLE_PREFIX}.
*
* @param tablePrefix the tablePrefix to set
*/
public void setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
}
/**
* A unique grouping identifier for all messages persisted with this store. Using multiple regions allows the store
* to be partitioned (if necessary) for different purposes. Defaults to <code>DEFAULT</code>.
*
* @param region the region name to set
*/
public void setRegion(String region) {
Assert.hasText(region, "Region must not be null or empty.");
this.region = region;
}
@Override
@Transactional
public String putIfAbsent(String key, String value) {
Assert.notNull(key, "'key' cannot be null");
Assert.notNull(value, "'value' cannot be null");
while (true) {
//try to insert if does not exists
int affectedRows = tryToPutIfAbsent(key, value);
if (affectedRows > 0) {
//it was not in the table, so we have just inserted it
return null;
}
else {
//value should be in table. try to return it
try {
return this.jdbcTemplate.queryForObject(this.getValueQuery, String.class, key, this.region);
}
catch (EmptyResultDataAccessException e) {
//somebody deleted it between calls. try to insert again (go to beginning of while loop)
}
}
}
}
private int tryToPutIfAbsent(String key, String value) {
return this.jdbcTemplate.update(this.putIfAbsentValueQuery, ps -> {
ps.setString(1, key);
ps.setString(2, value);
ps.setString(3, this.region);
ps.setString(4, key);
ps.setString(5, this.region);
});
}
@Override
@Transactional
public boolean replace(String key, String oldValue, String newValue) {
Assert.notNull(key, "'key' cannot be null");
Assert.notNull(oldValue, "'oldValue' cannot be null");
Assert.notNull(newValue, "'newValue' cannot be null");
int affectedRows = this.jdbcTemplate.update(this.replaceValueQuery, ps -> {
ps.setString(1, newValue);
ps.setString(2, key);
ps.setString(3, oldValue);
ps.setString(4, this.region);
});
return affectedRows > 0;
}
@Override
@Transactional
public void put(String key, String value) {
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
int affectedRows = tryToPutIfAbsent(key, value);
if (affectedRows == 0) {
//since value is not inserted, means it is already present
try {
//lock row for updating
this.jdbcTemplate.queryForObject(this.getValueForUpdateQuery, String.class, key, this.region);
}
catch (EmptyResultDataAccessException e) {
//if there are no rows with this key, somebody deleted it in between two calls
continue; //try to insert again from beginning
}
//lock successful, so - replace
this.jdbcTemplate.update(this.replaceValueByKeyQuery, ps -> {
ps.setString(1, value);
ps.setString(2, key);
ps.setString(3, this.region);
});
}
return;
}
}
@Override
@Transactional
public String get(String key) {
Assert.notNull(key, "'key' cannot be null");
try {
return this.jdbcTemplate.queryForObject(this.getValueQuery, String.class, key, this.region);
}
catch (EmptyResultDataAccessException e) {
//if there are no rows with this key, return null
return null;
}
}
@Override
@Transactional
public String remove(String key) {
Assert.notNull(key, "'key' cannot be null");
String oldValue;
try {
//select old value and lock row for removal
oldValue = this.jdbcTemplate.queryForObject(this.getValueForUpdateQuery, String.class, key, this.region);
}
catch (EmptyResultDataAccessException e) {
//key is not present, so no need to delete it
return null;
}
//delete row and return old value
int updated = this.jdbcTemplate.update(this.removeValueQuery, key, this.region);
if (updated != 0) {
return oldValue;
}
return null;
}
}

View File

@@ -0,0 +1,4 @@
/**
* Contains JDBC implementation of MetadataStore
*/
package org.springframework.integration.jdbc.metadata;

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR(255) NOT NULL,
METADATA_VALUE VARCHAR(4000),
REGION VARCHAR(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
);

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR(255) NOT NULL,
METADATA_VALUE VARCHAR(4000),
REGION VARCHAR(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
);

View File

@@ -8,5 +8,5 @@ DROP TABLE INT_MESSAGE_GROUP ;
DROP TABLE INT_GROUP_TO_MESSAGE ;
DROP TABLE INT_LOCK ;
DROP TABLE INT_CHANNEL_MESSAGE ;
DROP TABLE INT_METADATA_STORE ;
DROP SEQUENCE INT_MESSAGE_SEQ ;

View File

@@ -8,4 +8,4 @@ DROP TABLE INT_MESSAGE_GROUP ;
DROP TABLE INT_GROUP_TO_MESSAGE ;
DROP TABLE INT_LOCK ;
DROP TABLE INT_CHANNEL_MESSAGE ;
DROP TABLE INT_METADATA_STORE ;

View File

@@ -8,5 +8,5 @@ DROP TABLE INT_MESSAGE_GROUP IF EXISTS;
DROP TABLE INT_GROUP_TO_MESSAGE IF EXISTS;
DROP TABLE INT_LOCK IF EXISTS;
DROP TABLE INT_CHANNEL_MESSAGE IF EXISTS;
DROP TABLE INT_METADATA_STORE IF EXISTS;
DROP SEQUENCE INT_MESSAGE_SEQ IF EXISTS;

View File

@@ -8,5 +8,5 @@ DROP TABLE INT_MESSAGE_GROUP IF EXISTS;
DROP TABLE INT_GROUP_TO_MESSAGE IF EXISTS;
DROP TABLE INT_LOCK IF EXISTS;
DROP TABLE INT_CHANNEL_MESSAGE IF EXISTS;
DROP TABLE INT_METADATA_STORE IF EXISTS;
DROP SEQUENCE INT_MESSAGE_SEQ IF EXISTS;

View File

@@ -5,4 +5,4 @@ DROP TABLE IF EXISTS INT_MESSAGE_GROUP ;
DROP TABLE IF EXISTS INT_GROUP_TO_MESSAGE ;
DROP TABLE IF EXISTS INT_LOCK ;
DROP TABLE IF EXISTS INT_CHANNEL_MESSAGE ;
DROP TABLE IF EXISTS INT_METADATA_STORE ;

View File

@@ -8,5 +8,5 @@ DROP TABLE INT_MESSAGE_GROUP ;
DROP TABLE INT_GROUP_TO_MESSAGE ;
DROP TABLE INT_LOCK ;
DROP TABLE INT_CHANNEL_MESSAGE ;
DROP TABLE INT_METADATA_STORE ;
DROP SEQUENCE INT_MESSAGE_SEQ ;

View File

@@ -8,5 +8,5 @@ DROP TABLE INT_MESSAGE_GROUP ;
DROP TABLE INT_GROUP_TO_MESSAGE ;
DROP TABLE INT_LOCK ;
DROP TABLE INT_CHANNEL_MESSAGE ;
DROP TABLE INT_METADATA_STORE ;
DROP SEQUENCE INT_MESSAGE_SEQ ;

View File

@@ -8,5 +8,5 @@ DROP TABLE INT_MESSAGE_GROUP ;
DROP TABLE INT_GROUP_TO_MESSAGE ;
DROP TABLE INT_LOCK ;
DROP TABLE INT_CHANNEL_MESSAGE ;
DROP TABLE INT_METADATA_STORE ;
DROP SEQUENCE INT_MESSAGE_SEQ ;

View File

@@ -8,5 +8,5 @@ DROP TABLE INT_MESSAGE_GROUP ;
DROP TABLE INT_GROUP_TO_MESSAGE ;
DROP TABLE INT_LOCK ;
DROP TABLE INT_CHANNEL_MESSAGE ;
DROP TABLE INT_METADATA_STORE ;
DROP SEQUENCE INT_MESSAGE_SEQ ;

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR(255) NOT NULL,
METADATA_VALUE VARCHAR(4000),
REGION VARCHAR(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
);

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR(255) NOT NULL,
METADATA_VALUE VARCHAR(4000),
REGION VARCHAR(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
);

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR(255) NOT NULL,
METADATA_VALUE VARCHAR(4000),
REGION VARCHAR(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
) ENGINE=InnoDB;

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR2(255) NOT NULL,
METADATA_VALUE VARCHAR2(4000),
REGION VARCHAR2(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
);

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR(255) NOT NULL,
METADATA_VALUE VARCHAR(4000),
REGION VARCHAR(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
);

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR(255) NOT NULL,
METADATA_VALUE VARCHAR(4000),
REGION VARCHAR(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
);

View File

@@ -51,3 +51,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY VARCHAR(255) NOT NULL,
METADATA_VALUE VARCHAR(4000),
REGION VARCHAR(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
) LOCK DATAROWS;

View File

@@ -12,7 +12,7 @@ DROP TABLE $!{IFEXISTSBEFORE} INT_MESSAGE_GROUP $!{IFEXISTS};
DROP TABLE $!{IFEXISTSBEFORE} INT_GROUP_TO_MESSAGE $!{IFEXISTS};
DROP TABLE $!{IFEXISTSBEFORE} INT_LOCK $!{IFEXISTS};
DROP TABLE $!{IFEXISTSBEFORE} INT_CHANNEL_MESSAGE $!{IFEXISTS};
DROP TABLE $!{IFEXISTSBEFORE} INT_METADATA_STORE $!{IFEXISTS};
#if(${INT_MESSAGE_SEQ})
DROP SEQUENCE INT_MESSAGE_SEQ $!{IFEXISTS};
#end

View File

@@ -52,3 +52,11 @@ CREATE TABLE INT_CHANNEL_MESSAGE (
CREATE INDEX INT_CHANNEL_MSG_DATE_IDX ON INT_CHANNEL_MESSAGE (CREATED_DATE, MESSAGE_SEQUENCE);
CREATE INDEX INT_CHANNEL_MSG_PRIORITY_IDX ON INT_CHANNEL_MESSAGE (MESSAGE_PRIORITY DESC, CREATED_DATE, MESSAGE_SEQUENCE);
CREATE TABLE INT_METADATA_STORE (
METADATA_KEY ${VARCHAR}(255) NOT NULL,
METADATA_VALUE ${VARCHAR}(4000),
REGION ${VARCHAR}(100) NOT NULL,
constraint METADATA_STORE primary key (METADATA_KEY, REGION)
)#if(${VOODOO}) ${VOODOO}#end;

View File

@@ -0,0 +1,26 @@
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<jdbc:embedded-database id="dataSource" type="DERBY"/>
<jdbc:initialize-database data-source="dataSource" ignore-failures="DROPS">
<jdbc:script location="${int.drop.script}"/>
<jdbc:script location="${int.schema.script}"/>
</jdbc:initialize-database>
<context:property-placeholder location="int-${ENVIRONMENT:derby}.properties"
system-properties-mode="OVERRIDE"
ignore-unresolvable="true"
order="1"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2017 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
*
* 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.springframework.integration.jdbc.metadata;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import javax.sql.DataSource;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
/**
* @author Bojan Vukasovic
* @since 5.0
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext // close at the end after class
@Transactional
public class JdbcMetadataStoreTests {
@Autowired
private DataSource dataSource;
private JdbcMetadataStore metadataStore;
@Before
public void init() throws Exception {
metadataStore = new JdbcMetadataStore(dataSource);
metadataStore.afterPropertiesSet();
}
@Test
public void keyAndValuesArePreservedOnPut() {
metadataStore.put("foo", "bar");
metadataStore.put("foo", "bar1");
metadataStore.put("foo2", "bar2");
String bar1 = metadataStore.get("foo");
String bar2 = metadataStore.get("foo2");
assertEquals("bar1", bar1);
assertEquals("bar2", bar2);
}
@Test
public void keyAndValuesAreNotPreservedOnRemove() {
metadataStore.put("foo", "bar");
metadataStore.put("foo2", "bar2");
metadataStore.remove("foo");
String bar = metadataStore.get("foo");
metadataStore.remove("foo2");
String bar2 = metadataStore.get("foo2");
assertNull(bar);
assertNull(bar2);
}
@Test
public void keyAndValuesAreNotOverwrittenOnPutIfAbsent() {
metadataStore.put("foo", "bar");
metadataStore.putIfAbsent("foo", "bar1");
String bar = metadataStore.get("foo");
assertEquals("bar", bar);
}
@Test
public void nonExistentKeyIsNotRemoved() {
metadataStore.remove("non-existent");
String ne = metadataStore.get("non-existent");
assertNull(ne);
}
@Test
public void existingKeyValueIsReplacedWithNewValueWhenOldValueMatches() {
metadataStore.put("foo", "bar");
metadataStore.replace("foo", "bar", "bar2");
String bar2 = metadataStore.get("foo");
assertEquals("bar2", bar2);
}
@Test
public void existingKeyValueIsNotReplacedWithNewValueWhenOldValueDoesNotMatch() {
metadataStore.put("foo", "bar");
metadataStore.replace("foo", "bar1", "bar2");
String bar = metadataStore.get("foo");
assertEquals("bar", bar);
}
}