├── best-rabbit-db ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── springsource │ │ │ └── open │ │ │ └── foo │ │ │ ├── Handler.java │ │ │ ├── FooHandler.java │ │ │ └── ListenerApplication.java │ └── test │ │ ├── resources │ │ └── schema.sql │ │ └── java │ │ └── com │ │ └── springsource │ │ └── open │ │ └── foo │ │ ├── async │ │ ├── AsynchronousMessageTriggerSunnyDayTests.java │ │ ├── AsynchronousMessageTriggerAndPartialRollbackTests.java │ │ ├── AsynchronousMessageTriggerAndRollbackTests.java │ │ └── AbstractAsynchronousMessageTriggerTests.java │ │ ├── test │ │ ├── DatasourceTests.java │ │ └── MessagingTests.java │ │ ├── FailureSimulator.java │ │ └── sync │ │ ├── SynchronousMessageTriggerAndRollbackTests.java │ │ ├── SynchronousMessageTriggerSunnyDayTests.java │ │ └── SynchronousMessageTriggerAndPartialRollbackTests.java ├── docker-compose.yml └── pom.xml ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── atomikos-db ├── src │ ├── test │ │ ├── resources │ │ │ ├── first.sql │ │ │ └── second.sql │ │ └── java │ │ │ └── com │ │ │ └── springsource │ │ │ └── open │ │ │ └── db │ │ │ ├── AuditService.java │ │ │ ├── SimpleAuditService.java │ │ │ ├── test │ │ │ └── DatasourceTests.java │ │ │ ├── MultipleDatasourceTests.java │ │ │ ├── BaseDatasourceTests.java │ │ │ └── TransactionPropagationTests.java │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── com │ │ └── springsource │ │ └── open │ │ └── db │ │ └── AtomikosApplication.java └── pom.xml ├── best-db-db ├── src │ ├── test │ │ ├── resources │ │ │ ├── first.sql │ │ │ └── second.sql │ │ └── java │ │ │ ├── com │ │ │ └── springsource │ │ │ │ └── open │ │ │ │ └── db │ │ │ │ ├── DatasourceTests.java │ │ │ │ └── MultipleDatasourceTests.java │ │ │ └── test │ │ │ └── jdbc │ │ │ └── datasource │ │ │ └── DataSourceInitializer.java │ └── main │ │ ├── resources │ │ └── application.properties │ │ └── java │ │ └── com │ │ └── springsource │ │ └── open │ │ └── db │ │ ├── ChainedTransactionManager.java │ │ └── BestDatabaseApplication.java └── pom.xml ├── shared-jms-db ├── src │ ├── test │ │ ├── resources │ │ │ ├── application.properties │ │ │ └── schema.sql │ │ └── java │ │ │ └── com │ │ │ └── springsource │ │ │ └── open │ │ │ └── jms │ │ │ ├── FailureSimulator.java │ │ │ ├── test │ │ │ ├── DatasourceTests.java │ │ │ └── MessagingTests.java │ │ │ ├── SynchronousMessageTriggerAndRollbackTests.java │ │ │ ├── SynchronousMessageTriggerSunnyDayTests.java │ │ │ └── SynchronousMessageTriggerAndPartialRollbackTests.java │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring │ │ │ └── jms-context.xml │ │ └── java │ │ └── com │ │ └── springsource │ │ └── open │ │ └── jms │ │ ├── SharedDatabaseApplication.java │ │ └── JmsTransactionAwareDataSourceProxy.java └── pom.xml ├── best-jms-db ├── src │ ├── test │ │ ├── resources │ │ │ └── schema.sql │ │ └── java │ │ │ └── com │ │ │ └── springsource │ │ │ └── open │ │ │ └── foo │ │ │ ├── async │ │ │ ├── AsynchronousMessageTriggerSunnyDayTests.java │ │ │ ├── AsynchronousMessageTriggerAndRollbackTests.java │ │ │ ├── AsynchronousMessageTriggerAndPartialRollbackTests.java │ │ │ └── AbstractAsynchronousMessageTriggerTests.java │ │ │ ├── test │ │ │ ├── DatasourceTests.java │ │ │ └── MessagingTests.java │ │ │ ├── FailureSimulator.java │ │ │ └── sync │ │ │ ├── SynchronousMessageTriggerAndRollbackTests.java │ │ │ ├── SynchronousMessageTriggerSunnyDayTests.java │ │ │ └── SynchronousMessageTriggerAndPartialRollbackTests.java │ └── main │ │ ├── java │ │ └── com │ │ │ └── springsource │ │ │ └── open │ │ │ └── foo │ │ │ ├── Handler.java │ │ │ ├── FooHandler.java │ │ │ └── ListenerApplication.java │ │ └── resources │ │ └── application.properties └── pom.xml ├── best-kafka-db ├── src │ ├── test │ │ ├── resources │ │ │ └── schema.sql │ │ └── java │ │ │ └── com │ │ │ └── springsource │ │ │ └── open │ │ │ └── foo │ │ │ ├── async │ │ │ ├── AsynchronousMessageTriggerSunnyDayTests.java │ │ │ ├── AsynchronousMessageTriggerAndRollbackTests.java │ │ │ ├── AsynchronousMessageTriggerAndPartialRollbackTests.java │ │ │ └── AbstractAsynchronousMessageTriggerTests.java │ │ │ ├── test │ │ │ ├── DatasourceTests.java │ │ │ └── MessagingTests.java │ │ │ └── FailureSimulator.java │ └── main │ │ ├── java │ │ └── com │ │ │ └── springsource │ │ │ └── open │ │ │ └── foo │ │ │ ├── Handler.java │ │ │ ├── FooHandler.java │ │ │ └── ListenerApplication.java │ │ └── resources │ │ └── application.properties ├── docker-compose.yml └── pom.xml ├── shared-kafka-db ├── src │ ├── main │ │ ├── resources │ │ │ └── application.properties │ │ └── java │ │ │ └── com │ │ │ └── springsource │ │ │ └── open │ │ │ └── foo │ │ │ ├── Handler.java │ │ │ ├── FooHandler.java │ │ │ └── ListenerApplication.java │ └── test │ │ ├── resources │ │ └── schema.sql │ │ └── java │ │ └── com │ │ └── springsource │ │ └── open │ │ └── foo │ │ ├── FailureSimulator.java │ │ ├── async │ │ ├── AsynchronousMessageTriggerSunnyDayTests.java │ │ ├── AsynchronousMessageTriggerAndRollbackTests.java │ │ └── AbstractAsynchronousMessageTriggerTests.java │ │ └── test │ │ ├── DatasourceTests.java │ │ └── MessagingTests.java ├── docker-compose.yml └── pom.xml ├── .gitignore ├── pom.xml ├── README.md ├── mvnw.cmd └── mvnw /best-rabbit-db/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.continue-on-error=true 2 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsyer/dist-tx/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /best-rabbit-db/docker-compose.yml: -------------------------------------------------------------------------------- 1 | rabbitmq: 2 | image: rabbitmq:management 3 | ports: 4 | - "5672:5672" 5 | - "15672:15672" 6 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip 2 | -------------------------------------------------------------------------------- /atomikos-db/src/test/resources/first.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_FOOS; 2 | create table T_FOOS ( 3 | id integer not null primary key, 4 | name varchar(80), 5 | foo_date timestamp 6 | ); 7 | -------------------------------------------------------------------------------- /best-db-db/src/test/resources/first.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_FOOS; 2 | create table T_FOOS ( 3 | id integer not null primary key, 4 | name varchar(80), 5 | foo_date timestamp 6 | ); 7 | -------------------------------------------------------------------------------- /best-db-db/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.initialize=false 2 | first.datasource.url=jdbc:hsqldb:mem:first 3 | second.datasource.url=jdbc:hsqldb:mem:second 4 | -------------------------------------------------------------------------------- /shared-jms-db/src/test/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.continue-on-error=true 2 | spring.datasource.url=jdbc:derby:test;create=true 3 | spring.activemq.broker-url=vm://localhost?async=false -------------------------------------------------------------------------------- /best-jms-db/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_FOOS; 2 | create table T_FOOS ( 3 | id integer not null primary key, 4 | name varchar(80) not null, 5 | foo_date timestamp, 6 | unique(name) 7 | ); 8 | -------------------------------------------------------------------------------- /best-kafka-db/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_FOOS; 2 | create table T_FOOS ( 3 | id integer not null primary key, 4 | name varchar(80) not null, 5 | foo_date timestamp, 6 | unique(name) 7 | ); 8 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_FOOS; 2 | create table T_FOOS ( 3 | id integer not null primary key, 4 | name varchar(80) not null, 5 | foo_date timestamp, 6 | unique(name) 7 | ); 8 | -------------------------------------------------------------------------------- /shared-jms-db/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_FOOS; 2 | create table T_FOOS ( 3 | id integer not null primary key, 4 | name varchar(80) not null, 5 | foo_date timestamp, 6 | unique(name) 7 | ); 8 | -------------------------------------------------------------------------------- /atomikos-db/src/test/resources/second.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_AUDITS; 2 | create table T_AUDITS ( 3 | id integer not null primary key, 4 | operation varchar(20), 5 | name varchar(80), 6 | audit_date timestamp 7 | ); 8 | -------------------------------------------------------------------------------- /best-db-db/src/test/resources/second.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_AUDITS; 2 | create table T_AUDITS ( 3 | id integer not null primary key, 4 | operation varchar(20), 5 | name varchar(80), 6 | audit_date timestamp 7 | ); 8 | -------------------------------------------------------------------------------- /atomikos-db/src/test/java/com/springsource/open/db/AuditService.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.db; 2 | 3 | 4 | public interface AuditService { 5 | 6 | void update(int id, String operation, String name); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /shared-kafka-db/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.continue-on-error=true 2 | spring.kafka.consumer.enable-auto-commit=false 3 | 4 | logging.level.com.springsource=debug 5 | logging.level.org.apache.kafka=warn 6 | -------------------------------------------------------------------------------- /best-jms-db/src/main/java/com/springsource/open/foo/Handler.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | public interface Handler { 4 | 5 | void handle(String msg); 6 | 7 | void resetItemCount(); 8 | 9 | int getItemCount(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /best-kafka-db/src/main/java/com/springsource/open/foo/Handler.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | public interface Handler { 4 | 5 | void handle(String msg); 6 | 7 | void resetItemCount(); 8 | 9 | int getItemCount(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /best-rabbit-db/src/main/java/com/springsource/open/foo/Handler.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | public interface Handler { 4 | 5 | void handle(String msg); 6 | 7 | void resetItemCount(); 8 | 9 | int getItemCount(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /shared-kafka-db/src/main/java/com/springsource/open/foo/Handler.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | public interface Handler { 4 | 5 | void handle(String msg, long offset); 6 | 7 | void resetItemCount(); 8 | 9 | int getItemCount(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /best-jms-db/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.continue-on-error=true 2 | spring.artemis.mode=embedded 3 | spring.artemis.embedded.enabled=true 4 | spring.artemis.embedded.queues=sync,async 5 | logging.level.org.springframework.jms.annotation.JmsListenerAnnotationBeanPostProcessor=OFF -------------------------------------------------------------------------------- /best-kafka-db/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.continue-on-error=true 2 | spring.kafka.producer.transaction-id-prefix=tx- 3 | spring.kafka.consumer.auto-offset-reset=earliest 4 | spring.kafka.consumer.enable-auto-commit=false 5 | spring.kafka.consumer.properties.isolation.level=read_committed 6 | 7 | logging.level.com.springsource=debug 8 | logging.level.org.apache.kafka=warn 9 | -------------------------------------------------------------------------------- /shared-kafka-db/src/test/resources/schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE T_FOOS; 2 | create table T_FOOS ( 3 | id integer not null primary key, 4 | name varchar(80) not null, 5 | foo_date timestamp, 6 | unique(name) 7 | ); 8 | DROP TABLE T_OFFSETS; 9 | create table T_OFFSETS ( 10 | id integer not null primary key, 11 | topic varchar(80) not null, 12 | part int, 13 | offset int, 14 | unique(topic, part, offset) 15 | ); 16 | -------------------------------------------------------------------------------- /atomikos-db/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.initialize=false 2 | spring.jta.atomikos.properties.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory 3 | spring.jta.atomikos.properties.output-dir=atomikos 4 | spring.jta.atomikos.properties.log-base-dir=atomikos 5 | first.datasource.name=first 6 | first.datasource.poolSize=2 7 | second.datasource.name=second 8 | second.datasource.poolSize=2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Operating System Files 2 | 3 | *.DS_Store 4 | Thumbs.db 5 | *.sw? 6 | .#* 7 | *# 8 | *~ 9 | *.sublime-* 10 | 11 | # Build Artifacts 12 | 13 | .gradle/ 14 | build/ 15 | target/ 16 | bin/ 17 | dependency-reduced-pom.xml 18 | 19 | # Eclipse Project Files 20 | 21 | .classpath 22 | .project 23 | .settings/ 24 | .attach_pid* 25 | 26 | # VSCode 27 | .vscode/ 28 | 29 | # IntelliJ IDEA Files 30 | 31 | *.iml 32 | *.ipr 33 | *.iws 34 | *.idea 35 | 36 | .springBeans 37 | .factorypath 38 | 39 | README.html 40 | 41 | atomikos/ 42 | derby-home/ 43 | derby.log 44 | -------------------------------------------------------------------------------- /shared-kafka-db/src/test/java/com/springsource/open/foo/FailureSimulator.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | import org.aspectj.lang.annotation.AfterReturning; 4 | import org.aspectj.lang.annotation.Aspect; 5 | import org.springframework.dao.DataIntegrityViolationException; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Aspect 9 | @Component 10 | public class FailureSimulator { 11 | 12 | /** 13 | * Just throws a {@link DataIntegrityViolationException}. 14 | */ 15 | public void simulateBusinessProcessingFailure() { 16 | throw new DataIntegrityViolationException("Simulated failure."); 17 | } 18 | 19 | @AfterReturning("execution(* *..*Handler+.handle(String,..)) && args(msg, ..)") 20 | public void maybeFail(String msg) { 21 | if (msg.contains("fail")) { 22 | System.err.println("Failing..."); 23 | simulateBusinessProcessingFailure(); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /atomikos-db/src/test/java/com/springsource/open/db/SimpleAuditService.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.db; 2 | 3 | import java.util.Date; 4 | 5 | import javax.sql.DataSource; 6 | 7 | import org.springframework.beans.factory.annotation.Qualifier; 8 | import org.springframework.jdbc.core.JdbcTemplate; 9 | import org.springframework.stereotype.Repository; 10 | import org.springframework.transaction.annotation.Propagation; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | @Repository 14 | public class SimpleAuditService implements AuditService { 15 | 16 | private final JdbcTemplate jdbcTemplate; 17 | 18 | public SimpleAuditService(@Qualifier("secondDataSource") DataSource dataSource) { 19 | this.jdbcTemplate = new JdbcTemplate(dataSource); 20 | } 21 | 22 | @Transactional(propagation=Propagation.REQUIRES_NEW) 23 | public void update(int id, String operation, String name) { 24 | jdbcTemplate.update( 25 | "INSERT into T_AUDITS (id,operation,name,audit_date) " 26 | + "values (?,?,?,?)", id, operation, name, new Date()); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /best-jms-db/src/main/java/com/springsource/open/foo/FooHandler.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | import java.util.Date; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | import javax.sql.DataSource; 7 | 8 | import org.apache.commons.logging.Log; 9 | import org.apache.commons.logging.LogFactory; 10 | import org.springframework.jdbc.core.JdbcTemplate; 11 | import org.springframework.jms.annotation.JmsListener; 12 | import org.springframework.stereotype.Service; 13 | 14 | @Service 15 | public class FooHandler implements Handler { 16 | 17 | private static final Log log = LogFactory.getLog(FooHandler.class); 18 | private final JdbcTemplate jdbcTemplate; 19 | private final AtomicInteger count = new AtomicInteger(0); 20 | 21 | public FooHandler(DataSource dataSource) { 22 | this.jdbcTemplate = new JdbcTemplate(dataSource); 23 | } 24 | 25 | @JmsListener(destination="async") 26 | public void handle(String msg) { 27 | 28 | log.debug("Received message: [" + msg + "]"); 29 | 30 | Date date = new Date(); 31 | jdbcTemplate.update( 32 | "INSERT INTO T_FOOS (ID, name, foo_date) values (?, ?,?)", count.getAndIncrement(), msg, date); 33 | 34 | log.debug(String 35 | .format("Inserted foo with name=%s, date=%s", msg, date)); 36 | 37 | } 38 | 39 | public void resetItemCount() { 40 | count.set(0); 41 | } 42 | 43 | public int getItemCount() { 44 | return count.get(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /best-kafka-db/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | zookeeper: 4 | image: confluentinc/cp-zookeeper:5.3.0 5 | hostname: zookeeper 6 | container_name: zookeeper 7 | ports: 8 | - "2181:2181" 9 | environment: 10 | ZOOKEEPER_CLIENT_PORT: 2181 11 | ZOOKEEPER_TICK_TIME: 2000 12 | 13 | broker: 14 | image: confluentinc/cp-enterprise-kafka:5.3.0 15 | hostname: broker 16 | container_name: broker 17 | depends_on: 18 | - zookeeper 19 | ports: 20 | - "29092:29092" 21 | - "9092:9092" 22 | environment: 23 | KAFKA_BROKER_ID: 1 24 | KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' 25 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 26 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:29092,PLAINTEXT_HOST://localhost:9092 27 | KAFKA_METRIC_REPORTERS: io.confluent.metrics.reporter.ConfluentMetricsReporter 28 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 29 | KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 30 | KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 31 | KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 32 | CONFLUENT_METRICS_REPORTER_BOOTSTRAP_SERVERS: broker:29092 33 | CONFLUENT_METRICS_REPORTER_ZOOKEEPER_CONNECT: zookeeper:2181 34 | CONFLUENT_METRICS_REPORTER_TOPIC_REPLICAS: 1 35 | CONFLUENT_METRICS_ENABLE: 'true' 36 | CONFLUENT_SUPPORT_CUSTOMER_ID: 'anonymous' 37 | -------------------------------------------------------------------------------- /shared-kafka-db/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | zookeeper: 4 | image: confluentinc/cp-zookeeper:5.3.0 5 | hostname: zookeeper 6 | container_name: zookeeper 7 | ports: 8 | - "2181:2181" 9 | environment: 10 | ZOOKEEPER_CLIENT_PORT: 2181 11 | ZOOKEEPER_TICK_TIME: 2000 12 | 13 | broker: 14 | image: confluentinc/cp-enterprise-kafka:5.3.0 15 | hostname: broker 16 | container_name: broker 17 | depends_on: 18 | - zookeeper 19 | ports: 20 | - "29092:29092" 21 | - "9092:9092" 22 | environment: 23 | KAFKA_BROKER_ID: 1 24 | KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' 25 | KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT 26 | KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:29092,PLAINTEXT_HOST://localhost:9092 27 | KAFKA_METRIC_REPORTERS: io.confluent.metrics.reporter.ConfluentMetricsReporter 28 | KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 29 | KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 30 | KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 31 | KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0 32 | CONFLUENT_METRICS_REPORTER_BOOTSTRAP_SERVERS: broker:29092 33 | CONFLUENT_METRICS_REPORTER_ZOOKEEPER_CONNECT: zookeeper:2181 34 | CONFLUENT_METRICS_REPORTER_TOPIC_REPLICAS: 1 35 | CONFLUENT_METRICS_ENABLE: 'true' 36 | CONFLUENT_SUPPORT_CUSTOMER_ID: 'anonymous' 37 | -------------------------------------------------------------------------------- /best-rabbit-db/src/main/java/com/springsource/open/foo/FooHandler.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | import java.util.Date; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | import javax.sql.DataSource; 7 | 8 | import org.apache.commons.logging.Log; 9 | import org.apache.commons.logging.LogFactory; 10 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 11 | import org.springframework.jdbc.core.JdbcTemplate; 12 | import org.springframework.stereotype.Service; 13 | 14 | @Service 15 | public class FooHandler implements Handler { 16 | 17 | private static final Log log = LogFactory.getLog(FooHandler.class); 18 | private final JdbcTemplate jdbcTemplate; 19 | private final AtomicInteger count = new AtomicInteger(0); 20 | 21 | public FooHandler(DataSource dataSource) { 22 | this.jdbcTemplate = new JdbcTemplate(dataSource); 23 | } 24 | 25 | @RabbitListener(queues="async") 26 | public void handle(String msg) { 27 | 28 | log.debug("Received message: [" + msg + "]"); 29 | 30 | Date date = new Date(); 31 | jdbcTemplate.update( 32 | "INSERT INTO T_FOOS (ID, name, foo_date) values (?, ?,?)", count.getAndIncrement(), msg, date); 33 | 34 | log.debug(String 35 | .format("Inserted foo with name=%s, date=%s", msg, date)); 36 | 37 | } 38 | 39 | public void resetItemCount() { 40 | count.set(0); 41 | } 42 | 43 | public int getItemCount() { 44 | return count.get(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /best-kafka-db/src/main/java/com/springsource/open/foo/FooHandler.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | import java.util.Date; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | import javax.sql.DataSource; 7 | 8 | import org.apache.commons.logging.Log; 9 | import org.apache.commons.logging.LogFactory; 10 | 11 | import org.springframework.jdbc.core.JdbcTemplate; 12 | import org.springframework.kafka.annotation.KafkaListener; 13 | import org.springframework.stereotype.Service; 14 | 15 | @Service 16 | public class FooHandler implements Handler { 17 | 18 | private static final Log log = LogFactory.getLog(FooHandler.class); 19 | 20 | private final JdbcTemplate jdbcTemplate; 21 | 22 | private final AtomicInteger count = new AtomicInteger(0); 23 | 24 | public FooHandler(DataSource dataSource) { 25 | this.jdbcTemplate = new JdbcTemplate(dataSource); 26 | } 27 | 28 | @Override 29 | @KafkaListener(id = "group", topics = "async", autoStartup = "false") 30 | public void handle(String msg) { 31 | 32 | log.debug("Received message: [" + msg + "]"); 33 | 34 | Date date = new Date(); 35 | jdbcTemplate.update("INSERT INTO T_FOOS (ID, name, foo_date) values (?, ?,?)", count.getAndIncrement(), msg, 36 | date); 37 | 38 | log.debug(String.format("Inserted foo with name=%s, date=%s", msg, date)); 39 | 40 | } 41 | 42 | @Override 43 | public void resetItemCount() { 44 | count.set(0); 45 | } 46 | 47 | @Override 48 | public int getItemCount() { 49 | return count.get(); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /shared-kafka-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerSunnyDayTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | import org.springframework.test.jdbc.JdbcTestUtils; 24 | 25 | public class AsynchronousMessageTriggerSunnyDayTests extends AbstractAsynchronousMessageTriggerTests { 26 | 27 | @Test 28 | public void testCleanData() { 29 | kafkaTemplate.send("async", "foo"); 30 | kafkaTemplate.send("async", "bar"); 31 | } 32 | 33 | @Override 34 | protected void checkPostConditions() throws Exception { 35 | 36 | // Two messages committed 37 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 38 | // No messages rolled back so positioned at end. 39 | assertEquals(1, consumerOffset()); 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerSunnyDayTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.test.jdbc.JdbcTestUtils; 25 | 26 | public class AsynchronousMessageTriggerSunnyDayTests extends AbstractAsynchronousMessageTriggerTests { 27 | 28 | @Test 29 | public void testCleanData() { 30 | jmsTemplate.convertAndSend("async", "foo"); 31 | jmsTemplate.convertAndSend("async", "bar"); 32 | } 33 | 34 | @Override 35 | protected void checkPostConditions() { 36 | 37 | // Two messages committed 38 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, 39 | "T_FOOS")); 40 | List list = getMessages(); 41 | // No messages rolled back so queue was empty 42 | assertEquals(0, list.size()); 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerSunnyDayTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.test.jdbc.JdbcTestUtils; 25 | 26 | public class AsynchronousMessageTriggerSunnyDayTests extends AbstractAsynchronousMessageTriggerTests { 27 | 28 | @Test 29 | public void testCleanData() { 30 | jmsTemplate.convertAndSend("async", "foo"); 31 | jmsTemplate.convertAndSend("async", "bar"); 32 | } 33 | 34 | @Override 35 | protected void checkPostConditions() { 36 | 37 | // Two messages committed 38 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, 39 | "T_FOOS")); 40 | List list = getMessages(); 41 | // No messages rolled back so queue was empty 42 | assertEquals(0, list.size()); 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /shared-kafka-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerAndRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.test.jdbc.JdbcTestUtils; 22 | 23 | import static org.junit.Assert.assertEquals; 24 | 25 | public class AsynchronousMessageTriggerAndRollbackTests extends AbstractAsynchronousMessageTriggerTests { 26 | 27 | @Test 28 | public void testBusinessFailure() { 29 | kafkaTemplate.send("async", "foo"); 30 | kafkaTemplate.send("async", "bar.fail"); 31 | } 32 | 33 | @Override 34 | protected void checkPostConditions() throws Exception { 35 | 36 | // One failed and rolled back, the other committed 37 | assertEquals(1, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 38 | // One message rolled back and returned to queue, so it will start again at 1 39 | assertEquals(0, consumerOffset()); 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /best-kafka-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerSunnyDayTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | import org.springframework.test.jdbc.JdbcTestUtils; 24 | 25 | public class AsynchronousMessageTriggerSunnyDayTests extends AbstractAsynchronousMessageTriggerTests { 26 | 27 | @Test 28 | public void testCleanData() { 29 | kafkaTemplate.executeInTransaction(t -> t.send("async", "foo")); 30 | kafkaTemplate.executeInTransaction(t -> t.send("async", "bar")); 31 | } 32 | 33 | @Override 34 | protected void checkPostConditions() throws Exception { 35 | 36 | // Two messages committed 37 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 38 | // No messages rolled back so positioned at end (slot 1 and 3 contain tx state). 39 | assertEquals(3, consumerOffset()); 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /best-kafka-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerAndRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.test.jdbc.JdbcTestUtils; 22 | 23 | import static org.junit.Assert.assertEquals; 24 | 25 | public class AsynchronousMessageTriggerAndRollbackTests extends AbstractAsynchronousMessageTriggerTests { 26 | 27 | @Test 28 | public void testBusinessFailure() { 29 | kafkaTemplate.executeInTransaction(t -> t.send("async", "foo")); 30 | kafkaTemplate.executeInTransaction(t -> t.send("async", "bar.fail")); 31 | } 32 | 33 | @Override 34 | protected void checkPostConditions() throws Exception { 35 | 36 | // One failed and rolled back, the other committed 37 | assertEquals(1, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 38 | // One message rolled back and returned to queue 39 | assertEquals(1, consumerOffset()); 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerAndRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.test.jdbc.JdbcTestUtils; 25 | 26 | public class AsynchronousMessageTriggerAndRollbackTests extends AbstractAsynchronousMessageTriggerTests { 27 | 28 | 29 | @Test 30 | public void testBusinessFailure() { 31 | jmsTemplate.convertAndSend("async", "foo"); 32 | jmsTemplate.convertAndSend("async", "bar.fail"); 33 | } 34 | 35 | @Override 36 | protected void checkPostConditions() { 37 | 38 | // One failed and rolled back, the other committed 39 | assertEquals(1, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 40 | List list = getMessages(); 41 | // One message rolled back and returned to queue 42 | assertEquals(1, list.size()); 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/test/DatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import org.junit.jupiter.api.Test; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.boot.test.context.SpringBootTest; 24 | import org.springframework.jdbc.core.JdbcTemplate; 25 | import org.springframework.transaction.annotation.Transactional; 26 | 27 | @SpringBootTest 28 | public class DatasourceTests { 29 | 30 | @Autowired 31 | private JdbcTemplate simpleJdbcTemplate; 32 | 33 | @Transactional @Test 34 | public void testTemplate() throws Exception { 35 | simpleJdbcTemplate.execute("delete from T_FOOS"); 36 | int count = simpleJdbcTemplate.queryForObject("select count(*) from T_FOOS", Integer.class); 37 | assertEquals(0, count); 38 | 39 | simpleJdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, "foo"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /best-kafka-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerAndPartialRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import org.junit.jupiter.api.Test; 20 | 21 | import org.springframework.test.jdbc.JdbcTestUtils; 22 | 23 | import static org.junit.Assert.assertEquals; 24 | 25 | public class AsynchronousMessageTriggerAndPartialRollbackTests extends AbstractAsynchronousMessageTriggerTests { 26 | 27 | @Test 28 | public void testPartialFailureWithDuplicateMessage() { 29 | kafkaTemplate.executeInTransaction(t -> t.send("async", "foo")); 30 | kafkaTemplate.executeInTransaction(t -> t.send("async", "bar.fail.partial")); 31 | } 32 | 33 | @Override 34 | protected void checkPostConditions() throws Exception { 35 | 36 | // Both committed 37 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 38 | // One message rolled back and returned to queue 39 | assertEquals(1, consumerOffset()); 40 | 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerAndPartialRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.test.jdbc.JdbcTestUtils; 25 | 26 | public class AsynchronousMessageTriggerAndPartialRollbackTests extends AbstractAsynchronousMessageTriggerTests { 27 | 28 | @Test 29 | public void testPartialFailureWithDuplicateMessage() { 30 | jmsTemplate.convertAndSend("async", "foo"); 31 | jmsTemplate.convertAndSend("async", "bar.fail.partial"); 32 | } 33 | 34 | @Override 35 | protected void checkPostConditions() { 36 | 37 | // Both committed 38 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 39 | List list = getMessages(); 40 | // One message rolled back and returned to queue 41 | assertEquals(1, list.size()); 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /best-kafka-db/src/test/java/com/springsource/open/foo/test/DatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import org.junit.jupiter.api.Test; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.boot.test.context.SpringBootTest; 24 | import org.springframework.jdbc.core.JdbcTemplate; 25 | import org.springframework.transaction.annotation.Transactional; 26 | 27 | @SpringBootTest 28 | public class DatasourceTests { 29 | 30 | @Autowired 31 | private JdbcTemplate simpleJdbcTemplate; 32 | 33 | @Transactional @Test 34 | public void testTemplate() throws Exception { 35 | simpleJdbcTemplate.execute("delete from T_FOOS"); 36 | int count = simpleJdbcTemplate.queryForObject("select count(*) from T_FOOS", Integer.class); 37 | assertEquals(0, count); 38 | 39 | simpleJdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, "foo"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerAndPartialRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.test.jdbc.JdbcTestUtils; 25 | 26 | public class AsynchronousMessageTriggerAndPartialRollbackTests extends AbstractAsynchronousMessageTriggerTests { 27 | 28 | @Test 29 | public void testPartialFailureWithDuplicateMessage() { 30 | jmsTemplate.convertAndSend("async", "foo"); 31 | jmsTemplate.convertAndSend("async", "bar.fail.partial"); 32 | } 33 | 34 | @Override 35 | protected void checkPostConditions() { 36 | 37 | // Both committed 38 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 39 | List list = getMessages(); 40 | // One message rolled back and returned to queue 41 | assertEquals(1, list.size()); 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/async/AsynchronousMessageTriggerAndRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.List; 22 | 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.test.jdbc.JdbcTestUtils; 25 | 26 | public class AsynchronousMessageTriggerAndRollbackTests extends AbstractAsynchronousMessageTriggerTests { 27 | 28 | 29 | @Test 30 | public void testBusinessFailure() { 31 | jmsTemplate.convertAndSend("async", "foo"); 32 | jmsTemplate.convertAndSend("async", "bar.fail"); 33 | } 34 | 35 | @Override 36 | protected void checkPostConditions() { 37 | 38 | // One failed and rolled back, the other committed 39 | assertEquals(1, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 40 | List list = getMessages(); 41 | // One message rolled back and returned to queue 42 | assertEquals(1, list.size()); 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/test/DatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import org.junit.jupiter.api.Test; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.boot.test.context.SpringBootTest; 24 | import org.springframework.jdbc.core.JdbcTemplate; 25 | import org.springframework.transaction.annotation.Transactional; 26 | 27 | @SpringBootTest 28 | public class DatasourceTests { 29 | 30 | @Autowired 31 | private JdbcTemplate simpleJdbcTemplate; 32 | 33 | @Transactional @Test 34 | public void testTemplate() throws Exception { 35 | simpleJdbcTemplate.execute("delete from T_FOOS"); 36 | int count = simpleJdbcTemplate.queryForObject("select count(*) from T_FOOS", Integer.class); 37 | assertEquals(0, count); 38 | 39 | simpleJdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, "foo"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /shared-jms-db/src/main/resources/META-INF/spring/jms-context.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | vm://localhost 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /shared-jms-db/src/test/java/com/springsource/open/jms/FailureSimulator.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.jms; 2 | 3 | import javax.sql.DataSource; 4 | 5 | import org.apache.activemq.broker.BrokerService; 6 | import org.apache.commons.logging.Log; 7 | import org.apache.commons.logging.LogFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.dao.DataIntegrityViolationException; 10 | import org.springframework.jdbc.core.JdbcTemplate; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Component 14 | public class FailureSimulator { 15 | 16 | private static final Log logger = LogFactory.getLog(FailureSimulator.class); 17 | 18 | private JdbcTemplate jdbcTemplate; 19 | 20 | @Autowired 21 | private BrokerService brokerService; 22 | 23 | @Autowired 24 | public void setDataSource(DataSource dataSource) { 25 | this.jdbcTemplate = new JdbcTemplate(dataSource); 26 | } 27 | 28 | /** 29 | * Causes the JMS session to fail on commit, as if the middleware has 30 | * failed. Can be used to simulate failure of JMS independent of business 31 | * processing, causing duplicate messages even if best efforts 1PC is used. 32 | * @throws Exception 33 | */ 34 | public void simulateMessageSystemFailure() throws Exception { 35 | 36 | logger.debug("ACTIVEMQ_MSGS: "+jdbcTemplate.queryForList("SELECT * FROM ACTIVEMQ_MSGS")); 37 | // Simulate a message system failure before the main transaction 38 | // commits... 39 | brokerService.getTransportConnectors().get(0).stop(); 40 | 41 | } 42 | 43 | /** 44 | * Just throws a {@link DataIntegrityViolationException}. 45 | */ 46 | public void simulateBusinessProcessingFailure() { 47 | throw new DataIntegrityViolationException("Simulated failure."); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 1.4.0.RELEASE 9 | 10 | 11 | com.springsource.open 12 | spring-tx-parent 13 | 0.0.1-SNAPSHOT 14 | pom 15 | Parent Demo 16 | 17 | 18 | shared-jms-db 19 | shared-kafka-db 20 | atomikos-db 21 | best-jms-db 22 | best-kafka-db 23 | best-rabbit-db 24 | best-db-db 25 | 26 | 27 | 28 | 1.8 29 | 30 | 31 | 32 | 33 | spring-libs-snapshot 34 | http://repo.spring.io/libs-snapshot 35 | 36 | true 37 | 38 | 39 | true 40 | 41 | 42 | 43 | 44 | 45 | 46 | spring-libs-snapshot 47 | http://repo.spring.io/libs-snapshot 48 | 49 | true 50 | 51 | 52 | true 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /best-db-db/src/test/java/com/springsource/open/db/DatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.db; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import javax.sql.DataSource; 22 | 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.beans.factory.annotation.Autowired; 25 | import org.springframework.beans.factory.annotation.Qualifier; 26 | import org.springframework.boot.test.context.SpringBootTest; 27 | import org.springframework.jdbc.core.JdbcTemplate; 28 | import org.springframework.transaction.annotation.Transactional; 29 | 30 | @SpringBootTest 31 | public class DatasourceTests { 32 | 33 | private JdbcTemplate simpleJdbcTemplate; 34 | 35 | @Autowired 36 | public void setDataSource(@Qualifier("firstDataSource") DataSource dataSource) { 37 | this.simpleJdbcTemplate = new JdbcTemplate(dataSource); 38 | } 39 | 40 | @Transactional 41 | @Test 42 | public void testSimpleQuery() throws Exception { 43 | simpleJdbcTemplate.execute("delete from T_FOOS"); 44 | int count = simpleJdbcTemplate 45 | .queryForObject("select count(*) from T_FOOS", Integer.class); 46 | assertEquals(0, count); 47 | simpleJdbcTemplate.update( 48 | "INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, 49 | "foo"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /atomikos-db/src/test/java/com/springsource/open/db/test/DatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.db.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.Date; 22 | 23 | import com.springsource.open.db.BaseDatasourceTests; 24 | 25 | import org.junit.jupiter.api.Test; 26 | import org.springframework.transaction.annotation.Transactional; 27 | 28 | public class DatasourceTests extends BaseDatasourceTests { 29 | 30 | @Transactional @Test 31 | public void testMainDataSource() throws Exception { 32 | getJdbcTemplate().execute("delete from T_FOOS"); 33 | int count = getJdbcTemplate().queryForObject("select count(*) from T_FOOS", Integer.class); 34 | assertEquals(0, count); 35 | 36 | getJdbcTemplate().update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, "foo"); 37 | } 38 | 39 | @Transactional @Test 40 | public void testOtherDataSource() throws Exception { 41 | getOtherJdbcTemplate().execute("delete from T_AUDITS"); 42 | int count = getOtherJdbcTemplate().queryForObject("select count(*) from T_AUDITS", Integer.class); 43 | assertEquals(0, count); 44 | 45 | getOtherJdbcTemplate().update("INSERT into T_AUDITS (id,operation,name,audit_date) values (?,?,?,?)", 0, "INSERT", "foo", new Date()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /best-kafka-db/src/test/java/com/springsource/open/foo/FailureSimulator.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | import org.apache.kafka.clients.producer.Producer; 4 | import org.aspectj.lang.annotation.AfterReturning; 5 | import org.aspectj.lang.annotation.Aspect; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.dao.DataIntegrityViolationException; 9 | import org.springframework.kafka.core.KafkaOperations.ProducerCallback; 10 | import org.springframework.kafka.core.KafkaTemplate; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Aspect 14 | @Component 15 | public class FailureSimulator { 16 | 17 | private KafkaTemplate kafkaTemplate; 18 | 19 | @Autowired 20 | public void setKafkaTemplate(KafkaTemplate kafkaTemplate) { 21 | this.kafkaTemplate = kafkaTemplate; 22 | } 23 | 24 | /** 25 | * Causes Kafka to fail on commit, as if the middleware has failed. 26 | */ 27 | public void simulateMessageSystemFailure() { 28 | 29 | // Simulate a message system failure before the main transaction 30 | // commits... 31 | kafkaTemplate.execute(new ProducerCallback() { 32 | 33 | @Override 34 | public Void doInKafka(Producer producer) { 35 | try { 36 | producer.abortTransaction(); 37 | } 38 | catch (Exception e) { 39 | // swallow it 40 | e.printStackTrace(); 41 | } 42 | return null; 43 | } 44 | }); 45 | 46 | } 47 | 48 | /** 49 | * Just throws a {@link DataIntegrityViolationException}. 50 | */ 51 | public void simulateBusinessProcessingFailure() { 52 | throw new DataIntegrityViolationException("Simulated failure."); 53 | } 54 | 55 | @AfterReturning("execution(* *..*Handler+.handle(String)) && args(msg)") 56 | public void maybeFail(String msg) { 57 | if (msg.contains("fail")) { 58 | if (msg.contains("partial")) { 59 | simulateMessageSystemFailure(); 60 | } 61 | else { 62 | simulateBusinessProcessingFailure(); 63 | } 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /shared-jms-db/src/test/java/com/springsource/open/jms/test/DatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.jms.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import javax.sql.DataSource; 22 | 23 | import org.junit.Test; 24 | import org.junit.runner.RunWith; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.beans.factory.annotation.Qualifier; 27 | import org.springframework.boot.test.context.SpringBootTest; 28 | import org.springframework.jdbc.core.JdbcTemplate; 29 | import org.springframework.test.context.junit4.SpringRunner; 30 | import org.springframework.test.jdbc.JdbcTestUtils; 31 | import org.springframework.transaction.annotation.Transactional; 32 | 33 | @RunWith(SpringRunner.class) 34 | @SpringBootTest 35 | public class DatasourceTests { 36 | 37 | private JdbcTemplate simpleJdbcTemplate; 38 | 39 | @Autowired 40 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 41 | this.simpleJdbcTemplate = new JdbcTemplate(dataSource); 42 | } 43 | 44 | @Transactional @Test 45 | public void testTemplate() throws Exception { 46 | int count = JdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "T_FOOS"); 47 | simpleJdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", count, "spam"); 48 | assertEquals(count + 1, JdbcTestUtils.countRowsInTable(simpleJdbcTemplate, "T_FOOS")); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/FailureSimulator.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | import org.aspectj.lang.annotation.AfterReturning; 4 | import org.aspectj.lang.annotation.Aspect; 5 | import org.springframework.amqp.rabbit.core.ChannelCallback; 6 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.dao.DataIntegrityViolationException; 9 | import org.springframework.stereotype.Component; 10 | 11 | import com.rabbitmq.client.Channel; 12 | 13 | @Aspect 14 | @Component 15 | public class FailureSimulator { 16 | 17 | private RabbitTemplate jmsTemplate; 18 | 19 | @Autowired 20 | public void setJmsTemplate(RabbitTemplate jmsTemplate) { 21 | this.jmsTemplate = jmsTemplate; 22 | } 23 | 24 | /** 25 | * Causes the JMS session to fail on commit, as if the middleware has 26 | * failed. Can be used to simulate failure of JMS independent of business 27 | * processing, causing duplicate messages even if best efforts 1PC is used. 28 | */ 29 | public void simulateMessageSystemFailure() { 30 | 31 | // Simulate a message system failure before the main transaction 32 | // commits... 33 | jmsTemplate.execute(new ChannelCallback() { 34 | public Object doInRabbit(Channel session) throws Exception { 35 | try { 36 | session.abort(); 37 | } catch (Exception e) { 38 | // swallow it 39 | e.printStackTrace(); 40 | } 41 | return null; 42 | } 43 | }); 44 | 45 | } 46 | 47 | /** 48 | * Just throws a {@link DataIntegrityViolationException}. 49 | */ 50 | public void simulateBusinessProcessingFailure() { 51 | throw new DataIntegrityViolationException("Simulated failure."); 52 | } 53 | 54 | @AfterReturning("execution(* *..*Handler+.handle(String)) && args(msg)") 55 | public void maybeFail(String msg) { 56 | if (msg.contains("fail")) { 57 | if (msg.contains("partial")) { 58 | simulateMessageSystemFailure(); 59 | } else { 60 | simulateBusinessProcessingFailure(); 61 | } 62 | } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/test/MessagingTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import org.junit.jupiter.api.BeforeEach; 26 | import org.junit.jupiter.api.Test; 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.jms.core.JmsTemplate; 30 | 31 | @SpringBootTest 32 | public class MessagingTests { 33 | 34 | @Autowired 35 | private JmsTemplate jmsTemplate; 36 | 37 | @BeforeEach 38 | public void onSetUp() throws Exception { 39 | Thread.sleep(100L); 40 | getMessages(); // drain queue 41 | jmsTemplate.convertAndSend("queue", "foo"); 42 | jmsTemplate.convertAndSend("queue", "bar"); 43 | } 44 | 45 | @Test 46 | public void testMessaging() throws Exception { 47 | List list = getMessages(); 48 | System.err.println(list); 49 | assertEquals(2, list.size()); 50 | assertTrue(list.contains("foo")); 51 | } 52 | 53 | private List getMessages() { 54 | String next = ""; 55 | List msgs = new ArrayList(); 56 | while (next != null) { 57 | next = (String) jmsTemplate.receiveAndConvert("queue"); 58 | if (next != null) 59 | msgs.add(next); 60 | } 61 | return msgs; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/test/MessagingTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import org.junit.jupiter.api.BeforeEach; 26 | import org.junit.jupiter.api.Test; 27 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 28 | import org.springframework.beans.factory.annotation.Autowired; 29 | import org.springframework.boot.test.context.SpringBootTest; 30 | 31 | @SpringBootTest 32 | public class MessagingTests { 33 | 34 | @Autowired 35 | private RabbitTemplate jmsTemplate; 36 | 37 | @BeforeEach 38 | public void onSetUp() throws Exception { 39 | Thread.sleep(100L); 40 | getMessages(); // drain queue 41 | jmsTemplate.convertAndSend("queue", "foo"); 42 | jmsTemplate.convertAndSend("queue", "bar"); 43 | } 44 | 45 | @Test 46 | public void testMessaging() throws Exception { 47 | List list = getMessages(); 48 | System.err.println(list); 49 | assertEquals(2, list.size()); 50 | assertTrue(list.contains("foo")); 51 | } 52 | 53 | private List getMessages() { 54 | String next = ""; 55 | List msgs = new ArrayList(); 56 | while (next != null) { 57 | next = (String) jmsTemplate.receiveAndConvert("queue"); 58 | if (next != null) 59 | msgs.add(next); 60 | } 61 | return msgs; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /shared-kafka-db/src/test/java/com/springsource/open/foo/test/DatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | 22 | import java.util.Map; 23 | 24 | import org.junit.jupiter.api.Test; 25 | import org.springframework.beans.factory.annotation.Autowired; 26 | import org.springframework.boot.test.context.SpringBootTest; 27 | import org.springframework.dao.EmptyResultDataAccessException; 28 | import org.springframework.jdbc.core.JdbcTemplate; 29 | import org.springframework.transaction.annotation.Transactional; 30 | 31 | @SpringBootTest 32 | public class DatasourceTests { 33 | 34 | @Autowired 35 | private JdbcTemplate simpleJdbcTemplate; 36 | 37 | @Transactional 38 | @Test 39 | public void testTemplate() throws Exception { 40 | simpleJdbcTemplate.execute("delete from T_FOOS"); 41 | int count = simpleJdbcTemplate.queryForObject("select count(*) from T_FOOS", Integer.class); 42 | assertEquals(0, count); 43 | 44 | simpleJdbcTemplate.update("INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, "foo"); 45 | } 46 | 47 | @Transactional 48 | @Test 49 | public void testOffsets() throws Exception { 50 | simpleJdbcTemplate.execute("delete from T_OFFSETS"); 51 | assertThrows(EmptyResultDataAccessException.class, 52 | () -> simpleJdbcTemplate.queryForMap("SELECT * FROM T_OFFSETS WHERE ID=0 and TOPIC=?", "async")); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /shared-kafka-db/src/main/java/com/springsource/open/foo/FooHandler.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | import java.util.Date; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | import javax.sql.DataSource; 7 | 8 | import org.apache.commons.logging.Log; 9 | import org.apache.commons.logging.LogFactory; 10 | import org.springframework.jdbc.core.JdbcTemplate; 11 | import org.springframework.kafka.annotation.KafkaListener; 12 | import org.springframework.kafka.support.KafkaHeaders; 13 | import org.springframework.messaging.handler.annotation.Header; 14 | import org.springframework.messaging.handler.annotation.Payload; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | @Service 19 | public class FooHandler implements Handler { 20 | 21 | private static final Log log = LogFactory.getLog(FooHandler.class); 22 | 23 | private final JdbcTemplate jdbcTemplate; 24 | 25 | private final AtomicInteger count = new AtomicInteger(0); 26 | 27 | public FooHandler(DataSource dataSource) { 28 | this.jdbcTemplate = new JdbcTemplate(dataSource); 29 | } 30 | 31 | @Override 32 | @KafkaListener(id = "group", topics = "async", autoStartup = "false") 33 | @Transactional 34 | public void handle(@Payload String msg, @Header(KafkaHeaders.OFFSET) long offset) { 35 | 36 | log.debug("Received message: [" + msg + "]"); 37 | 38 | Date date = new Date(); 39 | jdbcTemplate.update("INSERT INTO T_FOOS (ID, name, foo_date) values (?, ?,?)", count.getAndIncrement(), msg, 40 | date); 41 | 42 | int updated = jdbcTemplate.update("UPDATE T_OFFSETS set topic=?, part=0, offset=? where ID=0", "async", 43 | offset); 44 | if (updated < 1) { 45 | jdbcTemplate.update("INSERT into T_OFFSETS (ID, topic, part, offset) values (?,?,?,?)", 0, "async", 0, 46 | offset); 47 | } 48 | 49 | log.debug(String.format("Inserted foo with name=%s, date=%s, offset=%d", msg, date, offset)); 50 | 51 | } 52 | 53 | @Override 54 | public void resetItemCount() { 55 | count.set(0); 56 | } 57 | 58 | @Override 59 | public int getItemCount() { 60 | return count.get(); 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /atomikos-db/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.springsource.open 5 | spring-atomikos-db 6 | 2.0.0.CI-SNAPSHOT 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.2.2.RELEASE 11 | 12 | 13 | jar 14 | Spring DB-DB 15 | 18 | 19 | true 20 | 1.8 21 | 22 | 23 | 24 | strict 25 | 26 | false 27 | 28 | 29 | 30 | fast 31 | 32 | true 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-jta-atomikos 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-jdbc 44 | 45 | 46 | org.apache.derby 47 | derby 48 | 49 | 50 | org.springframework.boot 51 | spring-boot-starter-test 52 | test 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-configuration-processor 57 | true 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /best-rabbit-db/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.springsource.open 5 | spring-best-rabbit-db 6 | 2.0.0.CI-SNAPSHOT 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.2.2.RELEASE 11 | 12 | 13 | jar 14 | Spring Best Efforts JMS-DB 15 | 18 | 19 | true 20 | 1.8 21 | 22 | 23 | 24 | strict 25 | 26 | false 27 | 28 | 29 | 30 | fast 31 | 32 | true 33 | 34 | 35 | 36 | 37 | 38 | org.hsqldb 39 | hsqldb 40 | test 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-amqp 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-aop 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-jdbc 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-test 58 | test 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /shared-jms-db/src/test/java/com/springsource/open/jms/test/MessagingTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.jms.test; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | import org.junit.Before; 26 | import org.junit.Test; 27 | import org.junit.runner.RunWith; 28 | import org.springframework.beans.factory.annotation.Autowired; 29 | import org.springframework.boot.test.context.SpringBootTest; 30 | import org.springframework.jms.core.JmsTemplate; 31 | import org.springframework.test.context.junit4.SpringRunner; 32 | 33 | @RunWith(SpringRunner.class) 34 | @SpringBootTest 35 | public class MessagingTests { 36 | 37 | @Autowired 38 | private JmsTemplate jmsTemplate; 39 | 40 | @Before 41 | public void onSetUp() throws Exception { 42 | Thread.sleep(100L); 43 | getMessages(); // drain queue 44 | jmsTemplate.convertAndSend("queue", "foo"); 45 | jmsTemplate.convertAndSend("queue", "bar"); 46 | } 47 | 48 | @Test 49 | public void testMessaging() throws Exception { 50 | List list = getMessages(); 51 | System.err.println(list); 52 | assertEquals(2, list.size()); 53 | assertTrue(list.contains("foo")); 54 | } 55 | 56 | private List getMessages() { 57 | String next = ""; 58 | List msgs = new ArrayList(); 59 | while (next != null) { 60 | next = (String) jmsTemplate.receiveAndConvert("queue"); 61 | if (next != null) 62 | msgs.add(next); 63 | } 64 | return msgs; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /best-kafka-db/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.springsource.open 5 | spring-best-kafka-db 6 | 2.0.0.CI-SNAPSHOT 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.2.2.RELEASE 11 | 12 | 13 | jar 14 | Spring Best Efforts JMS-DB 15 | 18 | 19 | true 20 | 1.8 21 | 22 | 23 | 24 | org.hsqldb 25 | hsqldb 26 | test 27 | 28 | 29 | 30 | org.springframework.kafka 31 | spring-kafka 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-aop 36 | 37 | 38 | org.springframework.data 39 | spring-data-commons 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-jdbc 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | org.springframework.kafka 52 | spring-kafka-test 53 | test 54 | 55 | 56 | org.awaitility 57 | awaitility 58 | test 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /shared-kafka-db/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.springsource.open 5 | spring-shared-kafka-db 6 | 2.0.0.CI-SNAPSHOT 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.2.2.RELEASE 11 | 12 | 13 | jar 14 | Spring Best Efforts JMS-DB 15 | 18 | 19 | true 20 | 1.8 21 | 22 | 23 | 24 | org.hsqldb 25 | hsqldb 26 | test 27 | 28 | 29 | 30 | org.springframework.kafka 31 | spring-kafka 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-aop 36 | 37 | 38 | org.springframework.data 39 | spring-data-commons 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-jdbc 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-test 48 | test 49 | 50 | 51 | org.springframework.kafka 52 | spring-kafka-test 53 | test 54 | 55 | 56 | org.awaitility 57 | awaitility 58 | test 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /best-jms-db/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.springsource.open 5 | spring-best-jms-db 6 | 2.0.0.CI-SNAPSHOT 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 2.2.2.RELEASE 11 | 12 | 13 | jar 14 | Spring Best Efforts JMS-DB 15 | 18 | 19 | true 20 | 1.8 21 | 22 | 23 | 24 | strict 25 | 26 | false 27 | 28 | 29 | 30 | fast 31 | 32 | true 33 | 34 | 35 | 36 | 37 | 38 | org.hsqldb 39 | hsqldb 40 | test 41 | 42 | 43 | org.apache.activemq 44 | artemis-jms-server 45 | 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-starter-artemis 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-aop 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-jdbc 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-test 62 | test 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/FailureSimulator.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.foo; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import javax.jms.JMSException; 6 | import javax.jms.Session; 7 | 8 | import org.aspectj.lang.annotation.AfterReturning; 9 | import org.aspectj.lang.annotation.Aspect; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.dao.DataIntegrityViolationException; 12 | import org.springframework.jms.connection.SessionProxy; 13 | import org.springframework.jms.core.JmsTemplate; 14 | import org.springframework.jms.core.SessionCallback; 15 | import org.springframework.stereotype.Component; 16 | 17 | @Aspect 18 | @Component 19 | public class FailureSimulator { 20 | 21 | private JmsTemplate jmsTemplate; 22 | 23 | @Autowired 24 | public void setJmsTemplate(JmsTemplate jmsTemplate) { 25 | this.jmsTemplate = jmsTemplate; 26 | } 27 | 28 | /** 29 | * Causes the JMS session to fail on commit, as if the middleware has 30 | * failed. Can be used to simulate failure of JMS independent of business 31 | * processing, causing duplicate messages even if best efforts 1PC is used. 32 | */ 33 | public void simulateMessageSystemFailure() { 34 | 35 | // Simulate a message system failure before the main transaction 36 | // commits... 37 | jmsTemplate.execute(new SessionCallback() { 38 | public Object doInJms(Session session) throws JMSException { 39 | try { 40 | assertTrue("Not a SessionProxy - wrong spring version?", 41 | session instanceof SessionProxy); 42 | ((SessionProxy) session).getTargetSession().rollback(); 43 | } catch (Exception e) { 44 | // swallow it 45 | e.printStackTrace(); 46 | } 47 | return null; 48 | } 49 | }); 50 | 51 | } 52 | 53 | /** 54 | * Just throws a {@link DataIntegrityViolationException}. 55 | */ 56 | public void simulateBusinessProcessingFailure() { 57 | throw new DataIntegrityViolationException("Simulated failure."); 58 | } 59 | 60 | @AfterReturning("execution(* *..*Handler+.handle(String)) && args(msg)") 61 | public void maybeFail(String msg) { 62 | if (msg.contains("fail")) { 63 | if (msg.contains("partial")) { 64 | simulateMessageSystemFailure(); 65 | } else { 66 | simulateBusinessProcessingFailure(); 67 | } 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /best-kafka-db/src/test/java/com/springsource/open/foo/test/MessagingTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.test; 18 | 19 | import java.time.Duration; 20 | 21 | import com.springsource.open.foo.FooHandler; 22 | 23 | import org.awaitility.Awaitility; 24 | import org.junit.jupiter.api.BeforeEach; 25 | import org.junit.jupiter.api.Test; 26 | 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.kafka.config.KafkaListenerEndpointRegistry; 30 | import org.springframework.kafka.core.KafkaTemplate; 31 | import org.springframework.test.annotation.DirtiesContext; 32 | 33 | import static org.assertj.core.api.Assertions.assertThat; 34 | import static org.hamcrest.Matchers.greaterThanOrEqualTo; 35 | 36 | @SpringBootTest 37 | @DirtiesContext // when running from maven, the wrong listener is invoked 38 | public class MessagingTests { 39 | 40 | @Autowired 41 | private KafkaTemplate kafkaTemplate; 42 | 43 | @Autowired 44 | private FooHandler consumer; 45 | 46 | @BeforeEach 47 | public void onSetUp(@Autowired KafkaListenerEndpointRegistry registry) throws Exception { 48 | registry.getListenerContainer("group").start(); 49 | Thread.sleep(100L); 50 | kafkaTemplate.executeInTransaction(t -> t.send("async", "foo")); 51 | kafkaTemplate.executeInTransaction(t -> t.send("async", "bar")); 52 | } 53 | 54 | @Test 55 | public void testMessaging() throws Exception { 56 | Awaitility.waitAtMost(Duration.ofSeconds(30)).until(this.consumer::getItemCount, greaterThanOrEqualTo(2)); 57 | assertThat(consumer.getItemCount()).isGreaterThanOrEqualTo(2); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /atomikos-db/src/test/java/com/springsource/open/db/MultipleDatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.db; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.Date; 22 | 23 | import org.junit.jupiter.api.Test; 24 | import org.springframework.test.context.transaction.AfterTransaction; 25 | import org.springframework.test.context.transaction.BeforeTransaction; 26 | import org.springframework.transaction.annotation.Transactional; 27 | 28 | public class MultipleDatasourceTests extends BaseDatasourceTests { 29 | 30 | @BeforeTransaction 31 | public void clearData() { 32 | getJdbcTemplate().update("delete from T_FOOS"); 33 | getOtherJdbcTemplate().update("delete from T_AUDITS"); 34 | } 35 | 36 | @AfterTransaction 37 | public void checkPostConditions() { 38 | 39 | int count = getJdbcTemplate().queryForObject("select count(*) from T_FOOS", Integer.class); 40 | // This change was rolled back by the test framework 41 | assertEquals(0, count); 42 | 43 | count = getOtherJdbcTemplate().queryForObject("select count(*) from T_AUDITS", Integer.class); 44 | // This rolled back as well because of the XA 45 | assertEquals(0, count); 46 | 47 | } 48 | 49 | @Transactional 50 | @Test 51 | public void testInsertIntoTwoDataSources() throws Exception { 52 | 53 | int count = getJdbcTemplate().update( 54 | "INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, 55 | "foo"); 56 | assertEquals(1, count); 57 | 58 | count = getOtherJdbcTemplate() 59 | .update( 60 | "INSERT into T_AUDITS (id,operation,name,audit_date) values (?,?,?,?)", 61 | 0, "INSERT", "foo", new Date()); 62 | assertEquals(1, count); 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /shared-kafka-db/src/test/java/com/springsource/open/foo/test/MessagingTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.test; 18 | 19 | import java.time.Duration; 20 | 21 | import com.springsource.open.foo.FooHandler; 22 | 23 | import org.awaitility.Awaitility; 24 | import org.junit.jupiter.api.BeforeEach; 25 | import org.junit.jupiter.api.Test; 26 | 27 | import org.springframework.beans.factory.annotation.Autowired; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.kafka.config.KafkaListenerEndpointRegistry; 30 | import org.springframework.kafka.core.KafkaTemplate; 31 | import org.springframework.test.annotation.DirtiesContext; 32 | 33 | import static org.assertj.core.api.Assertions.assertThat; 34 | import static org.hamcrest.Matchers.greaterThanOrEqualTo; 35 | 36 | @SpringBootTest 37 | @DirtiesContext // when running from maven, the wrong listener is invoked 38 | public class MessagingTests { 39 | 40 | @Autowired 41 | private KafkaTemplate kafkaTemplate; 42 | 43 | @Autowired 44 | private FooHandler consumer; 45 | 46 | @BeforeEach 47 | public void onSetUp(@Autowired KafkaListenerEndpointRegistry registry) throws Exception { 48 | registry.getListenerContainer("group").start(); 49 | Thread.sleep(100L); 50 | kafkaTemplate.executeInTransaction(t -> t.send("async", "foo")); 51 | kafkaTemplate.executeInTransaction(t -> t.send("async", "bar")); 52 | } 53 | 54 | @Test 55 | public void testMessaging() throws Exception { 56 | Awaitility.waitAtMost(Duration.ofSeconds(30)).until(this.consumer::getItemCount, greaterThanOrEqualTo(2)); 57 | assertThat(consumer.getItemCount()).isGreaterThanOrEqualTo(2); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /best-db-db/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.springsource.open 5 | spring-best-db-db 6 | 2.0.0.CI-SNAPSHOT 7 | jar 8 | 9 | org.springframework.boot 10 | spring-boot-starter-parent 11 | 2.2.2.RELEASE 12 | 13 | 14 | Spring Best Efforts DB-DB 15 | 18 | 19 | true 20 | 1.8 21 | 22 | 23 | 24 | strict 25 | 26 | false 27 | 28 | 29 | 30 | fast 31 | 32 | true 33 | 34 | 35 | 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-jdbc 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-aop 44 | 45 | 46 | org.hsqldb 47 | hsqldb 48 | test 49 | 50 | 51 | commons-io 52 | commons-io 53 | 1.2 54 | test 55 | 56 | 57 | org.springframework.boot 58 | spring-boot-starter-test 59 | test 60 | 61 | 62 | org.springframework.boot 63 | spring-boot-configuration-processor 64 | true 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /shared-jms-db/src/main/java/com/springsource/open/jms/SharedDatabaseApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.jms; 18 | 19 | import java.io.File; 20 | import java.io.IOException; 21 | 22 | import javax.annotation.PostConstruct; 23 | 24 | import org.springframework.beans.BeansException; 25 | import org.springframework.beans.factory.config.BeanPostProcessor; 26 | import org.springframework.boot.SpringApplication; 27 | import org.springframework.boot.autoconfigure.SpringBootApplication; 28 | import org.springframework.context.annotation.Bean; 29 | import org.springframework.context.annotation.ImportResource; 30 | import org.springframework.jms.core.JmsTemplate; 31 | 32 | @SpringBootApplication 33 | @ImportResource("classpath:/META-INF/spring/jms-context.xml") 34 | public class SharedDatabaseApplication { 35 | 36 | @PostConstruct 37 | public void init() throws IOException { 38 | File directory = new File("derby-home"); 39 | System.setProperty("derby.system.home", directory.getCanonicalPath()); 40 | } 41 | 42 | @Bean 43 | public BeanPostProcessor jmsPostProcessor() { 44 | return new BeanPostProcessor() { 45 | 46 | @Override 47 | public Object postProcessBeforeInitialization(Object bean, String beanName) 48 | throws BeansException { 49 | return bean; 50 | } 51 | 52 | @Override 53 | public Object postProcessAfterInitialization(Object bean, String beanName) 54 | throws BeansException { 55 | if (bean instanceof JmsTemplate) { 56 | JmsTemplate template = (JmsTemplate) bean; 57 | template.setReceiveTimeout(200L); 58 | template.setSessionTransacted(true); 59 | } 60 | return bean; 61 | } 62 | }; 63 | } 64 | 65 | public static void main(String[] args) throws Exception { 66 | SpringApplication.run(SharedDatabaseApplication.class, args); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /best-rabbit-db/src/main/java/com/springsource/open/foo/ListenerApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo; 18 | 19 | import org.springframework.amqp.core.Queue; 20 | import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory; 21 | import org.springframework.amqp.rabbit.connection.ConnectionFactory; 22 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 23 | import org.springframework.beans.BeansException; 24 | import org.springframework.beans.factory.config.BeanPostProcessor; 25 | import org.springframework.boot.SpringApplication; 26 | import org.springframework.boot.autoconfigure.SpringBootApplication; 27 | import org.springframework.context.annotation.Bean; 28 | import org.springframework.transaction.PlatformTransactionManager; 29 | 30 | @SpringBootApplication 31 | public class ListenerApplication { 32 | 33 | @Bean 34 | public Queue sync() { 35 | return new Queue("queue"); 36 | } 37 | 38 | @Bean 39 | public Queue async() { 40 | return new Queue("async"); 41 | } 42 | 43 | @Bean 44 | public RabbitTemplate jmsTemplate(ConnectionFactory connectionFactory) { 45 | RabbitTemplate jmsTemplate = new RabbitTemplate(connectionFactory); 46 | jmsTemplate.setReceiveTimeout(200); 47 | jmsTemplate.setChannelTransacted(true); 48 | return jmsTemplate; 49 | } 50 | 51 | @Bean 52 | public BeanPostProcessor connectionFactoryPostProcessor(PlatformTransactionManager transactionManager) { 53 | return new BeanPostProcessor() { 54 | 55 | @Override 56 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 57 | if (bean instanceof SimpleRabbitListenerContainerFactory) { 58 | ((SimpleRabbitListenerContainerFactory) bean).setTransactionManager(transactionManager); 59 | } 60 | return bean; 61 | } 62 | }; 63 | } 64 | 65 | public static void main(String[] args) throws Exception { 66 | SpringApplication.run(ListenerApplication.class, args); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /best-kafka-db/src/main/java/com/springsource/open/foo/ListenerApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo; 18 | 19 | import javax.sql.DataSource; 20 | 21 | import org.apache.kafka.clients.admin.NewTopic; 22 | 23 | import org.springframework.boot.SpringApplication; 24 | import org.springframework.boot.autoconfigure.SpringBootApplication; 25 | import org.springframework.context.annotation.Bean; 26 | import org.springframework.context.annotation.Primary; 27 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 28 | import org.springframework.kafka.listener.AfterRollbackProcessor; 29 | import org.springframework.kafka.listener.DefaultAfterRollbackProcessor; 30 | import org.springframework.kafka.transaction.ChainedKafkaTransactionManager; 31 | import org.springframework.kafka.transaction.KafkaTransactionManager; 32 | import org.springframework.util.backoff.FixedBackOff; 33 | 34 | @SpringBootApplication 35 | public class ListenerApplication { 36 | 37 | @Bean 38 | public NewTopic asyncTopic() { 39 | return new NewTopic("async", 1, (short) 1); 40 | } 41 | 42 | @Bean 43 | @Primary 44 | public ChainedKafkaTransactionManager chainedKafkaTransactionManager( 45 | KafkaTransactionManager ktm, DataSourceTransactionManager dstm) { 46 | 47 | return new ChainedKafkaTransactionManager<>(ktm, dstm); 48 | } 49 | 50 | @Bean 51 | public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) { 52 | return new DataSourceTransactionManager(dataSource); 53 | } 54 | 55 | @Bean 56 | public AfterRollbackProcessor arp() { 57 | // no retries, default logging recoverer, no back off 58 | DefaultAfterRollbackProcessor arp = 59 | new DefaultAfterRollbackProcessor<>(new FixedBackOff(0L, 0L)); 60 | arp.setCommitRecovered(false); 61 | return arp; 62 | } 63 | 64 | public static void main(String[] args) throws Exception { 65 | SpringApplication.run(ListenerApplication.class, args); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /best-jms-db/src/main/java/com/springsource/open/foo/ListenerApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo; 18 | 19 | import javax.jms.ConnectionFactory; 20 | 21 | import org.springframework.beans.BeansException; 22 | import org.springframework.beans.factory.config.BeanPostProcessor; 23 | import org.springframework.boot.SpringApplication; 24 | import org.springframework.boot.autoconfigure.SpringBootApplication; 25 | import org.springframework.context.annotation.Bean; 26 | import org.springframework.jms.config.DefaultJmsListenerContainerFactory; 27 | import org.springframework.jms.connection.TransactionAwareConnectionFactoryProxy; 28 | import org.springframework.jms.core.JmsTemplate; 29 | import org.springframework.transaction.PlatformTransactionManager; 30 | 31 | @SpringBootApplication 32 | public class ListenerApplication { 33 | 34 | @Bean 35 | public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) { 36 | JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory); 37 | jmsTemplate.setReceiveTimeout(200); 38 | jmsTemplate.setSessionTransacted(true); 39 | return jmsTemplate; 40 | } 41 | 42 | @Bean 43 | public BeanPostProcessor connectionFactoryPostProcessor(PlatformTransactionManager transactionManager) { 44 | return new BeanPostProcessor() { 45 | 46 | @Override 47 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 48 | if (bean instanceof ConnectionFactory) { 49 | TransactionAwareConnectionFactoryProxy proxy = new TransactionAwareConnectionFactoryProxy( 50 | (ConnectionFactory) bean); 51 | proxy.setSynchedLocalTransactionAllowed(true); 52 | return proxy; 53 | } 54 | if (bean instanceof DefaultJmsListenerContainerFactory) { 55 | ((DefaultJmsListenerContainerFactory) bean).setTransactionManager(transactionManager); 56 | } 57 | return bean; 58 | } 59 | }; 60 | } 61 | 62 | public static void main(String[] args) throws Exception { 63 | SpringApplication.run(ListenerApplication.class, args); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /atomikos-db/src/test/java/com/springsource/open/db/BaseDatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.db; 18 | 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import java.io.File; 22 | 23 | import javax.sql.DataSource; 24 | 25 | import org.junit.jupiter.api.AfterAll; 26 | import org.junit.jupiter.api.BeforeAll; 27 | import org.junit.jupiter.api.BeforeEach; 28 | import org.springframework.beans.factory.annotation.Autowired; 29 | import org.springframework.beans.factory.annotation.Qualifier; 30 | import org.springframework.boot.test.context.SpringBootTest; 31 | import org.springframework.jdbc.core.JdbcTemplate; 32 | import org.springframework.transaction.PlatformTransactionManager; 33 | import org.springframework.transaction.jta.JtaTransactionManager; 34 | 35 | @SpringBootTest 36 | public abstract class BaseDatasourceTests { 37 | 38 | @Autowired 39 | private PlatformTransactionManager transactionManager; 40 | 41 | private JdbcTemplate jdbcTemplate; 42 | private JdbcTemplate otherJdbcTemplate; 43 | 44 | protected JdbcTemplate getJdbcTemplate() { 45 | return jdbcTemplate; 46 | } 47 | 48 | protected JdbcTemplate getOtherJdbcTemplate() { 49 | return otherJdbcTemplate; 50 | } 51 | 52 | @BeforeEach 53 | public void check() { 54 | assertTrue("Wrong transactionManager: " + transactionManager, 55 | transactionManager instanceof JtaTransactionManager); 56 | } 57 | 58 | @BeforeAll 59 | @AfterAll 60 | public static void clearLog() { 61 | // Ensure that Atomikos logging directory exists 62 | File dir = new File("atomikos"); 63 | if (!dir.exists()) { 64 | dir.mkdir(); 65 | } 66 | // ...and delete any stale locks (this would be a sign of a crash) 67 | File tmlog = new File("atomikos/tmlog.lck"); 68 | if (tmlog.exists()) { 69 | tmlog.delete(); 70 | } 71 | } 72 | 73 | @Autowired 74 | public void setDataSources(@Qualifier("firstDataSource") DataSource dataSource, 75 | @Qualifier("secondDataSource") DataSource otherDataSource) { 76 | this.jdbcTemplate = new JdbcTemplate(dataSource); 77 | this.otherJdbcTemplate = new JdbcTemplate(otherDataSource); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /atomikos-db/src/test/java/com/springsource/open/db/TransactionPropagationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.db; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import org.junit.jupiter.api.Test; 22 | import org.springframework.beans.factory.annotation.Autowired; 23 | import org.springframework.test.context.transaction.AfterTransaction; 24 | import org.springframework.test.context.transaction.BeforeTransaction; 25 | import org.springframework.transaction.annotation.Transactional; 26 | 27 | public class TransactionPropagationTests extends BaseDatasourceTests { 28 | 29 | // Inject a service that can be proxied so we can apply tx propagation 30 | @Autowired 31 | private AuditService auditService; 32 | 33 | @BeforeTransaction 34 | public void clearData() { 35 | getJdbcTemplate().update("delete from T_FOOS"); 36 | getOtherJdbcTemplate().update("delete from T_AUDITS"); 37 | } 38 | 39 | @AfterTransaction 40 | public void checkPostConditions() { 41 | 42 | int count = getJdbcTemplate().queryForObject("select count(*) from T_FOOS", Integer.class); 43 | // This change was rolled back by the test framework 44 | assertEquals(0, count); 45 | 46 | count = getOtherJdbcTemplate().queryForObject("select count(*) from T_AUDITS", Integer.class); 47 | // This one doesn't roll back because of TX propagation 48 | assertEquals(1, count); 49 | 50 | } 51 | 52 | @Transactional 53 | @Test 54 | public void testInsertIntoTwoDataSources() throws Exception { 55 | int count; 56 | count = getOtherJdbcTemplate().queryForObject("select count(*) from T_AUDITS", Integer.class); 57 | assertEquals(0, count); 58 | count = getJdbcTemplate().queryForObject("select count(*) from T_FOOS", Integer.class); 59 | assertEquals(0, count); 60 | 61 | getJdbcTemplate().update( 62 | "INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, 63 | "foo"); 64 | count = getJdbcTemplate().queryForObject("select count(*) from T_FOOS", Integer.class); 65 | assertEquals(1, count); 66 | 67 | auditService.update(0, "INSERT", "foo"); 68 | getOtherJdbcTemplate().update("UPDATE T_AUDITS set name=? where name=?", 69 | "bar", "foo"); 70 | count = getOtherJdbcTemplate().queryForObject("select count(*) from T_AUDITS", Integer.class); 71 | assertEquals(1, count); 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/sync/SynchronousMessageTriggerAndRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.sync; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.jupiter.api.Test; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.beans.factory.annotation.Qualifier; 31 | import org.springframework.boot.test.context.SpringBootTest; 32 | import org.springframework.jdbc.core.JdbcTemplate; 33 | import org.springframework.jms.core.JmsTemplate; 34 | import org.springframework.test.context.transaction.AfterTransaction; 35 | import org.springframework.test.context.transaction.BeforeTransaction; 36 | import org.springframework.test.jdbc.JdbcTestUtils; 37 | import org.springframework.transaction.annotation.Transactional; 38 | 39 | @SpringBootTest 40 | public class SynchronousMessageTriggerAndRollbackTests { 41 | 42 | @Autowired 43 | private JmsTemplate jmsTemplate; 44 | 45 | private JdbcTemplate jdbcTemplate; 46 | 47 | @Autowired 48 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 49 | this.jdbcTemplate = new JdbcTemplate(dataSource); 50 | } 51 | 52 | @BeforeTransaction 53 | public void clearData() { 54 | getMessages(); // drain queue 55 | jmsTemplate.convertAndSend("queue", "foo"); 56 | jmsTemplate.convertAndSend("queue", "bar"); 57 | jdbcTemplate.update("delete from T_FOOS"); 58 | } 59 | 60 | @AfterTransaction 61 | public void checkPostConditions() { 62 | 63 | assertEquals(0, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 64 | List list = getMessages(); 65 | assertEquals(2, list.size()); 66 | 67 | } 68 | 69 | @Test 70 | @Transactional 71 | public void testReceiveMessageUpdateDatabase() throws Exception { 72 | 73 | List list = getMessages(); 74 | assertEquals(2, list.size()); 75 | assertTrue(list.contains("foo")); 76 | 77 | int id = 0; 78 | for (String name : list) { 79 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 80 | } 81 | 82 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 83 | 84 | } 85 | 86 | private List getMessages() { 87 | String next = ""; 88 | List msgs = new ArrayList(); 89 | while (next != null) { 90 | next = (String) jmsTemplate.receiveAndConvert("queue"); 91 | if (next != null) 92 | msgs.add(next); 93 | } 94 | return msgs; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/sync/SynchronousMessageTriggerAndRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.sync; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.jupiter.api.Test; 29 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.beans.factory.annotation.Qualifier; 32 | import org.springframework.boot.test.context.SpringBootTest; 33 | import org.springframework.jdbc.core.JdbcTemplate; 34 | import org.springframework.test.context.transaction.AfterTransaction; 35 | import org.springframework.test.context.transaction.BeforeTransaction; 36 | import org.springframework.test.jdbc.JdbcTestUtils; 37 | import org.springframework.transaction.annotation.Transactional; 38 | 39 | @SpringBootTest 40 | public class SynchronousMessageTriggerAndRollbackTests { 41 | 42 | @Autowired 43 | private RabbitTemplate jmsTemplate; 44 | 45 | private JdbcTemplate jdbcTemplate; 46 | 47 | @Autowired 48 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 49 | this.jdbcTemplate = new JdbcTemplate(dataSource); 50 | } 51 | 52 | @BeforeTransaction 53 | public void clearData() { 54 | getMessages(); // drain queue 55 | jmsTemplate.convertAndSend("queue", "foo"); 56 | jmsTemplate.convertAndSend("queue", "bar"); 57 | jdbcTemplate.update("delete from T_FOOS"); 58 | } 59 | 60 | @AfterTransaction 61 | public void checkPostConditions() { 62 | 63 | assertEquals(0, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 64 | List list = getMessages(); 65 | assertEquals(2, list.size()); 66 | 67 | } 68 | 69 | @Test 70 | @Transactional 71 | public void testReceiveMessageUpdateDatabase() throws Exception { 72 | 73 | List list = getMessages(); 74 | assertEquals(2, list.size()); 75 | assertTrue(list.contains("foo")); 76 | 77 | int id = 0; 78 | for (String name : list) { 79 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 80 | } 81 | 82 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 83 | 84 | } 85 | 86 | private List getMessages() { 87 | String next = ""; 88 | List msgs = new ArrayList(); 89 | while (next != null) { 90 | next = (String) jmsTemplate.receiveAndConvert("queue"); 91 | if (next != null) 92 | msgs.add(next); 93 | } 94 | return msgs; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/sync/SynchronousMessageTriggerSunnyDayTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.sync; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.jupiter.api.Test; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.beans.factory.annotation.Qualifier; 31 | import org.springframework.boot.test.context.SpringBootTest; 32 | import org.springframework.jdbc.core.JdbcTemplate; 33 | import org.springframework.jms.core.JmsTemplate; 34 | import org.springframework.test.annotation.Rollback; 35 | import org.springframework.test.context.transaction.AfterTransaction; 36 | import org.springframework.test.context.transaction.BeforeTransaction; 37 | import org.springframework.test.jdbc.JdbcTestUtils; 38 | import org.springframework.transaction.annotation.Transactional; 39 | 40 | @SpringBootTest 41 | public class SynchronousMessageTriggerSunnyDayTests { 42 | 43 | @Autowired 44 | private JmsTemplate jmsTemplate; 45 | 46 | private JdbcTemplate jdbcTemplate; 47 | 48 | @Autowired 49 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 50 | this.jdbcTemplate = new JdbcTemplate(dataSource); 51 | } 52 | 53 | @BeforeTransaction 54 | public void clearData() { 55 | getMessages(); // drain queue 56 | jmsTemplate.convertAndSend("queue", "foo"); 57 | jmsTemplate.convertAndSend("queue", "bar"); 58 | jdbcTemplate.update("delete from T_FOOS"); 59 | } 60 | 61 | @AfterTransaction 62 | public void checkPostConditions() { 63 | 64 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 65 | List list = getMessages(); 66 | assertEquals(0, list.size()); 67 | 68 | } 69 | 70 | @Test 71 | @Transactional 72 | @Rollback(false) 73 | public void testReceiveMessageUpdateDatabase() throws Exception { 74 | 75 | List list = getMessages(); 76 | assertEquals(2, list.size()); 77 | assertTrue(list.contains("foo")); 78 | 79 | int id = 0; 80 | for (String name : list) { 81 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 82 | } 83 | 84 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 85 | 86 | } 87 | 88 | private List getMessages() { 89 | String next = ""; 90 | List msgs = new ArrayList(); 91 | while (next != null) { 92 | next = (String) jmsTemplate.receiveAndConvert("queue"); 93 | if (next != null) 94 | msgs.add(next); 95 | } 96 | return msgs; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /best-db-db/src/main/java/com/springsource/open/db/ChainedTransactionManager.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.db; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import org.springframework.transaction.PlatformTransactionManager; 8 | import org.springframework.transaction.TransactionDefinition; 9 | import org.springframework.transaction.TransactionException; 10 | import org.springframework.transaction.TransactionStatus; 11 | import org.springframework.transaction.support.AbstractPlatformTransactionManager; 12 | import org.springframework.transaction.support.DefaultTransactionStatus; 13 | 14 | /** 15 | * @author Dave Syer 16 | * 17 | */ 18 | @SuppressWarnings("serial") 19 | public class ChainedTransactionManager extends AbstractPlatformTransactionManager { 20 | 21 | private final List transactionManagers = new ArrayList(); 22 | private final ArrayList reversed; 23 | 24 | public ChainedTransactionManager( 25 | List transactionManagers) { 26 | this.transactionManagers.addAll(transactionManagers); 27 | reversed = new ArrayList(transactionManagers); 28 | Collections.reverse(reversed); 29 | } 30 | 31 | @Override 32 | protected void doBegin(Object transaction, TransactionDefinition definition) 33 | throws TransactionException { 34 | @SuppressWarnings("unchecked") 35 | List list = (List) transaction; 36 | for (PlatformTransactionManager transactionManager : transactionManagers) { 37 | DefaultTransactionStatus element = (DefaultTransactionStatus) transactionManager 38 | .getTransaction(definition); 39 | list.add(0, element); 40 | } 41 | } 42 | 43 | @Override 44 | protected void doCommit(DefaultTransactionStatus status) throws TransactionException { 45 | @SuppressWarnings("unchecked") 46 | List list = (List) status 47 | .getTransaction(); 48 | int i = 0; 49 | for (PlatformTransactionManager transactionManager : reversed) { 50 | TransactionStatus local = list.get(i++); 51 | try { 52 | transactionManager.commit(local); 53 | } 54 | catch (TransactionException e) { 55 | logger.error("Error in commit", e); 56 | // Rollback will ensue as long as rollbackOnCommitFailure=true 57 | throw e; 58 | } 59 | } 60 | } 61 | 62 | @Override 63 | protected Object doGetTransaction() throws TransactionException { 64 | return new ArrayList(); 65 | } 66 | 67 | @Override 68 | protected void doRollback(DefaultTransactionStatus status) 69 | throws TransactionException { 70 | @SuppressWarnings("unchecked") 71 | List list = (List) status 72 | .getTransaction(); 73 | int i = 0; 74 | TransactionException lastException = null; 75 | for (PlatformTransactionManager transactionManager : reversed) { 76 | TransactionStatus local = list.get(i++); 77 | try { 78 | transactionManager.rollback(local); 79 | } 80 | catch (TransactionException e) { 81 | // Log exception and try to complete rollback 82 | lastException = e; 83 | logger.error("Error in rollback", e); 84 | } 85 | } 86 | if (lastException != null) { 87 | throw lastException; 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/sync/SynchronousMessageTriggerSunnyDayTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.sync; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.jupiter.api.Test; 29 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.beans.factory.annotation.Qualifier; 32 | import org.springframework.boot.test.context.SpringBootTest; 33 | import org.springframework.jdbc.core.JdbcTemplate; 34 | import org.springframework.test.annotation.Rollback; 35 | import org.springframework.test.context.transaction.AfterTransaction; 36 | import org.springframework.test.context.transaction.BeforeTransaction; 37 | import org.springframework.test.jdbc.JdbcTestUtils; 38 | import org.springframework.transaction.annotation.Transactional; 39 | 40 | @SpringBootTest 41 | public class SynchronousMessageTriggerSunnyDayTests { 42 | 43 | @Autowired 44 | private RabbitTemplate jmsTemplate; 45 | 46 | private JdbcTemplate jdbcTemplate; 47 | 48 | @Autowired 49 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 50 | this.jdbcTemplate = new JdbcTemplate(dataSource); 51 | } 52 | 53 | @BeforeTransaction 54 | public void clearData() { 55 | getMessages(); // drain queue 56 | jmsTemplate.convertAndSend("queue", "foo"); 57 | jmsTemplate.convertAndSend("queue", "bar"); 58 | jdbcTemplate.update("delete from T_FOOS"); 59 | } 60 | 61 | @AfterTransaction 62 | public void checkPostConditions() { 63 | 64 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 65 | List list = getMessages(); 66 | assertEquals(0, list.size()); 67 | 68 | } 69 | 70 | @Test 71 | @Transactional 72 | @Rollback(false) 73 | public void testReceiveMessageUpdateDatabase() throws Exception { 74 | 75 | List list = getMessages(); 76 | assertEquals(2, list.size()); 77 | assertTrue(list.contains("foo")); 78 | 79 | int id = 0; 80 | for (String name : list) { 81 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 82 | } 83 | 84 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 85 | 86 | } 87 | 88 | private List getMessages() { 89 | String next = ""; 90 | List msgs = new ArrayList(); 91 | while (next != null) { 92 | next = (String) jmsTemplate.receiveAndConvert("queue"); 93 | if (next != null) 94 | msgs.add(next); 95 | } 96 | return msgs; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/async/AbstractAsynchronousMessageTriggerTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import javax.sql.DataSource; 25 | 26 | import org.junit.jupiter.api.AfterEach; 27 | import org.junit.jupiter.api.BeforeEach; 28 | import org.springframework.beans.BeansException; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.beans.factory.annotation.Qualifier; 31 | import org.springframework.boot.test.context.SpringBootTest; 32 | import org.springframework.context.ApplicationContext; 33 | import org.springframework.context.ApplicationContextAware; 34 | import org.springframework.context.Lifecycle; 35 | import org.springframework.jdbc.core.JdbcTemplate; 36 | import org.springframework.jms.core.JmsTemplate; 37 | 38 | import com.springsource.open.foo.Handler; 39 | 40 | @SpringBootTest 41 | public abstract class AbstractAsynchronousMessageTriggerTests implements 42 | ApplicationContextAware { 43 | 44 | @Autowired 45 | protected JmsTemplate jmsTemplate; 46 | 47 | @Autowired 48 | private Handler handler; 49 | 50 | protected JdbcTemplate jdbcTemplate; 51 | 52 | private Lifecycle lifecycle; 53 | 54 | @Autowired 55 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 56 | this.jdbcTemplate = new JdbcTemplate(dataSource); 57 | } 58 | 59 | public void setApplicationContext(ApplicationContext applicationContext) 60 | throws BeansException { 61 | this.lifecycle = (Lifecycle) applicationContext; 62 | } 63 | 64 | @BeforeEach 65 | public void clearData() { 66 | // Start the listeners... 67 | lifecycle.start(); 68 | getMessages(); // drain queue 69 | handler.resetItemCount(); 70 | jdbcTemplate.update("delete from T_FOOS"); 71 | } 72 | 73 | @AfterEach 74 | public void waitForMessages() throws Exception { 75 | 76 | int count = 0; 77 | while (handler.getItemCount() < 2 && (count++) < 30) { 78 | Thread.sleep(100); 79 | } 80 | // Stop the listeners... 81 | lifecycle.stop(); 82 | // Give it time to finish up... 83 | Thread.sleep(2000); 84 | assertTrue("Wrong item count: " + handler.getItemCount(), handler.getItemCount() >= 2); 85 | 86 | checkPostConditions(); 87 | 88 | } 89 | 90 | protected abstract void checkPostConditions(); 91 | 92 | protected List getMessages() { 93 | String next = ""; 94 | List msgs = new ArrayList(); 95 | while (next != null) { 96 | next = (String) jmsTemplate.receiveAndConvert("async"); 97 | if (next != null) 98 | msgs.add(next); 99 | } 100 | return msgs; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/async/AbstractAsynchronousMessageTriggerTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import static org.junit.Assert.assertTrue; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | import javax.sql.DataSource; 25 | 26 | import org.junit.jupiter.api.AfterEach; 27 | import org.junit.jupiter.api.BeforeEach; 28 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 29 | import org.springframework.beans.BeansException; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.beans.factory.annotation.Qualifier; 32 | import org.springframework.boot.test.context.SpringBootTest; 33 | import org.springframework.context.ApplicationContext; 34 | import org.springframework.context.ApplicationContextAware; 35 | import org.springframework.context.Lifecycle; 36 | import org.springframework.jdbc.core.JdbcTemplate; 37 | 38 | import com.springsource.open.foo.Handler; 39 | 40 | @SpringBootTest 41 | public abstract class AbstractAsynchronousMessageTriggerTests implements 42 | ApplicationContextAware { 43 | 44 | @Autowired 45 | protected RabbitTemplate jmsTemplate; 46 | 47 | @Autowired 48 | private Handler handler; 49 | 50 | protected JdbcTemplate jdbcTemplate; 51 | 52 | private Lifecycle lifecycle; 53 | 54 | @Autowired 55 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 56 | this.jdbcTemplate = new JdbcTemplate(dataSource); 57 | } 58 | 59 | public void setApplicationContext(ApplicationContext applicationContext) 60 | throws BeansException { 61 | this.lifecycle = (Lifecycle) applicationContext; 62 | } 63 | 64 | @BeforeEach 65 | public void clearData() { 66 | // Start the listeners... 67 | lifecycle.start(); 68 | getMessages(); // drain queue 69 | handler.resetItemCount(); 70 | jdbcTemplate.update("delete from T_FOOS"); 71 | } 72 | 73 | @AfterEach 74 | public void waitForMessages() throws Exception { 75 | 76 | int count = 0; 77 | while (handler.getItemCount() < 2 && (count++) < 30) { 78 | Thread.sleep(100); 79 | } 80 | // Stop the listeners... 81 | lifecycle.stop(); 82 | // Give it time to finish up... 83 | Thread.sleep(2000); 84 | assertTrue("Wrong item count: " + handler.getItemCount(), handler.getItemCount() >= 2); 85 | 86 | checkPostConditions(); 87 | 88 | } 89 | 90 | protected abstract void checkPostConditions(); 91 | 92 | protected List getMessages() { 93 | String next = ""; 94 | List msgs = new ArrayList(); 95 | while (next != null) { 96 | next = (String) jmsTemplate.receiveAndConvert("async"); 97 | if (next != null) 98 | msgs.add(next); 99 | } 100 | return msgs; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /shared-kafka-db/src/main/java/com/springsource/open/foo/ListenerApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo; 18 | 19 | import java.util.Collection; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | import javax.sql.DataSource; 24 | 25 | import org.apache.kafka.clients.admin.NewTopic; 26 | import org.apache.kafka.common.TopicPartition; 27 | import org.springframework.boot.SpringApplication; 28 | import org.springframework.boot.autoconfigure.SpringBootApplication; 29 | import org.springframework.context.annotation.Bean; 30 | import org.springframework.dao.EmptyResultDataAccessException; 31 | import org.springframework.jdbc.core.JdbcTemplate; 32 | import org.springframework.kafka.listener.ConsumerAwareRebalanceListener; 33 | 34 | @SpringBootApplication 35 | public class ListenerApplication { 36 | 37 | @Bean 38 | public NewTopic asyncTopic() { 39 | return new NewTopic("async", 1, (short) 1); 40 | } 41 | 42 | @Bean 43 | public ConsumerAwareRebalanceListener listenerContanerCustomizer(ConsumerConfiguration config) { 44 | return new ConsumerAwareRebalanceListener() { 45 | @Override 46 | public void onPartitionsAssigned(org.apache.kafka.clients.consumer.Consumer consumer, 47 | Collection partitions) { 48 | for (TopicPartition partition : partitions) { 49 | long offset = config.getOffset(partition.topic(), partition.partition()) + 1; 50 | System.err.println("Seeking: " + partition + " to offset=" + offset + " from position=" 51 | + consumer.position(partition)); 52 | consumer.seek(partition, offset); 53 | } 54 | } 55 | }; 56 | } 57 | 58 | @Bean 59 | ConsumerConfiguration config(DataSource dataSource) { 60 | return new ConsumerConfiguration(dataSource); 61 | } 62 | 63 | public static void main(String[] args) throws Exception { 64 | SpringApplication.run(ListenerApplication.class, args); 65 | } 66 | 67 | } 68 | 69 | class ConsumerConfiguration { 70 | private Map cache = new HashMap<>(); 71 | private final JdbcTemplate jdbcTemplate; 72 | 73 | public ConsumerConfiguration(DataSource dataSource) { 74 | this.jdbcTemplate = new JdbcTemplate(dataSource); 75 | } 76 | 77 | public long getOffset(String topic, int partition) { 78 | init(topic); 79 | return this.cache.get(topic); 80 | } 81 | 82 | private void init(String topic) { 83 | Long initialized = this.cache.get(topic); 84 | if (initialized != null) { 85 | return; 86 | } 87 | try { 88 | Map offset = jdbcTemplate.queryForMap("SELECT * FROM T_OFFSETS WHERE ID=0 and TOPIC=?", 89 | topic); 90 | this.cache.put(topic, (Long) offset.get("offset")); 91 | } catch (EmptyResultDataAccessException e) { 92 | Long offset = -1L; 93 | jdbcTemplate.update("INSERT into T_OFFSETS (ID, topic, part, offset) values (?, ?, ?, ?)", 0, topic, 0, 94 | offset); 95 | this.cache.put(topic, offset); 96 | } 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /shared-jms-db/src/test/java/com/springsource/open/jms/SynchronousMessageTriggerAndRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.jms; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.beans.factory.annotation.Qualifier; 32 | import org.springframework.boot.test.context.SpringBootTest; 33 | import org.springframework.jdbc.core.JdbcTemplate; 34 | import org.springframework.jms.core.JmsTemplate; 35 | import org.springframework.test.annotation.DirtiesContext; 36 | import org.springframework.test.context.junit4.SpringRunner; 37 | import org.springframework.test.context.transaction.AfterTransaction; 38 | import org.springframework.test.context.transaction.BeforeTransaction; 39 | import org.springframework.test.jdbc.JdbcTestUtils; 40 | import org.springframework.transaction.annotation.Transactional; 41 | 42 | @RunWith(SpringRunner.class) 43 | @SpringBootTest 44 | public class SynchronousMessageTriggerAndRollbackTests { 45 | 46 | @Autowired 47 | private JmsTemplate jmsTemplate; 48 | 49 | private JdbcTemplate jdbcTemplate; 50 | 51 | @Autowired 52 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 53 | this.jdbcTemplate = new JdbcTemplate(dataSource); 54 | } 55 | 56 | @BeforeTransaction 57 | public void clearData() { 58 | getMessages(); // drain queue 59 | jmsTemplate.convertAndSend("queue", "foo"); 60 | jmsTemplate.convertAndSend("queue", "bar"); 61 | jdbcTemplate.update("delete from T_FOOS"); 62 | } 63 | 64 | @AfterTransaction 65 | public void checkPostConditions() { 66 | 67 | List list = getMessages(); 68 | assertEquals(2, list.size()); 69 | assertEquals(0, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 70 | 71 | } 72 | 73 | @Test 74 | @Transactional 75 | @DirtiesContext 76 | public void testReceiveMessageUpdateDatabase() throws Exception { 77 | 78 | List list = getMessages(); 79 | assertEquals(2, list.size()); 80 | assertTrue(list.contains("foo")); 81 | 82 | int id = 0; 83 | for (String name : list) { 84 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 85 | } 86 | 87 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 88 | 89 | } 90 | 91 | private List getMessages() { 92 | String next = ""; 93 | List msgs = new ArrayList(); 94 | while (next != null) { 95 | next = (String) jmsTemplate.receiveAndConvert("queue"); 96 | if (next != null) 97 | msgs.add(next); 98 | } 99 | return msgs; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /shared-jms-db/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.springsource.open 5 | spring-shared-jms-db 6 | 2.0.0.CI-SNAPSHOT 7 | jar 8 | 9 | org.springframework.boot 10 | spring-boot-starter-parent 11 | 1.4.0.RELEASE 12 | 13 | 14 | Spring Shared JMS-DB 15 | 18 | 19 | true 20 | 1.8 21 | 22 | 23 | 24 | strict 25 | 26 | false 27 | 28 | 29 | 30 | fast 31 | 32 | true 33 | 34 | 35 | 36 | 37 | 38 | org.apache.derby 39 | derby 40 | test 41 | 42 | 43 | org.apache.activemq 44 | activemq-core 45 | 5.1.0 46 | 47 | 48 | commons-logging 49 | commons-logging 50 | 51 | 52 | commons-logging 53 | commons-logging-api 54 | 55 | 56 | mx4j 57 | mx4j 58 | 59 | 60 | org.apache.geronimo.specs 61 | geronimo-j2ee-management_1.0_spec 62 | 63 | 64 | org.apache.activemq 65 | activeio-core 66 | 67 | 68 | org.apache.camel 69 | camel-core 70 | 71 | 72 | test 73 | 74 | 75 | javax.jms 76 | jms-api 77 | 78 | 79 | org.apache.geronimo.specs 80 | geronimo-j2ee-management_1.1_spec 81 | 1.0.1 82 | test 83 | 84 | 85 | 86 | org.springframework 87 | spring-jms 88 | 89 | 90 | org.springframework.boot 91 | spring-boot-starter-jdbc 92 | 93 | 94 | org.springframework.boot 95 | spring-boot-starter-aop 96 | 97 | 98 | org.springframework.boot 99 | spring-boot-starter-test 100 | test 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /shared-jms-db/src/test/java/com/springsource/open/jms/SynchronousMessageTriggerSunnyDayTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.jms; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.Test; 29 | import org.junit.runner.RunWith; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.beans.factory.annotation.Qualifier; 32 | import org.springframework.boot.test.context.SpringBootTest; 33 | import org.springframework.jdbc.core.JdbcTemplate; 34 | import org.springframework.jms.core.JmsTemplate; 35 | import org.springframework.test.annotation.DirtiesContext; 36 | import org.springframework.test.annotation.Rollback; 37 | import org.springframework.test.context.junit4.SpringRunner; 38 | import org.springframework.test.context.transaction.AfterTransaction; 39 | import org.springframework.test.context.transaction.BeforeTransaction; 40 | import org.springframework.test.jdbc.JdbcTestUtils; 41 | import org.springframework.transaction.annotation.Transactional; 42 | 43 | @RunWith(SpringRunner.class) 44 | @SpringBootTest 45 | public class SynchronousMessageTriggerSunnyDayTests { 46 | 47 | @Autowired 48 | private JmsTemplate jmsTemplate; 49 | 50 | private JdbcTemplate jdbcTemplate; 51 | 52 | @Autowired 53 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 54 | this.jdbcTemplate = new JdbcTemplate(dataSource); 55 | } 56 | 57 | @BeforeTransaction 58 | public void clearData() { 59 | getMessages(); // drain queue 60 | jmsTemplate.convertAndSend("queue", "foo"); 61 | jmsTemplate.convertAndSend("queue", "bar"); 62 | jdbcTemplate.update("delete from T_FOOS"); 63 | } 64 | 65 | @AfterTransaction 66 | public void checkPostConditions() { 67 | 68 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 69 | List list = getMessages(); 70 | assertEquals(0, list.size()); 71 | 72 | } 73 | 74 | @Test 75 | @Transactional 76 | @Rollback(false) 77 | @DirtiesContext 78 | public void testReceiveMessageUpdateDatabase() throws Exception { 79 | 80 | List list = getMessages(); 81 | assertEquals(2, list.size()); 82 | assertTrue(list.contains("foo")); 83 | 84 | int id = 0; 85 | for (String name : list) { 86 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 87 | } 88 | 89 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 90 | 91 | } 92 | 93 | private List getMessages() { 94 | String next = ""; 95 | List msgs = new ArrayList(); 96 | while (next != null) { 97 | next = (String) jmsTemplate.receiveAndConvert("queue"); 98 | if (next != null) 99 | msgs.add(next); 100 | } 101 | return msgs; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /best-jms-db/src/test/java/com/springsource/open/foo/sync/SynchronousMessageTriggerAndPartialRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.sync; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.jupiter.api.Test; 29 | import org.springframework.beans.factory.annotation.Autowired; 30 | import org.springframework.beans.factory.annotation.Qualifier; 31 | import org.springframework.boot.test.context.SpringBootTest; 32 | import org.springframework.jdbc.core.JdbcTemplate; 33 | import org.springframework.jms.core.JmsTemplate; 34 | import org.springframework.test.annotation.Rollback; 35 | import org.springframework.test.context.transaction.AfterTransaction; 36 | import org.springframework.test.context.transaction.BeforeTransaction; 37 | import org.springframework.test.jdbc.JdbcTestUtils; 38 | import org.springframework.transaction.annotation.Transactional; 39 | 40 | import com.springsource.open.foo.FailureSimulator; 41 | 42 | @SpringBootTest 43 | public class SynchronousMessageTriggerAndPartialRollbackTests { 44 | 45 | @Autowired 46 | private JmsTemplate jmsTemplate; 47 | 48 | @Autowired 49 | private FailureSimulator failureSimulator; 50 | 51 | private JdbcTemplate jdbcTemplate; 52 | 53 | @Autowired 54 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 55 | this.jdbcTemplate = new JdbcTemplate(dataSource); 56 | } 57 | 58 | @BeforeTransaction 59 | public void clearData() { 60 | getMessages(); // drain queue 61 | jmsTemplate.convertAndSend("queue", "foo"); 62 | jmsTemplate.convertAndSend("queue", "bar"); 63 | jdbcTemplate.update("delete from T_FOOS"); 64 | } 65 | 66 | @AfterTransaction 67 | public void checkPostConditions() { 68 | 69 | // The database committed... 70 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 71 | List list = getMessages(); 72 | // ...but the messages rolled back 73 | assertEquals(2, list.size()); 74 | 75 | } 76 | 77 | @Test 78 | @Transactional 79 | @Rollback(false) 80 | public void testReceiveMessageUpdateDatabase() throws Exception { 81 | 82 | List list = getMessages(); 83 | assertEquals(2, list.size()); 84 | assertTrue(list.contains("foo")); 85 | 86 | int id = 0; 87 | for (String name : list) { 88 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 89 | } 90 | 91 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 92 | 93 | failureSimulator.simulateMessageSystemFailure(); 94 | 95 | } 96 | 97 | private List getMessages() { 98 | String next = ""; 99 | List msgs = new ArrayList(); 100 | while (next != null) { 101 | next = (String) jmsTemplate.receiveAndConvert("queue"); 102 | if (next != null) 103 | msgs.add(next); 104 | } 105 | return msgs; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /best-rabbit-db/src/test/java/com/springsource/open/foo/sync/SynchronousMessageTriggerAndPartialRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.sync; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.jupiter.api.Test; 29 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.beans.factory.annotation.Qualifier; 32 | import org.springframework.boot.test.context.SpringBootTest; 33 | import org.springframework.jdbc.core.JdbcTemplate; 34 | import org.springframework.test.annotation.Rollback; 35 | import org.springframework.test.context.transaction.AfterTransaction; 36 | import org.springframework.test.context.transaction.BeforeTransaction; 37 | import org.springframework.test.jdbc.JdbcTestUtils; 38 | import org.springframework.transaction.annotation.Transactional; 39 | 40 | import com.springsource.open.foo.FailureSimulator; 41 | 42 | @SpringBootTest 43 | public class SynchronousMessageTriggerAndPartialRollbackTests { 44 | 45 | @Autowired 46 | private RabbitTemplate jmsTemplate; 47 | 48 | @Autowired 49 | private FailureSimulator failureSimulator; 50 | 51 | private JdbcTemplate jdbcTemplate; 52 | 53 | @Autowired 54 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 55 | this.jdbcTemplate = new JdbcTemplate(dataSource); 56 | } 57 | 58 | @BeforeTransaction 59 | public void clearData() { 60 | getMessages(); // drain queue 61 | jmsTemplate.convertAndSend("queue", "foo"); 62 | jmsTemplate.convertAndSend("queue", "bar"); 63 | jdbcTemplate.update("delete from T_FOOS"); 64 | } 65 | 66 | @AfterTransaction 67 | public void checkPostConditions() { 68 | 69 | // The database committed... 70 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 71 | List list = getMessages(); 72 | // ...but the messages rolled back 73 | assertEquals(2, list.size()); 74 | 75 | } 76 | 77 | @Test 78 | @Transactional 79 | @Rollback(false) 80 | public void testReceiveMessageUpdateDatabase() throws Exception { 81 | 82 | List list = getMessages(); 83 | assertEquals(2, list.size()); 84 | assertTrue(list.contains("foo")); 85 | 86 | int id = 0; 87 | for (String name : list) { 88 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 89 | } 90 | 91 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 92 | 93 | failureSimulator.simulateMessageSystemFailure(); 94 | 95 | } 96 | 97 | private List getMessages() { 98 | String next = ""; 99 | List msgs = new ArrayList(); 100 | while (next != null) { 101 | next = (String) jmsTemplate.receiveAndConvert("queue"); 102 | if (next != null) 103 | msgs.add(next); 104 | } 105 | return msgs; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /atomikos-db/src/main/java/com/springsource/open/db/AtomikosApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.db; 18 | 19 | import java.io.File; 20 | 21 | import javax.annotation.PostConstruct; 22 | import javax.sql.DataSource; 23 | 24 | import org.apache.derby.jdbc.EmbeddedXADataSource; 25 | import org.springframework.beans.factory.annotation.Qualifier; 26 | import org.springframework.beans.factory.annotation.Value; 27 | import org.springframework.boot.SpringApplication; 28 | import org.springframework.boot.autoconfigure.SpringBootApplication; 29 | import org.springframework.boot.context.properties.ConfigurationProperties; 30 | import org.springframework.boot.jdbc.XADataSourceWrapper; 31 | import org.springframework.context.annotation.Bean; 32 | import org.springframework.core.io.ClassPathResource; 33 | import org.springframework.jdbc.datasource.init.DataSourceInitializer; 34 | import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; 35 | 36 | @SpringBootApplication 37 | public class AtomikosApplication { 38 | 39 | private final XADataSourceWrapper wrapper; 40 | 41 | public AtomikosApplication(XADataSourceWrapper wrapper) { 42 | this.wrapper = wrapper; 43 | } 44 | 45 | @PostConstruct 46 | public void init() throws Exception { 47 | File directory = new File("derby-home"); 48 | System.setProperty("derby.system.home", directory.getCanonicalPath()); 49 | System.setProperty("derby.storage.fileSyncTransactionLog", "true"); 50 | System.setProperty("derby.storage.pageCacheSize", "100"); 51 | } 52 | 53 | public static void main(String[] args) throws Exception { 54 | SpringApplication.run(AtomikosApplication.class, args); 55 | } 56 | 57 | @Bean 58 | @ConfigurationProperties("first.datasource") 59 | public DataSource firstDataSource(@Value("${first.datasource.name}") String name) throws Exception { 60 | return xaDataSource(name); 61 | } 62 | 63 | @Bean 64 | @ConfigurationProperties("second.datasource") 65 | public DataSource secondDataSource(@Value("${second.datasource.name}") String name) throws Exception { 66 | return xaDataSource(name); 67 | } 68 | 69 | private DataSource xaDataSource(String name) throws Exception { 70 | EmbeddedXADataSource dataSource = new EmbeddedXADataSource(); 71 | dataSource.setCreateDatabase("create"); 72 | dataSource.setDatabaseName(name); 73 | DataSource wrapped = wrapper.wrapDataSource(dataSource); 74 | return wrapped; 75 | } 76 | 77 | @Bean 78 | public DataSourceInitializer firstDataSourceInitializer( 79 | @Qualifier("firstDataSource") DataSource dataSource) { 80 | return initialize(dataSource, "first"); 81 | } 82 | 83 | @Bean 84 | public DataSourceInitializer secondDataSourceInitializer( 85 | @Qualifier("secondDataSource") DataSource dataSource) { 86 | return initialize(dataSource, "second"); 87 | } 88 | 89 | private DataSourceInitializer initialize(DataSource dataSource, String name) { 90 | DataSourceInitializer initializer = new DataSourceInitializer(); 91 | initializer.setDataSource(dataSource); 92 | ResourceDatabasePopulator populator = new ResourceDatabasePopulator( 93 | new ClassPathResource(name + ".sql")); 94 | populator.setIgnoreFailedDrops(true); 95 | initializer.setDatabasePopulator(populator); 96 | return initializer; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /shared-kafka-db/src/test/java/com/springsource/open/foo/async/AbstractAsynchronousMessageTriggerTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import java.util.Collections; 20 | import java.util.concurrent.ExecutionException; 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.concurrent.TimeoutException; 23 | 24 | import com.springsource.open.foo.Handler; 25 | 26 | import org.apache.kafka.clients.admin.AdminClient; 27 | import org.junit.jupiter.api.AfterEach; 28 | import org.junit.jupiter.api.BeforeEach; 29 | 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.boot.test.context.SpringBootTest; 32 | import org.springframework.jdbc.core.JdbcTemplate; 33 | import org.springframework.kafka.config.KafkaListenerEndpointRegistry; 34 | import org.springframework.kafka.config.TopicBuilder; 35 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 36 | import org.springframework.kafka.core.KafkaAdmin; 37 | import org.springframework.kafka.core.KafkaTemplate; 38 | 39 | import static org.assertj.core.api.Assertions.assertThat; 40 | 41 | @SpringBootTest("spring.kafka.consumer.group-id=group") 42 | public abstract class AbstractAsynchronousMessageTriggerTests { 43 | 44 | private AdminClient client; 45 | 46 | @Autowired 47 | protected KafkaTemplate kafkaTemplate; 48 | 49 | @Autowired 50 | private Handler handler; 51 | 52 | @Autowired 53 | protected JdbcTemplate jdbcTemplate; 54 | 55 | @BeforeEach 56 | public void clearData(@Autowired KafkaAdmin admin, @Autowired KafkaListenerEndpointRegistry registry, 57 | @Autowired DefaultKafkaProducerFactory pf) 58 | throws InterruptedException, ExecutionException, TimeoutException { 59 | 60 | pf.reset(); // close the producer(s) because his metadata will be invalidated when we delete the topic 61 | this.client = AdminClient.create(admin.getConfig()); 62 | this.client.deleteTopics(Collections.singletonList("async")).all().get(10, TimeUnit.SECONDS); 63 | int n = 0; 64 | while (n++ < 100) { 65 | try { 66 | this.client 67 | .createTopics( 68 | Collections.singletonList(TopicBuilder.name("async").partitions(1).replicas(1).build())) 69 | .all().get(10, TimeUnit.SECONDS); 70 | break; 71 | } 72 | catch (ExecutionException e) { 73 | // race with async topic deletion 74 | Thread.sleep(100); 75 | } 76 | } 77 | 78 | // Start the listeners... 79 | handler.resetItemCount(); 80 | registry.getListenerContainer("group").start(); 81 | jdbcTemplate.update("delete from T_FOOS"); 82 | jdbcTemplate.update("delete from T_OFFSETS"); 83 | } 84 | 85 | @AfterEach 86 | public void waitForMessages(@Autowired KafkaListenerEndpointRegistry registry) throws Exception { 87 | 88 | int count = 0; 89 | while (handler.getItemCount() < 2 && (count++) < 30) { 90 | Thread.sleep(100); 91 | } 92 | // Stop the listeners... 93 | registry.getListenerContainer("group").stop(); 94 | // Give it time to finish up... 95 | Thread.sleep(2000); 96 | assertThat(handler.getItemCount()).isGreaterThanOrEqualTo(2); 97 | 98 | checkPostConditions(); 99 | this.client.close(); 100 | } 101 | 102 | protected abstract void checkPostConditions() throws Exception; 103 | 104 | protected long consumerOffset() throws InterruptedException, ExecutionException, TimeoutException { 105 | return this.jdbcTemplate.queryForObject("SELECT OFFSET from T_OFFSETS where ID=0", Long.class); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /best-kafka-db/src/test/java/com/springsource/open/foo/async/AbstractAsynchronousMessageTriggerTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.foo.async; 18 | 19 | import java.util.Collections; 20 | import java.util.concurrent.ExecutionException; 21 | import java.util.concurrent.TimeUnit; 22 | import java.util.concurrent.TimeoutException; 23 | 24 | import com.springsource.open.foo.Handler; 25 | 26 | import org.apache.kafka.clients.admin.AdminClient; 27 | import org.junit.jupiter.api.AfterEach; 28 | import org.junit.jupiter.api.BeforeEach; 29 | 30 | import org.springframework.beans.factory.annotation.Autowired; 31 | import org.springframework.boot.test.context.SpringBootTest; 32 | import org.springframework.jdbc.core.JdbcTemplate; 33 | import org.springframework.kafka.config.KafkaListenerEndpointRegistry; 34 | import org.springframework.kafka.config.TopicBuilder; 35 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 36 | import org.springframework.kafka.core.KafkaAdmin; 37 | import org.springframework.kafka.core.KafkaTemplate; 38 | 39 | import static org.assertj.core.api.Assertions.assertThat; 40 | 41 | @SpringBootTest("spring.kafka.consumer.group-id=group") 42 | public abstract class AbstractAsynchronousMessageTriggerTests { 43 | 44 | private AdminClient client; 45 | 46 | @Autowired 47 | protected KafkaTemplate kafkaTemplate; 48 | 49 | @Autowired 50 | private Handler handler; 51 | 52 | @Autowired 53 | protected JdbcTemplate jdbcTemplate; 54 | 55 | @BeforeEach 56 | public void clearData(@Autowired KafkaAdmin admin, @Autowired KafkaListenerEndpointRegistry registry, 57 | @Autowired DefaultKafkaProducerFactory pf) 58 | throws InterruptedException, ExecutionException, TimeoutException { 59 | 60 | pf.reset(); // close the producer(s) because his metadata will be invalidated when we delete the topic 61 | this.client = AdminClient.create(admin.getConfig()); 62 | this.client.deleteTopics(Collections.singletonList("async")).all().get(10, TimeUnit.SECONDS); 63 | int n = 0; 64 | while (n++ < 100) { 65 | try { 66 | this.client 67 | .createTopics( 68 | Collections.singletonList(TopicBuilder.name("async").partitions(1).replicas(1).build())) 69 | .all().get(10, TimeUnit.SECONDS); 70 | break; 71 | } 72 | catch (ExecutionException e) { 73 | // race with async topic deletion 74 | Thread.sleep(100); 75 | } 76 | } 77 | 78 | // Start the listeners... 79 | handler.resetItemCount(); 80 | registry.getListenerContainer("group").start(); 81 | jdbcTemplate.update("delete from T_FOOS"); 82 | } 83 | 84 | @AfterEach 85 | public void waitForMessages(@Autowired KafkaListenerEndpointRegistry registry) throws Exception { 86 | 87 | int count = 0; 88 | while (handler.getItemCount() < 2 && (count++) < 30) { 89 | Thread.sleep(100); 90 | } 91 | // Stop the listeners... 92 | registry.getListenerContainer("group").stop(); 93 | // Give it time to finish up... 94 | Thread.sleep(2000); 95 | assertThat(handler.getItemCount()).isGreaterThanOrEqualTo(2); 96 | 97 | checkPostConditions(); 98 | this.client.close(); 99 | } 100 | 101 | protected abstract void checkPostConditions() throws Exception; 102 | 103 | protected long consumerOffset() throws InterruptedException, ExecutionException, TimeoutException { 104 | return this.client.listConsumerGroupOffsets("group").partitionsToOffsetAndMetadata().get(10, TimeUnit.SECONDS) 105 | .values().iterator().next().offset(); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /best-db-db/src/test/java/com/springsource/open/db/MultipleDatasourceTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.db; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | 21 | import java.util.Date; 22 | 23 | import javax.sql.DataSource; 24 | 25 | import org.junit.jupiter.api.Test; 26 | import org.springframework.beans.factory.annotation.Autowired; 27 | import org.springframework.beans.factory.annotation.Qualifier; 28 | import org.springframework.boot.test.context.SpringBootTest; 29 | import org.springframework.dao.DataIntegrityViolationException; 30 | import org.springframework.jdbc.core.JdbcTemplate; 31 | import org.springframework.test.context.transaction.AfterTransaction; 32 | import org.springframework.test.context.transaction.BeforeTransaction; 33 | import org.springframework.transaction.annotation.Transactional; 34 | 35 | @SpringBootTest 36 | public class MultipleDatasourceTests { 37 | 38 | private JdbcTemplate jdbcTemplate; 39 | private JdbcTemplate otherJdbcTemplate; 40 | 41 | @Autowired 42 | public void setDataSources(@Qualifier("firstDataSource") DataSource dataSource, 43 | @Qualifier("secondDataSource") DataSource otherDataSource) { 44 | this.jdbcTemplate = new JdbcTemplate(dataSource); 45 | this.otherJdbcTemplate = new JdbcTemplate(otherDataSource); 46 | } 47 | 48 | @BeforeTransaction 49 | public void clearData() { 50 | jdbcTemplate.update("delete from T_FOOS"); 51 | otherJdbcTemplate.update("delete from T_AUDITS"); 52 | } 53 | 54 | @AfterTransaction 55 | public void checkPostConditions() { 56 | 57 | int count = jdbcTemplate.queryForObject("select count(*) from T_FOOS", Integer.class); 58 | // This change was rolled back by the test framework 59 | assertEquals(0, count); 60 | 61 | count = otherJdbcTemplate.queryForObject("select count(*) from T_AUDITS", Integer.class); 62 | // This rolls back as well if the connections are managed together 63 | assertEquals(0, count); 64 | 65 | } 66 | 67 | /** 68 | * Vanilla test case for two inserts into two data sources. Both should roll 69 | * back. 70 | * 71 | * @throws Exception 72 | */ 73 | @Transactional 74 | @Test 75 | public void testInsertIntoTwoDataSources() throws Exception { 76 | 77 | int count = jdbcTemplate.update( 78 | "INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, 79 | "foo"); 80 | assertEquals(1, count); 81 | 82 | count = otherJdbcTemplate 83 | .update( 84 | "INSERT into T_AUDITS (id,operation,name,audit_date) values (?,?,?,?)", 85 | 0, "INSERT", "foo", new Date()); 86 | assertEquals(1, count); 87 | 88 | } 89 | 90 | /** 91 | * Shows how to check the operation on the inner data source to see if it 92 | * has already been committed, and if it has do something different, instead 93 | * of just hitting a {@link DataIntegrityViolationException}. 94 | * 95 | * @throws Exception 96 | */ 97 | @Transactional 98 | @Test 99 | public void testInsertWithCheckForDuplicates() throws Exception { 100 | 101 | int count = jdbcTemplate.update( 102 | "INSERT into T_FOOS (id,name,foo_date) values (?,?,null)", 0, 103 | "foo"); 104 | assertEquals(1, count); 105 | 106 | count = otherJdbcTemplate 107 | .update( 108 | "UPDATE T_AUDITS set operation=?, name=?, audit_date=? where id=?", 109 | "UPDATE", "foo", new Date(), 0); 110 | 111 | if (count == 0) { 112 | count = otherJdbcTemplate 113 | .update( 114 | "INSERT into T_AUDITS (id,operation,name,audit_date) values (?,?,?,?)", 115 | 0, "INSERT", "foo", new Date()); 116 | } 117 | 118 | assertEquals(1, count); 119 | 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /best-db-db/src/main/java/com/springsource/open/db/BestDatabaseApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012-2015 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.db; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import javax.sql.DataSource; 23 | 24 | import org.springframework.beans.factory.annotation.Qualifier; 25 | import org.springframework.boot.SpringApplication; 26 | import org.springframework.boot.autoconfigure.SpringBootApplication; 27 | import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; 28 | import org.springframework.boot.context.properties.ConfigurationProperties; 29 | import org.springframework.context.annotation.Bean; 30 | import org.springframework.context.annotation.Primary; 31 | import org.springframework.core.io.ClassPathResource; 32 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 33 | import org.springframework.jdbc.datasource.init.DataSourceInitializer; 34 | import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; 35 | import org.springframework.transaction.PlatformTransactionManager; 36 | 37 | @SpringBootApplication 38 | public class BestDatabaseApplication { 39 | 40 | public static void main(String[] args) throws Exception { 41 | SpringApplication.run(BestDatabaseApplication.class, args); 42 | } 43 | 44 | @Bean 45 | public ChainedTransactionManager transactionManager( 46 | @Qualifier("firstDataSource") DataSource firstDataSource, 47 | @Qualifier("secondDataSource") DataSource secondDataSource) { 48 | ChainedTransactionManager transactionManager = new ChainedTransactionManager( 49 | getTransactionManagers(firstDataSource, secondDataSource)); 50 | return transactionManager; 51 | } 52 | 53 | private List getTransactionManagers( 54 | DataSource... sources) { 55 | List list = new ArrayList<>(); 56 | for (DataSource dataSource : sources) { 57 | DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager( 58 | dataSource); 59 | dataSourceTransactionManager 60 | .setTransactionSynchronizationName("SYNCHRONIZATION_NEVER"); 61 | list.add(dataSourceTransactionManager); 62 | } 63 | return list; 64 | } 65 | 66 | @Bean 67 | @Primary 68 | @ConfigurationProperties("first.datasource") 69 | public DataSourceProperties firstDataSourceProperties() { 70 | return new DataSourceProperties(); 71 | } 72 | 73 | @Bean 74 | @Primary 75 | public DataSource firstDataSource(@Qualifier("firstDataSourceProperties") DataSourceProperties properties) { 76 | return properties.initializeDataSourceBuilder().build(); 77 | } 78 | 79 | @Bean 80 | @ConfigurationProperties("second.datasource") 81 | public DataSourceProperties secondDataSourceProperties() { 82 | return new DataSourceProperties(); 83 | } 84 | 85 | @Bean 86 | public DataSource secondDataSource(@Qualifier("secondDataSourceProperties") DataSourceProperties properties) { 87 | return properties.initializeDataSourceBuilder().build(); 88 | } 89 | 90 | @Bean 91 | public DataSourceInitializer firstDataSourceInitializer( 92 | @Qualifier("firstDataSource") DataSource dataSource) { 93 | return initialize(dataSource, "first"); 94 | } 95 | 96 | @Bean 97 | public DataSourceInitializer secondDataSourceInitializer( 98 | @Qualifier("secondDataSource") DataSource dataSource) { 99 | return initialize(dataSource, "second"); 100 | } 101 | 102 | private DataSourceInitializer initialize(DataSource dataSource, String name) { 103 | DataSourceInitializer initializer = new DataSourceInitializer(); 104 | initializer.setDataSource(dataSource); 105 | ResourceDatabasePopulator populator = new ResourceDatabasePopulator( 106 | new ClassPathResource(name + ".sql")); 107 | populator.setIgnoreFailedDrops(true); 108 | initializer.setDatabasePopulator(populator); 109 | return initializer; 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /shared-jms-db/src/test/java/com/springsource/open/jms/SynchronousMessageTriggerAndPartialRollbackTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.springsource.open.jms; 18 | 19 | import static org.junit.Assert.assertEquals; 20 | import static org.junit.Assert.assertTrue; 21 | 22 | import java.util.ArrayList; 23 | import java.util.Date; 24 | import java.util.List; 25 | 26 | import javax.sql.DataSource; 27 | 28 | import org.junit.After; 29 | import org.junit.Before; 30 | import org.junit.Test; 31 | import org.junit.runner.RunWith; 32 | import org.springframework.beans.factory.annotation.Autowired; 33 | import org.springframework.beans.factory.annotation.Qualifier; 34 | import org.springframework.boot.test.context.SpringBootTest; 35 | import org.springframework.jdbc.core.JdbcTemplate; 36 | import org.springframework.jms.connection.SynchedLocalTransactionFailedException; 37 | import org.springframework.jms.core.JmsTemplate; 38 | import org.springframework.test.annotation.DirtiesContext; 39 | import org.springframework.test.context.junit4.SpringRunner; 40 | import org.springframework.test.jdbc.JdbcTestUtils; 41 | import org.springframework.transaction.PlatformTransactionManager; 42 | import org.springframework.transaction.TransactionStatus; 43 | import org.springframework.transaction.support.TransactionCallback; 44 | import org.springframework.transaction.support.TransactionTemplate; 45 | 46 | @RunWith(SpringRunner.class) 47 | @SpringBootTest 48 | public class SynchronousMessageTriggerAndPartialRollbackTests { 49 | 50 | @Autowired 51 | private JmsTemplate jmsTemplate; 52 | 53 | @Autowired 54 | private PlatformTransactionManager transactionManager; 55 | 56 | @Autowired 57 | private FailureSimulator failureSimulator; 58 | 59 | private JdbcTemplate jdbcTemplate; 60 | 61 | @Autowired 62 | public void setDataSource(@Qualifier("dataSource") DataSource dataSource) { 63 | this.jdbcTemplate = new JdbcTemplate(dataSource); 64 | } 65 | 66 | @Before 67 | public void clearData() throws Exception { 68 | 69 | getMessages(); // drain queue 70 | jmsTemplate.convertAndSend("queue", "foo"); 71 | jmsTemplate.convertAndSend("queue", "bar"); 72 | jdbcTemplate.update("delete from T_FOOS"); 73 | } 74 | 75 | /** 76 | * We expect a total rollback, despite the fact that the business processing 77 | * was successful and only the JMS commit failed. 78 | * 79 | * @throws Exception 80 | */ 81 | @After 82 | public void checkPostConditions() throws Exception { 83 | 84 | assertEquals(0, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 85 | List list = getMessages(); 86 | assertEquals(2, list.size()); 87 | 88 | } 89 | 90 | @Test(expected = SynchedLocalTransactionFailedException.class) 91 | @DirtiesContext 92 | public void testReceiveMessageUpdateDatabase() throws Throwable { 93 | 94 | /* 95 | * We can't actually test this using @Transactional because the 96 | * exception we expect is going to come on commit, which happens in the 97 | * test framework in that case, outside this method. So we have to use a 98 | * TransactionTemplate. 99 | */ 100 | 101 | new TransactionTemplate(transactionManager).execute(new TransactionCallback() { 102 | public Object doInTransaction(TransactionStatus status) { 103 | 104 | try { 105 | doReceiveAndInsert(); 106 | } 107 | catch (Exception e) { 108 | throw new RuntimeException(e); 109 | } 110 | 111 | return null; 112 | 113 | } 114 | 115 | }); 116 | 117 | } 118 | 119 | private void doReceiveAndInsert() throws Exception { 120 | 121 | List list = getMessages(); 122 | assertEquals(2, list.size()); 123 | assertTrue(list.contains("foo")); 124 | 125 | int id = 0; 126 | for (String name : list) { 127 | jdbcTemplate.update("INSERT INTO T_FOOS (id,name,foo_date) values (?,?,?)", id++, name, new Date()); 128 | } 129 | 130 | assertEquals(2, JdbcTestUtils.countRowsInTable(jdbcTemplate, "T_FOOS")); 131 | 132 | // Simulate infrastructure failure in the messaging system... 133 | failureSimulator.simulateMessageSystemFailure(); 134 | 135 | } 136 | 137 | private List getMessages() { 138 | String next = ""; 139 | List msgs = new ArrayList(); 140 | while (next != null) { 141 | next = (String) jmsTemplate.receiveAndConvert("queue"); 142 | if (next != null) 143 | msgs.add(next); 144 | } 145 | return msgs; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Distributed transactions in Spring with and without XA 2 | 3 | ## The sample code 4 | 5 | This project has updates to the sample code from the [JavaWorld article](http://www.javaworld.com/article/2077963/open-source-tools/distributed-transactions-in-spring--with-and-without-xa.html) on distributed transactions from 2008. It is packaged as a set of Maven projects. All the projects should work as standalones; there are no dependencies between projects and no parent POM for the Maven metadata. If you don't want to use Maven at all, you need to use the dependency information in the `pom.xml` to create a classpath for the build. 6 | 7 | All of the samples use Spring to configure the underlying infrastructure (databases and so on), and the configuration is in `src/main/java/**`, with annotations for dependency injection, and using Spring Boot autoconfiguration wherever possible. They also all use embedded database and messaging instances, so you don't need to start any external processes (except for the RabbitMQ example). This is not intended for production use, and I have heard reports of XA problems with several open source RDBMS platforms, including Apache Derby (used in the XA samples without any problems, but this is not an exhaustive test). 8 | 9 | Unit tests are used to show the features of each pattern in action. To run them in Eclipse just right click (on a test or on the project) and choose `Run As->JUnit Test`. In VSCode open up indeividual test classes and run them one by one with the `Run Test` link added to the class by the Java extensions. All tests should pass. Most use the integration test support from Spring to roll back a transaction automatically, so that the tests can make assertions about the success of the most common failure scenario (full rollback). 10 | 11 | 12 | ### Project `atomikos-db` 13 | This is the XA/JTA example, included for the sake of completeness. It uses Atomikos for the JTA implementation and Apache Derby for the `XADataSource`. The tests show two data sources being updated in the same transaction and then rolling back together. 14 | 15 | ### Project `best-jms-db` 16 | 17 | This is the project showing a Best Efforts 1PC approach to message-driven database updates. The important features of the asynchronous pattern are described in the text above. The main entry point is the unit test `AsynchronousMessageTriggerAndRollbackTests`. There is also a version of the `SynchronousMessageTriggerAndRollbackTests` from the `shared-jms-db` sample showing that the synchronous example also works just fine with this pattern. 18 | 19 | ### Project `best-rabbit-db` 20 | 21 | This is the project showing a Best Efforts 1PC approach using AMQP over RabbitMQ. The features and the tests are the same as for the `best-jms-db` sample. 22 | 23 | ### Project `best-db-db` 24 | 25 | This is the project showing a Best Efforts 1PC approach to linked database updates. The important features are described in the article text. The main entry point is the unit test `MultipleDatasourceTests`. 26 | 27 | ### Project `shared-jms-db` 28 | 29 | This is the project showing a shared resource approach to message-driven database updates. It only works with a very old version of ActiveMQ (5.1) and Spring Boot (1.4). The important features of the configuration are described in the article text. The main entry point is the `SynchronousMessage*Tests`unit test . 30 | 31 | The `JmsTransactionAwareDataSourceProxy` that is used to synchronize the JMS `Session` with the Spring transaction is an extension of the Spring `TransactionAwareDataSourceProxy`. It might not be the best way to implement this pattern, but it is the quickest and most direct that works for the purpose of this example. 32 | 33 | One other thing that is worth mentioning is the use of ActiveMQ with an embedded broker to keep the test self-contained (the broker URL starts with `vm://`). A distributed system would probably have more than one participant in the messaging, and multiple brokers would be needed, but they should all be embedded and used with `async=false` to make the shared resource pattern work. The embedded brokers in the various processes that comprise a system communicate through network connectors. 34 | 35 | It might help if we summarize the ActiveMQ specific features of this pattern, just to be clear what we have done. The main points to note are: 36 | 37 | * The embedded broker with `async=false` so that the JMS persistence happens in the same thread as the main transaction. 38 | 39 | * The use of the `JDBCPersistenceAdapter` in the broker, where the injected `DataSource` is a special proxy that ensures that transaction boundaries are respected. 40 | 41 | * In a distributed system, unlike in the samples, synonyms (or equivalent) would have to be used to link the ActiveMQ data to the business data in the RDBMS platform. 42 | 43 | * A distributed system would also have to allow all the embedded brokers to communicate with each other using a network connector. This is standard practice for large messaging installations anyway, but to use the shared resource pattern it is mandatory. See the ActiveMQ topologies documentation for more details. 44 | 45 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven2 Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | set MAVEN_CMD_LINE_ARGS=%* 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | 121 | set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar"" 122 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 123 | 124 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS% 125 | if ERRORLEVEL 1 goto error 126 | goto end 127 | 128 | :error 129 | set ERROR_CODE=1 130 | 131 | :end 132 | @endlocal & set ERROR_CODE=%ERROR_CODE% 133 | 134 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 135 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 136 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 137 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 138 | :skipRcPost 139 | 140 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 141 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 142 | 143 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 144 | 145 | exit /B %ERROR_CODE% -------------------------------------------------------------------------------- /best-db-db/src/test/java/test/jdbc/datasource/DataSourceInitializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2006-2007 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package test.jdbc.datasource; 18 | 19 | import java.io.IOException; 20 | import java.util.IdentityHashMap; 21 | import java.util.List; 22 | import java.util.Map; 23 | 24 | import javax.sql.DataSource; 25 | 26 | import org.apache.commons.io.IOUtils; 27 | import org.apache.commons.logging.Log; 28 | import org.apache.commons.logging.LogFactory; 29 | import org.springframework.beans.factory.BeanInitializationException; 30 | import org.springframework.beans.factory.DisposableBean; 31 | import org.springframework.beans.factory.InitializingBean; 32 | import org.springframework.context.support.ClassPathXmlApplicationContext; 33 | import org.springframework.core.io.Resource; 34 | import org.springframework.dao.DataAccessException; 35 | import org.springframework.jdbc.core.JdbcTemplate; 36 | import org.springframework.jdbc.datasource.DataSourceTransactionManager; 37 | import org.springframework.transaction.TransactionStatus; 38 | import org.springframework.transaction.support.TransactionCallback; 39 | import org.springframework.transaction.support.TransactionTemplate; 40 | import org.springframework.util.Assert; 41 | import org.springframework.util.ClassUtils; 42 | import org.springframework.util.StringUtils; 43 | 44 | /** 45 | * Wrapper for a {@link DataSource} that can run scripts on start up and shut 46 | * down. Us as a bean definition

47 | * 48 | * Run this class to initialize a database in a running server process. 49 | * Make sure the server is running first by launching the "hsql-server" from the 50 | * hsql.server project. Then you can right click in Eclipse and 51 | * Run As -> Java Application. Do the same any time you want to wipe the 52 | * database and start again. 53 | * 54 | * @author Dave Syer 55 | * 56 | */ 57 | public class DataSourceInitializer implements InitializingBean, DisposableBean { 58 | 59 | private static final Log logger = LogFactory.getLog(DataSourceInitializer.class); 60 | 61 | private Resource[] initScripts; 62 | 63 | private Resource[] destroyScripts; 64 | 65 | private DataSource dataSource; 66 | 67 | private boolean ignoreFailedDrop = true; 68 | 69 | private static Map initialized = new IdentityHashMap(); 70 | 71 | /** 72 | * Main method as convenient entry point. 73 | * 74 | * @param args 75 | */ 76 | public static void main(String... args) { 77 | new ClassPathXmlApplicationContext(ClassUtils.addResourcePathToPackagePath(DataSourceInitializer.class, 78 | DataSourceInitializer.class.getSimpleName() + "-context.xml")).close(); 79 | } 80 | 81 | /** 82 | * @throws Throwable 83 | * @see java.lang.Object#finalize() 84 | */ 85 | protected void finalize() throws Throwable { 86 | super.finalize(); 87 | initialized .clear(); 88 | logger.debug("finalize called"); 89 | } 90 | 91 | public void destroy() { 92 | if (destroyScripts==null) return; 93 | for (int i = 0; i < destroyScripts.length; i++) { 94 | Resource destroyScript = initScripts[i]; 95 | try { 96 | doExecuteScript(destroyScript); 97 | } 98 | catch (Exception e) { 99 | if (logger.isDebugEnabled()) { 100 | logger.warn("Could not execute destroy script [" + destroyScript + "]", e); 101 | } 102 | else { 103 | logger.warn("Could not execute destroy script [" + destroyScript + "]"); 104 | } 105 | } 106 | } 107 | } 108 | 109 | public void afterPropertiesSet() throws Exception { 110 | Assert.notNull(dataSource, "DataSource should not be null"); 111 | if (!initialized.containsKey(dataSource)) { 112 | initialized.put(dataSource, false); 113 | } 114 | initialize(); 115 | } 116 | 117 | private void initialize() { 118 | if (!initialized.get(dataSource)) { 119 | destroy(); 120 | if (initScripts != null) { 121 | for (int i = 0; i < initScripts.length; i++) { 122 | Resource initScript = initScripts[i]; 123 | doExecuteScript(initScript); 124 | } 125 | } 126 | initialized.put(dataSource, true); 127 | } 128 | } 129 | 130 | private void doExecuteScript(final Resource scriptResource) { 131 | if (scriptResource == null || !scriptResource.exists()) 132 | return; 133 | TransactionTemplate transactionTemplate = new TransactionTemplate(new DataSourceTransactionManager(dataSource)); 134 | transactionTemplate.execute(new TransactionCallback() { 135 | 136 | @SuppressWarnings("unchecked") 137 | public Void doInTransaction(TransactionStatus status) { 138 | JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 139 | String[] scripts; 140 | try { 141 | scripts = StringUtils.delimitedListToStringArray(stripComments(IOUtils.readLines(scriptResource 142 | .getInputStream())), ";"); 143 | } 144 | catch (IOException e) { 145 | throw new BeanInitializationException("Cannot load script from [" + scriptResource + "]", e); 146 | } 147 | for (int i = 0; i < scripts.length; i++) { 148 | String script = scripts[i].trim(); 149 | if (StringUtils.hasText(script)) { 150 | try { 151 | jdbcTemplate.execute(script); 152 | } 153 | catch (DataAccessException e) { 154 | if (ignoreFailedDrop && script.toLowerCase().startsWith("drop")) { 155 | logger.debug("DROP script failed (ignoring): " + script); 156 | } 157 | else { 158 | throw e; 159 | } 160 | } 161 | } 162 | } 163 | return null; 164 | } 165 | 166 | }); 167 | 168 | } 169 | 170 | private String stripComments(List list) { 171 | StringBuffer buffer = new StringBuffer(); 172 | for (String line : list) { 173 | if (!line.startsWith("//") && !line.startsWith("--")) { 174 | buffer.append(line + "\n"); 175 | } 176 | } 177 | return buffer.toString(); 178 | } 179 | 180 | public void setInitScripts(Resource[] initScripts) { 181 | this.initScripts = initScripts; 182 | } 183 | 184 | public void setDestroyScripts(Resource[] destroyScripts) { 185 | this.destroyScripts = destroyScripts; 186 | } 187 | 188 | public void setDataSource(DataSource dataSource) { 189 | this.dataSource = dataSource; 190 | } 191 | 192 | public void setIgnoreFailedDrop(boolean ignoreFailedDrop) { 193 | this.ignoreFailedDrop = ignoreFailedDrop; 194 | } 195 | 196 | } 197 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven2 Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # 58 | # Look for the Apple JDKs first to preserve the existing behaviour, and then look 59 | # for the new JDKs provided by Oracle. 60 | # 61 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then 62 | # 63 | # Apple JDKs 64 | # 65 | export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home 66 | fi 67 | 68 | if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then 69 | # 70 | # Apple JDKs 71 | # 72 | export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 73 | fi 74 | 75 | if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then 76 | # 77 | # Oracle JDKs 78 | # 79 | export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home 80 | fi 81 | 82 | if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then 83 | # 84 | # Apple JDKs 85 | # 86 | export JAVA_HOME=`/usr/libexec/java_home` 87 | fi 88 | ;; 89 | esac 90 | 91 | if [ -z "$JAVA_HOME" ] ; then 92 | if [ -r /etc/gentoo-release ] ; then 93 | JAVA_HOME=`java-config --jre-home` 94 | fi 95 | fi 96 | 97 | if [ -z "$M2_HOME" ] ; then 98 | ## resolve links - $0 may be a link to maven's home 99 | PRG="$0" 100 | 101 | # need this for relative symlinks 102 | while [ -h "$PRG" ] ; do 103 | ls=`ls -ld "$PRG"` 104 | link=`expr "$ls" : '.*-> \(.*\)$'` 105 | if expr "$link" : '/.*' > /dev/null; then 106 | PRG="$link" 107 | else 108 | PRG="`dirname "$PRG"`/$link" 109 | fi 110 | done 111 | 112 | saveddir=`pwd` 113 | 114 | M2_HOME=`dirname "$PRG"`/.. 115 | 116 | # make it fully qualified 117 | M2_HOME=`cd "$M2_HOME" && pwd` 118 | 119 | cd "$saveddir" 120 | # echo Using m2 at $M2_HOME 121 | fi 122 | 123 | # For Cygwin, ensure paths are in UNIX format before anything is touched 124 | if $cygwin ; then 125 | [ -n "$M2_HOME" ] && 126 | M2_HOME=`cygpath --unix "$M2_HOME"` 127 | [ -n "$JAVA_HOME" ] && 128 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 129 | [ -n "$CLASSPATH" ] && 130 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 131 | fi 132 | 133 | # For Migwn, ensure paths are in UNIX format before anything is touched 134 | if $mingw ; then 135 | [ -n "$M2_HOME" ] && 136 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 137 | [ -n "$JAVA_HOME" ] && 138 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 139 | # TODO classpath? 140 | fi 141 | 142 | if [ -z "$JAVA_HOME" ]; then 143 | javaExecutable="`which javac`" 144 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 145 | # readlink(1) is not available as standard on Solaris 10. 146 | readLink=`which readlink` 147 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 148 | if $darwin ; then 149 | javaHome="`dirname \"$javaExecutable\"`" 150 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 151 | else 152 | javaExecutable="`readlink -f \"$javaExecutable\"`" 153 | fi 154 | javaHome="`dirname \"$javaExecutable\"`" 155 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 156 | JAVA_HOME="$javaHome" 157 | export JAVA_HOME 158 | fi 159 | fi 160 | fi 161 | 162 | if [ -z "$JAVACMD" ] ; then 163 | if [ -n "$JAVA_HOME" ] ; then 164 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 165 | # IBM's JDK on AIX uses strange locations for the executables 166 | JAVACMD="$JAVA_HOME/jre/sh/java" 167 | else 168 | JAVACMD="$JAVA_HOME/bin/java" 169 | fi 170 | else 171 | JAVACMD="`which java`" 172 | fi 173 | fi 174 | 175 | if [ ! -x "$JAVACMD" ] ; then 176 | echo "Error: JAVA_HOME is not defined correctly." >&2 177 | echo " We cannot execute $JAVACMD" >&2 178 | exit 1 179 | fi 180 | 181 | if [ -z "$JAVA_HOME" ] ; then 182 | echo "Warning: JAVA_HOME environment variable is not set." 183 | fi 184 | 185 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 186 | 187 | # For Cygwin, switch paths to Windows format before running java 188 | if $cygwin; then 189 | [ -n "$M2_HOME" ] && 190 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 191 | [ -n "$JAVA_HOME" ] && 192 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 193 | [ -n "$CLASSPATH" ] && 194 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 195 | fi 196 | 197 | # traverses directory structure from process work directory to filesystem root 198 | # first directory with .mvn subdirectory is considered project base directory 199 | find_maven_basedir() { 200 | local basedir=$(pwd) 201 | local wdir=$(pwd) 202 | while [ "$wdir" != '/' ] ; do 203 | if [ -d "$wdir"/.mvn ] ; then 204 | basedir=$wdir 205 | break 206 | fi 207 | wdir=$(cd "$wdir/.."; pwd) 208 | done 209 | echo "${basedir}" 210 | } 211 | 212 | # concatenates all lines of a file 213 | concat_lines() { 214 | if [ -f "$1" ]; then 215 | echo "$(tr -s '\n' ' ' < "$1")" 216 | fi 217 | } 218 | 219 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)} 220 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 221 | 222 | # Provide a "standardized" way to retrieve the CLI args that will 223 | # work with both Windows and non-Windows executions. 224 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 225 | export MAVEN_CMD_LINE_ARGS 226 | 227 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 228 | 229 | exec "$JAVACMD" \ 230 | $MAVEN_OPTS \ 231 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 232 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 233 | ${WRAPPER_LAUNCHER} "$@" 234 | -------------------------------------------------------------------------------- /shared-jms-db/src/main/java/com/springsource/open/jms/JmsTransactionAwareDataSourceProxy.java: -------------------------------------------------------------------------------- 1 | package com.springsource.open.jms; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.InvocationTargetException; 5 | import java.lang.reflect.Method; 6 | import java.lang.reflect.Proxy; 7 | import java.sql.Connection; 8 | import java.sql.SQLException; 9 | import java.sql.Statement; 10 | 11 | import javax.jms.JMSException; 12 | import javax.jms.Session; 13 | import javax.sql.DataSource; 14 | 15 | import org.apache.commons.logging.Log; 16 | import org.apache.commons.logging.LogFactory; 17 | import org.springframework.jdbc.datasource.ConnectionProxy; 18 | import org.springframework.jdbc.datasource.DataSourceUtils; 19 | import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; 20 | import org.springframework.jms.connection.SynchedLocalTransactionFailedException; 21 | import org.springframework.jms.core.JmsTemplate; 22 | import org.springframework.jms.core.SessionCallback; 23 | import org.springframework.transaction.support.TransactionSynchronizationAdapter; 24 | import org.springframework.transaction.support.TransactionSynchronizationManager; 25 | 26 | /** 27 | * A {@link DataSource} for components that need to participate in a 28 | * Spring-managed transaction, where there might also be a JMS transaction with 29 | * a shared resource (the JDBC {@link Connection}). Internally the 30 | * {@link DataSource} proxies its {@link Connection} instances and masks off 31 | * calls to transactional and cleanup methods if a local transaction is already 32 | * underway. It also registers a synchronization with an ongoing transaction to 33 | * force a commit of a JMS transaction before the local 34 | * {@link DataSource} transaction. 35 | * 36 | * @author Dave Syer 37 | * 38 | */ 39 | public class JmsTransactionAwareDataSourceProxy extends TransactionAwareDataSourceProxy { 40 | 41 | private static final Log logger = LogFactory.getLog(JmsTransactionAwareDataSourceProxy.class); 42 | 43 | private static final String RESOURCE_KEY = JmsTransactionAwareDataSourceProxy.class.getName() 44 | + ".JMS_SYNCHRONIZATION"; 45 | 46 | private JmsTemplate jmsTemplate; 47 | 48 | /** 49 | * Public setter for a {@link JmsTemplate} that can be used internally to 50 | * synchronize a JMS {@link Session} with an ongoing transaction. This is 51 | * not really a {@link DataSource} concern, and should be factored out into 52 | * another utility class (it's only here for a demo to make a point about 53 | * how it works as quickly as possible). 54 | * 55 | * @param jmsTemplate 56 | */ 57 | public void setJmsTemplate(JmsTemplate jmsTemplate) { 58 | this.jmsTemplate = jmsTemplate; 59 | } 60 | 61 | @Override 62 | protected Connection getTransactionAwareConnectionProxy(DataSource targetDataSource) { 63 | // Deal with the JMS Session: 64 | maybeRegisterSynchronization(); 65 | return (Connection) Proxy.newProxyInstance(ConnectionProxy.class.getClassLoader(), 66 | new Class[] { ConnectionProxy.class }, new TransactionAwareInvocationHandler(targetDataSource)); 67 | } 68 | 69 | /** 70 | * Invocation handler that delegates close calls on JDBC Connections to 71 | * DataSourceUtils making it aware of thread-bound transactions. 72 | */ 73 | private class TransactionAwareInvocationHandler implements InvocationHandler { 74 | 75 | private final DataSource targetDataSource; 76 | 77 | private Connection target; 78 | 79 | private boolean closed = false; 80 | 81 | public TransactionAwareInvocationHandler(DataSource targetDataSource) { 82 | this.targetDataSource = targetDataSource; 83 | } 84 | 85 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 86 | // Invocation on ConnectionProxy interface coming in... 87 | 88 | if (method.getName().equals("equals")) { 89 | // Only considered as equal when proxies are identical. 90 | return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE); 91 | } 92 | else if (method.getName().equals("hashCode")) { 93 | // Use hashCode of Connection proxy. 94 | return new Integer(System.identityHashCode(proxy)); 95 | } 96 | else if (method.getName().equals("toString")) { 97 | // Allow for differentiating between the proxy and the raw 98 | // Connection. 99 | StringBuffer buf = new StringBuffer("Transaction-aware proxy for target Connection "); 100 | if (this.target != null) { 101 | buf.append("[").append(this.target.toString()).append("]"); 102 | } 103 | else { 104 | buf.append(" from DataSource [").append(this.targetDataSource).append("]"); 105 | } 106 | } 107 | else if (method.getName().equals("close")) { 108 | // Handle close method: only close if not within a transaction. 109 | DataSourceUtils.doReleaseConnection(this.target, this.targetDataSource); 110 | this.closed = true; 111 | return null; 112 | } 113 | else if (method.getName().equals("setAutoCommit")) { 114 | // Handle setAutoCommit method: only call if not within a 115 | // transaction. 116 | if (DataSourceUtils.isConnectionTransactional(this.target, this.targetDataSource)) { 117 | return null; 118 | } 119 | } 120 | else if (method.getName().equals("commit")) { 121 | logger.debug("Committing proxied connection."); 122 | if (DataSourceUtils.isConnectionTransactional(this.target, this.targetDataSource)) { 123 | logger.debug("Vetoed commit of proxied connection " 124 | + "(probably by a non-Spring aware component in a Spring transaction synchronization)."); 125 | return null; 126 | } 127 | } 128 | else if (method.getName().equals("rollback")) { 129 | logger.debug("Rolling back proxied connection."); 130 | if (DataSourceUtils.isConnectionTransactional(this.target, this.targetDataSource)) { 131 | logger.debug("Vetoed rollback of proxied connection " 132 | + "(probably by a non-Spring aware component in a Spring transaction synchronization)."); 133 | return null; 134 | } 135 | } 136 | 137 | if (this.target == null) { 138 | if (this.closed) { 139 | throw new SQLException("Connection handle already closed"); 140 | } 141 | if (shouldObtainFixedConnection(this.targetDataSource)) { 142 | this.target = DataSourceUtils.doGetConnection(this.targetDataSource); 143 | } 144 | } 145 | Connection actualTarget = this.target; 146 | if (actualTarget == null) { 147 | actualTarget = DataSourceUtils.doGetConnection(this.targetDataSource); 148 | } 149 | 150 | if (method.getName().equals("getTargetConnection")) { 151 | // Handle getTargetConnection method: return underlying 152 | // Connection. 153 | return actualTarget; 154 | } 155 | 156 | // Invoke method on target Connection. 157 | try { 158 | Object retVal = method.invoke(actualTarget, args); 159 | 160 | // If return value is a Statement, apply transaction timeout. 161 | // Applies to createStatement, prepareStatement, prepareCall. 162 | if (retVal instanceof Statement) { 163 | if (args != null && args.length == 1) { 164 | logger.debug("Statement prepared for: " + args[0]); 165 | } 166 | DataSourceUtils.applyTransactionTimeout((Statement) retVal, this.targetDataSource); 167 | } 168 | 169 | return retVal; 170 | } 171 | catch (InvocationTargetException ex) { 172 | throw ex.getTargetException(); 173 | } 174 | finally { 175 | if (actualTarget != this.target) { 176 | DataSourceUtils.doReleaseConnection(actualTarget, this.targetDataSource); 177 | } 178 | } 179 | } 180 | } 181 | 182 | /** 183 | * If a transaction is already in flight, register a synchronization to 184 | * commit the Session before the main transaction commits. 185 | */ 186 | private void maybeRegisterSynchronization() { 187 | if (TransactionSynchronizationManager.hasResource(RESOURCE_KEY) 188 | || !TransactionSynchronizationManager.isSynchronizationActive() || jmsTemplate == null) { 189 | return; 190 | } 191 | 192 | TransactionSynchronizationManager.bindResource(RESOURCE_KEY, Boolean.TRUE); 193 | 194 | logger.debug("Registering JMS SessionCallback for synchronization with local transaction"); 195 | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { 196 | 197 | @Override 198 | public void beforeCompletion() { 199 | TransactionSynchronizationManager.unbindResource(RESOURCE_KEY); 200 | } 201 | 202 | @Override 203 | public void beforeCommit(boolean readOnly) { 204 | 205 | jmsTemplate.execute(new SessionCallback() { 206 | public Object doInJms(final Session session) throws JMSException { 207 | try { 208 | logger.debug("Committing JMS Session"); 209 | session.commit(); 210 | } 211 | catch (JMSException e) { 212 | throw new SynchedLocalTransactionFailedException( 213 | "Failed to commit JMS Session in shared resource synchronization.", e); 214 | } 215 | return null; 216 | } 217 | }); 218 | 219 | } 220 | 221 | }); 222 | 223 | } 224 | 225 | } 226 | --------------------------------------------------------------------------------